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 ();
}