Touch screen drivers control dashboard for miniature locomotive. Features meters for speed, volts, power. Switches for lights, horns. Drives multiple STM3_ESC brushless motor controllers for complete brushless loco system as used in "The Brute" - www.jons-workshop.com
Dependencies: TS_DISCO_F746NG mbed Servo LCD_DISCO_F746NG BSP_DISCO_F746NG QSPI_DISCO_F746NG AsyncSerial FastPWM
main.cpp
- Committer:
- JonFreeman
- Date:
- 2018-05-09
- Revision:
- 8:5945d506a872
- Parent:
- 7:3b1f44cd4735
- Child:
- 9:644867052318
File content as of revision 8:5945d506a872:
/* April 2018 - Jon Freeman Touch Screen controller communicates with 1, 2, ... n Twin BLDC Controller boards via opto-isolated serial port. 9 pid D connector retained but wiring NOT compatible with 2017. This time pins are : - 1 Not Used on TS2018, connection from Twin BLDC Controller only - Pot wiper 2 Not Used on TS2018, connection from Twin BLDC Controller only - GND 3 Not Used on TS2018, connection from Twin BLDC Controller only - Weak +5 (top of pot) 4 Not Used on TS2018, connection from Twin BLDC Controller only - Possible Fwd / Rev switched between pins 2 and 3 above 5 TS2018 high voltage output - power up signal to Twin BLDC Controllers, +10 to + 70 Volt (full supply via 3k3 0.5W safety resistor) 6 Twin BLDC Rx- <- TS2018 Tx data ||GND to avoid exposing TS +5v rail to the outside world 7 Twin BLDC Rx+ <- TS2018 +5v ||Tx\ to avoid exposing TS +5v rail to the outside world, INVERTED Txd idles lo 8 Twin BLDC Tx- <- TS2018 GND 9 Twin BLDC Tx+ <- TS2018 Rx data with 1k pullup to +5, line idles hi */ #include "mbed.h" #include "Electric_Loco.h" #include "AsyncSerial.hpp" #include "Servo.h" #include "TS_DISCO_F746NG.h" #include "LCD_DISCO_F746NG.h" #include <cctype> //#include <cerrno> LCD_DISCO_F746NG lcd; TS_DISCO_F746NG touch_screen; //FastPWM maxv (D12, 1), // maxi (D11, 1); // pin, prescaler value Serial pc (USBTX, USBRX); // AsyncSerial does not work here. Comms to 'PuTTY' or similar comms programme on pc DigitalOut reverse_pin (D7); // These pins no longer used to set mode and direction, now commands issued to com DigitalOut forward_pin (D9); //was D6, these two decode to fwd, rev, regen_braking and park DigitalOut I_sink1 (D14); // a horn DigitalOut I_sink2 (D15); // lamp DigitalOut I_sink3 (D3); // lamp DigitalOut I_sink4 (D4); DigitalOut I_sink5 (D5); DigitalOut led_grn (LED1); // the only on board user led DigitalIn f_r_switch (D2); // was D0, Reads position of centre-off ignition switch //DigitalIn spareio_d8 (D8); //DigitalIn spareio_d9 (D9); DigitalIn spareio_d10 (D10); // D8, D9, D10 wired to jumper on pcb - not used to Apr 2017 AnalogIn ht_volts_ain (A0); // Jan 2017 AnalogIn ht_amps_ain (A1); // Jan 2017 //AnalogIn spare_ain2 (A2); //AnalogIn spare_ain3 (A3); //AnalogIn spare_ain4 (A4); // hardware on pcb for these 3 spare analogue inputs - not used to Apr 2017 //AnalogIn spare_ain5 (A5); // causes display flicker ! AsyncSerial com (A4, A5); // Com port to opto isolators on Twin BLDC Controller boards //AsyncSerial ir (D1, D0); // Second port does work, but gives the old broken-up display flicker nonsense problem Servo servo1 (D6); // Now used to adjust Honda speed extern uint32_t odometer_out () ; // Read latest total of metres travelled ever extern bool odometer_zero () ; // Returns true on success extern bool odometer_update (uint32_t pulsetot, uint16_t pow, uint16_t volt) ; // Hall pulse total updated once per sec and saved in blocks of 4096 bytes on QSPI onboard memory extern int get_button_press (struct point & pt) ; extern void displaytext (int x, int y, const int font, uint32_t BCol, uint32_t TCol, char * txt) ; extern void displaytext (int x, int y, const int font, char * txt) ; extern void displaytext (int x, int y, char * txt) ; extern void setup_buttons () ; extern void draw_numeric_keypad (int colour) ; extern void draw_button_hilight (int bu, int colour) ; extern void read_presses (int * a) ; extern void read_keypresses (struct ky_bd & a) ; extern void SliderGraphic (struct slide & q) ; extern void vm_set () ; extern void update_meters (double, double, double) ; extern void setup_pccom () ; extern void setup_lococom () ; extern void clicore (struct parameters & a) ; extern struct parameters pccom, lococom; static const int DAMPER_DECAY = 42, // Small num -> fast 'viscous damper' on dead-mans function with finger removed from panel MAF_PTS = 140, // Moving Average Filter points. Filters reduce noise on volatage and current readings FWD = 0, REV = ~FWD; //from .h struct slide { int position; int oldpos; int state; int direction; bool recalc_run; bool handbrake_slipping; double handbrake_effort; double loco_speed } ; struct slide slider ; int V_maf[MAF_PTS + 2], I_maf[MAF_PTS + 2], maf_ptr = 0; // ********* These should be uint16_t uint32_t sys_timer_32Hz = 0; double last_V = 0.0, last_I = 0.0, recent_distance = 0.0; bool qtrsec_trig = false; bool trigger_current_read = false; volatile bool trigger_32ms = false; class speed_2018 { static const int SPEED_AVE_PTS = 5; // AVE_PTS - points in moving average filters uint32_t speed_maf_mem [(SPEED_AVE_PTS + 1) * 2][8], // Allow for up to 8 axles axle_total [8], // allow up to 8 axles mafptr, board_IDs [4], // allow up to 4 boards board_count, b, reqno; public: speed_2018 () { // Constructor memset(speed_maf_mem, 0, sizeof(speed_maf_mem)); for (int i = 0; i < sizeof(axle_total) / sizeof(uint32_t); i++) axle_total[i] = 0; mafptr = 0; board_count = 0; b = 0; reqno = 0; } bool request_rpm () ; void rpm_update(struct parameters & a) ; void set_board_IDs (uint32_t *) ; double mph () ; } speed ; void speed_2018::set_board_IDs (uint32_t * a) { board_count = 0; while (a[board_count]) { board_IDs[board_count] = a[board_count]; board_count++; } pc.printf ("set_board_IDs %d\r\n", board_count); } double speed_2018::mph () { if (!board_count) { // pc.printf ("No boards\r\n"); return 0.0; } int t[8] = {0,0,0,0,0,0,0,0}; for (int i = 0; i < SPEED_AVE_PTS; i++) { for (int j = 0; j < board_count * 2; j++) t[j] += speed_maf_mem[i][j]; } int j = 0; for (int i = 0; i < board_count * 2; i++) { j += t[i]; axle_total[i] = t[i]; } return (rpm2mph * ((double) j) / (SPEED_AVE_PTS * board_count * 2)); } bool speed_2018::request_rpm () { // Issue "'n'rpm\r" to BLDC board to request RPM if (board_IDs[0] == 0) return false; // No boards identified if (board_IDs[reqno] == 0) reqno = 0; com.putc (board_IDs[reqno++]); com.printf ("rpm\r"); return true; } void speed_2018::rpm_update(struct parameters & a) { // Puts new readings into mem speed_maf_mem [mafptr][b++] = (uint32_t)a.dbl[0]; speed_maf_mem [mafptr][b++] = (uint32_t)a.dbl[1]; if ((b + 1) >= (board_count * 2)) { b = 0; mafptr++; if (mafptr >= SPEED_AVE_PTS) mafptr = 0; } } void rpm_push (struct parameters & a) { // Latest RPM reports from Dual BLDC Motor Controllers arrive here speed.rpm_update (a); // pc.printf ("RPM%d %d, mph %.1f\r\n", (int)a.dbl[0], (int)a.dbl[1], speed2.mph()); } void set_V_limit (double p) // Sets max motor voltage { if (p < 0.0) p = 0.0; if (p > 1.0) p = 1.0; last_V = p; com.printf ("v%d\r", (int)(p * 99.0)); } void set_I_limit (double p) // Sets max motor current { if (p < 0.0) p = 0.0; if (p > 1.0) p = 1.0; last_I = p; // New 30/4/2018 ; no use for this yet, included to be consistent with V com.printf ("i%d\r", (int)(p * 99.0)); } double read_ammeter () { int32_t a = 0; for (int b = 0; b < MAF_PTS; b++) a += I_maf[b]; a /= MAF_PTS; a -= 0x4000; double i = (double) (0 - a); return i / 200.0; // Fiddled to get current reading close enough } double read_voltmeter () { int a = 0; for (int b = 0; b < MAF_PTS; b++) a += V_maf[b]; a /= MAF_PTS; double v = (double) a; return (v / 932.0) + 0.0; // fiddled to suit resistor values } // Interrupt Service Routines void ISR_current_reader (void) // FIXED at 250us { static int ms32 = 0, ms250 = 0; trigger_current_read = true; // every 250us, i.e. 4kHz NOTE only sets trigger here, readings taken in main loop ms32++; if (ms32 > 124) { // 31.25ms, not 32ms, is 32Hz ms32 = 0; sys_timer_32Hz++; // , usable anywhere as general measure of elapsed time trigger_32ms = true; ms250++; if (ms250 > 7) { ms250 = 0; qtrsec_trig = true; } } } // End of Interrupt Service Routines bool inlist (struct ky_bd & a, int key) { int i = 0; while (i < a.count) { if (key == a.ky[i].keynum) return true; i++; } return false; } void stuff_to_do_every_250us () // Take readings of system voltage and current { if (!trigger_current_read) return; trigger_current_read = false; int ch; ch++; I_maf[maf_ptr] = ht_amps_ain.read_u16(); // Read ACS709 ammeter module V_maf[maf_ptr] = ht_volts_ain.read_u16(); // Read system voltage maf_ptr++; if (maf_ptr > MAF_PTS - 1) maf_ptr = 0; } void set_run_mode (int mode) { // NOTE Nov 2017 - Handbrake not implemented if (mode == HANDBRAKE_SLIPPING) slider.handbrake_slipping = true; else slider.handbrake_slipping = false; switch (mode) { // STATES, INACTIVE, RUN, NEUTRAL_DRIFT, REGEN_BRAKE, PARK}; // case HANDBRAKE_SLIPPING: // break; case PARK: // PARKED new rom code IS now finished. // forward_pin = 0; // reverse_pin = 0; slider.state = mode; set_V_limit (0.075); // was 0.1 set_I_limit (slider.handbrake_effort); // char tmp[16]; // sprintf (tmp, "vi7 %d\r", (int)(slider.handbrake_effort * 99.0)); // com.printf ("%s", tmp); break; case REGEN_BRAKE: // BRAKING, pwm affects degree com.printf ("rb\r"); // forward_pin = 1; // reverse_pin = 1; slider.state = mode; break; case NEUTRAL_DRIFT: slider.state = mode; set_I_limit (0.0); // added after first test runs, looking for cause of mechanical startup snatch set_V_limit (0.0); // added after first test runs, looking for cause of mechanical startup snatch break; case RUN: if (slider.direction) { com.printf ("fw\r"); // forward_pin = 0; // reverse_pin = 1; } else { com.printf ("re\r"); // forward_pin = 1; // reverse_pin = 0; } slider.state = mode; break; default: break; } } void throttle (double p) { // New Apr 2018 ; servo adjusts throttle lever on Honda GX120 const double THR_MAX = 0.92; const double THR_MIN = 0.09; const double RANGE = THR_MAX - THR_MIN; if (p > 1.0) p = 1.0; if (p < 0.0) p = 0.0; // p = 1.0 - p; // if direction needs swapping p *= RANGE; p += THR_MIN; servo1 = p; } void lights (int onoff) { I_sink2 = onoff; // lamp right I_sink3 = onoff; // lamp left } int main() { int qtr_sec = 0, seconds = 0, minutes = 0; double electrical_power_Watt = 0.0; ky_bd kybd_a, kybd_b; memset (&kybd_a, 0, sizeof(kybd_a)); memset (&kybd_b, 0, sizeof(kybd_b)); pc.baud (9600); com.baud (19200); pc.printf ("\r\n\n\nLoco_TS_2018 Loco Controller starting, testing qspi mem\r\n"); // ir.baud (1200); I_sink1 = 0; // turn outputs off I_sink2 = 0; // lamp right I_sink3 = 0; // lamp left I_sink4 = 0; I_sink5 = 0; // spareio_d8.mode (PullUp); now output driving throttle servo // spareio_d9.mode (PullUp); spareio_d10.mode(PullUp); Ticker tick250us; // Setup User Interrupt Vectors tick250us.attach_us (&ISR_current_reader, 250); // count 125 of these to trig 31.25ms // tick32ms.attach_us (&ISR_tick_32ms, 32001); // tick32ms.attach_us (&ISR_tick_32ms, 31250); // then count 8 pulses per 250ms // tick250ms.attach_us (&ISR_tick_250ms, 250002); #ifdef QSPI extern int qspifindfree (uint8_t* dest, uint32_t addr) ; extern int ask_QSPI_OK () ; extern bool qspimemcheck () ; extern int qspiinit () ; int qspi_ok = ask_QSPI_OK (); //extern int qspieraseblock (uint32_t addr) ; //extern int qspiwr (uint8_t* src, uint32_t addr) ; //extern int qspiwr (uint8_t* src, uint32_t addr, uint32_t len) ; //extern int qspird (uint8_t* dest, uint32_t addr, uint32_t len) ; //#define BUFFER_SIZE ((uint32_t)32) //#define QSPI_BUFFER_SIZE ((uint32_t)4270) // Big enough for 4096 byte block //#define WRITE_READ_ADDR ((uint32_t)0x0050) //#define WRITE_READ_ADDR ((uint32_t)0x0010) //#define WRITE_READ_ADDR2 ((uint32_t)0x0020) //#define WRITE_READ_ADDR3 ((uint32_t)0x4030) //#define QSPI_BASE_ADDR ((uint32_t)0x90000000) // 123456789012345 // uint8_t WriteBuffer[QSPI_BUFFER_SIZE] = "Hello World !!!\0"; // uint8_t ReadBuffer[QSPI_BUFFER_SIZE]; // const uint8_t MemInitString[] = "Electric Locomotive Controller - Jon Freeman B. Eng. Hons - November 2017\0"; // const uint8_t Ifound_String[] = "I found the man sir, god I wish I hadn't!\0"; // pc.printf ("[%s]\r\n", MemInitString); // pc.printf ("[%s]\r\n", Ifound_String); // pc.printf("\n\nQSPI demo started\r\n"); // Check initialization if (qspiinit() != qspi_ok) error("Initialization FAILED\r\n"); else pc.printf("Initialization PASSED\r\n"); // Check memory informations if (!qspimemcheck ()) pc.printf ("Problem with qspimemcheck\r\n"); /* // Erase memory qspieraseblock (WRITE_READ_ADDR); qspieraseblock (WRITE_READ_ADDR2); qspieraseblock (WRITE_READ_ADDR3); qspieraseblock (0x02000); // Write memory qspiwr(WriteBuffer, WRITE_READ_ADDR); qspird(ReadBuffer, WRITE_READ_ADDR, 20); qspiwr((uint8_t*)"Oh what a joy it is.", 0x02000); qspird(ReadBuffer, 0x02000, 20); qspieraseblock (0x02000); */ // Read memory // if (qspi.Read(ReadBuffer, WRITE_READ_ADDR, 11) != QSPI_OK) /* qspird(ReadBuffer, WRITE_READ_ADDR, 256); qspird(ReadBuffer, WRITE_READ_ADDR + 1, 256); qspird(ReadBuffer, 0, 256); // Jon's play with Write memory qspiwr ((uint8_t*)MemInitString, WRITE_READ_ADDR2); qspiwr ((uint8_t*)Ifound_String, WRITE_READ_ADDR3); qspird(ReadBuffer, 0, 256); // shows correct write of "Electric Locomotive Controller" after "Hello World !!!" qspird(ReadBuffer, WRITE_READ_ADDR2, 250); qspird(ReadBuffer, WRITE_READ_ADDR3, 250); pc.printf ("\r\r\r\n"); qspiwr ((uint8_t*)"Today I have to pack the car full of all sorts of stuff including 7.25 gauge loco and take it up to Begbrook to show members of Bristol Society of Model and Experimental Engineers!", 2000); qspird(ReadBuffer, 0, 4096); int pos = qspifindfree (ReadBuffer, 0); pc.printf ("qspifindfree reports %d\r\n", pos); */ //bool odometer_update (uint32_t pulsetot, uint16_t pow, uint16_t volt) ; // Hall pulse total updated once per sec and saved in blocks of 4096 bytes on QSPI onboard memory #endif if (f_r_switch) { slider.direction = FWD; // make decision from key switch position here com.printf ("fw\r"); } else { slider.direction = REV; // make decision from key switch position here com.printf ("re\r"); } // max_pwm_ticks = SystemCoreClock / (2 * PWM_HZ); // prescaler min value is 2, or so it would seem. SystemCoreClock returns 216000000 on F746NG board // maxv.period_ticks (MAX_PWM_TICKS + 1); // around 18 kHz // maxi.period_ticks (MAX_PWM_TICKS + 1); set_I_limit (0.0); set_V_limit (0.0); throttle (0.0); // Set revs to idle. Start engine and warm up before powering up control setup_pccom (); setup_lococom (); pc.printf ("Jon's Touch Screen Loco 2018 sytem starting up %s\r\n", slider.direction ? "Forward":"Reverse"); uint8_t lcd_status = touch_screen.Init(lcd.GetXSize(), lcd.GetYSize()); if (lcd_status != TS_OK) { lcd.Clear(LCD_COLOR_RED); lcd.SetBackColor(LCD_COLOR_RED); lcd.SetTextColor(LCD_COLOR_WHITE); lcd.DisplayStringAt(0, LINE(5), (uint8_t *)"TOUCHSCREEN INIT FAIL", CENTER_MODE); wait (20); } else { lcd.Clear(LCD_COLOR_DARKBLUE); lcd.SetBackColor(LCD_COLOR_GREEN); lcd.SetTextColor(LCD_COLOR_WHITE); lcd.DisplayStringAt(0, LINE(5), (uint8_t *)"TOUCHSCREEN INIT OK", CENTER_MODE); } lcd.SetFont(&Font16); lcd.Clear(LCD_COLOR_LIGHTGRAY); setup_buttons(); // draws buttons slider.oldpos = 0; slider.loco_speed = 0.0; slider.handbrake_effort = 0.1; slider.position = MAX_POS - 2; // Low down in REGEN_BRAKE position - NOT to power-up in PARK SliderGraphic (slider); // sets slider.state to value determined by slider.position set_run_mode (REGEN_BRAKE); // sets slider.mode lcd.SetBackColor(LCD_COLOR_DARKBLUE); vm_set(); // Draw 3 analogue meter movements, speedo, voltmeter, ammeter // if (odometer_zero ()) // pc.printf ("TRUE from odometer_zero\r\n"); // else // pc.printf ("FALSE from odometer_zero\r\n"); double torque_req = 0.0; bool toggle32ms = false; // Main loop uint32_t boards_fitted[8], bfptr = 0; for (int i = 0; i < 8; i++) boards_fitted[i] = 0; sys_timer_32Hz = 0; int last_digit = 0, board_cnt = 0, ch; while (sys_timer_32Hz < 12) { // Sniff out system, discover motor controllers connected while (!trigger_32ms) // command_line_interpreter (); clicore (pccom); trigger_32ms = false; if (sys_timer_32Hz < 11) { // issue "0who\r", "1who\r" ... "9who\r" com.putc ((sys_timer_32Hz - 1) | '0'); com.printf ("who\r"); } while (com.readable()) { ch = com.getc(); if (ch != '\r') { if (isdigit(ch)) last_digit = ch; } else { // got '\r' at end of line if (isdigit(last_digit)) boards_fitted[board_cnt++] = last_digit; last_digit = 0; } } } // boards_fitted[0] = '4'; // boards_fitted[1] = '5'; while (boards_fitted[bfptr]) { // This works, identified BLDC Motor Controller board ID chars '0' to '9' listed in boards_fitted[] pc.printf ("Board %c found\r\n", boards_fitted[bfptr++]); } speed.set_board_IDs (boards_fitted); // store number and IDs of Dual BLDC boards identified bfptr = 0; clicore (pccom); pc.printf ("pcmenuitems %d, commenuitems %d\r\n", pccom.numof_menu_items, lococom.numof_menu_items); // perror (strerror(errno)); // Done setup, time to roll ! lights (1); // Headlights ON! while (1) { struct ky_bd * present_kybd, * previous_kybd; bool sliderpress = false; // command_line_interpreter () ; // Do any actions from command line via usb link clicore (pccom); stuff_to_do_every_250us () ; if (trigger_32ms == true) { // Stuff to do every 32 milli secs trigger_32ms = false; clicore (lococom); toggle32ms = !toggle32ms; if (toggle32ms) { present_kybd = &kybd_a; previous_kybd = &kybd_b; } else { present_kybd = &kybd_b; previous_kybd = &kybd_a; } read_keypresses (*present_kybd); sliderpress = false; slider.recalc_run = false; int j = 0; // if (present2->count > previous_kybd->count) pc.printf ("More presses\r\n"); // if (present2->count < previous_kybd->count) pc.printf ("Fewer presses\r\n"); if (present_kybd->count || previous_kybd->count) { // at least one key pressed this time or last time int k; double dbl; // pc.printf ("Keys action may be required"); // if key in present and ! in previous, found new key press to handle // if key ! in present and in previous, found new key release to handle if (inlist(*present_kybd, SLIDER)) { // Finger is on slider, so Update slider graphic here sliderpress = true; k = present_kybd->slider_y; // get position of finger on slider if (slider.state == RUN && k != slider.position) // Finger has moved within RUN range slider.recalc_run = true; if (slider.state == RUN && k >= NEUTRAL_VAL) { // Finger has moved from RUN to BRAKE range slider.position = k = NEUTRAL_VAL; // kill drive for rapid reaction to braking throttle (0.0); } else { // nice slow non-jerky glidey movement required dbl = (double)(k - slider.position); dbl /= 13.179; // Where did 13.179 come from ? if (dbl < 0.0) dbl -= 1.0; if (dbl > 0.0) dbl += 1.0; slider.position += (int)dbl; } SliderGraphic (slider); // sets slider.state to value determined by slider.position set_run_mode (slider.state); draw_button_hilight (SLIDER, LCD_COLOR_YELLOW) ; if (slider.state == REGEN_BRAKE) { double brake_effort = ((double)(slider.position - NEUTRAL_VAL) / (double)(MAX_POS - NEUTRAL_VAL)); // brake_effort normalised to range 0.0 to 1.0 brake_effort *= 0.97; // upper limit to braking effort, observed effect before was quite fierce pc.printf ("Brake effort %.2f\r\n", brake_effort); /* set_pwm (brake_effort); */ set_V_limit (sqrt(brake_effort)); // sqrt gives more linear feel to control set_I_limit (1.0); throttle (0.0); } } else { // pc.printf ("Slider not touched\r\n"); } j = 0; while (j < present_kybd->count) { // handle new key presses k = present_kybd->ky[j++].keynum; if (inlist(*present_kybd, k)) { switch (k) { // Here for auto-repeat type key behaviour case 21: // key is 'voltmeter' // set_V_limit (last_V * 1.002 + 0.001); break; case 22: // key is 'ammeter' // set_V_limit (last_V * 0.99); break; } // endof switch (k) } // endof if (inlist(*present2, k)) { if (inlist(*present_kybd, k) && !inlist(*previous_kybd, k)) { // New key press detected pc.printf ("Handle Press %d\r\n", k); draw_button_hilight (k, LCD_COLOR_YELLOW) ; switch (k) { // Handle new touch screen button presses here - single action per press, not autorepeat case SPEEDO_BUT: // pc.printf ("Speedometer key pressed %d\r\n", k); break; case VMETER_BUT: // // pc.printf ("Voltmeter key pressed %d\r\n", k); I_sink5 = 1; // Turn on hi-horn break; case AMETER_BUT: // // pc.printf ("Ammeter key pressed %d\r\n", k); I_sink4 = 1; // Turn on lo-horn break; default: pc.printf ("Unhandled keypress %d\r\n", k); break; } // endof switch (button) } } // endof while - handle new key presses j = 0; while (j < previous_kybd->count) { // handle new key releases k = previous_kybd->ky[j++].keynum; if (inlist(*previous_kybd, k) && !inlist(*present_kybd, k)) { pc.printf ("Handle Release %d\r\n", k); draw_button_hilight (k, LCD_COLOR_DARKBLUE) ; switch (k) { // Handle new touch screen button RELEASes here - single action per press, not autorepeat case SPEEDO_BUT: // pc.printf ("Speedometer key released %d\r\n", k); break; case VMETER_BUT: // I_sink5 = 0; // Turn hi-tone horn off // pc.printf ("Voltmeter key released %d\r\n", k); break; case AMETER_BUT: // I_sink4 = 0; // Turn lo-tone horn off // pc.printf ("Ammeter key released %d\r\n", k); break; default: pc.printf ("Unhandled keyreleas %d\r\n", k); break; } // endof switch (button) } } // endof while - handle new key releases } // endof at least one key pressed this time or last time if (sliderpress == false) { // need to glide dead-mans function towards neutral here if (slider.position < NEUTRAL_VAL) { slider.position += 1 + (NEUTRAL_VAL - slider.position) / DAMPER_DECAY; SliderGraphic (slider); slider.recalc_run = true; } } if (slider.recalc_run) { // range of slider.position in RUN mode is min_pos_() to NEUTRAL_VAL - 1 slider.recalc_run = false; // All RUN power and pwm calcs done here int b = slider.position; if (b > NEUTRAL_VAL) b = NEUTRAL_VAL; if (b < MIN_POS) // if finger position is above top of slider limit b = MIN_POS; b = NEUTRAL_VAL - b; // now got integer going positive for increasing power demand torque_req = (double) b; torque_req /= (NEUTRAL_VAL - MIN_POS); // in range 0.0 to 1.0 pc.printf ("torque_rec = %.3f, last_V = %.3f\r\n", torque_req, last_V); set_I_limit (torque_req); if (torque_req < 0.05) { set_V_limit (last_V / 2.0); throttle (torque_req * 6.0); } else { throttle (0.3 + (torque_req / 2.0)); if (last_V < 0.99) set_V_limit (last_V + 0.05); // ramp voltage up rather than slam to max } } } // endof doing 32ms stuff if (qtrsec_trig == true) { // do every quarter second stuff here qtrsec_trig = false; double speedmph = speed.mph(), amps = read_ammeter(), volts = read_voltmeter(); slider.loco_speed = speedmph; electrical_power_Watt = volts * amps; // visible throughout main update_meters (speedmph, electrical_power_Watt, volts) ; // displays speed, volts and power (volts times amps) led_grn = !led_grn; if (slider.state == PARK) { if (speedmph > LOCO_HANDBRAKE_ESCAPE_SPEED / 4.0) { slider.handbrake_effort *= 1.1; if (slider.handbrake_effort > 0.55) slider.handbrake_effort = 0.55; set_run_mode (PARK); pc.printf ("Handbrake slipping, effort %.2f\r\n", slider.handbrake_effort); } if (speedmph < 0.02) { slider.handbrake_effort *= 0.9; if (slider.handbrake_effort < 0.05) slider.handbrake_effort = 0.05; set_run_mode (PARK); pc.printf ("Handbrake not slipping, effort %.2f\r\n", slider.handbrake_effort); } } speed.request_rpm (); // issues "'n'rpm\r", takes care of cycling through available boards in sequence // switch (qtr_sec) { // Can do sequential stuff quarter second apart here // } // End of switch qtr_sec qtr_sec++; // Can do stuff once per second here if(qtr_sec > 3) { qtr_sec = 0; seconds++; com.printf ("kd\r"); // Kick the WatchDog timers in the Twin BLDC drive boards if (seconds > 59) { seconds = 0; minutes++; // do once per minute stuff here } // fall back into once per second #ifdef QSPI // recent_distance += 300; recent_distance += (speedmph * (111.76 * 4.0)); // Convert mph to distance mm travelled in one second uint32_t new_metres = ((uint32_t)recent_distance) / 1000; recent_distance -= (double)(new_metres * 1000); if (!odometer_update (new_metres, (uint16_t)electrical_power_Watt, (uint16_t)(volts * 500.0))) pc.printf ("Probs with odometer_update"); char dist[20]; sprintf (dist, "%05d m", odometer_out()); displaytext (236, 226, 2, dist); /* Odometer Update stuff now needs fixing to take updagte in form of mm travelled in previous period //bool odometer_update (uint32_t pulsetot, uint16_t pow, uint16_t volt) ; // Hall pulse total updated once per sec and saved in blocks of 4096 bytes on QSPI onboard memory bool distance_measurement::update (uint32_t new_metres_travelled, uint16_t powr, uint16_t volt) { if (!odometer_update (historic_distance, (uint16_t)electrical_power_Watt, (uint16_t)(volts * 500.0))) pc.printf ("Probs with odometer_update"); char dist[20]; // sprintf (dist, "%05d m", speed.metres_travelled()); displaytext (236, 226, 2, dist); */ #endif // calc_motor_amps( mva); } // endof if(qtr_sec > 3 } // endof if (qtrsec_trig == true) { } // endof while(1) main programme loop }