
Adjusts the great pinscape controller to work with a cheap linear potentiometer instead of the expensive CCD array
Fork of Pinscape_Controller by
Revision 2:c174f9ee414a, committed 2014-07-22
- Comitter:
- mjr
- Date:
- Tue Jul 22 04:33:47 2014 +0000
- Parent:
- 1:d913e0afb2ac
- Child:
- 3:3514575d4f86
- Commit message:
- Before change to ISR for accelerometer
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CRC32/crc32.cpp Tue Jul 22 04:33:47 2014 +0000 @@ -0,0 +1,65 @@ +#include "crc32.h" + +#define CRC32_POLYNOMIAL 0xEDB88320L + +void CRC32Value(unsigned long &CRC, unsigned char c) +{ + ///////////////////////////////////////////////////////////////////////////////////// + //CRC must be initialized as zero + //c is a character from the sequence that is used to form the CRC + //this code is a modification of the code from the Novatel OEM615 specification + ///////////////////////////////////////////////////////////////////////////////////// + unsigned long ulTemp1 = ( CRC >> 8 ) & 0x00FFFFFFL; + unsigned long ulCRC = ((int) CRC ^ c ) & 0xff ; + for (int j = 8 ; j > 0; j-- ) + { + if ( ulCRC & 1 ) + { + ulCRC = ( ulCRC >> 1 ) ^ CRC32_POLYNOMIAL; + } + else + { + ulCRC >>= 1; + } + } + CRC = ulTemp1 ^ ulCRC; +} + +/* -------------------------------------------------------------------------- +Calculates the CRC-32 of a block of data all at once +//the CRC is from the complete message (header plus data) +//but excluding (of course) the CRC at the end +-------------------------------------------------------------------------- */ +unsigned long CRC32(const void *data, int len) +{ + ////////////////////////////////////////////////////////////////////// + //the below code tests the CRC32Value procedure used in a markov form + ////////////////////////////////////////////////////////////////////// + unsigned long CRC = 0; + const unsigned char *p = (const unsigned char *)data; + for (int i = 0 ; i < len ; i++) + CRC32Value(CRC, *p++); + + return CRC; +} + +/* +unsigned long CalculateBlockCRC32( + unsigned long ulCount, + unsigned char *ucBuffer ) +{ +//////////////////////////////////////////// +//original code from the OEM615 manual +//////////////////////////////////////////// + unsigned long ulTemp1; + unsigned long ulTemp2; + unsigned long ulCRC = 0; + while ( ulCount-- != 0 ) + { + ulTemp1 = ( ulCRC >> 8 ) & 0x00FFFFFFL; + ulTemp2 = CRC32Value( ((int) ulCRC ^ *ucBuffer++ ) & 0xff ); + ulCRC = ulTemp1 ^ ulTemp2; + } + return( ulCRC ); +} +*/ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CRC32/crc32.h Tue Jul 22 04:33:47 2014 +0000 @@ -0,0 +1,10 @@ + +#ifndef CRC32_H +#define CRC32_H + +void CRC32Value(unsigned long &CRC, unsigned char c); +unsigned long CRC32(const void *data, int len); + +#endif + + \ No newline at end of file
--- a/FreescaleIAP.cpp Wed Jul 16 23:33:12 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,190 +0,0 @@ -#include "FreescaleIAP.h" - -//#define IAPDEBUG - -enum FCMD { - Read1s = 0x01, - ProgramCheck = 0x02, - ReadResource = 0x03, - ProgramLongword = 0x06, - EraseSector = 0x09, - Read1sBlock = 0x40, - ReadOnce = 0x41, - ProgramOnce = 0x43, - EraseAll = 0x44, - VerifyBackdoor = 0x45 - }; - -inline void run_command(void); -bool check_boundary(int address, unsigned int length); -bool check_align(int address); -IAPCode verify_erased(int address, unsigned int length); -IAPCode check_error(void); -IAPCode program_word(int address, char *data);IAPCode program_word(int address, char *data); - -IAPCode erase_sector(int address) { - #ifdef IAPDEBUG - printf("IAP: Erasing at %x\r\n", address); - #endif - if (check_align(address)) - return AlignError; - - //Setup command - FTFA->FCCOB0 = EraseSector; - FTFA->FCCOB1 = (address >> 16) & 0xFF; - FTFA->FCCOB2 = (address >> 8) & 0xFF; - FTFA->FCCOB3 = address & 0xFF; - - run_command(); - - return check_error(); -} - -IAPCode program_flash(int address, char *data, unsigned int length) { - #ifdef IAPDEBUG - printf("IAP: Programming flash at %x with length %d\r\n", address, length); - #endif - if (check_align(address)) - return AlignError; - - IAPCode eraseCheck = verify_erased(address, length); - if (eraseCheck != Success) - return eraseCheck; - - IAPCode progResult; - for (int i = 0; i < length; i+=4) { - progResult = program_word(address + i, data + i); - if (progResult != Success) - return progResult; - } - - return Success; -} - -uint32_t flash_size(void) { - uint32_t retval = (SIM->FCFG2 & 0x7F000000u) >> (24-13); - if (SIM->FCFG2 & (1<<23)) //Possible second flash bank - retval += (SIM->FCFG2 & 0x007F0000u) >> (16-13); - return retval; -} - -IAPCode program_word(int address, char *data) { - #ifdef IAPDEBUG - printf("IAP: Programming word at %x, %d - %d - %d - %d\r\n", address, data[0], data[1], data[2], data[3]); - #endif - if (check_align(address)) - return AlignError; - - //Setup command - FTFA->FCCOB0 = ProgramLongword; - FTFA->FCCOB1 = (address >> 16) & 0xFF; - FTFA->FCCOB2 = (address >> 8) & 0xFF; - FTFA->FCCOB3 = address & 0xFF; - FTFA->FCCOB4 = data[3]; - FTFA->FCCOB5 = data[2]; - FTFA->FCCOB6 = data[1]; - FTFA->FCCOB7 = data[0]; - - run_command(); - - return check_error(); -} - -/* Clear possible flags which are set, run command, wait until done */ -inline void run_command(void) { - //Clear possible old errors, start command, wait until done - FTFA->FSTAT = FTFA_FSTAT_FPVIOL_MASK | FTFA_FSTAT_ACCERR_MASK | FTFA_FSTAT_RDCOLERR_MASK; - FTFA->FSTAT = FTFA_FSTAT_CCIF_MASK; - while (!(FTFA->FSTAT & FTFA_FSTAT_CCIF_MASK)); -} - - - -/* Check if no flash boundary is violated - Returns true on violation */ -bool check_boundary(int address, unsigned int length) { - int temp = (address+length - 1) / SECTOR_SIZE; - address /= SECTOR_SIZE; - bool retval = (address != temp); - #ifdef IAPDEBUG - if (retval) - printf("IAP: Boundary violation\r\n"); - #endif - return retval; -} - -/* Check if address is correctly aligned - Returns true on violation */ -bool check_align(int address) { - bool retval = address & 0x03; - #ifdef IAPDEBUG - if (retval) - printf("IAP: Alignment violation\r\n"); - #endif - return retval; -} - -/* Check if an area of flash memory is erased - Returns error code or Success (in case of fully erased) */ -IAPCode verify_erased(int address, unsigned int length) { - #ifdef IAPDEBUG - printf("IAP: Verify erased at %x with length %d\r\n", address, length); - #endif - - if (check_align(address)) - return AlignError; - - //Setup command - FTFA->FCCOB0 = Read1s; - FTFA->FCCOB1 = (address >> 16) & 0xFF; - FTFA->FCCOB2 = (address >> 8) & 0xFF; - FTFA->FCCOB3 = address & 0xFF; - FTFA->FCCOB4 = (length >> 10) & 0xFF; - FTFA->FCCOB5 = (length >> 2) & 0xFF; - FTFA->FCCOB6 = 0; - - run_command(); - - IAPCode retval = check_error(); - if (retval == RuntimeError) { - #ifdef IAPDEBUG - printf("IAP: Flash was not erased\r\n"); - #endif - return EraseError; - } - return retval; - -} - -/* Check if an error occured - Returns error code or Success*/ -IAPCode check_error(void) { - if (FTFA->FSTAT & FTFA_FSTAT_FPVIOL_MASK) { - #ifdef IAPDEBUG - printf("IAP: Protection violation\r\n"); - #endif - return ProtectionError; - } - if (FTFA->FSTAT & FTFA_FSTAT_ACCERR_MASK) { - #ifdef IAPDEBUG - printf("IAP: Flash access error\r\n"); - #endif - return AccessError; - } - if (FTFA->FSTAT & FTFA_FSTAT_RDCOLERR_MASK) { - #ifdef IAPDEBUG - printf("IAP: Collision error\r\n"); - #endif - return CollisionError; - } - if (FTFA->FSTAT & FTFA_FSTAT_MGSTAT0_MASK) { - #ifdef IAPDEBUG - printf("IAP: Runtime error\r\n"); - #endif - return RuntimeError; - } - #ifdef IAPDEBUG - printf("IAP: No error reported\r\n"); - #endif - return Success; -} \ No newline at end of file
--- a/FreescaleIAP.h Wed Jul 16 23:33:12 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,87 +0,0 @@ -/* - * Freescale FTFA Flash Memory programmer - * - * Sample usage: - -#include "mbed.h" -#include "FreescaleIAP.h" - -int main() { - int address = flash_size() - SECTOR_SIZE; //Write in last sector - - int *data = (int*)address; - printf("Starting\r\n"); - erase_sector(address); - int numbers[10] = {0, 1, 10, 100, 1000, 10000, 1000000, 10000000, 100000000, 1000000000}; - program_flash(address, (char*)&numbers, 40); //10 integers of 4 bytes each: 40 bytes length - printf("Resulting flash: \r\n"); - for (int i = 0; i<10; i++) - printf("%d\r\n", data[i]); - - printf("Done\r\n\n"); - - - while (true) { - } -} - -*/ - -#ifndef FREESCALEIAP_H -#define FREESCALEIAP_H - -#include "mbed.h" - -#ifdef TARGET_KLXX -#define SECTOR_SIZE 1024 -#elif TARGET_K20D5M -#define SECTOR_SIZE 2048 -#elif TARGET_K64F -#define SECTOR_SIZE 4096 -#else -#define SECTOR_SIZE 1024 -#endif - -enum IAPCode { - BoundaryError = -99, //Commands may not span several sectors - AlignError, //Data must be aligned on longword (two LSBs zero) - ProtectionError, //Flash sector is protected - AccessError, //Something went wrong - CollisionError, //During writing something tried to flash which was written to - LengthError, //The length must be multiples of 4 - RuntimeError, - EraseError, //The flash was not erased before writing to it - Success = 0 - }; - -/** Erase a flash sector - * - * The size erased depends on the used device - * - * @param address address in the sector which needs to be erased - * @param return Success if no errors were encountered, otherwise one of the error states - */ -IAPCode erase_sector(int address); - -/** Program flash - * - * Before programming the used area needs to be erased. The erase state is checked - * before programming, and will return an error if not erased. - * - * @param address starting address where the data needs to be programmed (must be longword alligned: two LSBs must be zero) - * @param data pointer to array with the data to program - * @param length number of bytes to program (must be a multiple of 4) - * @param return Success if no errors were encountered, otherwise one of the error states - */ -IAPCode program_flash(int address, char *data, unsigned int length); - -/** - * Returns size of flash memory - * - * This is the first address which is not flash - * - * @param return length of flash memory in bytes - */ -uint32_t flash_size(void); - -#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FreescaleIAP/FreescaleIAP.cpp Tue Jul 22 04:33:47 2014 +0000 @@ -0,0 +1,205 @@ +#include "FreescaleIAP.h" + +//#define IAPDEBUG + +enum FCMD { + Read1s = 0x01, + ProgramCheck = 0x02, + ReadResource = 0x03, + ProgramLongword = 0x06, + EraseSector = 0x09, + Read1sBlock = 0x40, + ReadOnce = 0x41, + ProgramOnce = 0x43, + EraseAll = 0x44, + VerifyBackdoor = 0x45 +}; + +static inline void run_command(FTFA_Type *); +bool check_boundary(int address, unsigned int length); +bool check_align(int address); +IAPCode check_error(void); + +FreescaleIAP::FreescaleIAP() +{ +} + +FreescaleIAP::~FreescaleIAP() +{ +} + +// execute an FTFA command +static inline void run_command(FTFA_Type *ftfa) +{ + // disable interupts + __disable_irq(); + + // Clear possible old errors, start command, wait until done + ftfa->FSTAT = FTFA_FSTAT_FPVIOL_MASK | FTFA_FSTAT_ACCERR_MASK | FTFA_FSTAT_RDCOLERR_MASK; + ftfa->FSTAT = FTFA_FSTAT_CCIF_MASK; + while (!(ftfa->FSTAT & FTFA_FSTAT_CCIF_MASK)); + + // re-enable interrupts + __enable_irq(); +} + + +IAPCode FreescaleIAP::erase_sector(int address) { + #ifdef IAPDEBUG + printf("IAP: Erasing at %x\r\n", address); + #endif + if (check_align(address)) + return AlignError; + + //Setup command + FTFA->FCCOB0 = EraseSector; + FTFA->FCCOB1 = (address >> 16) & 0xFF; + FTFA->FCCOB2 = (address >> 8) & 0xFF; + FTFA->FCCOB3 = address & 0xFF; + + run_command(FTFA); + + return check_error(); +} + +IAPCode FreescaleIAP::program_flash(int address, const void *vp, unsigned int length) { + + const char *data = (const char *)vp; + + #ifdef IAPDEBUG + printf("IAP: Programming flash at %x with length %d\r\n", address, length); + #endif + if (check_align(address)) + return AlignError; + + IAPCode eraseCheck = verify_erased(address, length); + if (eraseCheck != Success) + return eraseCheck; + + IAPCode progResult; + for (int i = 0; i < length; i+=4) { + progResult = program_word(address + i, data + i); + if (progResult != Success) + return progResult; + } + + return Success; +} + +uint32_t FreescaleIAP::flash_size(void) { + uint32_t retval = (SIM->FCFG2 & 0x7F000000u) >> (24-13); + if (SIM->FCFG2 & (1<<23)) //Possible second flash bank + retval += (SIM->FCFG2 & 0x007F0000u) >> (16-13); + return retval; +} + +IAPCode FreescaleIAP::program_word(int address, const char *data) { + #ifdef IAPDEBUG + printf("IAP: Programming word at %x, %d - %d - %d - %d\r\n", address, data[0], data[1], data[2], data[3]); + #endif + if (check_align(address)) + return AlignError; + + //Setup command + FTFA->FCCOB0 = ProgramLongword; + FTFA->FCCOB1 = (address >> 16) & 0xFF; + FTFA->FCCOB2 = (address >> 8) & 0xFF; + FTFA->FCCOB3 = address & 0xFF; + FTFA->FCCOB4 = data[3]; + FTFA->FCCOB5 = data[2]; + FTFA->FCCOB6 = data[1]; + FTFA->FCCOB7 = data[0]; + + run_command(FTFA); + + return check_error(); +} + +/* Check if no flash boundary is violated + Returns true on violation */ +bool check_boundary(int address, unsigned int length) { + int temp = (address+length - 1) / SECTOR_SIZE; + address /= SECTOR_SIZE; + bool retval = (address != temp); + #ifdef IAPDEBUG + if (retval) + printf("IAP: Boundary violation\r\n"); + #endif + return retval; +} + +/* Check if address is correctly aligned + Returns true on violation */ +bool check_align(int address) { + bool retval = address & 0x03; + #ifdef IAPDEBUG + if (retval) + printf("IAP: Alignment violation\r\n"); + #endif + return retval; +} + +/* Check if an area of flash memory is erased + Returns error code or Success (in case of fully erased) */ +IAPCode FreescaleIAP::verify_erased(int address, unsigned int length) { + #ifdef IAPDEBUG + printf("IAP: Verify erased at %x with length %d\r\n", address, length); + #endif + + if (check_align(address)) + return AlignError; + + //Setup command + FTFA->FCCOB0 = Read1s; + FTFA->FCCOB1 = (address >> 16) & 0xFF; + FTFA->FCCOB2 = (address >> 8) & 0xFF; + FTFA->FCCOB3 = address & 0xFF; + FTFA->FCCOB4 = (length >> 10) & 0xFF; + FTFA->FCCOB5 = (length >> 2) & 0xFF; + FTFA->FCCOB6 = 0; + + run_command(FTFA); + + IAPCode retval = check_error(); + if (retval == RuntimeError) { + #ifdef IAPDEBUG + printf("IAP: Flash was not erased\r\n"); + #endif + return EraseError; + } + return retval; + +} + +/* Check if an error occured + Returns error code or Success*/ +IAPCode check_error(void) { + if (FTFA->FSTAT & FTFA_FSTAT_FPVIOL_MASK) { + #ifdef IAPDEBUG + printf("IAP: Protection violation\r\n"); + #endif + return ProtectionError; + } + if (FTFA->FSTAT & FTFA_FSTAT_ACCERR_MASK) { + #ifdef IAPDEBUG + printf("IAP: Flash access error\r\n"); + #endif + return AccessError; + } + if (FTFA->FSTAT & FTFA_FSTAT_RDCOLERR_MASK) { + #ifdef IAPDEBUG + printf("IAP: Collision error\r\n"); + #endif + return CollisionError; + } + if (FTFA->FSTAT & FTFA_FSTAT_MGSTAT0_MASK) { + #ifdef IAPDEBUG + printf("IAP: Runtime error\r\n"); + #endif + return RuntimeError; + } + #ifdef IAPDEBUG + printf("IAP: No error reported\r\n"); + #endif + return Success; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FreescaleIAP/FreescaleIAP.h Tue Jul 22 04:33:47 2014 +0000 @@ -0,0 +1,102 @@ +/* + * Freescale FTFA Flash Memory programmer + * + * Sample usage: + +#include "mbed.h" +#include "FreescaleIAP.h" + +int main() { + int address = flash_size() - SECTOR_SIZE; //Write in last sector + + int *data = (int*)address; + printf("Starting\r\n"); + erase_sector(address); + int numbers[10] = {0, 1, 10, 100, 1000, 10000, 1000000, 10000000, 100000000, 1000000000}; + program_flash(address, (char*)&numbers, 40); //10 integers of 4 bytes each: 40 bytes length + printf("Resulting flash: \r\n"); + for (int i = 0; i<10; i++) + printf("%d\r\n", data[i]); + + printf("Done\r\n\n"); + + + while (true) { + } +} + +*/ + +#ifndef FREESCALEIAP_H +#define FREESCALEIAP_H + +#include "mbed.h" + +#ifdef TARGET_KLXX +#define SECTOR_SIZE 1024 +#elif TARGET_K20D5M +#define SECTOR_SIZE 2048 +#elif TARGET_K64F +#define SECTOR_SIZE 4096 +#else +#define SECTOR_SIZE 1024 +#endif + +enum IAPCode { + BoundaryError = -99, //Commands may not span several sectors + AlignError, //Data must be aligned on longword (two LSBs zero) + ProtectionError, //Flash sector is protected + AccessError, //Something went wrong + CollisionError, //During writing something tried to flash which was written to + LengthError, //The length must be multiples of 4 + RuntimeError, + EraseError, //The flash was not erased before writing to it + Success = 0 +}; + + +class FreescaleIAP +{ +public: + FreescaleIAP(); + ~FreescaleIAP(); + + /** Erase a flash sector + * + * The size erased depends on the used device + * + * @param address address in the sector which needs to be erased + * @param return Success if no errors were encountered, otherwise one of the error states + */ + IAPCode erase_sector(int address); + + /** Program flash + * + * Before programming the used area needs to be erased. The erase state is checked + * before programming, and will return an error if not erased. + * + * @param address starting address where the data needs to be programmed (must be longword alligned: two LSBs must be zero) + * @param data pointer to array with the data to program + * @param length number of bytes to program (must be a multiple of 4) + * @param return Success if no errors were encountered, otherwise one of the error states + */ + IAPCode program_flash(int address, const void *data, unsigned int length); + + /** + * Returns size of flash memory + * + * This is the first address which is not flash + * + * @param return length of flash memory in bytes + */ + uint32_t flash_size(void); + +private: + // program a word of flash + IAPCode program_word(int address, const char *data); + + // verify that a flash area has been erased + IAPCode verify_erased(int address, unsigned int length); +}; + +#endif
--- a/MMA8451Q/MMA8451Q.cpp Wed Jul 16 23:33:12 2014 +0000 +++ b/MMA8451Q/MMA8451Q.cpp Tue Jul 22 04:33:47 2014 +0000 @@ -79,10 +79,9 @@ uint8_t d4[2] = {REG_CTRL_REG_2, (d3[0] & ~MODS_MASK) | MODS1_MASK}; writeRegs(d4, 2); - // set 50 Hz mode + // set 100 Hz mode uint8_t d5[1]; readRegs(REG_CTRL_REG_1, d5, 1); - uint8_t d6[2] = {REG_CTRL_REG_1, (d5[0] & ~DR_MASK) | DR_100_HZ}; writeRegs(d6, 2);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TSL1410R/tsl1410r.h Tue Jul 22 04:33:47 2014 +0000 @@ -0,0 +1,55 @@ +/* + * TSL1410R interface class. + * + * This provides a high-level interface for the Taos TSL1410R linear CCD array sensor. + */ + + #include "mbed.h" + + #ifndef TSL1410R_H + #define TSL1410R_H + +class TSL1410R +{ +public: + // set up with the two DigitalOut ports (SI and clock), and the + // analog in port for reading the currently selected pixel value + TSL1410R(PinName siPort, PinName clockPort, PinName aoPort); + + // Read the pixels. Fills in pix[] with the pixel values, scaled 0-0xffff. + // n is the number of pixels to read; if this is less than the physical + // array size (npix), we'll read every mth pixel, where m = npix/n. E.g., + // if you want 640 pixels out of 1280 on the sensor, we'll read every + // other pixel. If you want 320, we'll read every fourth pixel. + // + // We clock an SI pulse at the beginning of the read. This starts the + // next integration cycle: the pixel array will reset on the SI, and + // the integration starts 18 clocks later. So by the time this returns, + // the next sample will have been integrating for npix-18 clocks. In + // many cases this is enough time to allow immediately reading the next + // sample; if more integration time is required, the caller can simply + // sleep/spin for the desired additional time, or can do other work that + // takes the desired additional time. + // + // If the caller has other work to tend to that takes longer than the + // desired maximum integration time, it can call clear() to clock out + // the current pixels and start a fresh integration cycle. + void read(uint16_t *pix, int n); + + // Clock through all pixels to clear the array. Pulses SI at the + // beginning of the operation, which starts a new integration cycle. + // The caller can thus immediately call read() to read the pixels + // integrated while the clear() was taking place. + void clear(); + + // number of pixels in the array + static const int nPix = 1280; + + +private: + DigitalOut si; + DigitalOut clock; + AnalogIn ao; +}; + +#endif /* TSL1410R_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TSL1410R/tsl410r.cpp Tue Jul 22 04:33:47 2014 +0000 @@ -0,0 +1,58 @@ +#include "mbed.h" +#include "tsl1410r.h" + +TSL1410R::TSL1410R(PinName siPort, PinName clockPort, PinName aoPort) + : si(siPort), clock(clockPort), ao(aoPort) +{ + // clear out power-on noise by clocking through all pixels twice + clear(); + clear(); +} + +void TSL1410R::clear() +{ + // clock in an SI pulse + si = 1; + clock = 1; + clock = 0; + si = 0; + + // clock out all pixels + for (int i = 0 ; i < nPix + 1 ; ++i) { + clock = 1; + clock = 0; + } +} + +void TSL1410R::read(uint16_t *pix, int n) +{ + // Start the next integration cycle by pulsing SI and one clock. + si = 1; + clock = 1; + clock = 0; + si = 0; + + // figure how many pixels to skip on each read + int skip = nPix/n - 1; + + // read the pixels + for (int src = 0, dst = 0 ; src < nPix ; ++src) + { + // read this pixel + pix[dst++] = ao.read_u16(); + + // clock in the next pixel + clock = 1; + clock = 0; + + // clock skipped pixels + for (int i = 0 ; i < skip ; ++i, ++src) { + clock = 1; + clock = 0; + } + } + + // clock out one extra pixel to leave A1 in the high-Z state + clock = 1; + clock = 0; +}
--- a/USBJoystick.cpp Wed Jul 16 23:33:12 2014 +0000 +++ b/USBJoystick.cpp Tue Jul 22 04:33:47 2014 +0000 @@ -42,7 +42,7 @@ report.data[4] = _z & 0xff; report.length = 5; - return send(&report); + return sendNB(&report); } bool USBJoystick::move(int16_t x, int16_t y) {
--- a/main.cpp Wed Jul 16 23:33:12 2014 +0000 +++ b/main.cpp Tue Jul 22 04:33:47 2014 +0000 @@ -3,6 +3,31 @@ #include "MMA8451Q.h" #include "tsl1410r.h" #include "FreescaleIAP.h" +#include "crc32.h" + +// customization of the joystick class to expose connect/suspend status +class MyUSBJoystick: public USBJoystick +{ +public: + MyUSBJoystick(uint16_t vendor_id, uint16_t product_id, uint16_t product_release) + : USBJoystick(vendor_id, product_id, product_release) + { + connected_ = false; + suspended_ = false; + } + + int isConnected() const { return connected_; } + int isSuspended() const { return suspended_; } + +protected: + virtual void connectStateChanged(unsigned int connected) + { connected_ = connected; } + virtual void suspendStateChanged(unsigned int suspended) + { suspended_ = suspended; } + + int connected_; + int suspended_; +}; // on-board RGB LED elements - we use these for diagnostics PwmOut led1(LED1), led2(LED2), led3(LED3); @@ -62,6 +87,36 @@ } }; +// Non-volatile memory structure. We store persistent a small +// amount of persistent data in flash memory to retain calibration +// data between sessions. +struct NVM +{ + // checksum - we use this to determine if the flash record + // has been initialized + uint32_t checksum; + + // signature value + static const uint32_t SIGNATURE = 0x4D4A522A; + static const uint16_t VERSION = 0x0002; + + // stored data (excluding the checksum) + struct + { + // signature and version - further verification that we have valid + // initialized data + uint32_t sig; + uint16_t vsn; + + // direction - 0 means unknown, 1 means bright end is pixel 0, 2 means reversed + uint8_t dir; + + // plunger calibration min and max + int plungerMin; + int plungerMax; + } d; +}; + int main(void) { // turn off our on-board indicator LED @@ -69,9 +124,42 @@ led2 = 1; led3 = 1; - // plunger calibration data - const int npix = 320; - int plungerMin = 0, plungerMax = npix; + // set up a flash memory controller + FreescaleIAP iap; + + // use the last sector of flash for our non-volatile memory structure + int flash_addr = (iap.flash_size() - SECTOR_SIZE); + NVM *flash = (NVM *)flash_addr; + NVM cfg; + + // check for valid flash + bool flash_valid = (flash->d.sig == flash->SIGNATURE + && flash->d.vsn == flash->VERSION + && flash->checksum == CRC32(&flash->d, sizeof(flash->d))); + + // Number of pixels we read from the sensor on each frame. This can be + // less than the physical pixel count if desired; we'll read every nth + // piexl if so. E.g., with a 1280-pixel physical sensor, if npix is 320, + // we'll read every 4th pixel. VP doesn't seem to have very high + // resolution internally for the plunger, so it's probably not necessary + // to use the full resolution of the sensor - about 160 pixels seems + // perfectly adequate. We can read the sensor faster (and thus provide + // a higher refresh rate) if we read fewer pixels in each frame. + const int npix = 160; + + // if the flash is valid, load it; otherwise initialize to defaults + if (flash_valid) { + memcpy(&cfg, flash, sizeof(cfg)); + printf("Flash restored: plunger min=%d, max=%d\r\n", + cfg.d.plungerMin, cfg.d.plungerMax); + } + else { + printf("Factory reset\r\n"); + cfg.d.sig = cfg.SIGNATURE; + cfg.d.vsn = cfg.VERSION; + cfg.d.plungerMin = 0; + cfg.d.plungerMax = npix; + } // plunger calibration button debounce timer Timer calBtnTimer; @@ -97,32 +185,20 @@ acTimer.start(); int t0ac = acTimer.read_ms(); - // set up a timer for reading the plunger sensor - Timer ccdTimer; - ccdTimer.start(); - int t0ccd = ccdTimer.read_ms(); - -#if 0 - // DEBUG - Timer ccdDbgTimer; - ccdDbgTimer.start(); - int t0ccdDbg = ccdDbgTimer.read_ms(); -#endif - // Create the joystick USB client. Light the on-board indicator LED // red while connecting, and change to green after we connect. - led1 = 0.75; - USBJoystick js(0xFAFA, 0x00F7, 0x0001); + led1 = 0; + MyUSBJoystick js(0xFAFA, 0x00F7, 0x0001); led1 = 1; - led2 = 0.75; - + led2 = 0; + // create the accelerometer object const int MMA8451_I2C_ADDRESS = (0x1d<<1); MMA8451Q accel(PTE25, PTE24, MMA8451_I2C_ADDRESS); // create the CCD array object TSL1410R ccd(PTE20, PTE21, PTB0); - + // recent accelerometer readings, for auto centering int iAccPrv = 0, nAccPrv = 0; const int maxAccPrv = 5; @@ -130,9 +206,12 @@ // last accelerometer report, in mouse coordinates int x = 127, y = 127, z = 0; - + // raw accelerator centerpoint, on the unit interval (-1.0 .. +1.0) float xCenter = 0.0, yCenter = 0.0; + + // start the first CCD integration cycle + ccd.clear(); // we're all set up - now just loop, processing sensor reports and // host requests @@ -219,21 +298,49 @@ calBtnState = 3; // reset the calibration limits - plungerMax = 0; - plungerMin = npix; + cfg.d.plungerMax = 0; + cfg.d.plungerMin = npix; } break; + + case 3: + // Already in calibration mode - pushing the button in this + // state doesn't change the current state, but we won't leave + // this state as long as it's held down. We can simply do + // nothing here. + break; } } else { - // Button released. If we're not already in calibration mode, - // reset the button state. Once calibration mode starts, it sticks - // until the calibration time elapses. - if (calBtnState != 3) + // Button released. If we're in calibration mode, and + // the calibration time has elapsed, end the calibration + // and save the results to flash. + // + // Otherwise, return to the base state without saving anything. + // If the button is released before we make it to calibration + // mode, it simply cancels the attempt. + if (calBtnState == 3 + && calBtnTimer.read_ms() - calBtnDownTime > 17500) + { + // exit calibration mode calBtnState = 0; - else if (calBtnTimer.read_ms() - calBtnDownTime > 32500) + + // Save the current configuration state to flash, so that it + // will be preserved through power off. Update the checksum + // first so that we recognize the flash record as valid. + cfg.checksum = CRC32(&cfg.d, sizeof(cfg.d)); + iap.erase_sector(flash_addr); + iap.program_flash(flash_addr, &cfg, sizeof(cfg)); + + // the flash state is now valid + flash_valid = true; + } + else if (calBtnState != 3) + { + // didn't make it to calibration mode - cancel the operation calBtnState = 0; + } } // light/flash the calibration button light, if applicable @@ -258,89 +365,86 @@ if (calBtnLit != newCalBtnLit) { calBtnLit = newCalBtnLit; - calBtnLed = (calBtnLit ? 1 : 0); + if (calBtnLit) { + calBtnLed = 1; + led1 = 0; + led2 = 0; + led3 = 1; + } + else { + calBtnLed = 0; + led1 = 1; + led2 = 1; + led3 = 0; + } } // read the plunger sensor int znew = z; - /* if (ccdTimer.read_ms() - t0ccd > 33) */ + uint16_t pix[npix]; + ccd.read(pix, npix); + + // get the average brightness at each end of the sensor + long avg1 = (long(pix[0]) + long(pix[1]) + long(pix[2]) + long(pix[3]) + long(pix[4]))/5; + long avg2 = (long(pix[npix-1]) + long(pix[npix-2]) + long(pix[npix-3]) + long(pix[npix-4]) + long(pix[npix-5]))/5; + + // figure the midpoint in the brightness; multiply by 3 so that we can + // compare sums of three pixels at a time to smooth out noise + long midpt = (avg1 + avg2)/2 * 3; + + // Work from the bright end to the dark end. VP interprets the + // Z axis value as the amount the plunger is pulled: the minimum + // is the rest position, the maximum is fully pulled. So we + // essentially want to report how much of the sensor is lit, + // since this increases as the plunger is pulled back. + int si = 1, di = 1; + if (avg1 < avg2) + si = npix - 2, di = -1; + + // scan for the midpoint + uint16_t *pixp = pix + si; + for (int n = 1 ; n < npix - 1 ; ++n, pixp += di) { - // read the sensor at reduced resolution - uint16_t pix[npix]; - ccd.read(pix, npix, 0); - -#if 0 - // debug - send samples every 5 seconds - if (ccdDbgTimer.read_ms() - t0ccdDbg > 5000) - { - for (int i = 0 ; i < npix ; ++i) - printf("%x ", pix[i]); - printf("\r\n\r\n"); - - ccdDbgTimer.reset(); - t0ccdDbg = ccdDbgTimer.read_ms(); - } -#endif - - // check which end is the brighter - this is the "tip" end - // of the plunger - long avg1 = (long(pix[0]) + long(pix[1]) + long(pix[2]) + long(pix[3]) + long(pix[4]))/5; - long avg2 = (long(pix[npix-1]) + long(pix[npix-2]) + long(pix[npix-3]) + long(pix[npix-4]) + long(pix[npix-5]))/5; - - // figure the midpoint in the brightness - long midpt = (avg1 + avg2)/2 * 3; - - // Work from the bright end to the dark end. VP interprets the - // Z axis value as the amount the plunger is pulled: the minimum - // is the rest position, the maximum is fully pulled. So we - // essentially want to report how much of the sensor is lit, - // since this increases as the plunger is pulled back. - int si = 1, di = 1; - if (avg1 < avg2) - si = npix - 1, di = -1; - - // scan for the midpoint - for (int n = 1, i = si ; n < npix - 1 ; ++n, i += di) + // if we've crossed the midpoint, report this position + if (long(pixp[-1]) + long(pixp[0]) + long(pixp[1]) < midpt) { - // if we've crossed the midpoint, report this position - if (long(pix[i-1]) + long(pix[i]) + long(pix[i+1]) < midpt) + // note the new position + int pos = n; + + // if the bright end and dark end don't differ by enough, skip this + // reading entirely - we must have an overexposed or underexposed frame + if (labs(avg1 - avg2) < 0x3333) + break; + + // Calibrate, or apply calibration, depending on the mode. + // In either case, normalize to a 0-127 range. VP appears to + // ignore negative Z axis values. + if (calBtnState == 3) { - // note the new position - int pos = abs(i - si); - - // Calibrate, or apply calibration, depending on the mode. - // In either case, normalize to a 0-127 range. VP appears to - // ignore negative Z axis values. - if (calBtnState == 3) - { - // calibrating - note if we're expanding the calibration envelope - if (pos < plungerMin) - plungerMin = pos; - if (pos > plungerMax) - plungerMax = pos; - - // normalize to the full physical range while calibrating - znew = int(float(pos)/npix * 127); - } - else - { - // running normally - normalize to the calibration range - if (pos < plungerMin) - pos = plungerMin; - if (pos > plungerMax) - pos = plungerMax; - znew = int(float(pos - plungerMin)/(plungerMax - plungerMin + 1) * 127); - } - - // done - break; + // calibrating - note if we're expanding the calibration envelope + if (pos < cfg.d.plungerMin) + cfg.d.plungerMin = pos; + if (pos > cfg.d.plungerMax) + cfg.d.plungerMax = pos; + + // normalize to the full physical range while calibrating + znew = int(float(pos)/npix * 127); } + else + { + // running normally - normalize to the calibration range + if (pos < cfg.d.plungerMin) + pos = cfg.d.plungerMin; + if (pos > cfg.d.plungerMax) + pos = cfg.d.plungerMax; + znew = int(float(pos - cfg.d.plungerMin) + / (cfg.d.plungerMax - cfg.d.plungerMin + 1) * 127); + } + + // done + break; } - - // reset the timer - ccdTimer.reset(); - t0ccd = ccdTimer.read_ms(); - } + } // read the accelerometer float xa, ya; @@ -391,26 +495,79 @@ // figure the new mouse report data int xnew = (int)(127 * xa); int ynew = (int)(127 * ya); + + // store the updated joystick coordinates + x = xnew; + y = ynew; + z = znew; - // send an update if the position has changed - // if (xnew != x || ynew != y || znew != z) + // if we're in USB suspend or disconnect mode, spin + if (js.isSuspended() || !js.isConnected()) { - x = xnew; - y = ynew; - z = znew; + // go dark (turn off the indicator LEDs) + led2 = 1; + led3 = 1; + led1 = 1; + + // wait until we're connected and come out of suspend mode + while (js.isSuspended() || !js.isConnected()) + { + // spin for a bit + wait(1); + + // if we're not suspended, flash red; otherwise stay dark + if (!js.isSuspended()) + led1 = !led1; + } + } - // Send the status report. Note that the X axis needs to be - // reversed, becasue the native accelerometer reports seem to - // assume that the card is component side down. - js.update(x, -y, z, 0); - } + // Send the status report. Note one of the axes needs to be + // reversed, because the native accelerometer reports seem to + // assume that the card is component side down; we have to + // reverse one or the other axis to account for the reversed + // coordinate system. It doesn't really matter which one, + // but reversing Y seems to give intuitive results when viewed + // in the Windows joystick control panel. Note that the + // coordinate system we report is ultimately arbitrary, since + // Visual Pinball has preference settings that let us set up + // axis reversals and a global rotation for the joystick. + js.update(x, -y, z, 0); - // show a heartbeat flash in blue every so often - if (hbTimer.read_ms() - t0Hb > 1000) + // show a heartbeat flash in blue every so often if not in + // calibration mode + if (calBtnState < 2 && hbTimer.read_ms() - t0Hb > 1000) { - // invert the blue LED state - hb = !hb; - led3 = (hb ? .5 : 1); + if (js.isSuspended()) + { + // suspended - turn off the LEDs entirely + led1 = 1; + led2 = 1; + led3 = 1; + } + else if (!js.isConnected()) + { + // not connected - flash red + hb = !hb; + led1 = (hb ? 0 : 1); + led2 = 1; + led3 = 1; + } + else if (flash_valid) + { + // connected, NVM valid - flash blue/green + hb = !hb; + led1 = 1; + led2 = (hb ? 0 : 1); + led3 = (hb ? 1 : 0); + } + else + { + // connected, factory reset - flash yellow/green + hb = !hb; + led1 = (hb ? 0 : 1); + led2 = 0; + led3 = 0; + } // reset the heartbeat timer hbTimer.reset();
--- a/tsl1410r.h Wed Jul 16 23:33:12 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -/* - * TSL1410R interface class. - * - * This provides a high-level interface for the Taos TSL1410R linear CCD array sensor. - */ - - #include "mbed.h" - - #ifndef TSL1410R_H - #define TSL1410R_H - -class TSL1410R -{ -public: - // set up with the two DigitalOut ports (SI and clock), and the - // analog in port for reading the currently selected pixel value - TSL1410R(PinName siPort, PinName clockPort, PinName aoPort); - - // Integrate light and read the pixels. Fills in pix[] with the pixel values, - // scaled 0-0xffff. n is the number of pixels to read; if this is less than - // the total number of pixels npix, we'll read every mth pixel, where m = npix/n. - // E.g., if you want 640 pixels out of 1280 on the sensor, we'll read every - // other pixel. If you want 320, we'll read every fourth pixel. - // Before reading, we'll pause for integrate_us additional microseconds during - // the integration phase; use 0 for no additional integration time. - void read(uint16_t *pix, int n, int integrate_us); - - // clock through all pixels to clear the array - void clear(); - - // number of pixels in the array - static const int nPix = 1280; - - -private: - DigitalOut si; - DigitalOut clock; - AnalogIn ao; -}; - -#endif /* TSL1410R_H */
--- a/tsl410r.cpp Wed Jul 16 23:33:12 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -#include "mbed.h" -#include "tsl1410r.h" - -TSL1410R::TSL1410R(PinName siPort, PinName clockPort, PinName aoPort) - : si(siPort), clock(clockPort), ao(aoPort) -{ - // clear out power-on noise by clocking through all pixels twice - clear(); - clear(); -} - -void TSL1410R::clear() -{ - // clock in an SI pulse - si = 1; - clock = 1; - clock = 0; - si = 0; - - // clock out all pixels - for (int i = 0 ; i < nPix+1 ; ++i) { - clock = 1; - clock = 0; - } -} - -void TSL1410R::read(uint16_t *pix, int n, int integrate_us) -{ - // Start an integration cycle - pulse SI, then clock all pixels. The - // CCD will integrate light starting 18 clocks after the SI pulse, and - // continues integrating until the next SI pulse, which cannot occur - // until all pixels have been clocked. - si = 1; - clock = 1; - clock = 0; - si = 0; - for (int i = 0 ; i < nPix+1 ; ++i) { - clock = 1; - clock = 0; - } - - // delay by the specified additional integration time - wait_us(integrate_us); - - // end the current integration cycle and hold the integrated values - si = 1; - clock = 1; - clock = 0; - si = 0; - - // figure how many pixels to skip on each read - int skip = nPix/n - 1; - - // read the pixels - for (int src = 0, dst = 0 ; src < nPix ; ++src) - { - // read this pixel - pix[dst++] = ao.read_u16(); - - // clock in the next pixel - clock = 1; - clock = 0; - - // clock skipped pixels - for (int i = 0 ; i < skip ; ++i, ++src) { - clock = 1; - clock = 0; - } - } - - // clock out one extra pixel to make sure the device is ready for another go - clock = 1; - clock = 0; -}