
Adjusts the great pinscape controller to work with a cheap linear potentiometer instead of the expensive CCD array
Fork of Pinscape_Controller by
Revision 1:d913e0afb2ac, committed 2014-07-16
- Comitter:
- mjr
- Date:
- Wed Jul 16 23:33:12 2014 +0000
- Parent:
- 0:5acbbe3f4cf4
- Child:
- 2:c174f9ee414a
- Commit message:
- Before removing time/frequency limit on reading the plunger sensor
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FreescaleIAP.cpp Wed Jul 16 23:33:12 2014 +0000 @@ -0,0 +1,190 @@ +#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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FreescaleIAP.h Wed Jul 16 23:33:12 2014 +0000 @@ -0,0 +1,87 @@ +/* + * 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
--- a/MMA8451Q.lib Fri Jul 11 03:26:11 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -http://mbed.org/users/emilmont/code/MMA8451Q/#c4d879a39775
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MMA8451Q/MMA8451Q.cpp Wed Jul 16 23:33:12 2014 +0000 @@ -0,0 +1,176 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "MMA8451Q.h" + +#define REG_WHO_AM_I 0x0D +#define REG_CTRL_REG_1 0x2A +#define REG_CTRL_REG_2 0x2B +#define REG_CTRL_REG_3 0x2c +#define REG_CTRL_REG_4 0x2D +#define REG_CTRL_REG_5 0x2E +#define REG_OFF_X 0x2F +#define REG_OFF_Y 0x30 +#define REG_OFF_Z 0x31 +#define XYZ_DATA_CFG_REG 0x0E +#define REG_OUT_X_MSB 0x01 +#define REG_OUT_Y_MSB 0x03 +#define REG_OUT_Z_MSB 0x05 + +#define UINT14_MAX 16383 + +#define CTL_ACTIVE 0x01 +#define FS_MASK 0x03 +#define FS_2G 0x00 +#define FS_4G 0x01 +#define FS_8G 0x02 + +#define HPF_OUT_MASK 0x10 + +#define MODS1_MASK 0x02 +#define MODS0_MASK 0x01 +#define SMODS_MASK 0x18 +#define MODS_MASK 0x03 + +#define DR_MASK 0x38 +#define DR_800_HZ 0x00 +#define DR_400_HZ 0x08 +#define DR_200_HZ 0x10 +#define DR_100_HZ 0x18 +#define DR_50_HZ 0x20 +#define DR_12_HZ 0x28 +#define DR_6_HZ 0x30 +#define DR_1_HZ 0x38 + + +MMA8451Q::MMA8451Q(PinName sda, PinName scl, int addr) : m_i2c(sda, scl), m_addr(addr) +{ + // go to standby mode + standby(); + + // read the curent config register + uint8_t d1[1]; + readRegs(XYZ_DATA_CFG_REG, d1, 1); + + // set 2g mode + uint8_t d2[2] = { XYZ_DATA_CFG_REG, (d1[0] & ~FS_MASK) | FS_2G }; + writeRegs(d2, 2); + + // read the ctl2 register + uint8_t d3[1]; + readRegs(REG_CTRL_REG_2, d3, 1); + + // set the high resolution mode + uint8_t d4[2] = {REG_CTRL_REG_2, (d3[0] & ~MODS_MASK) | MODS1_MASK}; + writeRegs(d4, 2); + + // set 50 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); + + // enter active mode + active(); +} + +MMA8451Q::~MMA8451Q() { } + +void MMA8451Q::standby() +{ + // read the current control register + uint8_t d1[1]; + readRegs(REG_CTRL_REG_1, d1, 1); + + // write it back witht he Active bit cleared + uint8_t d2[2] = { REG_CTRL_REG_1, d1[0] & ~CTL_ACTIVE }; + writeRegs(d2, 2); +} + +void MMA8451Q::active() +{ + // read the current control register + uint8_t d1[1]; + readRegs(REG_CTRL_REG_1, d1, 1); + + // write it back out with the Active bit set + uint8_t d2[2] = { REG_CTRL_REG_1, d1[0] | CTL_ACTIVE }; + writeRegs(d2, 2); +} + +uint8_t MMA8451Q::getWhoAmI() { + uint8_t who_am_i = 0; + readRegs(REG_WHO_AM_I, &who_am_i, 1); + return who_am_i; +} + +float MMA8451Q::getAccX() { + return (float(getAccAxis(REG_OUT_X_MSB))/4096.0); +} + +void MMA8451Q::getAccXY(float &x, float &y) +{ + // read the X and Y output registers + uint8_t res[4]; + readRegs(REG_OUT_X_MSB, res, 4); + + // translate the x value + uint16_t acc = (res[0] << 8) | (res[1]); + x = int16_t(acc)/(4*4096.0); + + // translate the y value + acc = (res[2] << 9) | (res[3]); + y = int16_t(acc)/(4*4096.0); +} + +float MMA8451Q::getAccY() { + return (float(getAccAxis(REG_OUT_Y_MSB))/4096.0); +} + +float MMA8451Q::getAccZ() { + return (float(getAccAxis(REG_OUT_Z_MSB))/4096.0); +} + +void MMA8451Q::getAccAllAxis(float * res) { + res[0] = getAccX(); + res[1] = getAccY(); + res[2] = getAccZ(); +} + +int16_t MMA8451Q::getAccAxis(uint8_t addr) { + int16_t acc; + uint8_t res[2]; + readRegs(addr, res, 2); + + acc = (res[0] << 6) | (res[1] >> 2); + if (acc > UINT14_MAX/2) + acc -= UINT14_MAX; + + return acc; +} + +void MMA8451Q::readRegs(int addr, uint8_t * data, int len) { + char t[1] = {addr}; + m_i2c.write(m_addr, t, 1, true); + m_i2c.read(m_addr, (char *)data, len); +} + +void MMA8451Q::writeRegs(uint8_t * data, int len) { + m_i2c.write(m_addr, (char *)data, len); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MMA8451Q/MMA8451Q.h Wed Jul 16 23:33:12 2014 +0000 @@ -0,0 +1,125 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MMA8451Q_H +#define MMA8451Q_H + +#include "mbed.h" + +/** +* MMA8451Q accelerometer example +* +* @code +* #include "mbed.h" +* #include "MMA8451Q.h" +* +* #define MMA8451_I2C_ADDRESS (0x1d<<1) +* +* int main(void) { +* +* MMA8451Q acc(P_E25, P_E24, MMA8451_I2C_ADDRESS); +* PwmOut rled(LED_RED); +* PwmOut gled(LED_GREEN); +* PwmOut bled(LED_BLUE); +* +* while (true) { +* rled = 1.0 - abs(acc.getAccX()); +* gled = 1.0 - abs(acc.getAccY()); +* bled = 1.0 - abs(acc.getAccZ()); +* wait(0.1); +* } +* } +* @endcode +*/ +class MMA8451Q +{ +public: + /** + * MMA8451Q constructor + * + * @param sda SDA pin + * @param sdl SCL pin + * @param addr addr of the I2C peripheral + */ + MMA8451Q(PinName sda, PinName scl, int addr); + + /** + * MMA8451Q destructor + */ + ~MMA8451Q(); + + /** + * Enter standby mode + */ + void standby(); + + /** + * Enter active mode + */ + void active(); + + /** + * Get the value of the WHO_AM_I register + * + * @returns WHO_AM_I value + */ + uint8_t getWhoAmI(); + + /** + * Get X axis acceleration + * + * @returns X axis acceleration + */ + float getAccX(); + + /** + * Get Y axis acceleration + * + * @returns Y axis acceleration + */ + float getAccY(); + + /** + * Read an X,Y pair + */ + void getAccXY(float &x, float &y); + + /** + * Get Z axis acceleration + * + * @returns Z axis acceleration + */ + float getAccZ(); + + /** + * Get XYZ axis acceleration + * + * @param res array where acceleration data will be stored + */ + void getAccAllAxis(float * res); + +private: + I2C m_i2c; + int m_addr; + void readRegs(int addr, uint8_t * data, int len); + void writeRegs(uint8_t * data, int len); + int16_t getAccAxis(uint8_t addr); + +}; + +#endif
--- a/USBJoystick.cpp Fri Jul 11 03:26:11 2014 +0000 +++ b/USBJoystick.cpp Wed Jul 16 23:33:12 2014 +0000 @@ -99,9 +99,9 @@ LOGICAL_MINIMUM(1), 0x81, // each value ranges -127... LOGICAL_MAXIMUM(1), 0x7f, // ...to 127 REPORT_SIZE(1), 0x08, // 8 bits per report - REPORT_COUNT(1), 0x03, // 3 reports + REPORT_COUNT(1), 0x03, // 2 reports INPUT(1), 0x02, // Data, Variable, Absolute - + REPORT_COUNT(1), 0x08, // input report count (LEDWiz messages) 0x09, 0x01, // usage 0x91, 0x01, // Output (array)
--- a/main.cpp Fri Jul 11 03:26:11 2014 +0000 +++ b/main.cpp Wed Jul 16 23:33:12 2014 +0000 @@ -1,31 +1,34 @@ #include "mbed.h" #include "USBJoystick.h" #include "MMA8451Q.h" -#include "tls1410r.h" +#include "tsl1410r.h" +#include "FreescaleIAP.h" +// on-board RGB LED elements - we use these for diagnostics PwmOut led1(LED1), led2(LED2), led3(LED3); -DigitalOut out1(PTE29); - +// calibration button - switch input and LED output +DigitalIn calBtn(PTE29); +DigitalOut calBtnLed(PTE23); static int pbaIdx = 0; // on/off state for each LedWiz output -static uint8_t ledOn[32]; +static uint8_t wizOn[32]; // profile (brightness/blink) state for each LedWiz output -static uint8_t ledVal[32] = { +static uint8_t wizVal[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static double ledState(int idx) +static float wizState(int idx) { - if (ledOn[idx]) { + if (wizOn[idx]) { // on - map profile brightness state to PWM level - uint8_t val = ledVal[idx]; + uint8_t val = wizVal[idx]; if (val >= 1 && val <= 48) return 1.0 - val/48.0; else if (val >= 129 && val <= 132) @@ -39,27 +42,75 @@ } } -static void updateLeds() +static void updateWizOuts() +{ + led1 = wizState(0); + led2 = wizState(1); + led3 = wizState(2); +} + +struct AccPrv { - led1 = ledState(0); - led2 = ledState(1); - led3 = ledState(2); -} + AccPrv() : x(0), y(0) { } + float x; + float y; + + double dist(AccPrv &b) + { + float dx = x - b.x, dy = y - b.y; + return sqrt(dx*dx + dy*dy); + } +}; int main(void) { + // turn off our on-board indicator LED led1 = 1; led2 = 1; led3 = 1; - Timer timer; + + // plunger calibration data + const int npix = 320; + int plungerMin = 0, plungerMax = npix; + + // plunger calibration button debounce timer + Timer calBtnTimer; + calBtnTimer.start(); + int calBtnDownTime = 0; + int calBtnLit = false; + + // Calibration button state: + // 0 = not pushed + // 1 = pushed, not yet debounced + // 2 = pushed, debounced, waiting for hold time + // 3 = pushed, hold time completed - in calibration mode + int calBtnState = 0; + + // set up a timer for our heartbeat indicator + Timer hbTimer; + hbTimer.start(); + int t0Hb = hbTimer.read_ms(); + int hb = 0; + + // set a timer for accelerometer auto-centering + Timer acTimer; + 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 - // set up a timer for spacing USB reports - timer.start(); - float t0 = timer.read_ms(); - float tout1 = timer.read_ms(); - - // Create the joystick USB client. Show a read LED while connecting, and - // change to green when connected. + // 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 = 1; @@ -68,13 +119,23 @@ // create the accelerometer object const int MMA8451_I2C_ADDRESS = (0x1d<<1); MMA8451Q accel(PTE25, PTE24, MMA8451_I2C_ADDRESS); - printf("MMA8451 ID: %d\r\n", accel.getWhoAmI()); // create the CCD array object - TLS1410R ccd(PTE20, PTE21, PTB0); + TSL1410R ccd(PTE20, PTE21, PTB0); + + // recent accelerometer readings, for auto centering + int iAccPrv = 0, nAccPrv = 0; + const int maxAccPrv = 5; + AccPrv accPrv[maxAccPrv]; - // process sensor reports and LedWiz requests forever - int x = 0, y = 127, z = 0; + // 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; + + // we're all set up - now just loop, processing sensor reports and + // host requests for (;;) { // Look for an incoming report. Continue processing input as @@ -85,7 +146,8 @@ while (js.readNB(&report) && report.length == 8) { uint8_t *data = report.data; - if (data[0] == 64) { + if (data[0] == 64) + { // LWZ-SBA - first four bytes are bit-packed on/off flags // for the outputs; 5th byte is the pulse speed (0-7) //printf("LWZ-SBA %02x %02x %02x %02x ; %02x\r\n", @@ -98,16 +160,17 @@ bit = 1; ++ri; } - ledOn[i] = ((data[ri] & bit) != 0); + wizOn[i] = ((data[ri] & bit) != 0); } - // update the physical LED state - updateLeds(); + // update the physical outputs + updateWizOuts(); // reset the PBA counter pbaIdx = 0; } - else { + else + { // LWZ-PBA - full state dump; each byte is one output // in the current bank. pbaIdx keeps track of the bank; // this is incremented implicitly by each PBA message. @@ -116,73 +179,242 @@ // update all output profile settings for (int i = 0 ; i < 8 ; ++i) - ledVal[pbaIdx + i] = data[i]; + wizVal[pbaIdx + i] = data[i]; // update the physical LED state if this is the last bank if (pbaIdx == 24) - updateLeds(); + updateWizOuts(); // advance to the next bank pbaIdx = (pbaIdx + 8) & 31; } } - -#if 1 - // check the accelerometer + + // check for plunger calibration + if (!calBtn) { - // read the accelerometer - float xa = accel.getAccX(); - float ya = accel.getAccY(); - - // figure the new joystick position - int xnew = (int)(127 * xa); - int ynew = (int)(127 * ya); - - // send an update if the position has changed - if (xnew != x || ynew != y) + // check the state + switch (calBtnState) { - x = xnew; - y = ynew; - - // send the status report - js.update(x, y, z, 0); + case 0: + // button not yet pushed - start debouncing + calBtnTimer.reset(); + calBtnDownTime = calBtnTimer.read_ms(); + calBtnState = 1; + break; + + case 1: + // pushed, not yet debounced - if the debounce time has + // passed, start the hold period + if (calBtnTimer.read_ms() - calBtnDownTime > 50) + calBtnState = 2; + break; + + case 2: + // in the hold period - if the button has been held down + // for the entire hold period, move to calibration mode + if (calBtnTimer.read_ms() - calBtnDownTime > 2050) + { + // enter calibration mode + calBtnState = 3; + + // reset the calibration limits + plungerMax = 0; + plungerMin = npix; + } + break; } } -#else - // Send a joystick report if it's been long enough since the - // last report - if (timer.read_ms() - t0 > 250) + 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) + calBtnState = 0; + else if (calBtnTimer.read_ms() - calBtnDownTime > 32500) + calBtnState = 0; + } + + // light/flash the calibration button light, if applicable + int newCalBtnLit = calBtnLit; + switch (calBtnState) { - // send the current joystick status report - js.update(x, y, z, 0); - - // update our internal joystick position record - x += dx; - y += dy; - z += dz; - if (x > xmax || x < xmin) { - dx = -dx; - x += 2*dx; + case 2: + // in the hold period - flash the light + newCalBtnLit = (((calBtnTimer.read_ms() - calBtnDownTime)/250) & 1); + break; + + case 3: + // calibration mode - show steady on + newCalBtnLit = true; + break; + + default: + // not calibrating/holding - show steady off + newCalBtnLit = false; + break; + } + if (calBtnLit != newCalBtnLit) + { + calBtnLit = newCalBtnLit; + calBtnLed = (calBtnLit ? 1 : 0); + } + + // read the plunger sensor + int znew = z; + /* if (ccdTimer.read_ms() - t0ccd > 33) */ + { + // 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(); } - if (y > ymax || y < ymin) { - dy = -dy; - y += 2*dy; - } - if (z > zmax || z < zmin) { - dz = -dz; - z += 2*dz; - } +#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; - // note the time of the last report - t0 = timer.read_ms(); - } -#endif + // 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(pix[i-1]) + long(pix[i]) + long(pix[i+1]) < midpt) + { + // 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; + } + } + + // reset the timer + ccdTimer.reset(); + t0ccd = ccdTimer.read_ms(); + } + + // read the accelerometer + float xa, ya; + accel.getAccXY(xa, ya); + + // check for auto-centering every so often + if (acTimer.read_ms() - t0ac > 1000) + { + // add the sample to the history list + accPrv[iAccPrv].x = xa; + accPrv[iAccPrv].y = ya; + + // store the slot + iAccPrv += 1; + iAccPrv %= maxAccPrv; + nAccPrv += 1; + + // If we have a full complement, check for stability. The + // raw accelerometer input is in the rnage -4096 to 4096, but + // the class cover normalizes to a unit interval (-1.0 .. +1.0). + const float accTol = .005; + if (nAccPrv >= maxAccPrv + && accPrv[0].dist(accPrv[1]) < accTol + && accPrv[0].dist(accPrv[2]) < accTol + && accPrv[0].dist(accPrv[3]) < accTol + && accPrv[0].dist(accPrv[4]) < accTol) + { + // figure the new center + xCenter = (accPrv[0].x + accPrv[1].x + accPrv[2].x + accPrv[3].x + accPrv[4].x)/5.0; + yCenter = (accPrv[0].y + accPrv[1].y + accPrv[2].y + accPrv[3].y + accPrv[4].y)/5.0; + } + + // reset the auto-center timer + acTimer.reset(); + t0ac = acTimer.read_ms(); + } + + // adjust for our auto centering + xa -= xCenter; + ya -= yCenter; + + // confine to the unit interval + if (xa < -1.0) xa = -1.0; + if (xa > 1.0) xa = 1.0; + if (ya < -1.0) ya = -1.0; + if (ya > 1.0) ya = 1.0; - // pulse E29 - if (timer.read_ms() - tout1 > 2000) + // figure the new mouse report data + int xnew = (int)(127 * xa); + int ynew = (int)(127 * ya); + + // send an update if the position has changed + // if (xnew != x || ynew != y || znew != z) { - out1 = !out1; - tout1 = timer.read_ms(); + x = xnew; + y = ynew; + z = znew; + + // 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); } - } + + // show a heartbeat flash in blue every so often + if (hbTimer.read_ms() - t0Hb > 1000) + { + // invert the blue LED state + hb = !hb; + led3 = (hb ? .5 : 1); + + // reset the heartbeat timer + hbTimer.reset(); + t0Hb = hbTimer.read_ms(); + } + } }
--- a/tls1410r.cpp Fri Jul 11 03:26:11 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -#include "mbed.h" -#include "tls1410r.h" - -TLS1410R::TLS1410R(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 TLS1410R::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 TLS1410R::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; - - // clock in the next pixel - clock = 1; - clock = 0; - - // clock skipped pixels - for (int i = 0 ; i < skip ; ++i) { - clock = 1; - clock = 0; - } - } - - // clock out one extra pixel to make sure the device is ready for another go - clock = 1; - clock = 0; -}
--- a/tls1410r.h Fri Jul 11 03:26:11 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -/* - * TLS1410R interface class. - * - * This provides a high-level interface for the Taos TLS1410R linear CCD array sensor. - */ - - #include "mbed.h" - - #ifndef TLS1410R_H - #define TLS1410R_H - -class TLS1410R -{ -public: - // set up with the two DigitalOut ports (SI and clock), and the - // analog in port for reading the currently selected pixel value - TLS1410R(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 /* TLS1410R_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tsl1410r.h Wed Jul 16 23:33:12 2014 +0000 @@ -0,0 +1,41 @@ +/* + * 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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tsl410r.cpp Wed Jul 16 23:33:12 2014 +0000 @@ -0,0 +1,74 @@ +#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; +}