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:
- 2019-01-14
- Revision:
- 12:a25bdf135348
- Parent:
- 11:a573664b1a59
- Child:
- 14:6bcec5ac21ca
File content as of revision 12:a25bdf135348:
/* November 2018 - Jon Freeman Cloned from Loco_TS_2018_06 on 23rd November 2018 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 - 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" 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 // 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 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 /* 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. */ 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); //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 ! Servo servo1 (D6); // Model control servo used to adjust Honda engine speed 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 void setup_buttons () ; extern void rewrite_odometer () ; static const int MAF_PTS = 140, // Moving Average Filter points. Filters reduce noise on volatage and current readings FWD = 0, REV = ~FWD; 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; bool qtrsec_trig = false; bool trigger_current_read = false; volatile bool trigger_32ms = false; 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; double i = (double) (0 - a); return i / 200.0; // Fiddled to get current reading close enough } 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 { 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 >= 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 >= 8) { ms250 = 0; qtrsec_trig = true; } } } // End of Interrupt Service Routines void throttle (double p) { // New Apr 2018 ; servo adjusts throttle lever on Honda GX120 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) p = 0.0; // p = 1.0 - p; // if direction needs swapping p *= RANGE; p += THR_MIN; servo1 = p; } 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 } 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, volts = 0.0; pc.baud (9600); com2escs.baud (19200); I_sink1 = 0; // turn outputs off I_sink2 = 0; // lamp right I_sink3 = 0; // lamp left I_sink4 = 0; // low horn I_sink5 = 0; // high horn spareio_d10.mode(PullUp); Ticker tick250us; // Master 4kHz interrupt timebase // Setup User Interrupt Vectors tick250us.attach_us (&ISR_current_reader, 250); // count 125 interrupts to trig 31.25ms // 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"); slider.direction = f_r_switch ? REV : FWD; // Only place in the code where direction gets set. Centre-Off power switch REV-OFF-FWD. 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"); uint8_t lcd_status = touch_screen.Init(lcd.GetXSize(), lcd.GetYSize()); if (lcd_status != TS_OK) { 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"); lights (1); // Headlights ON! My_STM3_ESC_boards.search_for_escs (); // Build list of connected STM3_ESC IDs /* 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 (); pc.printf ("Controller_Error.all_good() ret'd %s\r\n", Controller_Error.all_good() ? "true" : "false"); while (1) { // main prog loop pcli.sniff (); // Do any actions from command line serial port via usb link 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 (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; 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 (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 (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); } } */ 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++; // Can do stuff once per second here if(qtr_sec > 3) { qtr_sec = 0; seconds++; if (seconds > 59) { seconds = 0; minutes++; // do once per minute stuff here Controller_Error.report_any (); } // fall back into once per second 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 ()