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

touch_handler.cpp

Committer:
JonFreeman
Date:
2019-03-04
Revision:
14:6bcec5ac21ca
Parent:
12:a25bdf135348

File content as of revision 14:6bcec5ac21ca:

#include "mbed.h"
#include "Electric_Loco.h"
#include "TS_DISCO_F746NG.h"
#include "LCD_DISCO_F746NG.h"

/**
void    read_keypresses    (struct ky_bd & a)
Sets values in struct ky_bd, containing :
        struct  ky_bd   {   int count,  slider_y; keystr key[MAX_TOUCHES + 1];   bool  sli;   }  ;
            struct  keystr  {   int keynum; int x;  int y;  }   ;
    sets a.count to number of fingers found pressing buttons
    fills a.key[].keynum with list of 'a.count' button return codes
    fills corresponding a.key[].x and a.key[].y with finger coordinates
    if button is SLIDER_BUTTON
        sets a.sli true else sets false
        sets a.slider_y with new slider y coordinate - 0 at top
*/
extern  STM3_ESC_Interface    My_STM3_ESC_boards   ;
extern  void    throttle    (double p)  ;            // New Apr 2018 ; servo adjusts throttle lever on Honda GX120
extern  void    read_keypresses    (struct ky_bd & a)   ;
extern  void    draw_button_hilight     (int but, int colour)   ;
extern  void    draw_button (struct button_specs & bu, int body_colour) ;
extern  void    horn    (int which, int onoff)  ;

extern  Serial  pc;
/**
int     screen_touch_handler::viscous_drag   (int up_viscosity, int dn_viscosity)    ;
    Usage
        position = viscous_drag (a, b);
    Uses latest slider_y and present 'position'
    Returns a value for new position moved in direction by amount determined by difference but reduced more for high viscosity
    This allows for different rates of control movement for rise and fall
*/
int     screen_touch_handler::viscous_drag   (int ip, double up_drag, double dn_drag)    {
    int s = (ip - position);    //  ip is final destination position of slider on scale
    if  (!s)            //  If no movement
        return  position;
    int rv;
    double  d = (double) s;
    if  (up_drag < 0.1) up_drag = 0.1;
    if  (dn_drag < 0.1) dn_drag = 0.1;  //  avoiding DIV0 errors
    if  (s < 0) {   //  Finger has moved up the slider
        rv = position - 1 + (int)(d / up_drag); 
    }
    if  (s > 0) {   //  Finger has moved down the slider
        rv = position + 1 + (int)(d / dn_drag);
    }
    return  rv;
}

void    screen_touch_handler::flush   ()    {   //  clear keyboard buffers
    kybd_a.count    = kybd_b.count  = 0;
    for (int i = 0; i <= MAX_TOUCHES; i++)  {
        kybd_a.key[i].keynum    = kybd_b.key[i].keynum = 0;
    }
}
/*
Dealing with Keys Pressed
    This time   and Last time   AUTOREPEAT
    This time   and !Last time  NEW_PRESS
    !This time  and Last time   NEW_RELEASE
    !This time  and !Last time  not detectable as key not in either list
*/

/**
bool    in_list  (struct ky_bd & a, int key)
Scans current keyboard buffer searching for 'key'.
Returns true if found, false otherwise
*/
bool    screen_touch_handler::in_list  (struct ky_bd & a, int key)
{
    int i = 0;
    while   (i < a.count)
        if  (key == a.key[i++].keynum)
            return  true;
    return  false;
}

void    screen_touch_handler::motor_power    ()  {  //  uses position to set motor volts and amps and Honda throttle
    int b = NEUTRAL_VAL - position;    //  now got integer going positive for increasing power demand
    double  torque_req = ((double) b) / (double)(NEUTRAL_VAL - MIN_POS);  //  in range 0.0 to 1.0
    if  (torque_req < 0.0)  torque_req = 0.0;
    if  (torque_req > 1.0)  torque_req = 1.0;
//                pc.printf   ("torque_rec = %.3f, last_V = %.3f\r\n", torque_req, last_V);
    My_STM3_ESC_boards.set_I_limit (torque_req);
    if  (torque_req < 0.05) {
        My_STM3_ESC_boards.set_V_limit (My_STM3_ESC_boards.last_V / 2.0);
        throttle    (torque_req * 6.0);
    }
    else    {   //  torque_rec >= 0.05
        throttle    (0.3 + (torque_req / 2.0));
        if  (My_STM3_ESC_boards.last_V < 0.99)
            My_STM3_ESC_boards.set_V_limit (My_STM3_ESC_boards.last_V + 0.05);   //  ramp voltage up rather than slam to max
    }   //  endof if/else  (torque_req < 0.05) {
}


