|
|
|
![]() |
|
|||||||
|
||||||||
![]() |
| Thread Tools | Rate Thread | Display Modes |
|
#1
|
||||
|
||||
|
Hi all
If the filesystem wasn't weird enough, I have yet another piece (a rather large piece) of code to share with you. During this year I implemented a kind of Plug and Play interface for controllers. Essentially, it tries to mimic the behaviour of controllers on a PC. What I mean is, you can plug in a set of controllers into any port on the OI and without any recompiling your code, and your controllers will do the same thing they did before even if they weren't plugged in the same spot. But there is more, the code also detects whether buttons are pressed or released or are being held. It also provides a way to quickly switch between controller configurations for different drivers. So if I want to use an XBOX controller, but my friend wants to use 2 joysticks, the framework allows you to program in two different configurations and switch between them on the fly. Also, it contains a configurable analog noise filter and a configurable deadband. Of course, adding this framework to your project might be slightly difficult as it requires a few low level things to be done. I highly recommend you make a backup of your code before trying to add this to your project as we will be changing a lot of the "Do not change" sections. Warning: If you have data in your EEPROM it will be erased as the filesystem must be used with HAL. See my thread on the filesystem to learn how it works and how to use it. Prerequisites: 1. Kevin's Serial Port Code 2. Kevin's EEPROM Code Installation Guide: 1. Download the zip file below and unzip the files into a new folder. 2. Open your MPLAB project 3. Open ifi_aliases.h and delete all the #defines as listed in the Removed ifi_aliases.txt file given to you 4. Open ifi_default.h, and find the rx_data_record structure 5. Replace it with the structure found in the given Rx_Data_Record.txt file 6. Add all the other *.c and *.h files to your project 7. Open Timer Interrupt Handler.txt and copy the code into your InterruptHandlerLow(). Be sure to #include "timers.h" into the file containing you InterruptHandleLow() routine. 8. Remove your slow/fast loops, they are no longer needed and will break the new code. Get/Putdata are handled in the timer interrupt. See below for timing help. Configuring the Code: When the robot starts up, it has to initialize the HAL subsystem and it has to find a writable filesystem on the EEPROM. The following code will initalize the filesystem and HAL. Code:
#include "hal.h"
#include "filesystem.h"
#include "timers.h"
RETURN_CODE err;
err = FS_Init();
if( err == FS_ERROR_NO_FILESYSTEM ){
FS_Format(FALSE); /* Use TRUE instead of FALSE for a full format */
err = FS_Init();
if( err != FS_ERROR_SUCCESS ){
printf("FS format and initialize failed."); /* This should never happen */
}
}
Timer_Initialize();
HAL_Initialize();
if( (rxdata.oi_sw_bytes[0] | rxdata.oi_sw_bytes[1]) != NULL)
{ HAL_Reconfigure(5); }
By default HAL has builtin drivers for a joystick and an xbox controller. It also provides support for custom control boxes, but for that you will have to write your own driver for that. If you just use joysticks, the hard part is done, if you use the USB chicklet however, you must tell HAL your USB chicklet mappings. To do this, open xbox.c and find: Code:
/* Enables the use of the Analog aux bytes */ #define MAP_ANALOG_AUX /* USB Chicklet mappings of digital buttons */ #define MAP_DIGI_BIT_1 XBOX_BUTTON_X #define MAP_DIGI_BIT_2 XBOX_BUTTON_Y #define MAP_DIGI_BIT_3 XBOX_BUTTON_A #define MAP_DIGI_BIT_4 XBOX_BUTTON_B /* USB chicklet mappings of analog aux 1 byte */ #define MAP_AUX1_BIT_1 XBOX_SHOULDER_LEFT_1 #define MAP_AUX1_BIT_2 XBOX_SHOULDER_LEFT_2 #define MAP_AUX1_BIT_3 XBOX_SHOULDER_RIGHT_1 #define MAP_AUX1_BIT_4 XBOX_SHOULDER_RIGHT_2 /* USB Chicklet mappings of analog aux 2 byte */ #define MAP_AUX2_BIT_1 XBOX_PAD_UP #define MAP_AUX2_BIT_2 XBOX_PAD_DOWN #define MAP_AUX2_BIT_3 XBOX_PAD_LEFT #define MAP_AUX2_BIT_4 XBOX_PAD_RIGHT Code:
#define XBOX_BUTTON_LOGO CONTROLLER_TYPE_XBOX | 0x00 #define XBOX_BUTTON_X CONTROLLER_TYPE_XBOX | 0x01 #define XBOX_BUTTON_Y CONTROLLER_TYPE_XBOX | 0x02 #define XBOX_BUTTON_A CONTROLLER_TYPE_XBOX | 0x03 #define XBOX_BUTTON_B CONTROLLER_TYPE_XBOX | 0x04 #define XBOX_BUTTON_START CONTROLLER_TYPE_XBOX | 0x05 #define XBOX_BUTTON_BACK CONTROLLER_TYPE_XBOX | 0x06 #define XBOX_SHOULDER_LEFT_1 CONTROLLER_TYPE_XBOX | 0x07 #define XBOX_SHOULDER_LEFT_2 CONTROLLER_TYPE_XBOX | 0x08 #define XBOX_SHOULDER_RIGHT_1 CONTROLLER_TYPE_XBOX | 0x09 #define XBOX_SHOULDER_RIGHT_2 CONTROLLER_TYPE_XBOX | 0x0A #define XBOX_PAD_UP CONTROLLER_TYPE_XBOX | 0x0B #define XBOX_PAD_DOWN CONTROLLER_TYPE_XBOX | 0x0C #define XBOX_PAD_LEFT CONTROLLER_TYPE_XBOX | 0x0D #define XBOX_PAD_RIGHT CONTROLLER_TYPE_XBOX | 0x0E #define XBOX_POV_LEFT CONTROLLER_TYPE_XBOX | 0x0F #define MAP_AUX1_BIT_1 XBOX_SHOULDER_LEFT_1 to #define MAP_AUX1_BIT_1 XBOX_BUTTON_LOGO Your input drivers are now configured. The next thing you must do is assign tasks to your buttons. This is done in hal_out.c. This file contains all the output drivers. An output driver is essentially a controller configuration. If you wanted two configurations you'de have two output drivers. Three drivers are provided by default. They are: a null output driver that does nothing, HAL_Out_AAP, the driver we used during the competition, and HAL_Out_Analog, the analog output driver. To create your own output driver you must make a function with the following prototype: Code:
void HAL_Out_MyDriver(unsigned char exec, unsigned char subid){...}
To act on a certain button press, an if statement can be used to compare the exec byte to the defined buttons in joystick.h and xbox.h. Examples follow bellow: Code:
void HAL_Out_MyDriver(unsigned char exec, unsigned char subid){
/* Check if XBOX A button was pressed */
if( exec == (XBOX_BUTTON_A | PRESSED) ) { /* do stuff */ }
/* Check if 2nd XBOX controller B button was released */
if( subid == 1 ){
if ( exec == (XBOX_BUTTON_B | RELEASED) ) { /*do stuff */ }
}
/* Check if joystick hat is pointing up */
if ( exec == ( JOYSTICK_HAT_UP | PRESSED ) ) { /* do stuff */ }
}
Code:
const rom hal_output_driver drivers_out[STUDENT_DRIVERS] = { &HAL_Out_AAP };
Code:
const rom hal_output_driver drivers_out[2] = { &HAL_Out_AAP, &HAL_Out_MyDriver };
Code:
HAL_Load_Student_Driver(1); The last step is to call the HAL function everytime new data is sent to the controller. Ideally you'de call HAL() every 26ms, but I'de recommend 13ms for safety. You'll notice HAL() takes one argument, simply send NULL to it since that was used for our task scheduler code. Your function call should look like this: Code:
HAL(NULL); When you restart the robot the configuration will be read out of the EEPROM and you will never have to configure again unless you change your controllers around. To force a reconfiguration, press any button on any controller and reset the robot. You're now done! ![]() Timing Issues: As you noticed, this code requires our timer code to be used. This code will get rid of the need for slow/fast loops since it handles Get/Putdata for you. This may make timing things like HAL a little tricky. The solution is to use our dynamic timer support. Simply put, dynamic timers run off the main system timer and can have custom intervals set to them. A simple timing example is presented below for HAL. Code:
/* Done at initialization time */
/* Set the first timer to have a tick interval of 13 seconds */
Timer_Set_Interval(0, 13);
Timer_Reset(0);
/* done in main loop */
static unsigned long last_tick;
if ( last_tick < Timer_Ticks(0) ){
last_tick = Timer_Ticks(0);
HAL(NULL);
/* Code in here will only run every 13ms since the timer will only increase its tick count every 13ms */
}
Even more configuration Options: In hal.h, you are given 7 defines that let you enable/disable/tune the analog noise/deadband filters. These are: Code:
/* Deadband filter options */ #define ANALOG_DEADBAND /* Comment to disable */ #define ANALOG_DEADBAND_TOP 140 #define ANALOG_DEADBAND_BOTTOM 114 #define ANALOG_DEADBAND_MIDDLE 127 /* Noise filter options */ #define ANALOG_NOISE_FILTER /* Comment to disable */ #define ANALOG_NOISE_FILTER_POWER_X 4 #define ANALOG_NOISE_FILTER_POWER_Y 4 #define HAL_CONFIG_FILE 0 the proper file number. HAL will choose a default output drive at startup, this can be changed by changing: #define HAL_DEFAULT_DRIVER 0 to the correct output driver. Other defines that are important, but work well with the defaults are: Code:
/* Default time to wait for controller responses (seconds) */ #define HAL_CONFIG_TIMEOUT 5 /* Time to wait before config starts */ #define HAL_WAIT_ENTER_CONFIG 2000 /* Time to wait for a button to be released to move on */ #define HAL_WAIT_BUTTON_RELEASE 1000 /* HAL polling time, poll at this time (in ms) if you are using timer based get/putdata... otherwise call HAL process in slow loop */ #define HAL_POLLING_INTERVAL 13 P.S. You'll notice I included our modified ifi_* files.. Use them at your own risk, they've been heavily modified to save on codespace and to make our systems work better for us. P.P.S. I apologize if I forgot to tell you something important. Last edited by dragoonex : 04-08-2008 at 17:14. |
|
#2
|
|||||
|
|||||
|
Re: Working Controller Plug and Play
Great Job
|
|
#3
|
|||||
|
|||||
|
Re: Working Controller Plug and Play
That is really impressive; I have never even contemplated doing that.
|
![]() |
| Thread Tools | |
| Display Modes | Rate This Thread |
|
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| 2007 FIRST Regionals Play by Play Announcer/Emcee Edition | Freddy Schurr | Regional Competitions | 27 | 04-04-2007 10:39 |
| pic: Aim High and Triple Play? | roboticsguy1988 | Extra Discussion | 3 | 14-08-2006 21:07 |
| Vex controller - outputs not working | jrecht | FIRST Tech Challenge | 2 | 06-02-2006 23:26 |
| kickoffs and play days | chocolateluvrlr | General Forum | 5 | 17-10-2004 10:49 |
| Robot Controller Not Working !?!?! | Team1425 | Electrical | 3 | 24-01-2004 20:52 |