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
Diff: main.cpp
- Revision:
- 12:a25bdf135348
- Parent:
- 11:a573664b1a59
- Child:
- 14:6bcec5ac21ca
--- a/main.cpp Sat Jun 23 09:37:41 2018 +0000 +++ b/main.cpp Mon Jan 14 16:39:41 2019 +0000 @@ -1,14 +1,15 @@ /* -April 2018 - Jon Freeman +November 2018 - Jon Freeman +Cloned from Loco_TS_2018_06 on 23rd November 2018 -Touch Screen controller communicates with 1, 2, ... n Twin BLDC Controller boards via opto-isolated serial port. +Touch Screen controller communicates with 1, 2, ... n Brushless STM3 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 +4 Not Used on TS2018, connection from Twin BLDC Controller only - 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 @@ -21,24 +22,47 @@ #include "Servo.h" #include "TS_DISCO_F746NG.h" #include "LCD_DISCO_F746NG.h" -#include <cctype> + +char const_version_string[] = {"1.0.0\0"}; // Version string, readable from serial ports + +LCD_DISCO_F746NG lcd ; // LCD display +TS_DISCO_F746NG touch_screen ; // Touch Screen +screen_touch_handler slider ; // Loco driver's slider control -LCD_DISCO_F746NG lcd; -TS_DISCO_F746NG touch_screen; +// see movingcoilmeter.h ffi +moving_coil_meter Voltmeter ( LCD_COLOR_BLACK, LCD_COLOR_WHITE, LCD_COLOR_RED, LCD_COLOR_BLUE, LCD_COLOR_MAGENTA, + VOLTMETER_X, VOLTMETER_Y, V_A_SIZE, 22.0, 61.0, 1.25 * PI, -0.25 * PI , 20, "V", ONE_DP, false), + Powermeter ( LCD_COLOR_BLACK, LCD_COLOR_WHITE, LCD_COLOR_RED, LCD_COLOR_BLUE, LCD_COLOR_BLUE, + AMMETER_X, AMMETER_Y, V_A_SIZE, -1400.0, 1400.0, 1.25 * PI, -0.25 * PI , 14, "Watt", NO_DPS, false), + Speedo ( SPEEDO_BODY_COLOUR, SPEEDO_DIAL_COLOUR, LCD_COLOR_RED, SPEEDO_TEXT_COLOUR, LCD_COLOR_BLACK, + SPEEDO_X, SPEEDO_Y, SPEEDO_SIZE, 0.0, 12.0, 1.25 * PI, -0.25 * PI , 12, "MPH", ONE_DP, false); + // 3 instances of moving coil meter graphic -//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 +error_handling_Jan_2019 Controller_Error ; // Provides array usable to store error codes. +STM3_ESC_Interface My_STM3_ESC_boards ; + +extern command_line_interpreter_core pcli, ploco; // pcli handles comms with pc, ploco handles comms with STM3_ESC boards -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 +/* +STRANGE BEHAVIOUR WARNING ! +This project requires two serial ports. +The following combination of one 'Serial' and one 'AsyncSerial' is the only combination found to work ! +MODSERIAL has not been adapted to F746NG, will not compile. +Does compile with BufferedSerial but crashes the whole thing. No startup. +*/ -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 +Serial pc (USBTX, USBRX); // AsyncSerial does not work here. Comms to 'PuTTY' or similar comms programme on pc +AsyncSerial com2escs (A4, A5); // Com port to opto isolators on Twin BLDC Controller boards. Only AsyncSerial works here + +//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); @@ -52,190 +76,42 @@ //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); // Model control servo used to adjust Honda engine speed -Servo servo1 (D6); // Now used to adjust Honda speed - -extern uint32_t odometer_out () ; // Read latest total of metres travelled ever +extern bool test_qspi () ; 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; +extern void rewrite_odometer () ; 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 ; - +int32_t V_maf[MAF_PTS + 2], I_maf[MAF_PTS + 2], maf_ptr = 0; // +volatile uint32_t sys_timer_32Hz = 0; +double recent_distance = 0.0; -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; +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 - double mph_maf [(SPEED_AVE_PTS + 1)][4]; // 22/06/2018 Allow for up to 4 ESC boards - 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, - mphmafptr, - 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; - mphmafptr = 0; - board_count = 0; - b = 0; - reqno = 0; - } - bool request_rpm () ; - bool request_mph () ; // 22/06/2018 - void rpm_update(struct parameters & a) ; - void mph_update(struct parameters & a) ; // 22/06/2018 - 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; -} - -bool speed_2018::request_mph () { // Issue "'n'mph\r" to BLDC board to request RPM 22/06/2018 - if (board_IDs[0] == 0) - return false; // No boards identified - if (board_IDs[reqno] == 0) - reqno = 0; - com.putc (board_IDs[reqno++]); - com.printf ("mph\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 speed_2018::mph_update(struct parameters & a) { // Puts new readings into mem 22/06/2018 -// mph_maf [mphmafptr][b++] = a.dbl[1]; - pc.printf ("%2fmph\r\n"); -// speed_maf_mem [mafptr][b++] = (uint32_t)a.dbl[1]; -/* if (b >= board_count) { - b = 0; - mphmafptr++; - if (mphmafptr >= SPEED_AVE_PTS) - mphmafptr = 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 mph_push (struct parameters & a) { // Latest RPM reports from Dual BLDC Motor Controllers arrive here 22/06/2018 - speed.mph_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 () +double read_voltmeter () { int32_t 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 +} + +double read_ammeter () // Returns amps taken by STM3escs - amps dumped due to over-voltage +{ // Could make sense to read this at up to 32 times per second + int32_t a = 0; // MAF data almost completely renewed at this rate + for (int b = 0; b < MAF_PTS; b++) // MAF updated every 250us, MAF size = MAF_PTS (once set to 140, probably still is) a += I_maf[b]; a /= MAF_PTS; a -= 0x4000; @@ -243,16 +119,46 @@ 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 +const int BIGMAFSIZ = 8; + +class ammeter_reading { + double bigImaf[BIGMAFSIZ]; + int bigImafptr; + double amps_longave, // internal use only + amps_latest, // update() called @ 32Hz. Value stored here is average over most recent 3125us + amps_filtered; // Average of the BIGMAFSIZ most recent samples stored in latest +public: + ammeter_reading () { // constructor + bigImafptr = 0; + amps_longave = amps_latest = amps_filtered = 0.0; + for (int i = 0; i < BIGMAFSIZ; i++) + bigImaf[i] = 0.0; + } + void update () ; // Read ammeter core, store most recent 32ms or so worth in amps_latest, 250ms average in amps_filtered + double latest () ; + double filtered() ; +} Ammeter ; + +double ammeter_reading::latest () { + return amps_latest; } +double ammeter_reading::filtered () { + return amps_filtered; +} + +void ammeter_reading::update () { + amps_latest = read_ammeter(); + amps_longave -= bigImaf[bigImafptr]; + bigImaf[bigImafptr] = amps_latest; + amps_longave += amps_latest; + bigImafptr++; + if (bigImafptr >= BIGMAFSIZ) + bigImafptr = 0; + amps_filtered = amps_longave / BIGMAFSIZ; +} + + // Interrupt Service Routines void ISR_current_reader (void) // FIXED at 250us @@ -260,12 +166,12 @@ 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 + if (ms32 >= 125) { // 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) { + if (ms250 >= 8) { ms250 = 0; qtrsec_trig = true; } @@ -274,84 +180,10 @@ // 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; + const double THR_MAX = 0.92; // Values tweaked to suit servo and linkage fitted to loco power unit + const double THR_MIN = 0.09; + const double RANGE = (THR_MAX - THR_MIN); if (p > 1.0) p = 1.0; if (p < 0.0) @@ -363,383 +195,141 @@ } +void horn (int which, int onoff) { + if (which == HI_HORN) + I_sink5 = onoff; + else + I_sink4 = onoff; +} + + void lights (int onoff) { I_sink2 = onoff; // lamp right I_sink3 = onoff; // lamp left } -int main() + +void draw_normal_run_screen () { + lcd.Clear(LCD_COLOR_LIGHTGRAY); + setup_buttons(); // draws buttons + slider.DrawSlider (); + // Draw 3 analogue meter movements, speedo, voltmeter, ammeter + Speedo.redraw(); + Voltmeter.redraw(); + Powermeter.redraw(); +} + + +int main() // Programme entry point { - 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)); + int qtr_sec = 0, seconds = 0, minutes = 0; + double electrical_power_Watt = 0.0, volts = 0.0; + pc.baud (9600); - com.baud (19200); - pc.printf ("\r\n\n\nLoco_TS_2018 Loco Controller starting\r\n"); + com2escs.baud (19200); I_sink1 = 0; // turn outputs off I_sink2 = 0; // lamp right I_sink3 = 0; // lamp left - I_sink4 = 0; - I_sink5 = 0; + I_sink4 = 0; // low horn + I_sink5 = 0; // high horn spareio_d10.mode(PullUp); - Ticker tick250us; + Ticker tick250us; // Master 4kHz interrupt timebase // Setup User Interrupt Vectors - tick250us.attach_us (&ISR_current_reader, 250); // count 125 of these to trig 31.25ms -#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) ; + tick250us.attach_us (&ISR_current_reader, 250); // count 125 interrupts to trig 31.25ms -//#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"); +// QSPI memory is now in constant use for odometer + if (!test_qspi()) + Controller_Error.set (FAULT_QSPI, -1); // pc.printf ("Problem with qspimemcheck\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); + slider.direction = f_r_switch ? REV : FWD; // Only place in the code where direction gets set. Centre-Off power switch REV-OFF-FWD. - // 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); -*/ + My_STM3_ESC_boards.set_I_limit (0.0); + My_STM3_ESC_boards.set_V_limit (0.0); + My_STM3_ESC_boards.message ("rb\r"); + throttle (0.0); // Set revs to idle. Start engine and warm up before powering up control + pc.printf ("\r\n\n\nJon's Loco_TS_2018 Loco Controller ver %s starting, direction %s\r\n", const_version_string, slider.direction ? "Forward":"Reverse"); -#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"); - } - 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 + Controller_Error.set (FAULT_TS, -1); + } + 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); // 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) - 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); - // Done setup, time to roll ! - lights (1); // Headlights ON! - while (1) { - - struct ky_bd * present_kybd, * previous_kybd; - bool sliderpress = false; - clicore (pccom); // Do any actions from command line via usb link - - stuff_to_do_every_250us () ; // Only does work when trigger_current_read flag has been set by ISR + My_STM3_ESC_boards.search_for_escs (); // Build list of connected STM3_ESC IDs - 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); - } +/* Controller_Error.set (3, 99); + pc.printf ("%lx red\r\n", LCD_COLOR_RED); //LCD_COLOR is 0xffrrggbb + pc.printf ("%lx grn\r\n", LCD_COLOR_GREEN); + pc.printf ("%lx blu\r\n", LCD_COLOR_BLUE); + pc.printf ("%lx blk\r\n", LCD_COLOR_BLACK); + pc.printf ("%lx white\r\n", LCD_COLOR_WHITE); +*/ + draw_normal_run_screen (); - 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) ; + pc.printf ("Controller_Error.all_good() ret'd %s\r\n", Controller_Error.all_good() ? "true" : "false"); - 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.98; // 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"); - } + while (1) { // main prog loop - 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 + pcli.sniff (); // Do any actions from command line serial port via usb link - 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 (trigger_current_read) { // flag set in interrupt handler every 250us + trigger_current_read = false; + I_maf[maf_ptr] = ht_amps_ain.read_u16(); // Read raw ACS709 ammeter module + V_maf[maf_ptr] = ht_volts_ain.read_u16(); // Read raw system voltage + maf_ptr++; + if (maf_ptr > MAF_PTS - 1) + maf_ptr = 0; + } // endof stuff to do every 250us - 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 (trigger_32ms == true) { // Stuff to do every 31.25 milli secs (32Hz) + trigger_32ms = false; + ploco.sniff (); // Only call within main loop, checks message responses from STM3_ESC boards + Ammeter.update (); // This updates Ammeter 'latest' and 'filtered' variables every 31.25ms + slider.HandleFingerInput (); // Do everything concerning fingers on touch screen + } // endof doing 32Hz 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) + volts = read_voltmeter(); // voltage and current readings updated @ 250us, these are averaged over 35ms or so + electrical_power_Watt = volts * Ammeter.filtered(); // visible throughout main + // Update meters + Powermeter.set_value(electrical_power_Watt); + Voltmeter.set_value (volts); + Speedo.set_value (My_STM3_ESC_boards.mph); + led_grn = !led_grn; +/* +Handbrake more sensibly implemented on STM3_ESC boards ? + if (slider.state == PARK) { - if (speedmph > LOCO_HANDBRAKE_ESCAPE_SPEED / 4.0) { + if (My_STM3_ESC_boards.mph > 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) { + if (My_STM3_ESC_boards.mph < 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 -// speed.request_mph (); // issues "'n'rpm\r", takes care of cycling through available boards in sequence +*/ + My_STM3_ESC_boards.request_mph (); // 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++; @@ -747,29 +337,27 @@ 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 + Controller_Error.report_any (); } // fall back into once per second -#ifdef QSPI - 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); - sprintf (dist, "%06dm", odometer_out()); // 12th June 2018 changed 05 to 06 to allow correct display of tot distance > 99999 metres -// displaytext (236, 226, 2, dist); - displaytext (241, 224, 2, dist); -#endif + if (seconds & 1) + Speedo.LED (0, LCD_COLOR_DARKGRAY); + else + Speedo.LED (0, LCD_COLOR_RED); + My_STM3_ESC_boards.message ("kd\r"); // Kick the WatchDog timers in the Twin BLDC drive boards + recent_distance += (My_STM3_ESC_boards.mph * (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"); + Controller_Error.set (FAULT_ODOMETER, 1); + } + rewrite_odometer () ; // Update text on speedo dial face } // endof if(qtr_sec > 3 } // endof if (qtrsec_trig == true) { } // endof while(1) main programme loop -} +} // endof main () - -