void    screen_touch_handler::HandleFingerInput   ()    {
    int key;
    if  (present_kybd == &kybd_b)    {       //  swap keyboard buffers
        present_kybd = &kybd_a;
        previous_kybd = &kybd_b;
    }   else    {
        present_kybd = &kybd_b;
        previous_kybd = &kybd_a;
    }
    read_keypresses   (*present_kybd);

//    slider_state_machine    (); //  Takes care of all actions required of slider

//void    screen_touch_handler::slider_state_machine    ()  {
    int old_position = position;    //  Used at end to decide if screen update required
    switch  (next_state)    {

        case    NEUTRAL_DRIFT:  //  slider is at 'N', loco not being driven or braked but may well be rolling
            if  (present_kybd->sli && previous_kybd->sli) {   //  finger is definitely on slider, found this time and last time
                if  (present_kybd->slider_y < NEUTRAL_VAL)
                    next_state = INTO_RUN;
                if  (present_kybd->slider_y > NEUTRAL_VAL)
                    next_state = INTO_REGEN_BRAKE;
            }
            break;        //    endof case    NEUTRAL_DRIFT

        case    INTO_NEUTRAL_DRIFT:
            My_STM3_ESC_boards.set_I_limit (0.0);  
            My_STM3_ESC_boards.set_V_limit (0.0);  
            throttle    (0.0);                      //  Honda revs to tickover
            position = NEUTRAL_VAL;     //  'position' is copy of slider_y after application of any viscous damping
            next_state = NEUTRAL_DRIFT;
            break;

        case    INTO_RUN:   //  cut motor power and set direction of travel
            if  (present_kybd->sli) {   //  finger is on slider
                throttle    (0.33);
                if  (direction) My_STM3_ESC_boards.message  ("fw\r");   //  Forward run command
                else            My_STM3_ESC_boards.message  ("re\r");   //  Reverse run command
                next_state  = RUN;
            }
            break;        //    endof case    INTO_RUN

        case    INTO_REGEN_BRAKE:   //  cut motor power and set 'rb'
            My_STM3_ESC_boards.message  ("rb\r");   //  Regen Brake command
            throttle    (0.0);                      //  Wind Honda engine revs down to tickover
            next_state  = REGEN_BRAKE;
            break;        //    endof case    INTO_REGEN_BRAKE

        case    REGEN_BRAKE:    //  established in regenerative braking mode, loco may or may not be rolling
            if  (present_kybd->sli) {   //  finger is on slider. No action without finger
                if (position != present_kybd->slider_y)   {   //  update braking effort according to finger movement
                    position = viscous_drag (present_kybd->slider_y, 5.0, 5.0);
                    if  (position <= NEUTRAL_VAL)       //  if position risen out of REGEN_BRAKE region
                        next_state = INTO_NEUTRAL_DRIFT;
                    else    {       //  position is within REGEN_BRAKE area
                        double  brake_effort = ((double)(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
                        My_STM3_ESC_boards.set_V_limit (sqrt(brake_effort));   //  sqrt gives more linear feel to control
                        My_STM3_ESC_boards.set_I_limit (1.0);
                    }   //  endof else position is within REGEN_BRAKE area
                }       //  endof if finger moved
            }           //  endof finger is on slider
            break;        //    endof case    REGEN_BRAKE

        case    RUN:            //  remains in this state while finger remains on slider in run range. Drive effort determined by finger position
            if  (My_STM3_ESC_boards.last_V < 0.99)
                My_STM3_ESC_boards.set_V_limit (My_STM3_ESC_boards.last_V + 0.05);   //  ramp voltage up rather than slam to max
            if  (present_kybd->sli) {   //  finger is on slider
                if (position != present_kybd->slider_y)   {   //  update driving power according to finger movement
                    position = viscous_drag (present_kybd->slider_y, 45.0, 20.0);
                    if  (position >= NEUTRAL_VAL)       //  if position falen below RUN region
                        next_state = INTO_NEUTRAL_DRIFT;
                    else               //  Finger is still in RUN range
                        motor_power ();     //  class member already has position, sets volts, amps and throttle
                }       //  endof update driving power according to finger movement
            }           //  endof finger is on slider
            else    {   //  finger not on slider
                throttle    (0.0);      //  Honda revs down to tickover
                next_state = RUN_DOWN;
            }
            break;        //    endof case    RUN

        case    RUN_DOWN:       //  if finger removed during RUN, slider is to auto-glide back down to 'N'
            if  (present_kybd->sli)     //  finger is on slider
                    next_state  = RUN;
            else    {                   //  finger is not on slider
                position = viscous_drag (NEUTRAL_VAL, 45.0, 20.0);
                if  (position >= NEUTRAL_VAL)   
                    next_state = INTO_NEUTRAL_DRIFT;
                else
                    motor_power ();
            }
            break;        //    endof RUN_DOWN

    }   //  endof       switch  (next_state)    {

    if  (position != old_position)  {   //  partial screen rewrite is required
        DrawSlider  ();
    }
//}   //  endof   void    slider_state_machine    ()  {


    if  (present_kybd->count || previous_kybd->count)   {   //  at least one key pressed this time or last time

        for (int i = 0; i < present_kybd->count; i++)   {   //  PRESENT Do for all keys pressed on this pass
            key = present_kybd->key[i].keynum;
            if  (in_list(*previous_kybd, key))   {           //  AUTOREPEAT  This key is being pressed now and last time also
//                pc.printf   ("Autorep key %d\r\n", key);
            }           //  endof   AUTOREPEAT  This key is being pressed now and last time also
            else    {   //  NEW_PRESS key is a new keypress
//                pc.printf   ("New Press %d\r\n", key);
                draw_button_hilight     (key, LCD_COLOR_YELLOW)  ;
                switch  (key)    {    //  Handle new touch screen button presses here - single action per press, not autorepeat
                    case    SLIDER_BUTTON:  //  All slider action handled in state machine above
                        break;
                    case    SPEEDO_BUTTON:  //  No action currently assigned to speedo button
                        break;
                    case    VMETER_BUTTON:  //  Turn on high tone horn
                        horn    (HI_HORN, 1);
                        break;
                    case    AMETER_BUTTON:  //  Turn on low tone horn
                        horn    (LO_HORN, 1);
                        break;
                    default:
                        pc.printf   ("Unhandled keypress %d\r\n", key);
                        break;
                }       //  endof   switch  (key) endof Handle new touch screen button presses here - single action per press, not autorepeat
            }           //  endof   NEW_PRESS key is a new keypress
        }       //  endof     PRESENT Do for all keys pressed on this pass

        for (int i = 0; i < previous_kybd->count; i++)   {   //  Do for all keys pressed on previous pass
            key = previous_kybd->key[i].keynum;
            if  (!in_list(*present_kybd, key))   {           //  NEW_RELEASE
//                pc.printf   ("New Release %d\r\n", key);
                draw_button_hilight     (key, LCD_COLOR_DARKBLUE)  ;
                switch  (key)    {    //  Handle new touch screen button RELEASEs here - single action per press, not autorepeat
                    case    SLIDER_BUTTON:  //
                        break;
                    case    SPEEDO_BUTTON:  //
                        break;
                    case    VMETER_BUTTON:  //
                        horn    (HI_HORN, 0);    //  Turn hi-tone horn off   (voltmeter key)
                        break;
                    case    AMETER_BUTTON:  //
                        horn    (LO_HORN, 0);    //  Turn lo-tone horn off   (powermeter key)
                        break;
                    default:
                        pc.printf   ("Unhandled key release %d\r\n", key);
                        break;
                }       //  endof   switch  (button)
            }                                               //  endof   NEW_RELEASE
/*            else    {     //  DO NOT NEED SECOND FIND OF AUTOREPEAT
                pc.printf   ("Autorep2\r\n");
            }*/
        }       //  endof     Do for all keys pressed on previous pass
    }           //  endof   at least one key pressed this time or last time
    else    {   //  no keys being pressed on this pass or previous pass
//        pc.printf   ("no key being pressed\r\n");
    }           //  endof   no keys being pressed on this pass or previous pass
}       //  endof   void    screen_touch_handler::HandleFingerInput   ()    {

    screen_touch_handler::screen_touch_handler  ()  {   //  default constructor
        present_kybd    = & kybd_a;
        previous_kybd   = & kybd_b;
        oldpos  = 0;
        position    = MAX_POS - 2;
        next_state  = INTO_REGEN_BRAKE;
        flush   ();
    }