Mirror with some correction
Dependencies: mbed FastIO FastPWM USBDevice
Diff: main.cpp
- Revision:
- 79:682ae3171a08
- Parent:
- 78:1e00b3fa11af
- Child:
- 80:94dc2946871b
--- a/main.cpp Sun Mar 19 05:30:53 2017 +0000 +++ b/main.cpp Thu Mar 23 05:19:05 2017 +0000 @@ -216,6 +216,7 @@ #include "math.h" #include "diags.h" #include "pinscape.h" +#include "NewMalloc.h" #include "USBJoystick.h" #include "MMA8451Q.h" #include "tsl1410r.h" @@ -313,108 +314,6 @@ #endif -// -------------------------------------------------------------------------- -// -// Custom memory allocator. We use our own version of malloc() for more -// efficient memory usage, and to provide diagnostics if we run out of heap. -// -// We can implement a more efficient malloc than the library can because we -// can make an assumption that the library can't: allocations are permanent. -// The normal malloc has to assume that allocations can be freed, so it has -// to track blocks individually. For the purposes of this program, though, -// we don't have to do this because virtually all of our allocations are -// de facto permanent. We only allocate dyanmic memory during setup, and -// once we set things up, we never delete anything. This means that we can -// allocate memory in bare blocks without any bookkeeping overhead. -// -// In addition, we can make a larger overall pool of memory available in -// a custom allocator. The RTL malloc() seems to have a pool of about 3K -// to work with, even though there really seems to be at least 8K left after -// reserving a reasonable amount of space for the stack. - -// halt with a diagnostic display if we run out of memory -void HaltOutOfMem() -{ - printf("\r\nOut Of Memory\r\n"); - // halt with the diagnostic display (by looping forever) - for (;;) - { - diagLED(1, 0, 0); - wait_us(200000); - diagLED(1, 0, 1); - wait_us(200000); - } -} - -// For our custom malloc, we take advantage of the known layout of the -// mbed library memory management. The mbed library puts all of the -// static read/write data at the low end of RAM; this includes the -// initialized statics and the "ZI" (zero-initialized) statics. The -// malloc heap starts just after the last static, growing upwards as -// memory is allocated. The stack starts at the top of RAM and grows -// downwards. -// -// To figure out where the free memory starts, we simply call the system -// malloc() to make a dummy allocation the first time we're called, and -// use the address it returns as the start of our free memory pool. The -// first malloc() call presumably returns the lowest byte of the pool in -// the compiler RTL's way of thinking, and from what we know about the -// mbed heap layout, we know everything above this point should be free, -// at least until we reach the lowest address used by the stack. -// -// The ultimate size of the stack is of course dynamic and unpredictable. -// In testing, it appears that we currently need a little over 1K. To be -// conservative, we'll reserve 2K for the stack, by taking it out of the -// space at top of memory we consider fair game for malloc. -// -// Note that we could do this a little more low-level-ly if we wanted. -// The ARM linker provides a pre-defined extern char[] variable named -// Image$$RW_IRAM1$$ZI$$Limit, which is always placed just after the -// last static data variable. In principle, this tells us the start -// of the available malloc pool. However, in testing, it doesn't seem -// safe to use this as the start of our malloc pool. I'm not sure why, -// but probably something in the startup code (either in the C RTL or -// the mbed library) is allocating from the pool before we get control. -// So we won't use that approach. Besides, that would tie us even more -// closely to the ARM compiler. With our malloc() probe approach, we're -// at least portable to any compiler that uses the same basic memory -// layout, with the heap above the statics and the stack at top of -// memory; this isn't universal, but it's very typical. - -static char *xmalloc_nxt = 0; -size_t xmalloc_rem = 0; -void *xmalloc(size_t siz) -{ - if (xmalloc_nxt == 0) - { - xmalloc_nxt = (char *)malloc(4); - xmalloc_rem = 0x20003000UL - 2*1024 - uint32_t(xmalloc_nxt); - } - - siz = (siz + 3) & ~3; - if (siz > xmalloc_rem) - HaltOutOfMem(); - - char *ret = xmalloc_nxt; - xmalloc_nxt += siz; - xmalloc_rem -= siz; - - return ret; -} - -// Overload operator new to call our custom malloc. This ensures that -// all 'new' allocations throughout the program (including library code) -// go through our private allocator. -void *operator new(size_t siz) { return xmalloc(siz); } -void *operator new[](size_t siz) { return xmalloc(siz); } - -// Since we don't do bookkeeping to track released memory, 'delete' does -// nothing. In actual testing, this routine appears to never be called. -// If it *is* ever called, it will simply leave the block in place, which -// will make it unavailable for re-use but will otherwise be harmless. -void operator delete(void *ptr) { } - - // --------------------------------------------------------------------------- // // Forward declarations @@ -2397,8 +2296,10 @@ // current LOGICAL on/off state as reported to the host. uint8_t logState : 1; - // previous logical on/off state, when keys were last processed for USB - // reports and local effects + // Previous logical on/off state, when keys were last processed for USB + // reports and local effects. This lets us detect edges (transitions) + // in the logical state, for effects that are triggered when the state + // changes rather than merely by the button being on or off. uint8_t prevLogState : 1; // Pulse state @@ -2414,7 +2315,7 @@ // door is open and off when the door is closed (or vice versa, but in either // case, the switch state corresponds to the current state of the door at any // given time, rather than pulsing on state changes). The "pulse mode" - // option brdiges this gap by generating a toggle key event each time + // option bridges this gap by generating a toggle key event each time // there's a change to the physical switch's state. // // Pulse state: @@ -2465,13 +2366,16 @@ uint8_t data; // key state byte for USB reports } mediaState = { false, 0 }; -// button scan interrupt ticker -Ticker buttonTicker; +// button scan interrupt timer +Timeout scanButtonsTimeout; // Button scan interrupt handler. We call this periodically via // a timer interrupt to scan the physical button states. void scanButtons() { + // schedule the next interrupt + scanButtonsTimeout.attach_us(&scanButtons, 1000); + // scan all button input pins ButtonState *bs = buttonState, *last = bs + nButtons; for ( ; bs < last ; ++bs) @@ -2591,7 +2495,7 @@ } // start the button scan thread - buttonTicker.attach_us(scanButtons, 1000); + scanButtonsTimeout.attach_us(scanButtons, 1000); // start the button state transition timer buttonTimer.start(); @@ -3866,6 +3770,9 @@ const uint8_t TV_RELAY_POWERON = 0x01; const uint8_t TV_RELAY_USB = 0x02; +// pulse timer for manual TV relay pulses +Timer tvRelayManualTimer; + // TV ON IR command state. When the main PSU2 power state reaches // the IR phase, we use this sub-state counter to send the TV ON // IR signals. We initialize to state 0 when the main state counter @@ -3906,6 +3813,17 @@ uint32_t tv_delay_time_us; void powerStatusUpdate(Config &cfg) { + // If the manual relay pulse timer is past the pulse time, end the + // manual pulse. The timer only runs when a pulse is active, so + // it'll never read as past the time limit if a pulse isn't on. + if (tvRelayManualTimer.read_us() > 250000) + { + // turn off the relay and disable the timer + tvRelayUpdate(TV_RELAY_USB, false); + tvRelayManualTimer.stop(); + tvRelayManualTimer.reset(); + } + // Only update every 1/4 second or so. Note that if the PSU2 // circuit isn't configured, the initialization routine won't // start the timer, so it'll always read zero and we'll always @@ -4110,15 +4028,6 @@ } } -// TV relay manual control timer. This lets us pulse the TV relay -// under manual control, separately from the TV ON timer. -Ticker tv_manualTicker; -void TVManualInt() -{ - tv_manualTicker.detach(); - tvRelayUpdate(TV_RELAY_USB, false); -} - // Operate the TV ON relay. This allows manual control of the relay // from the PC. See protocol message 65 submessage 11. // @@ -4145,9 +4054,10 @@ break; case 2: - // Pulse the relay. Turn it on, then set our timer for 250ms. + // Turn the relay on and reset the manual TV pulse timer tvRelayUpdate(TV_RELAY_USB, true); - tv_manualTicker.attach(&TVManualInt, 0.25); + tvRelayManualTimer.reset(); + tvRelayManualTimer.start(); break; } } @@ -4179,27 +4089,29 @@ // delay time in seconds before rebooting. uint8_t saveConfigRebootTime; +// status flag for successful config save - set to 0x40 on success +uint8_t saveConfigSucceededFlag; + // For convenience, a macro for the Config part of the NVM structure #define cfg (nvm.d.c) // flash memory controller interface FreescaleIAP iap; -// NVM structure in memory. This has to be aliend on a sector boundary, -// since we have to be able to erase its page(s) in order to write it. -// Further, we have to ensure that nothing else occupies any space within -// the same pages, since we'll erase that entire space whenever we write. -static const union +// figure the flash address for the config data +const NVM *configFlashAddr() { - NVM nvm; // the NVM structure - char guard[((sizeof(NVM) + SECTOR_SIZE - 1)/SECTOR_SIZE)*SECTOR_SIZE]; -} -flash_nvm_memory __attribute__ ((aligned(SECTOR_SIZE))) = { }; - -// figure the flash address as a pointer -NVM *configFlashAddr() -{ - return (NVM *)&flash_nvm_memory; + // figure the number of sectors we need, rounding up + int nSectors = (sizeof(NVM) + SECTOR_SIZE - 1)/SECTOR_SIZE; + + // figure the total size required from the number of sectors + int reservedSize = nSectors * SECTOR_SIZE; + + // locate it at the top of memory + uint32_t addr = iap.flashSize() - reservedSize; + + // return it as a read-only NVM pointer + return (const NVM *)addr; } // Load the config from flash. Returns true if a valid non-default @@ -4226,7 +4138,7 @@ // the free space, it won't collide with the linker area. // Figure how many sectors we need for our structure - NVM *flash = configFlashAddr(); + const NVM *flash = configFlashAddr(); // if the flash is valid, load it; otherwise initialize to defaults bool nvm_valid = flash->valid(); @@ -4245,55 +4157,17 @@ return nvm_valid; } -void saveConfigToFlash() +// save the config - returns true on success, false on failure +bool saveConfigToFlash() { // make sure the plunger sensor isn't busy waitPlungerIdle(); // get the config block location in the flash memory uint32_t addr = uint32_t(configFlashAddr()); - - // loop until we save it successfully - for (int i = 0 ; i < 5 ; ++i) - { - // show cyan while writing - diagLED(0, 1, 1); - - // save the data - nvm.save(iap, addr); - - // diagnostic lights off - diagLED(0, 0, 0); - - // verify the data - if (nvm.verify(addr)) - { - // show a diagnostic success flash (rapid green) - for (int j = 0 ; j < 4 ; ++j) - { - diagLED(0, 1, 0); - wait_us(50000); - diagLED(0, 0, 0); - wait_us(50000); - } - - // success - no need to write again - break; - } - else - { - // Write failed. For diagnostic purposes, flash red a few times. - // Then go back through the loop to make another attempt at the - // write. - for (int j = 0 ; j < 5 ; ++j) - { - diagLED(1, 0, 0); - wait_us(50000); - diagLED(0, 0, 0); - wait_us(50000); - } - } - } + + // save the data + return nvm.save(iap, addr); } // --------------------------------------------------------------------------- @@ -5709,7 +5583,7 @@ nvm.valid(), // a config is loaded if the config memory block is valid true, // we support sbx/pbx extensions true, // we support the new accelerometer settings - xmalloc_rem); // remaining memory size + mallocBytesFree()); // remaining memory size break; case 5: @@ -5962,11 +5836,6 @@ // say hello to the debug console, in case it's connected printf("\r\nPinscape Controller starting\r\n"); - - // debugging: print memory config info - // -> no longer very useful, since we use our own custom malloc/new allocator (see xmalloc() above) - // {int *a = new int; printf("Stack=%lx, heap=%lx, free=%ld\r\n", (long)&a, (long)a, (long)&a - (long)a);} - // clear the I2C connection clear_i2c(); @@ -6383,7 +6252,8 @@ uint16_t statusFlags = cfg.plunger.enabled // 0x01 | nightMode // 0x02 - | ((psu2_state & 0x07) << 2); // 0x04 0x08 0x10 + | ((psu2_state & 0x07) << 2) // 0x04 0x08 0x10 + | saveConfigSucceededFlag; // 0x40 if (IRLearningMode != 0) statusFlags |= 0x20; @@ -6513,7 +6383,8 @@ if (saveConfigPending != 0) { // save the configuration - saveConfigToFlash(); + if (saveConfigToFlash()) + saveConfigSucceededFlag = 0x40; // if desired, reboot after the specified delay if (saveConfigPending == SAVE_CONFIG_AND_REBOOT)