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

Revision:
7:3b1f44cd4735
Parent:
5:21a8ac83142c
Child:
8:5945d506a872
--- a/main.cpp	Tue May 01 08:34:36 2018 +0000
+++ b/main.cpp	Wed May 09 15:42:43 2018 +0000
@@ -22,6 +22,7 @@
 #include "TS_DISCO_F746NG.h"
 #include "LCD_DISCO_F746NG.h"
 #include <cctype>
+#include <cerrno>
 
 LCD_DISCO_F746NG    lcd;
 TS_DISCO_F746NG     touch_screen;
@@ -34,8 +35,8 @@
 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);  //  another horn
-DigitalOut  I_sink3          (D3);
+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
@@ -56,12 +57,9 @@
 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
 
-/*
-Speed now read via opto serial ports
-*/
-
 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
 
@@ -78,6 +76,11 @@
 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
@@ -99,55 +102,80 @@
 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][NUMBER_OF_MOTORS],
-                axle_total  [4],
-                mafptr;
-    double      average_rpm_out;
+    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));
-        axle_total[0] = axle_total[1] = axle_total[2] = axle_total[3] = 0;
+        for (int i = 0; i < sizeof(axle_total) / sizeof(uint32_t); i++)
+            axle_total[i] = 0;
         mafptr = 0;
-        average_rpm_out = 0.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 ()  ;
 }
-   speed2  ;
+   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 ()  {
-    int t[4] = {0,0,0,0};
+    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++) {
-        t[0] += speed_maf_mem[i][0];
-        t[1] += speed_maf_mem[i][1];
-        t[2] += speed_maf_mem[i][2];
-        t[3] += speed_maf_mem[i][3];
+        for (int j = 0; j < board_count * 2; j++)
+            t[j] += speed_maf_mem[i][j];
     }
     int j = 0;
-    for (int i = 0; i < 4; i++) {
+    for (int i = 0; i < board_count * 2; i++) {
         j += t[i];
         axle_total[i] = t[i];
     }
-    return  (rpm2mph * ((double) j) / (SPEED_AVE_PTS * 4));
+    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
-    if  (a.gp_i == 0)   {
-        speed_maf_mem   [mafptr][0] = (uint32_t)a.dbl[0];
-        speed_maf_mem   [mafptr][1] = (uint32_t)a.dbl[1];
-    }
-    else    {
-        speed_maf_mem   [mafptr][2] = (uint32_t)a.dbl[0];
-        speed_maf_mem   [mafptr][3] = (uint32_t)a.dbl[1];
+    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)
+        if  (mafptr >= SPEED_AVE_PTS)   
             mafptr = 0;
     }
 }
 
 
 void    rpm_push    (struct parameters & a)  {  //  Latest RPM reports from Dual BLDC Motor Controllers arrive here
-    speed2.rpm_update   (a);
+    speed.rpm_update   (a);
 //    pc.printf   ("RPM%d %d, mph %.1f\r\n", (int)a.dbl[0], (int)a.dbl[1], speed2.mph());
 }
 
@@ -161,36 +189,27 @@
         p = 1.0;
     last_V = p;
     com.printf  ("v%d\r", (int)(p * 99.0));
-//    p *= 0.95;   //  need limit, ffi see MCP1630 data
-//    p   = 1.0 - p;  //  because pwm is wrong way up
-//    maxv.pulsewidth_ticks  ((int)(p * MAX_PWM_TICKS));  //  PWM output on pin D12 inverted motor pwm
 }
 
 void    set_I_limit (double p)     //  Sets max motor current
 {
-//    int a;
     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));
-//    a = (int)(p * MAX_PWM_TICKS);
-//    if  (a > MAX_PWM_TICKS)
-//        a = MAX_PWM_TICKS;
-//    if  (a < 0)
-//        a = 0;
-//    maxi.pulsewidth_ticks  (a);  //  PWM output on pin D12 inverted motor pwm
 }
 
 double  read_ammeter ()
 {
-    int a = 0;
+    int32_t a = 0;
     for (int b = 0; b < MAF_PTS; b++)
         a += I_maf[b];
     a /= MAF_PTS;
-    double i = (double) a;
-    return  (i * 95.0 / 32768.0) - 95.0 + 0.46;  //  fiddled to suit current module
+    a -= 0x4000;
+    double i = (double) (0 - a);
+    return  i / 200.0;      //  Fiddled to get current reading close enough
 }
 
 double  read_voltmeter   ()
@@ -200,7 +219,6 @@
         a += V_maf[b];
     a /= MAF_PTS;
     double v = (double) a;
-//    return  (v / 940.0) + 0.3;  //  fiddled to suit resistor values
     return  (v / 932.0) + 0.0;  //  fiddled to suit resistor values
 }
 
@@ -245,8 +263,8 @@
     trigger_current_read = false;
     int ch;
     ch++;
-    I_maf[maf_ptr] = ht_amps_ain.read_u16();
-    V_maf[maf_ptr] = ht_volts_ain.read_u16();
+    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;
@@ -314,20 +332,53 @@
 }
 
 
-extern  void    setup_pccom ()  ;
-extern  void    setup_lococom ()  ;
-extern  void    clicore (struct parameters & a) ;
-extern  struct  parameters pccom, lococom;
+void    lights  (int onoff)  {
+    I_sink2 = onoff;    //  lamp right
+    I_sink3 = onoff;    //  lamp left
+}
 
 int main()
 {
+    errno = 0;
+    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, errno %d\r\n", errno);
+//    ir.baud (1200);
+    pc.printf   ("Ln 352 errno %d\r\n", errno);
+
+    I_sink1 = 0;    //  turn outputs off
+    I_sink2 = 0;    //  lamp right
+    pc.printf   ("Ln 355 errno %d\r\n", errno);
+    I_sink3 = 0;    //  lamp left
+    I_sink4 = 0;
+    I_sink5 = 0;
+    pc.printf   ("Ln 358 errno %d\r\n", errno);
+//    spareio_d8.mode (PullUp); now output driving throttle servo
+//    spareio_d9.mode (PullUp);
+    spareio_d10.mode(PullUp);
+
+    Ticker  tick250us;
+
+    pc.printf   ("Ln 365 errno %d\r\n", errno);
+//  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    ()  ;
+    pc.printf   ("Before ask_QSPI_OK errno %d\r\n", errno);
     int qspi_ok = ask_QSPI_OK   ();
+    pc.printf   ("After ask_QSPI_OK errno %d\r\n", errno);
 //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) ;
@@ -352,6 +403,7 @@
 //    pc.printf("\n\nQSPI demo started\r\n");
 
     // Check initialization
+    pc.printf   ("errno %d, qspi mem ", errno);
     if (qspiinit() != qspi_ok)
       error("Initialization FAILED\r\n");
     else
@@ -394,51 +446,22 @@
 */
 //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
 
-    pc.printf   ("\r\r\r\n");
+    pc.printf   ("End of qspi setup, errno %d\r\n", errno);
 #endif
 
-
-    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));
-
-//    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);
-    pc.baud (9600);
-    com.baud (19200);
-//    ir.baud (1200);
-    
-    I_sink1 = 0;    //  turn outputs off
-    I_sink2 = 0;
-    I_sink3 = 0;
-    I_sink4 = 0;
-    I_sink5 = 0;
-    if  (f_r_switch)
+    if  (f_r_switch)    {
         slider.direction = FWD; //  make decision from key switch position here
-    else
+        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);
-    if  (slider.direction)
-        com.printf  ("fw\r");
-    else
-        com.printf  ("re\r");
-//    com.printf  ("rvi22 55\rkd\r");
     throttle    (0.0);      //  Set revs to idle. Start engine and warm up before powering up control
     setup_pccom   ();
     setup_lococom ();
@@ -472,13 +495,15 @@
 
     vm_set();   //  Draw 3 analogue meter movements, speedo, voltmeter, ammeter
 
-//    odometer_zero   ();
-//    pc.printf   ("Back from odometer_zero\r\n");
+//    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
 
-    int boards_fitted[8], bfptr = 0;
+    uint32_t boards_fitted[8], bfptr = 0;
     for (int i = 0; i < 8; i++)
         boards_fitted[i] = 0;
     sys_timer_32Hz  = 0;
@@ -506,13 +531,22 @@
             }
         }
     }
+
+//    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;
@@ -524,7 +558,7 @@
 
         if  (trigger_32ms == true)  {       //  Stuff to do every 32 milli secs
             trigger_32ms = false;
-        clicore (lococom);
+            clicore (lococom);
             toggle32ms = !toggle32ms;
             if  (toggle32ms)    {
                 present_kybd = &kybd_a;
@@ -595,7 +629,7 @@
                                 break;
                         }   //  endof switch (k)
                     }       //  endof if (inlist(*present2, k)) {
-                    if  (inlist(*present_kybd, k) && !inlist(*previous_kybd, 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
@@ -603,10 +637,12 @@
                                 pc.printf   ("Speedometer key pressed %d\r\n", k);
                                 break;
                             case    VMETER_BUT:  //
-                                pc.printf   ("Voltmeter key pressed %d\r\n", k);
+//                                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);
+//                                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);
@@ -620,6 +656,22 @@
                     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
@@ -635,7 +687,6 @@
             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;
-//                double  torque_req;   //  now declared above to be used as parameter for throttle
                 if  (b > NEUTRAL_VAL)
                     b = NEUTRAL_VAL;
                 if  (b < MIN_POS)   //  if finger position is above top of slider limit
@@ -645,12 +696,12 @@
                 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);
-                
-                throttle    (torque_req);   //  Think about moderating this and including speed related element
-                
-                if  (torque_req < 0.05)
+                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
                 }
@@ -659,16 +710,7 @@
 
         if  (qtrsec_trig == true)  {    //  do every quarter second stuff here
             qtrsec_trig = false;
-//            speed.qtr_sec_update  ();
-//            double  speedmph = speed.MPH(), amps = 0.0 - read_ammeter(), volts = read_voltmeter();
-            double  speedmph = speed2.mph(), amps = 0.0 - read_ammeter(), volts = read_voltmeter();
-//static const double mph_2_mm_per_sec = 447.04;  //  exact
-//            double  mm_travelled_in_qtrsec = speedmph * mph_2_mm_per_sec / 4.0;
-//  1.0 mph = 0.44704 metre per sec
-//          = 0.11176 metre per quarter sec
-
-            recent_distance += (speedmph * 111.76);    //  Convert mph to distance mm travelled in quarter second
-
+            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)
@@ -687,21 +729,9 @@
                     pc.printf   ("Handbrake not slipping, effort %.2f\r\n", slider.handbrake_effort);
                 }
             }
-            switch  (qtr_sec)   {   //  Can do sequential stuff quarter second apart here
-                case    0:
-                case    2:
-                    com.putc    (boards_fitted[0]);
-                    com.printf  ("rpm\r");
-                    break;
-                case    1:
-                case    3:
-                    com.putc    (boards_fitted[1]);
-                    com.printf  ("rpm\r");
-                    break;
-                default:
-                    qtr_sec = 0;
-                    break;
-            }   //  End of switch qtr_sec
+            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) {
@@ -714,10 +744,23 @@
                     //  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];