Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: TS_DISCO_F746NG mbed Servo LCD_DISCO_F746NG BSP_DISCO_F746NG QSPI_DISCO_F746NG AsyncSerial FastPWM
Revision 12:a25bdf135348, committed 2019-01-14
- Comitter:
- JonFreeman
- Date:
- Mon Jan 14 16:39:41 2019 +0000
- Parent:
- 11:a573664b1a59
- Child:
- 13:390430631255
- Commit message:
- Tidied, better documented, more OOP, interim release
Changed in this revision
--- a/Electric_Loco.h Sat Jun 23 09:37:41 2018 +0000
+++ b/Electric_Loco.h Mon Jan 14 16:39:41 2019 +0000
@@ -1,5 +1,7 @@
#include "mbed.h"
-/* Updated 9 May 2018
+#include "movingcoilmeter.h"
+#include <cctype>
+/* Updated January 2019
Jon Freeman
5" and 7.25" gauge Electric Locomotive Controller - ST DISCO-F746NG
@@ -7,11 +9,11 @@
Display shows 'analogue' moving coil meter movements for :
Locomotive speed Miles per Hour
- System voltage (range 20v - 90v or thereabouts)
- Power Watts delivered to drive motors.
+ System voltage (range 10v - 63v or thereabouts)
+ Power Watts delivered to drive motors or dumped.
-Touch screen has three 'buttons', these are currently unused, and are where the meter movements show.
-Idea is to use two for two horns,
+Touch screen has three 'buttons', and are where the meter movements show.
+Two smaller meter buttons for two horns, larger speedo button currently unsused
Display has 'slider' touch control. This drives the loco.
Control in central position when not driving or drfting.
@@ -20,7 +22,6 @@
Take finger off and control drifts down to central 'neutral' position.
*/
-#define QSPI
#define MAX_TOUCHES 6 // Touch screen can decode up to this many simultaneous finger press positions
#define NEUTRAL_VAL 150 // Number of pixels
@@ -30,78 +31,146 @@
#define SLIDERW 50 // pixel width of slider
#define SLIDERH 268 // pixel height of slider
-// To get speedo reading correctly, need to use correct gear ratio and wheel size info
-//#define BOGIE_5_INCH
-#define BOGIE_7_and_a_quarter_INCH
+#define VOLTMETER_X 68 // Voltmeter screen position
+#define VOLTMETER_Y 68
+#define AMMETER_X 68 // Ammeter screen position - Now replaced by Power meter
+#define AMMETER_Y 202
+#define SPEEDO_X 274 // Speedometer screen position
+#define SPEEDO_Y 135
+//#define V_A_SIZE 54 // Size of voltmeter and ammeter
+//#define SPEEDO_SIZE 112
+
+#define V_A_SIZE 56 // Size of voltmeter and ammeter
+#define SPEEDO_SIZE 114
+#define SPEEDO_BODY_COLOUR LCD_COLOR_BLACK
+#define SPEEDO_DIAL_COLOUR LCD_COLOR_WHITE
+#define SPEEDO_TEXT_COLOUR LCD_COLOR_BLUE
+
+#define VMETER_BODY_COLOUR LCD_COLOR_BLACK
+#define VMETER_DIAL_COLOUR LCD_COLOR_WHITE
+#define VMETER_TEXT_COLOUR LCD_COLOR_BLUE
+
+#define AMETER_BODY_COLOUR LCD_COLOR_BLACK
+#define AMETER_DIAL_COLOUR LCD_COLOR_WHITE
+#define AMETER_TEXT_COLOUR LCD_COLOR_BLUE
const int
-
BUTTON_RAD = (SLIDERW / 2) - 4, // radius of circular 'knob' in slider control
MIN_POS = BUTTON_RAD + 5, // top of screen
MAX_POS = SLIDERH - (BUTTON_RAD + 1), // bottom of screen
- CIRC_CTR = SLIDERX + BUTTON_RAD + 4;
+ CIRC_CTR = SLIDERX + BUTTON_RAD + 4;
-static const double
-#ifdef BOGIE_7_and_a_quarter_INCH
- MOTOR_PINION_T = 17.0, // motor pinion teeth, wheel gear teeth and wheel dia required to calculate speed and distance.
- WHEEL_GEAR_T = 76.0,
- WHEEL_DIA_MM = 147.0,
-#endif
-#ifdef BOGIE_5_INCH
- MOTOR_PINION_T = 27.0, // motor pinion teeth, wheel gear teeth and wheel dia required to calculate speed and distance.
- WHEEL_GEAR_T = 85.0,
- WHEEL_DIA_MM = 98.0,
-#endif
- PI = 4.0 * atan(1.0),
- WHEEL_CIRCUMFERENCE_METRE = PI * WHEEL_DIA_MM / 1000.0,
- PULSES_PER_WHEEL_REV = 32.0 * WHEEL_GEAR_T / MOTOR_PINION_T,
- PULSES_PER_METRE = PULSES_PER_WHEEL_REV / WHEEL_CIRCUMFERENCE_METRE,
- rpm2mph = 60.0 // = Motor Revs per hour;
- * (MOTOR_PINION_T / WHEEL_GEAR_T) // = Wheel rev per hour
- * WHEEL_CIRCUMFERENCE_METRE // = metres per hour
- * 39.37 // = inches per hour
- / (1760 * 36) // = miles per hour
- ;
+static const double PI = 4.0 * atan(1.0);
+static const double TWO_PI = 8.0 * atan(1.0);
-const double LOCO_HANDBRAKE_ESCAPE_SPEED = 0.5;
-
+enum {HI_HORN, LO_HORN};
enum {NO_DPS, ONE_DP};
// Assign unique number to every button we may use, and keep count of total number of them
-enum {
- ENTER, SLIDER, SPEEDO_BUT, VMETER_BUT, AMETER_BUT,
- NUMOF_BUTTONS} ; // button names
-enum {
- STATES, INACTIVE, RUN, NEUTRAL_DRIFT, REGEN_BRAKE, PARK, HANDBRAKE_SLIPPING};
+enum {ENTER, SLIDER_BUTTON, SPEEDO_BUTTON, VMETER_BUTTON, AMETER_BUTTON,NUMOF_BUTTONS} ; // button names
+enum {STATES, RUN, NEUTRAL_DRIFT, REGEN_BRAKE, RUN_DOWN, INTO_RUN, INTO_REGEN_BRAKE, INTO_NEUTRAL_DRIFT};
+enum {SLIDER_PRESS, SLIDER_RELEASE, SLIDER_AUTOREP};
-struct slide { int position; int oldpos; int state; int direction; bool recalc_run; bool handbrake_slipping;
- double handbrake_effort; double loco_speed; } ;
struct point { int x; int y; } ;
-struct key { int keynum; int x; int y; bool pressed; } ;
-struct ky_bd { int count, slider_y; key ky[MAX_TOUCHES + 1]; bool sli; } ;
+struct keystr { int keynum; int x; int y; } ;
+struct ky_bd { int count, slider_y; keystr key[MAX_TOUCHES + 1]; bool sli; } ;
-const int MAX_PARAMS = 20;
+class screen_touch_handler
+ {
+ ky_bd kybd_a, kybd_b; // alternating present - previous keyboard structures
+ ky_bd * present_kybd, * previous_kybd; // pointers
+ bool in_list (struct ky_bd & , int ) ;
+ void motor_power () ;
+ void flush () ;
+ int viscous_drag (int, double, double) ;
+ int position;
+ int oldpos;
+ int next_state;
+ public:
+ int direction;
+ void DrawSlider () ;
+ void HandleFingerInput () ;
+ screen_touch_handler () ; // default constructor
+ } ;
+
+const int MAX_PARAMS = 30;
+const int MAX_CMD_LINE_LEN = 180;
+const int MAX_ESCS = 12;
+
struct parameters {
- struct kb_command const * clist;
- char cmd_line[120];
- char * cmd_line_ptr;
- int32_t position_in_list, numof_dbls, target_unit, numof_menu_items, com_no, cl_index, gp_i;
+ int32_t numof_menu_items, numof_cl_values_read;
double dbl[MAX_PARAMS];
- bool respond;
} ;
-class genio { // 23/06/2018 generic io handler
+enum {
+ FAULT_0,
+ FAULT_BOARD_ID_IN_MSG,
+ FAULT_TS,
+ FAULT_PC,
+ FAULT_COM,
+ FAULT_COM_NO_MATCH,
+ FAULT_COM_LINE_LEN,
+ FAULT_QSPI,
+ FAULT_ODOMETER,
+ FAULT_MAX,
+ NUMOF_REPORTABLE_TS_ERRORS
+ } ;
+
+class error_handling_Jan_2019
+{
+ int32_t TS_fault[NUMOF_REPORTABLE_TS_ERRORS] ; // Some number of reportable error codes, accessible through set and read members
public:
- uint32_t count; // Running total of times called
- bool available; // set true when number rec'd, set false by reading it
- int d_type; // loaded on setup indicating expected data type
- int d_len; // loaded on setup indicating expected num of parameters to handle
- uint32_t ui[MAX_PARAMS];
- double dbl[MAX_PARAMS];
- genio () {} // default constructor
- genio (int, int) ; // more useful constructor
- void store (struct parameters &) ; //
- bool read (uint32_t **) ;
- bool read (double &) ;
+ error_handling_Jan_2019 () { // default constructor
+ for (int i = 0; i < (sizeof(TS_fault) / sizeof(int32_t)); i++)
+ TS_fault[i] = 0;
+ }
+ void set (uint32_t, int32_t) ;
+ void clr (uint32_t) ;
+ uint32_t read (uint32_t) ;
+ bool all_good () ;
+ void report_any () ;
+} ;
+
+class command_line_interpreter_core {
+ parameters a ; // as opposed to clicore(¶meters)
+ struct kb_command const * clist;
+ int32_t portio, cl_index, target_unit;
+ char cmd_line[MAX_CMD_LINE_LEN];
+ char * cmd_line_ptr;
+ int clreadable ();
+ int clgetc ();
+ void clputc (int);
+ public:
+ command_line_interpreter_core () {}; // default constructor
+ command_line_interpreter_core (int, int, struct kb_command const * ) ; //{ // constructor including io port id value
+ void sniff () ; // The function to call often to sniff out commands from command line and act upon them
+} ;
+
+class STM3_ESC_Interface
+{
+ int board_IDs [MAX_ESCS], // allow up to 4 boards in practical system, size for 10+ as 10 discrete ID nums exist
+ board_count,
+ reqno;
+public:
+ double last_V, last_I, mph, esc_speeds[MAX_ESCS];
+ STM3_ESC_Interface () { // Constructor
+ board_count = 0;
+ reqno = 0;
+ last_V = last_I = mph = 0.0;
+ for (int i = 0; i < MAX_ESCS; i++) {
+ board_IDs[i] = 0;
+ esc_speeds[i] = 0.0;
+ }
+ }
+ bool request_mph () ; // Issue "'n'mph\r" to one particular STM3_ESC board to request RPM 22/06/2018
+ void mph_update (double) ; // touched 08/01/2019
+ void set_board_ID (int) ; // called in response to 'whon' coming back from a STM3_ESC
+ void search_for_escs () ; // one-off call during startup to identify all connected STM3_ESCs
+ void get_boards_list (int *) ; // copies board list
+ void set_V_limit (double) ; // Set max motor voltage
+ void set_I_limit (double) ; // Set max motor current
+ void message (char *) ; // Broadcast message to all STM3_ESCs
+ void message (int, char *) ; // Send message to one individual STM3_ESC
} ;
+
--- a/SD_DISCO_F746NG.lib Sat Jun 23 09:37:41 2018 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -https://developer.mbed.org/teams/ST/code/SD_DISCO_F746NG/#cf12f3d37081
--- a/cli_TS_nortos.cpp Sat Jun 23 09:37:41 2018 +0000
+++ b/cli_TS_nortos.cpp Mon Jan 14 16:39:41 2019 +0000
@@ -1,87 +1,131 @@
//Loco_TS_2018
#include "mbed.h"
-//#include "BufferedSerial.h"
#include "AsyncSerial.hpp"
#include <cctype>
#include "Electric_Loco.h"
using namespace std;
-//extern int I_Am () ; // Returns boards id number as ASCII char '0', '1' etc. Code for Broadcast = '\r'
-//typedef double fl_typ; //
-
const int BROADCAST = '\r';
// WithOUT RTOS
extern Serial pc;
-extern AsyncSerial com;
-extern void send_test () ;
+extern AsyncSerial com2escs;
+extern error_handling_Jan_2019 Controller_Error ;
+extern const char const_version_string[] ; // Version string, readable from serial ports
-genio::genio (int dtype, int dlen) {
+extern STM3_ESC_Interface My_STM3_ESC_boards ;
+
+/*genio::genio (int dtype, int dlen) { // constructor
d_type = dtype;
d_len = dlen;
for (int i = 0; i < MAX_PARAMS; i++) {
- ui[i] = 0;
+ si[i] = 0;
dbl[i] = 0.0;
}
- count = 0;
- available = false;
+ count = 0; // running total of times called
+ available = false; // bool
+ //available = 0; // if using integer rather than bool
}
-void genio::store (struct parameters & a) { //
- count++;
+void genio::store (struct parameters & a) { // copies up to MAX_PARAMS into int and double arrays
+ count++; // number of times this response rec'd this session
available = true;
- for (int i = 0; i < d_len; i++) {
- ui[i] = (int32_t)a.dbl[i];
+ for (int i = 0; i < d_len; i++) { // dlen // loaded on setup indicating expected num of parameters to handle. Why not just use MAX_PARAMS ?
+ si[i] = (int32_t)a.dbl[i];
dbl[i] = a.dbl[i];
}
}
-bool genio::read (uint32_t ** addr) {
+bool genio::read (int32_t ** addr) {
if (!available)
return false;
available = false;
- *addr = ui;
+ *addr = si; // int32_t ui[MAX_PARAMS];
return true;
}
+*/
+
+command_line_interpreter_core::command_line_interpreter_core (int p, int menulen, struct kb_command const * clistp) {
+ clist = clistp;
+ a.numof_menu_items = menulen;
+ a.numof_cl_values_read = 0;
+ portio = p;
+ cl_index = 0;
+}
+
+int command_line_interpreter_core::clreadable () {
+ if (portio == 0)
+ return pc.readable();
+ return com2escs.readable();
+}
+
+void command_line_interpreter_core::clputc (int c) {
+ if (portio == 0)
+ pc.putc (c);
+ else
+ com2escs.putc (c);
+}
+
+int command_line_interpreter_core::clgetc () {
+ if (portio == 0)
+ return pc.getc();
+ return com2escs.getc();
+}
+
void null_cmd (struct parameters & a)
{
pc.printf ("At null_cmd, parameters : First %.3f, second %.3f\r\n", a.dbl[0], a.dbl[1]);
}
-
void menucmd (struct parameters & a);
void kd_cmd (struct parameters & a) // kick the watchdog
{
}
-extern void rpm_push (struct parameters & a) ;
-extern void mph_push (struct parameters & a) ;
-void uprpm_cmd (struct parameters & a) // controller rec'd rpm from driver boards
-{
- rpm_push (a);
-// a.gp_i ^= 1; // toggle lsb
-}
-
void upmph_cmd (struct parameters & a) // controller rec'd mph from driver boards
{
- mph_push (a);
-// a.gp_i ^= 1; // toggle lsb
-}
-
-void uprdi_cmd (struct parameters & a) // controller rec'd rpm from driver boards
-{
-// pc.printf ("RPM %d %d\r\n", (int)a.dbl[0], (int)a.dbl[1]);
+ My_STM3_ESC_boards.mph_update (a.dbl[1]);
}
-/*void who_cmd (struct parameters & a)
+void TSver_cmd (struct parameters & a) // Report current TS software version to pc
{
- int i = I_Am ();
- if (I_Am() == a.target_unit)
- pc.printf ("Hi there, I am %c\r\n", a.target_unit);
-}*/
+ pc.printf ("ver %s\r\n", const_version_string);
+}
+
+void boards_cmd (struct parameters & a) {
+ int boards[MAX_ESCS], count = 0;
+ My_STM3_ESC_boards.get_boards_list (boards);
+ while (boards[count] && count < MAX_ESCS)
+ count++;
+ pc.printf ("Board Count = %d\r\n", count);
+ if (count) {
+ pc.printf ("Boards found ^");
+ for (int i = 0; i < count; i++)
+ pc.printf ("%c ", boards[i]);
+ pc.printf ("\r\n");
+ }
+ else
+ pc.printf ("No STM3_ESC boards found\r\n");
+}
+
+void who_cmd (struct parameters & a) // Have read "whon" back from STM3_ESC where 'n' is STM3_Esc board ID number (ascii digit)
+{
+ int v = (int)a.dbl[0] | '0';
+ if (a.numof_cl_values_read == 1 && isdigit(v)) {
+// pc.printf ("who%c, vals read %d\r\n", (int)a.dbl[0] | '0', a.numof_cl_values_read);
+ My_STM3_ESC_boards.set_board_ID (v);// set_board_id (v); // just to test and prove can not add same board twice
+ }
+ else
+ pc.printf ("Garbage response to 'who', numof vals %d, value %c\r\n", a.numof_cl_values_read, v);
+}
+
+void ver_req_cmd (struct parameters & a) // Touch Screen controller requesting version string from one specific ESC board
+{
+ pc.printf ("ver_req_cmd not yet implemented\r\n");
+}
struct kb_command {
const char * cmd_word; // points to text e.g. "menu"
@@ -92,142 +136,32 @@
struct kb_command const command_list[] = {
{"ls", "Lists available commands", menucmd},
{"?", "Lists available commands, same as ls", menucmd},
-// {"fw", "forward", fw_cmd},
-// {"re", "reverse", re_cmd},
-// {"rb", "regen brake 0 to 99 %", rb_cmd},
-// {"hb", "hand brake", hb_cmd},
-// {"v", "set motors V percent RANGE 0 to 100", v_cmd},
-// {"i", "set motors I percent RANGE 0 to 100", i_cmd},
-// {"vi", "set motors V and I percent RANGE 0 to 100", vi_cmd},
-// {"who", "search for connected units, e.g. 3who returs 'Hi there' if found", who_cmd},
-// {"mode", "read or set params in eeprom", mode_cmd},
-// {"erase", "set eeprom contents to all 0xff", erase_cmd},
+ {"ver", "report const_version_string", TSver_cmd}, // Report TS controller version
+ {"boards", "List STM3_ESC boards connected", boards_cmd},
{"kd", "kick the dog", kd_cmd},
- {"rpm", "rpm reading from 2 motors", uprpm_cmd},
{"nu", "do nothing", null_cmd},
};
-const int numof_menu_items = sizeof(command_list) / sizeof(kb_command);
-void menucmd (struct parameters & a)
-{
-// struct kb_command const * cl = command_list;
- pc.printf("\r\n\nTS_2018 Locomotive Touch Screen Controller\r\nAt menucmd function - listing commands:-\r\n");
- for(int i = 0; i < numof_menu_items; i++)
- pc.printf("[%s]\t\t%s\r\n", command_list[i].cmd_word, command_list[i].explan);
- pc.printf("End of List of Commands\r\n");
-}
-
struct kb_command const loco_command_list[] = {
{"ls", "Lists available commands", menucmd},
{"?", "Lists available commands, same as ls", menucmd},
- {"rpm", "rpm reading from 2 motors", uprpm_cmd},
+ {"who", "Reads 'whon' response from STM3_ESC where 'n' is board num ascii from eeprom", who_cmd},
+ {"ver", "report const_version_string", ver_req_cmd}, // Request software version of one specific ESC board
{"mph", "bogie mph reading", upmph_cmd}, // 22/06/2018
- {"rdi", "rpm reading from 2 motors", uprdi_cmd},
{"nu", "do nothing", null_cmd},
} ;
-struct parameters pccom, lococom;
-/*struct parameters {
- struct kb_command const * clist;
- char cmd_line[120];
- char * cmd_line_ptr;
- int32_t position_in_list, numof_dbls, target_unit, numof_menu_items;
- double dbl[MAX_PARAMS];
- bool respond;
-} ;
-*/
-int getc (int which_port)
-{
- if (which_port == 0)
- return pc.getc ();
- if (which_port == 1)
- return com.getc ();
- return -1;
-}
-int readable (int which_port)
-{
- if (which_port == 0)
- return pc.readable ();
- if (which_port == 1)
- return com.readable ();
- return -1;
-
-}
-
-void setup_pccom ()
-{
- pccom.clist = command_list;
- pccom.numof_menu_items = sizeof(command_list) / sizeof(kb_command);
- pccom.com_no = 0;
- pccom.cl_index = 0;
- pccom.gp_i = 0; // general puropse integer, not used to 30/4/2018
-}
-
-void setup_lococom ()
-{
- lococom.clist = loco_command_list;
- lococom.numof_menu_items = sizeof(loco_command_list) / sizeof(kb_command);
- lococom.com_no = 1;
- lococom.cl_index = 0;
- lococom.gp_i = 0; // general puropse integer, toggles 0 / 1 to best guess source of rpm
-}
-
-void clicore (struct parameters & a)
+void menucmd (struct parameters & a)
{
- int ch;
- char * pEnd;
- while (readable(a.com_no)) {
- ch = getc (a.com_no);
- if(ch != '\r') // was this the 'Enter' key?
- a.cmd_line[a.cl_index++] = ch; // added char to command being assembled
- else { // key was CR, may or may not be command to lookup
- a.target_unit = BROADCAST; // Broadcast
- a.cmd_line_ptr = a.cmd_line;
- a.cmd_line[a.cl_index] = 0; // null terminate command string
- if(a.cl_index) { // If have got some chars to lookup
- int i, wrdlen;
- if (isdigit(a.cmd_line[0])) { // Look for command with prefix digit
- a.cmd_line_ptr++; // point past identified digit prefix
- a.target_unit = a.cmd_line[0]; // '0' to '9'
- //pc.printf ("Got prefix %c\r\n", cmd_line[0]);
- }
- for (i = 0; i < a.numof_menu_items; i++) { // Look for input match in command list
- wrdlen = strlen(a.clist[i].cmd_word);
- if(strncmp(a.clist[i].cmd_word, a.cmd_line_ptr, wrdlen) == 0 && !isalpha(a.cmd_line_ptr[wrdlen])) { // If match found
- for (int k = 0; k < MAX_PARAMS; k++) {
- a.dbl[k] = 0.0;
- }
- a.position_in_list = i;
- a.numof_dbls = 0;
- pEnd = a.cmd_line_ptr + wrdlen;
- while (*pEnd) { // Assemble all numerics as doubles
- a.dbl[a.numof_dbls++] = strtod (pEnd, &pEnd);
- while (*pEnd && !isdigit(*pEnd) && '-' != *pEnd && '+' != *pEnd) {
- pEnd++;
- }
- }
- //pc.printf ("\r\n"); // Not allowed as many may output this.
- //for (int k = 0; k < param_block.numof_dbls; k++)
- // pc.printf ("Read %.3f\r\n", param_block.dbl[k]);
-// param_block.times[i] = clock();
-// if ((param_block.target_unit == BROADCAST) && (I_Am() == '0'))
-// param_block.respond = true;
- a.clist[i].f(a); // execute command
- i = a.numof_menu_items + 1; // to exit for loop
- } // end of match found
- } // End of for numof_menu_items
- if(i == a.numof_menu_items)
- pc.printf("No Match Found for CMD [%s]\r\n", a.cmd_line);
- } // End of If have got some chars to lookup
- //pc.printf("\r\n>");
- a.cl_index = 0;
- } // End of else key was CR, may or may not be command to lookup
- } // End of while (pc.readable())
+ pc.printf("\r\n\nTS_2018 Locomotive Touch Screen Controller\r\nAt menucmd function - listing commands:-\r\n");
+ for(int i = 0; i < a.numof_menu_items; i++)
+ pc.printf("[%s]\t\t%s\r\n", command_list[i].cmd_word, command_list[i].explan);
+ pc.printf("End of List of Commands\r\n");
}
-/*
+/**
New - March 2018
Using opto isolated serial port, paralleled up using same pair to multiple boards running this code.
New feature - commands have optional prefix digit 0-9 indicating which unit message is addressed to.
@@ -235,68 +169,68 @@
Only units recognising its address from prefix digit may respond. This avoids bus contention.
But for BROADCAST commands, '0' may respond on behalf of the group
*/
-//void command_line_interpreter (void const *argument)
-/*void command_line_interpreter ()
-{
- const int MAX_CMD_LEN = 120;
- static char cmd_line[MAX_CMD_LEN + 4];
- static int cl_index = 0;
+command_line_interpreter_core pcli (0, sizeof(command_list) / sizeof(kb_command), command_list);
+command_line_interpreter_core ploco (1, sizeof(loco_command_list) / sizeof(kb_command), loco_command_list);
+
+void command_line_interpreter_core::sniff () { // look for recieved commands and act upon them
int ch;
- char * pEnd, * cmd_line_ptr;
- static struct parameters param_block ;
- while (pc.readable()) {
- ch = pc.getc();
-// if (cl_index > MAX_CMD_LEN) { // trap out stupidly long command lines
-// pc.printf ("Error!! Stupidly long cmd line\r\n");
-// cl_index = 0;
-// }
- if(ch != '\r') // was this the 'Enter' key?
- cmd_line[cl_index++] = ch; // added char to command being assembled
+ char * pEnd;
+ while (clreadable()) {
+ ch = clgetc ();
+ if(ch != '\r') {// was this the 'Enter' key?
+ if (ch != '\n') { // Ignore line feeds
+ cmd_line[cl_index++] = ch; // added char to command being assembled
+ if (cl_index >= MAX_CMD_LINE_LEN-2) {
+ cl_index = 0; // Line is garbage anyway, so might as well junk the lot ?
+ pc.printf ("cl_index=%d TOO LONG in clicore\r\n", cl_index);
+ Controller_Error.set (FAULT_COM_LINE_LEN, 1);
+ }
+ } // endof if(ch != '\n') ignore line feeds
+ } // endof if(ch != '\r')
else { // key was CR, may or may not be command to lookup
- param_block.target_unit = BROADCAST; // Broadcast
+ target_unit = BROADCAST; // Broadcast
cmd_line_ptr = cmd_line;
cmd_line[cl_index] = 0; // null terminate command string
if(cl_index) { // If have got some chars to lookup
int i, wrdlen;
if (isdigit(cmd_line[0])) { // Look for command with prefix digit
cmd_line_ptr++; // point past identified digit prefix
- param_block.target_unit = cmd_line[0]; // '0' to '9'
- //pc.printf ("Got prefix %c\r\n", cmd_line[0]);
+ target_unit = cmd_line[0]; // '0' to '9'
+ //pc.printf ("Got prefix %c\r\n", a.cmd_line[0]);
}
- for (i = 0; i < numof_menu_items; i++) { // Look for input match in command list
- wrdlen = strlen(command_list[i].cmd_word);
- if(strncmp(command_list[i].cmd_word, cmd_line_ptr, wrdlen) == 0 && !isalpha(cmd_line_ptr[wrdlen])) { // If match found
+ for (i = 0; i < a.numof_menu_items; i++) { // Look for input match in command list
+ wrdlen = strlen(clist[i].cmd_word);
+ if(strncmp(clist[i].cmd_word, cmd_line_ptr, wrdlen) == 0 && !isalpha(cmd_line_ptr[wrdlen])) { // If match found
for (int k = 0; k < MAX_PARAMS; k++) {
- param_block.dbl[k] = 0.0;
+ a.dbl[k] = 0.0;
}
- param_block.position_in_list = i;
- param_block.numof_dbls = 0;
+ //position_in_list = i;
+ a.numof_cl_values_read = 0;
pEnd = cmd_line_ptr + wrdlen;
while (*pEnd) { // Assemble all numerics as doubles
- param_block.dbl[param_block.numof_dbls++] = strtod (pEnd, &pEnd);
+ a.dbl[a.numof_cl_values_read++] = strtod (pEnd, &pEnd);
while (*pEnd && !isdigit(*pEnd) && '-' != *pEnd && '+' != *pEnd) {
pEnd++;
}
}
//pc.printf ("\r\n"); // Not allowed as many may output this.
- //for (int k = 0; k < param_block.numof_dbls; k++)
+ //for (int k = 0; k < param_block.a.numof_cl_values_read; k++)
// pc.printf ("Read %.3f\r\n", param_block.dbl[k]);
// param_block.times[i] = clock();
// if ((param_block.target_unit == BROADCAST) && (I_Am() == '0'))
// param_block.respond = true;
- command_list[i].f(param_block); // execute command
- i = numof_menu_items + 1; // to exit for loop
+ clist[i].f(a); // execute command
+ i = a.numof_menu_items + 1; // to exit for loop
} // end of match found
} // End of for numof_menu_items
- if(i == numof_menu_items)
+ if(i == a.numof_menu_items) {
pc.printf("No Match Found for CMD [%s]\r\n", cmd_line);
+ Controller_Error.set (FAULT_COM_NO_MATCH, 1);
+ }
} // End of If have got some chars to lookup
- //pc.printf("\r\n>");
cl_index = 0;
} // End of else key was CR, may or may not be command to lookup
} // End of while (pc.readable())
-// Thread::wait(20); // Using RTOS on this project
-// }
-}
-*/
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/error_handler.cpp Mon Jan 14 16:39:41 2019 +0000
@@ -0,0 +1,78 @@
+#include "mbed.h"
+#include "Electric_Loco.h"
+/*class error_handling_Jan_2019
+{
+ int32_t TS_fault[NUMOF_REPORTABLE_TS_ERRORS] ; // Some number of reportable error codes, accessible through set and read members
+ public:
+ error_handling_Jan_2019 () { // default constructor
+ for (int i = 0; i < (sizeof(TS_fault) / sizeof(int32_t)); i++)
+ TS_fault[i] = 0;
+ }
+ void set (uint32_t, int32_t) ;
+ uint32_t read (uint32_t) ;
+ bool all_good () ;
+ void report_any () ;
+} ;
+*/
+
+const char * FaultList[] = {
+/*
+ FAULT_0,
+ FAULT_BOARD_ID_IN_MSG,
+ FAULT_TS,
+ FAULT_PC,
+ FAULT_COM,
+ FAULT_COM_NO_MATCH,
+ FAULT_COM_LINE_LEN,
+ FAULT_QSPI,
+ FAULT_ODOMETER,
+ FAULT_MAX,
+ NUMOF_REPORTABLE_TS_ERRORS
+*/
+ "Zero",
+ "BoardID in msg",
+ "TouchScreen",
+ "pc",
+ "com no match",
+ "com line len",
+ "qspi",
+ "odometer",
+ "max",
+ "endoflist",
+ " ",
+ } ;
+
+bool error_handling_Jan_2019::all_good () {
+ for (int i = 0; i < NUMOF_REPORTABLE_TS_ERRORS; i++)
+ if (TS_fault[i])
+ return false;
+ return true;
+}
+
+/**void error_handling_Jan_2019::set (uint32_t err_no, int32_t bits_to_set) {
+ Used to set bits in error int
+ Uses OR to set new bits without clearing other bits set previously
+*/
+void error_handling_Jan_2019::set (uint32_t err_no, int32_t bits_to_set) {
+ TS_fault[err_no] |= bits_to_set; // Uses OR to set new bits without clearing other bits set previously
+}
+
+/**void error_handling_Jan_2019::clr (uint32_t err_no) {
+ Used to clear all bits in error int
+*/
+void error_handling_Jan_2019::clr (uint32_t err_no) {
+ TS_fault[err_no] = 0;
+}
+
+uint32_t error_handling_Jan_2019::read (uint32_t err_no) {
+ return TS_fault[err_no];
+}
+
+extern Serial pc;
+void error_handling_Jan_2019::report_any () {
+ for (int i = 0; i < NUMOF_REPORTABLE_TS_ERRORS; i++) {
+ if (TS_fault[i])
+ pc.printf ("Error report, number %d, value %d, %s\r\n", i, TS_fault[i], FaultList[i]);
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/esc_comms.cpp Mon Jan 14 16:39:41 2019 +0000
@@ -0,0 +1,113 @@
+#include "mbed.h"
+#include "Electric_Loco.h"
+#include "AsyncSerial.hpp"
+
+extern Serial pc;
+extern AsyncSerial com2escs;
+extern error_handling_Jan_2019 Controller_Error ; // Provides array usable to store error codes.
+extern volatile bool trigger_32ms;
+extern command_line_interpreter_core pcli, ploco; // pcli handles comms with pc, ploco handles comms with STM3_ESC boards
+
+void STM3_ESC_Interface::message (int board, char * msg) // Send message to one individual STM3_ESC
+{
+ if (!(isdigit(board))) {
+ pc.printf ("Error in STM3_ESC_Interface::message, '%c' not valid board ID\r\n");
+ Controller_Error.set (FAULT_BOARD_ID_IN_MSG, -1);
+ return ;
+ }
+ com2escs.putc (board);
+ message (msg);
+}
+
+void STM3_ESC_Interface::message (char * msg) // Broadcast message to all STM3_ESCs
+{
+ com2escs.printf (msg);
+}
+
+void STM3_ESC_Interface::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;
+ com2escs.printf ("v%d\r", (int)(last_V * 99.0));
+}
+
+void STM3_ESC_Interface::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
+ com2escs.printf ("i%d\r", (int)(last_I * 99.0));
+}
+
+void STM3_ESC_Interface::get_boards_list (int * dest) {
+ for (int i = 0; i < MAX_ESCS; i++)
+ dest[i] = board_IDs[i];
+}
+
+void STM3_ESC_Interface::search_for_escs () { // Seek out all STM3_ESC boards connected to TS controller
+ char whotxt[] = "0who\r\0";
+ for (int i = 0; i < MAX_ESCS; i++)
+ board_IDs[i] = 0;
+ board_count = 0;
+ pc.printf ("Searching for connected STM3_ESC boards - ");
+ while (!trigger_32ms)
+ ploco.sniff (); // Allow any previous STM3_ESC comms opportunity to complete
+ while (whotxt[0] <= '9') { // Sniff out system, discover motor controllers connected
+ trigger_32ms = false;
+ message (whotxt); // Issue '0who' etc
+ whotxt[0]++;
+ while (!trigger_32ms) { // Give time for STM3_ESC board to respond
+ pcli.sniff (); // Check commands from pc also
+ ploco.sniff (); // This is where responses to 'who' get picked up and dealt with
+ }
+ } // Completed quick sniff to identify all connected STM3 ESC boards
+ if (board_count) {
+ pc.printf ("Found %d boards, IDs ", board_count);
+ for (int i = 0; i < board_count; i++)
+ pc.printf ("%c ", board_IDs[i]);
+ pc.printf ("\r\n");
+ }
+ else
+ pc.printf ("None found\r\n");
+}
+
+void STM3_ESC_Interface::set_board_ID (int a) { // called in response to 'whon' coming back from a STM3_ESC
+ board_count = 0; // reset and recalculate board_count
+ while (board_IDs[board_count]) {
+ if (board_IDs[board_count++] == a) {
+// pc.printf ("set_board_ID %c already listed\r\n", a);
+ return;
+ }
+ }
+ board_IDs[board_count++] = a;
+}
+
+bool STM3_ESC_Interface::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;
+ message (board_IDs[reqno++], "mph\r");
+ return true;
+}
+
+//void STM3_ESC_Interface::mph_update(struct parameters & a) { // Puts new readings into mem 22/06/2018
+void STM3_ESC_Interface::mph_update(double mph_reading) { // Puts new readings into mem 22/06/2018
+ static int identified_board = 0;
+ esc_speeds[identified_board++] = mph_reading; // A single mph reading returned from one STM3ESC. a.dbl[0] gives esc number
+ if (identified_board >= board_count)
+ identified_board = 0; // Circular buffer for number of STM3ESC boards sniffed and found
+ double temp = 0.0;
+ for (int j = 0; j < board_count; j++)
+ temp += esc_speeds[j];
+ if (board_count < 1) // Avoid possible DIV0 error
+ mph = 0.0;
+ else
+ mph = temp / board_count; // Updated average of most recent mph readings recieved
+}
+
--- a/graphics.cpp Sat Jun 23 09:37:41 2018 +0000
+++ b/graphics.cpp Mon Jan 14 16:39:41 2019 +0000
@@ -3,358 +3,53 @@
#include "LCD_DISCO_F746NG.h"
#include "Electric_Loco.h"
-#define VOLTMETER_X 68 // Voltmeter screen position
-#define VOLTMETER_Y 68
-#define AMMETER_X 68 // Ammeter screen position - Now replaced by Power meter
-#define AMMETER_Y 202
-#define SPEEDO_X 274 // Speedometer screen position
-#define SPEEDO_Y 135
-#define V_A_SIZE 54 // Size of voltmeter and ammeter
-#define SPEEDO_SIZE 112
-
-#define SPEEDO_BODY_COLOUR LCD_COLOR_BLACK
-#define SPEEDO_DIAL_COLOUR LCD_COLOR_WHITE
-#define SPEEDO_TEXT_COLOUR LCD_COLOR_BLUE
-
-#define VMETER_BODY_COLOUR LCD_COLOR_BLACK
-#define VMETER_DIAL_COLOUR LCD_COLOR_WHITE
-#define VMETER_TEXT_COLOUR LCD_COLOR_BLUE
-
-#define AMETER_BODY_COLOUR LCD_COLOR_BLACK
-#define AMETER_DIAL_COLOUR LCD_COLOR_WHITE
-#define AMETER_TEXT_COLOUR LCD_COLOR_BLUE
extern LCD_DISCO_F746NG lcd;
extern TS_DISCO_F746NG touch_screen;
extern Serial pc;
-static const int char_widths[] = {5, 7, 11, 14, 17, 17} ,
- meter_radius_min = 30, meter_radius_max = 120;
+/*
+moving_coil_meter Voltmeter ( LCD_COLOR_BLACK, // Frame / body colour
+ LCD_COLOR_WHITE, // Dial face colour
+ LCD_COLOR_RED, // Moving needle colour
+ LCD_COLOR_BLUE, // Text colour for Units e.g. 'V' and e.g. '32.7'
+ LCD_COLOR_MAGENTA, // Scale graduations colour
+ VOLTMETER_X, // X co-ord, centre of meter
+ VOLTMETER_Y, // Y co-ord, centre of meter
+ V_A_SIZE, // Meter is square with rounded corners. This is meter dial face radius
+ 22.0, // Scale not limited to e.g. 0 to 10. This is reading at anti-clock limit
+ 59.0, // This is reading at full scale deflection
+ 1.25 * PI, // Angle of needle at anti-clockwise limit
+ -0.25 * PI , // Angle of needle at full scale deflection (clockwise max)
+ 30, // Number of scale graduation marks drwan
+ "V", // Text for Units, e.g. 'V' or 'MPH'
+ ONE_DP, // NO_DPS or ONE_DP - supports only no decimal places or one
+ false) ; // true to show '+' or '-', false to supress sign display
+*/
+/*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
+
+*/
-// Uses our own generated sine and cosines from lookup table. For some unexplained reason, using inbuilt sin and cos fns cause display flicker !
-//extern double jcos (double angle); // Used in DrawNeedle, plain sin and cos functions cause display flicker !!
-//extern double jsin (double angle);
-#define jcos cos
-#define jsin sin
-/*void costabgen (int points) {
- double angle = 0.0;
- while (angle < 2.1 * PI) {
- pc.printf ("Angle %f, my cos %+f, c cos %+f\r\n", angle, jcos(angle), cos(angle));
-// pc.printf ("Angle %f, my sin %+f, c sin %+f\r\n", angle, jsin(angle), sin(angle));
- angle += PI / 24;
- }
-// double angle;
-*//* int step, perline = 0;
- double interval = PI / 2.0 / (double)points;
- pc.printf ("//At costabgen with %d points\r\n", points);
- pc.printf ("static const double costab[] = {\r\n");
- for (step = 0; step <= points; step++) {
- angle = interval * (double)step;
-// pc.printf ("cos %+.3f = %+.3f\r\n", angle, cos(angle));
- if (++perline == 8) {
- pc.printf ("%+.6f,\r\n", cos(angle));
- perline = 0;
- }
- else
- pc.printf ("%+.6f, ", cos(angle));
- wait (0.025);
- }
- pc.printf ("0.0\t}\t;\r\n//End of costab\r\n");
-*/
-//}
-
-/**
- * @brief Fills a triangle (between 3 points).
- * @param x1: Point 1 X position
- * @param y1: Point 1 Y position
- * @param x2: Point 2 X position
- * @param y2: Point 2 Y position
- * @param x3: Point 3 X position
- * @param y3: Point 3 Y position
- * @retval None
- */
-static void FillTriangle(uint16_t x1, uint16_t x2, uint16_t x3, uint16_t y1, uint16_t y2, uint16_t y3)
-{
- int16_t deltax = 0, deltay = 0, x = 0, y = 0, xinc1 = 0, xinc2 = 0,
- yinc1 = 0, yinc2 = 0, den = 0, num = 0, num_add = 0, num_pixels = 0,
- curpixel = 0;
-
- deltax = abs(x2 - x1); /* The difference between the x's */
- deltay = abs(y2 - y1); /* The difference between the y's */
- x = x1; /* Start x off at the first pixel */
- y = y1; /* Start y off at the first pixel */
-
- if (x2 >= x1) { /* The x-values are increasing */
- xinc1 = 1;
- xinc2 = 1;
- } else { /* The x-values are decreasing */
- xinc1 = -1;
- xinc2 = -1;
- }
-
- if (y2 >= y1) { /* The y-values are increasing */
- yinc1 = 1;
- yinc2 = 1;
- } else { /* The y-values are decreasing */
- yinc1 = -1;
- yinc2 = -1;
- }
-
- if (deltax >= deltay) { /* There is at least one x-value for every y-value */
- xinc1 = 0; /* Don't change the x when numerator >= denominator */
- yinc2 = 0; /* Don't change the y for every iteration */
- den = deltax;
- num = deltax / 2;
- num_add = deltay;
- num_pixels = deltax; /* There are more x-values than y-values */
- } else { /* There is at least one y-value for every x-value */
- xinc2 = 0; /* Don't change the x for every iteration */
- yinc1 = 0; /* Don't change the y when numerator >= denominator */
- den = deltay;
- num = deltay / 2;
- num_add = deltax;
- num_pixels = deltay; /* There are more y-values than x-values */
- }
-
- for (curpixel = 0; curpixel <= num_pixels; curpixel++) {
- lcd.DrawLine(x, y, x3, y3);
-
- num += num_add; /* Increase the numerator by the top of the fraction */
- if (num >= den) { /* Check if numerator >= denominator */
- num -= den; /* Calculate the new numerator value */
- x += xinc1; /* Change the x as appropriate */
- y += yinc1; /* Change the y as appropriate */
- }
- x += xinc2; /* Change the x as appropriate */
- y += yinc2; /* Change the y as appropriate */
- }
-}
-
-double anglefix (double a) { // Ensures 0.0 <= angle <= + two PI
- while (a > PI) a -= 2.0 * PI;
- while (a < 0.0) a += 2.0 * PI;
- return a;
-}
-
-class moving_coil_meter
-{
- int meter_radius, cent_x, cent_y, needle_len, scale_ticks,
- disc_colour, needle_colour, scale_colour, text_colour, body_colour, dec_places;
- double start_angle, end_angle, old_angle, value_min, value_max, rad_per_value, swept_angle, value_range;
- double Value; // This is the one that determines pointer angle
- bool draw_sign;
-
- void DrawNeedle (double alpha, int colour) ;
- void DrawScaleGraduations(int colour) ;
- double get_pointer_angle (double value) ;
- int get_font () ;
-
-public:
-
- moving_coil_meter () { // constructor
- meter_radius = 100;
- value_min = -1.0;
- value_max = 1.0;
- cent_x = cent_y = 150;
- disc_colour = LCD_COLOR_BLACK;
- needle_colour = LCD_COLOR_WHITE;
- scale_colour = LCD_COLOR_MAGENTA;
- text_colour = LCD_COLOR_RED;
- body_colour = LCD_COLOR_CYAN;
- old_angle = 0.0;
- }
-
- bool setup (int cx, int cy, int size, double lo, double hi, double start_ang, double end_ang, int scaleticks, char * units, int decimal_places, bool sign) ;
- void set_colours (int bod_colour, int bgcol, int needlecol, int textcolour, int scalecol) ;
- void set_value (double v) ;
-} Voltmeter, Powermeter, Speedo; // 3 instances of moving coil meter graphic
-
-void moving_coil_meter::set_colours (int bod_col, int bgcol, int needlecol, int textcol, int scalecol) {
- body_colour = bod_col;
- disc_colour = bgcol;
- needle_colour = needlecol;
- text_colour = textcol;
- scale_colour = scalecol;
-}
-
-void moving_coil_meter::DrawNeedle (double alpha, int colour)
-{
- point pixpts[4];
- int save_colour, ssa, sca;
- alpha = anglefix (alpha);
- double shortln = (needle_len / 18.7),
- sina = jsin(alpha),
- cosa = jcos(alpha);
-
- save_colour = lcd.GetTextColor ();
- ssa = (int)(shortln * sina);
- sca = (int)(shortln * cosa);
- old_angle = alpha;
- pixpts[0].x = cent_x - ssa;//(int)(shortln * sin(alpha));
- pixpts[0].y = cent_y - sca;//(int)(shortln * cos(alpha));
- pixpts[1].x = cent_x + (int)(needle_len * cosa);
- pixpts[1].y = cent_y - (int)(needle_len * sina); // - as increasing y is downwards
- pixpts[2].x = cent_x + ssa;//(int)(shortln * sin(alpha));
- pixpts[2].y = cent_y + sca;//(int)(shortln * cos(alpha));
- lcd.SetTextColor (colour);
- lcd.FillCircle (cent_x, cent_y, (int)(needle_len / 15.0));
- FillTriangle (pixpts[0].x, pixpts[1].x, pixpts[2].x, pixpts[0].y, pixpts[1].y, pixpts[2].y);
- lcd.SetTextColor (save_colour);
-}
-
-void moving_coil_meter::DrawScaleGraduations (int colour)
-{
- int save_colour = lcd.GetTextColor ();
- int i, radius_inner = (int) meter_radius - 2, radius_outer = (int) (meter_radius * 0.9);
- double ang, cosang, sinang, angle_step;
- lcd.SetTextColor (colour);
- ang = start_angle;
- angle_step = (start_angle - end_angle) / scale_ticks;
- for (i = 0; i <= scale_ticks; i++) { //
- cosang = cos(ang);
- sinang = sin(ang);
- lcd.DrawLine (cent_x + radius_outer * cosang, cent_y - radius_outer * sinang, cent_x + radius_inner * cosang, cent_y - radius_inner * sinang);
- ang -= angle_step;
- }
- lcd.SetTextColor (save_colour);
-}
void displaytext (int x, int y, const int font, char * txt) ;
-bool moving_coil_meter::setup (int cx, int cy, int size, double lo, double hi, double start_ang, double end_ang, int scaleticks, char * units, int decimal_places, bool sign)
-{
- bool retval = true;
- int font, charwid, x_offset;
- if (size < meter_radius_min || size > meter_radius_max)
- return false;
- meter_radius = size;
- if (meter_radius > cx || meter_radius > cy)
- return false;
- int corner_rad = meter_radius / 6,
- screw_hole_offset = meter_radius * 92 / 100,
- screw_rad = meter_radius / 13;
- cent_x = cx;
- cent_y = cy;
-
- start_angle = start_ang;
- end_angle = end_ang;
- value_min = lo;
- value_max = hi;
- scale_ticks = scaleticks;
- swept_angle = abs(start_angle - end_angle);
- value_range = (value_max - value_min);
- rad_per_value = swept_angle / value_range;
- dec_places = decimal_places;
- draw_sign = sign;
-
- needle_len = (int)(0.87 * (double)meter_radius);
- int oldcolour1 = lcd.GetTextColor ();
- int oldcolour2 = lcd.GetBackColor ();
- lcd.SetTextColor (body_colour);
- // Draw meter body as solid square with rounded corners, complete with mounting screw holes !
- lcd.FillRect (cent_x - meter_radius, cent_y - meter_radius - corner_rad, meter_radius * 2, corner_rad);
- lcd.FillRect (cent_x - meter_radius, cent_y + meter_radius, meter_radius * 2, corner_rad + 1);
- lcd.FillRect (cent_x - meter_radius - corner_rad, cent_y - meter_radius, 1 +(meter_radius + corner_rad) * 2, meter_radius * 2);
- lcd.FillCircle (cent_x - meter_radius, cent_y - meter_radius, corner_rad); // meter box has rounded corners
- lcd.FillCircle (cent_x - meter_radius, cent_y + meter_radius, corner_rad);
- lcd.FillCircle (cent_x + meter_radius, cent_y - meter_radius, corner_rad);
- lcd.FillCircle (cent_x + meter_radius, cent_y + meter_radius, corner_rad);
- lcd.SetTextColor (LCD_COLOR_DARKGRAY);
- lcd.FillCircle (cent_x - screw_hole_offset, cent_y - screw_hole_offset, screw_rad); // panel mounting screw holes near corners
- lcd.FillCircle (cent_x - screw_hole_offset, cent_y + screw_hole_offset, screw_rad);
- lcd.FillCircle (cent_x + screw_hole_offset, cent_y - screw_hole_offset, screw_rad);
- lcd.FillCircle (cent_x + screw_hole_offset, cent_y + screw_hole_offset, screw_rad);
- lcd.SetTextColor (disc_colour);
- lcd.FillCircle (cent_x, cent_y, meter_radius);
- DrawScaleGraduations (scale_colour); //drew the green trace around active needle-sweep angle
-
- font = get_font ();
- charwid = char_widths[font];
- x_offset = charwid * strlen(units) / 2;
- lcd.SetTextColor (text_colour);
- lcd.SetBackColor (disc_colour);
-// displaytext (cent_x - x_offset, cent_y + (meter_radius * 7) / 19, font, units);
- displaytext (cent_x - x_offset, cent_y + (meter_radius * 6) / 19, font, units);
- lcd.SetBackColor (oldcolour2);
- lcd.SetTextColor (oldcolour1);
- return retval;
+extern uint32_t odometer_out () ;
+void rewrite_odometer () {
+ char dist[20];
+ sprintf (dist, "%06dm", odometer_out()); // 12th June 2018 changed 05 to 06 to allow correct display of tot distance > 99999 metres
+ lcd.SetTextColor (LCD_COLOR_BLACK);
+ displaytext (241, 224, 2, dist);
}
-int moving_coil_meter::get_font ()
-{
- int font = meter_radius - meter_radius_min;
- font /= 17;
- if (font > 4)
- font = 4;
- if (font < 2)
- font = 2;
- return font;
-}
-
-double moving_coil_meter::get_pointer_angle (double v)
-{
- double vabvmin, retval;
- if (v < value_min) v = value_min;
- if (v > value_max) v = value_max;
- Value = v; // clipped copy of supplied value
- vabvmin = v - value_min;
- retval = start_angle - (vabvmin * rad_per_value);
- return anglefix (retval);
-}
-
-void moving_coil_meter::set_value (double meter_read_value)
-{
- char txt[32];
- int x_offset, font, charwid, lenchk;//,
- DrawNeedle (old_angle, disc_colour); // un-draw needle
- DrawNeedle (get_pointer_angle (meter_read_value), needle_colour) ; // re-draw needle
- // if (draw_sign) {
- if (dec_places == ONE_DP)
- sprintf (txt, " %+.1f \0", meter_read_value);
- else
- sprintf (txt, " %+.0f \0", meter_read_value);
- lenchk = strlen(txt);
- if (!draw_sign) {
- for (int i = 1; i < lenchk; i++)
- txt[i] = txt[i + 1];
-// lenchk--; // Stupidly, this gives the display flicker blight
- }
- lenchk = strlen(txt);// Stupidly, repeating this instead does NOT give the display flicker blight
- font = get_font();
- charwid = char_widths[font];
- x_offset = charwid * lenchk / 2;
- lcd.SetTextColor (text_colour);
- lcd.SetBackColor (disc_colour);
- if (lenchk > 0 && lenchk < 9)
- displaytext (cent_x - x_offset, cent_y + (meter_radius * 11) / 19, font, txt);
-}
-//bool moving_coil_meter::setup (int cx, int cy, int size, double lo, double hi, double start_ang, double end_ang,
-// int scale_ticks, char * units)
-void vm_set () //x y size minv maxv min angle max angle,
-{
- Speedo.set_colours (SPEEDO_BODY_COLOUR, SPEEDO_DIAL_COLOUR, LCD_COLOR_RED, SPEEDO_TEXT_COLOUR, LCD_COLOR_BLACK);
- Speedo.setup (SPEEDO_X, SPEEDO_Y, SPEEDO_SIZE, 0.0, 12.0, 1.25 * PI, -0.25 * PI , 12, "MPH", ONE_DP, false);
- Voltmeter.set_colours (LCD_COLOR_BLACK, LCD_COLOR_WHITE, LCD_COLOR_RED, LCD_COLOR_BLUE, LCD_COLOR_MAGENTA);
- Voltmeter.setup (VOLTMETER_X, VOLTMETER_Y, V_A_SIZE, 22.0, 59.0, 1.25 * PI, -0.25 * PI , 30, "V", ONE_DP, false);
- Powermeter.set_colours (LCD_COLOR_BLACK, LCD_COLOR_WHITE, LCD_COLOR_RED, LCD_COLOR_BLUE, LCD_COLOR_BLUE);
- Powermeter.setup (AMMETER_X, AMMETER_Y, V_A_SIZE, -1400.0, 1400.0, 1.25 * PI, -0.25 * PI , 14, "Watt", NO_DPS, false);
-}
-
-//void update_meters (double speed, double current, double voltage)
-void update_meters (double speed, double power, double voltage)
-{
-// Powermeter.set_value(voltage * current);
- Powermeter.set_value(power);
- Voltmeter.set_value (voltage);
- Speedo.set_value (speed);
-}
-
-
-
struct rect { struct point a, b; } ;
-struct butt_on {
+struct button_specs {
struct rect area;
int border_colour, body_colour;
bool in_use, pressed;//, released;
@@ -362,7 +57,7 @@
char txt2[12];
} ;
-struct butt_on button[NUMOF_BUTTONS];
+struct button_specs button[NUMOF_BUTTONS];
int get_button_press (struct point & pt) ;
int get_but_p (int x, int y)
@@ -373,14 +68,25 @@
return get_button_press (p);
}
-
+/**
+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
+*/
void read_keypresses (struct ky_bd & a)
{
int x;
a.count = 0;
a.sli = false;
for (x = 0; x < MAX_TOUCHES; x++)
- a.ky[x].keynum = -1;
+ a.key[x].keynum = -1;
int touches, but;
TS_StateTypeDef TS_State;
touch_screen.GetState(&TS_State);
@@ -388,12 +94,12 @@
for (int h = 0; h < touches; h++) {
but = get_but_p (TS_State.touchX[h], TS_State.touchY[h]);
if (but > - 1) {
- a.ky[a.count].keynum = but;
- a.ky[a.count].x = TS_State.touchX[h];
- a.ky[a.count].y = TS_State.touchY[h];
- if (but == SLIDER) {
+ a.key[a.count].keynum = but;
+ a.key[a.count].x = TS_State.touchX[h];
+ a.key[a.count].y = TS_State.touchY[h];
+ if (but == SLIDER_BUTTON) {
a.sli = true;
- a.slider_y = a.ky[a.count].y;
+ a.slider_y = a.key[a.count].y;
}
a.count++;
}
@@ -425,7 +131,7 @@
lcd.SetBackColor(obc);
}
-void draw_button (struct butt_on & bu)
+void draw_button (struct button_specs & bu)
{
int oldbgcolour;
lcd.SetTextColor (bu.body_colour);
@@ -451,7 +157,7 @@
if (but < 0 || but > NUMOF_BUTTONS) {
pc.printf ("Button out of range in draw_button_hilight %d\r\n", but) ;
} else {
- struct butt_on * bu = &button[but];
+ struct button_specs * bu = &button[but];
int oldbgcolour = lcd.GetBackColor();//, minx, miny, maxx, maxy;
lcd.SetTextColor(colour);
lcd.DrawRect(bu->area.a.x - 1, bu->area.a.y - 1, bu->area.b.x - bu->area.a.x + 2, bu->area.b.y - bu->area.a.y + 2);
@@ -461,13 +167,13 @@
}
}
-void draw_button (struct butt_on & bu, int body_colour)
+void draw_button (struct button_specs & bu, int body_colour)
{
bu.body_colour = body_colour;
draw_button (bu);
}
-void setup_button (struct butt_on & bu, int x1, int y1, int dx, int dy, int bord, int body, char * txt1, char * txt2)
+void setup_button (struct button_specs & bu, int x1, int y1, int dx, int dy, int bord, int body, char * txt1, char * txt2)
{
static const int margin = 3;
int xsize = lcd.GetXSize();
@@ -490,40 +196,23 @@
draw_button(bu);
}
-bool ifpressed (int key)
+/*bool ifpressed (int key)
{
return button[key].pressed;
}
-
-bool is_button_pressed (struct point & pt, struct butt_on & bu)
+*/
+bool is_button_pressed (struct point & pt, struct button_specs & bu)
{
if (bu.in_use) {
- if (bu.area.a.x < pt.x && bu.area.b.x > pt.x
- && bu.area.a.y < pt.y && bu.area.b.y > pt.y)
+ if ( bu.area.a.x < pt.x
+ && bu.area.b.x > pt.x
+ && bu.area.a.y < pt.y
+ && bu.area.b.y > pt.y)
return true;
}
return false;
}
-bool keyrelease (int key)
-{
- bool rv = false;
- if (button[key].pressed) {
- rv = true;
- button[key].pressed = false;
- }
- return rv;
-}
-void setpressed (int key, bool torf)
-{
- button[key].pressed = torf;
-}
-void setinuse (int key, bool torf)
-{
- button[key].in_use = torf;
-}
-
-
int get_button_press (struct point & pt)
{
for (int j = 0; j < NUMOF_BUTTONS; j++)
@@ -534,91 +223,71 @@
void setup_buttons ()
{
- setup_button (button[SPEEDO_BUT],
+ setup_button (button[SPEEDO_BUTTON],
SPEEDO_X - SPEEDO_SIZE, SPEEDO_Y - SPEEDO_SIZE,
SPEEDO_SIZE * 2, SPEEDO_SIZE * 2, SPEEDO_BODY_COLOUR, LCD_COLOR_RED, " X", "") ;
- setup_button (button[VMETER_BUT],
+ setup_button (button[VMETER_BUTTON],
VOLTMETER_X - V_A_SIZE, VOLTMETER_Y - V_A_SIZE, V_A_SIZE * 2, V_A_SIZE * 2, VMETER_BODY_COLOUR, LCD_COLOR_RED, " Y", "") ;
- setup_button (button[AMETER_BUT],
+ setup_button (button[AMETER_BUTTON],
AMMETER_X - V_A_SIZE, AMMETER_Y - V_A_SIZE, V_A_SIZE * 2, V_A_SIZE * 2, AMETER_BODY_COLOUR, LCD_COLOR_RED, " Z", "") ;
- setup_button (button[SLIDER], SLIDERX, SLIDERY, SLIDERW, SLIDERH, LCD_COLOR_BLUE, LCD_COLOR_MAGENTA, "", "") ;
+ setup_button (button[SLIDER_BUTTON], SLIDERX, SLIDERY, SLIDERW, SLIDERH, LCD_COLOR_BLUE, LCD_COLOR_MAGENTA, "", "") ;
}
-
-void SliderGraphic (struct slide & q) {
- int
- colr,
- oldbgcolr = lcd.GetBackColor (),
- oldtxtcolr = lcd.GetTextColor ();
+void screen_touch_handler::DrawSlider () {
+ uint32_t
+ colr,
+ oldbgcolr = lcd.GetBackColor (),
+ oldtxtcolr = lcd.GetTextColor ();
char txt[4];
txt[1] = 0;
- if (q.position > MAX_POS)
- q.position = MAX_POS;
- if (q.position < MIN_POS)
- q.position = MIN_POS;
- if (q.position == NEUTRAL_VAL)
- q.state = NEUTRAL_DRIFT;
- if (q.position > NEUTRAL_VAL)
- q.state = REGEN_BRAKE;
- if (q.position < NEUTRAL_VAL)
- if (q.state == REGEN_BRAKE) { // Ensure transition from BRAKE to RUN passes through NEUTRAL
- q.position = NEUTRAL_VAL;
- q.state = NEUTRAL_DRIFT;
- }
- else
- q.state = RUN;
- if (q.position == MAX_POS) {
- if (q.loco_speed < LOCO_HANDBRAKE_ESCAPE_SPEED)
- q.state = PARK;
- else {
- q.state = REGEN_BRAKE;
- q.position--;
- }
- }
- if (q.position != q.oldpos) {
+ if (position > MAX_POS)
+ position = MAX_POS;
+ if (position < MIN_POS)
+ position = MIN_POS;
+ if (position != oldpos) {
// Draw slider background colour rectangle overwriting previous circles
// Redraw black vertical
// Draw new circles
// Write text char
lcd.SetTextColor(LCD_COLOR_MAGENTA);
- lcd.FillRect (SLIDERX + 1, q.oldpos - BUTTON_RAD, SLIDERW - 2, SLIDERW);
+ lcd.FillRect (SLIDERX + 1, oldpos - BUTTON_RAD, SLIDERW - 2, SLIDERW);
lcd.SetTextColor(LCD_COLOR_BLACK);
lcd.FillRect (SLIDERX + (SLIDERW / 2) - 3, 6, 7, SLIDERH - 8);
- q.oldpos = q.position;
+ oldpos = position;
lcd.SetTextColor(LCD_COLOR_WHITE);
- lcd.DrawCircle (CIRC_CTR, q.position, BUTTON_RAD); // seel also FillCircle
- lcd.DrawCircle (CIRC_CTR, q.position, BUTTON_RAD - 1);
- switch (q.state) {
+ lcd.DrawCircle (CIRC_CTR, position, BUTTON_RAD); // seel also FillCircle
+ lcd.DrawCircle (CIRC_CTR, position, BUTTON_RAD - 1);
+ switch (next_state) {
+ case RUN_DOWN:
case RUN:
txt[0] = 'R';
colr = LCD_COLOR_GREEN;
break;
+ case INTO_NEUTRAL_DRIFT:
case NEUTRAL_DRIFT:
+ case INTO_RUN:
txt[0] = 'N';
colr = LCD_COLOR_BLUE;
break;
case REGEN_BRAKE:
+ case INTO_REGEN_BRAKE:
txt[0] = 'B';
colr = LCD_COLOR_ORANGE;
break;
- case PARK:
- txt[0] = 'P';
- colr = LCD_COLOR_RED;
- break;
default:
txt[0] = 'X';
colr = LCD_COLOR_CYAN;
+// pc.printf ("State %d\r\n", next_state);
} // End of switch
lcd.SetTextColor(colr);
- lcd.FillCircle (CIRC_CTR, q.position, BUTTON_RAD - 2);
+ lcd.FillCircle (CIRC_CTR, position, BUTTON_RAD - 2);
lcd.SetBackColor (colr);
lcd.SetTextColor(LCD_COLOR_YELLOW);
- displaytext(SLIDERX + 17, q.position - 10, 4, txt); // largest font
+ displaytext(SLIDERX + 17, position - 10, 4, txt); // largest font
lcd.SetBackColor (LCD_COLOR_BLACK);
} // End of else
lcd.SetTextColor (oldtxtcolr);
lcd.SetBackColor (oldbgcolr);
-// pc.printf ("SliderG %d, %d, %d\r\n", q.position, q.oldpos, q.state);
}
--- 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 ()
-
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/movingcoilmeter.cpp Mon Jan 14 16:39:41 2019 +0000
@@ -0,0 +1,252 @@
+#include "mbed.h"
+#include "TS_DISCO_F746NG.h"
+#include "LCD_DISCO_F746NG.h"
+#include "Electric_Loco.h"
+
+extern LCD_DISCO_F746NG lcd;
+static const int char_widths[] = {5, 7, 11, 14, 17, 17} ,
+ meter_radius_min = 30, meter_radius_max = 120;
+extern void displaytext (int x, int y, const int font, char * txt) ;
+
+void moving_coil_meter::DrawNeedle (double alpha, int colour)
+{
+ uint16_t x1, x2, x3, y1, y2, y3;
+ int save_colour, ssa, sca;
+ while (alpha > PI) alpha -= TWO_PI;
+ while (alpha < 0.0) alpha += TWO_PI;
+ double shortln = (needle_len / 18.7),
+ sina = sin(alpha),
+ cosa = cos(alpha);
+
+ save_colour = lcd.GetTextColor ();
+ ssa = (int)(shortln * sina);
+ sca = (int)(shortln * cosa);
+ old_angle = alpha;
+ x1 = cent_x - ssa;//(int)(shortln * sin(alpha));
+ y1 = cent_y - sca;//(int)(shortln * cos(alpha));
+ x2 = cent_x + (int)(needle_len * cosa);
+ y2 = cent_y - (int)(needle_len * sina); // - as increasing y is downwards
+ x3 = cent_x + ssa;//(int)(shortln * sin(alpha));
+ y3 = cent_y + sca;//(int)(shortln * cos(alpha));
+ lcd.SetTextColor (colour);
+ lcd.FillCircle (cent_x, cent_y, (int)(needle_len / 15.0));
+// FillTriangle (x1, x2, x3, y1, y2, y3);
+
+//static void FillTriangle(uint16_t x1, uint16_t x2, uint16_t x3, uint16_t y1, uint16_t y2, uint16_t y3)
+//{
+ int16_t deltax = 0, deltay = 0, x, y, xinc1 = 0, xinc2 = 0,
+ yinc1 = 0, yinc2 = 0, den = 0, num = 0, num_add = 0, num_pixels = 0,
+ curpixel = 0;
+
+ deltax = abs(x2 - x1); /* The difference between the x's */
+ deltay = abs(y2 - y1); /* The difference between the y's */
+ x = x1; /* Start x off at the first pixel */
+ y = y1; /* Start y off at the first pixel */
+
+ if (x2 >= x1) { /* The x-values are increasing */
+ xinc1 = 1;
+ xinc2 = 1;
+ } else { /* The x-values are decreasing */
+ xinc1 = -1;
+ xinc2 = -1;
+ }
+
+ if (y2 >= y1) { /* The y-values are increasing */
+ yinc1 = 1;
+ yinc2 = 1;
+ } else { /* The y-values are decreasing */
+ yinc1 = -1;
+ yinc2 = -1;
+ }
+
+ if (deltax >= deltay) { /* There is at least one x-value for every y-value */
+ xinc1 = 0; /* Don't change the x when numerator >= denominator */
+ yinc2 = 0; /* Don't change the y for every iteration */
+ den = deltax;
+ num = deltax / 2;
+ num_add = deltay;
+ num_pixels = deltax; /* There are more x-values than y-values */
+ } else { /* There is at least one y-value for every x-value */
+ xinc2 = 0; /* Don't change the x for every iteration */
+ yinc1 = 0; /* Don't change the y when numerator >= denominator */
+ den = deltay;
+ num = deltay / 2;
+ num_add = deltax;
+ num_pixels = deltay; /* There are more y-values than x-values */
+ }
+
+ for (curpixel = 0; curpixel <= num_pixels; curpixel++) {
+ lcd.DrawLine(x, y, x3, y3);
+
+ num += num_add; /* Increase the numerator by the top of the fraction */
+ if (num >= den) { /* Check if numerator >= denominator */
+ num -= den; /* Calculate the new numerator value */
+ x += xinc1; /* Change the x as appropriate */
+ y += yinc1; /* Change the y as appropriate */
+ }
+ x += xinc2; /* Change the x as appropriate */
+ y += yinc2; /* Change the y as appropriate */
+ }
+//}
+ lcd.SetTextColor (save_colour);
+}
+
+void moving_coil_meter::LED (int which_led, int colour) { // Meter mounting screw holes serve as LEDs. LCD_COLOR_DARKGRAY for off
+ lcd.SetTextColor (colour);
+ switch (which_led) {
+ case 0: // NW
+ lcd.FillCircle (cent_x - screw_hole_offset, cent_y - screw_hole_offset, screw_rad); // panel mounting screw holes near corners
+ break;
+ case 1: // SW
+ lcd.FillCircle (cent_x - screw_hole_offset, cent_y + screw_hole_offset, screw_rad);
+ break;
+ case 2: // NE
+ lcd.FillCircle (cent_x + screw_hole_offset, cent_y - screw_hole_offset, screw_rad);
+ break;
+ case 3: // SE
+ lcd.FillCircle (cent_x + screw_hole_offset, cent_y + screw_hole_offset, screw_rad);
+ break;
+ default:
+ break;
+ }
+}
+
+void moving_coil_meter::redraw ()
+{
+ int oldcolour1 = lcd.GetTextColor ();
+ int oldcolour2 = lcd.GetBackColor ();
+ lcd.SetTextColor (body_colour);
+ // Draw meter body as solid square with rounded corners, complete with mounting screw holes !
+
+ int font, charwid, x_offset;
+// int corner_rad = (meter_radius / 6),
+// screw_hole_offset = (meter_radius * 92 / 100),
+// screw_rad = (meter_radius / 13);
+
+ lcd.FillRect (cent_x - meter_radius, cent_y - meter_radius - corner_rad, meter_radius * 2, corner_rad);
+ lcd.FillRect (cent_x - meter_radius, cent_y + meter_radius, meter_radius * 2, corner_rad + 1);
+ lcd.FillRect (cent_x - meter_radius - corner_rad, cent_y - meter_radius, 1 +(meter_radius + corner_rad) * 2, meter_radius * 2);
+ lcd.FillCircle (cent_x - meter_radius, cent_y - meter_radius, corner_rad); // meter box has rounded corners
+ lcd.FillCircle (cent_x - meter_radius, cent_y + meter_radius, corner_rad);
+ lcd.FillCircle (cent_x + meter_radius, cent_y - meter_radius, corner_rad);
+ lcd.FillCircle (cent_x + meter_radius, cent_y + meter_radius, corner_rad); // done drawing meter body
+/* lcd.SetTextColor (LCD_COLOR_DARKGRAY);
+ lcd.FillCircle (cent_x - screw_hole_offset, cent_y - screw_hole_offset, screw_rad); // panel mounting screw holes near corners
+ lcd.FillCircle (cent_x - screw_hole_offset, cent_y + screw_hole_offset, screw_rad); // Jan 2019 now drawn as LEDs below
+ lcd.FillCircle (cent_x + screw_hole_offset, cent_y - screw_hole_offset, screw_rad);
+ lcd.FillCircle (cent_x + screw_hole_offset, cent_y + screw_hole_offset, screw_rad);
+*/
+ lcd.SetTextColor (disc_colour);
+ lcd.FillCircle (cent_x, cent_y, meter_radius); // main meter dial face
+
+ int save_colour = lcd.GetTextColor ();
+ int radius_inner = (int) meter_radius - 2, radius_outer = (int) (meter_radius * 0.9);
+ double ang, cosang, sinang, angle_step;
+ lcd.SetTextColor (scale_colour);
+ angle_step = (start_angle - end_angle) / scale_ticks;
+ ang = start_angle;
+ for (int i = 0; i <= scale_ticks; i++) { // DrawScaleGraduations
+ cosang = cos(ang);
+ sinang = sin(ang);
+ lcd.DrawLine (cent_x + radius_outer * cosang, cent_y - radius_outer * sinang, cent_x + radius_inner * cosang, cent_y - radius_inner * sinang);
+ ang -= angle_step;
+ }
+
+ font = get_font_size ();
+ charwid = char_widths[font];
+ x_offset = charwid * strlen(unit_txt) / 2;
+ lcd.SetTextColor (text_colour);
+ lcd.SetBackColor (disc_colour);
+ displaytext (cent_x - x_offset, cent_y + (meter_radius * 6) / 19, font, unit_txt);
+ lcd.SetBackColor (oldcolour2);
+ lcd.SetTextColor (oldcolour1);
+
+ LED (0, LCD_COLOR_RED); // NW
+ LED (1, LCD_COLOR_GREEN); // SW
+ LED (2, LCD_COLOR_BLUE); // NE
+ LED (3, LCD_COLOR_YELLOW); // SE
+}
+
+
+moving_coil_meter::moving_coil_meter ( int bod_col, int bgcol, int needlecol, int textcol, int scalecol,
+ int cx, int cy, int size, double lo, double hi, double start_ang, double end_ang,
+ int scaleticks, char * units, int decimal_places, bool sign)
+{
+ strncpy (unit_txt, units, 8);
+ body_colour = bod_col;
+ disc_colour = bgcol;
+ needle_colour = needlecol;
+ text_colour = textcol;
+ scale_colour = scalecol;
+ if (size < meter_radius_min)
+ size = meter_radius_min;
+ if (size > meter_radius_max)
+ size = meter_radius_max;
+ meter_radius = size;
+ cent_x = cx;
+ cent_y = cy;
+ start_angle = start_ang;
+ end_angle = end_ang;
+ value_min = lo;
+ value_max = hi;
+ scale_ticks = scaleticks;
+ swept_angle = abs(start_angle - end_angle);
+ value_range = (value_max - value_min);
+ dec_places = decimal_places;
+ draw_sign = sign; // bool
+ needle_len = (int)(0.87 * (double)meter_radius);
+ corner_rad = (meter_radius / 6),
+ screw_hole_offset = (meter_radius * 92 / 100),
+ screw_rad = (meter_radius / 13);
+}
+
+int moving_coil_meter::get_font_size ()
+{
+ int font = meter_radius - meter_radius_min;
+ font /= 17;
+ if (font > 4)
+ font = 4;
+ if (font < 2)
+ font = 2;
+ return font;
+}
+
+double anglefix (double a) { // Ensures 0.0 <= angle <= + two PI
+ while (a > PI) a -= TWO_PI;
+ while (a < 0.0) a += TWO_PI;
+ return a;
+}
+
+double moving_coil_meter::get_pointer_angle (double v)
+{
+// double a;
+ if (v < value_min) v = value_min;
+ if (v > value_max) v = value_max;
+ return anglefix (start_angle - ((v - value_min) * swept_angle / value_range)) ;
+}
+
+void moving_coil_meter::set_value (double meter_read_value)
+{
+ char txt[32];
+ int x_offset, font, lenchk;//,
+// double pointer_angle;
+ DrawNeedle (old_angle, disc_colour); // un-draw needle
+ DrawNeedle (get_pointer_angle (meter_read_value), needle_colour) ; // re-draw needle
+ if (dec_places == ONE_DP)
+ sprintf (txt, " %+.1f \0", meter_read_value);
+ else
+ sprintf (txt, " %+.0f \0", meter_read_value);
+ lenchk = strlen(txt);
+ if (!draw_sign) {
+ for (int i = 1; i < lenchk; i++)
+ txt[i] = txt[i + 1];
+ lenchk--; // Stupidly, this gives the display flicker blight
+ }
+// lenchk = strlen(txt);// Stupidly, repeating this instead does NOT give the display flicker blight
+ font = get_font_size ();
+ x_offset = char_widths[font] * lenchk / 2;
+ lcd.SetTextColor (text_colour);
+ lcd.SetBackColor (disc_colour);
+ if (lenchk > 0 && lenchk < 9)
+ displaytext (cent_x - x_offset, cent_y + (meter_radius * 11) / 19, font, txt);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/movingcoilmeter.h Mon Jan 14 16:39:41 2019 +0000
@@ -0,0 +1,56 @@
+#ifndef JON_FREEMAN_MOVING_COIL_METER
+#define JON_FREEMAN_MOVING_COIL_METER
+#include "mbed.h"
+
+/**
+class moving_coil_meter
+Create moving coil meter graphic using STM32F746G-DISCO.
+Usage example :
+
+top line of constructor :
+moving_coil_meter::moving_coil_meter ( int bod_col, int bgcol, int needlecol, int textcol, int scalecol,
+ int cx, int cy, int size, double lo, double hi, double start_ang, double end_ang,
+ int scaleticks, char * units, int decimal_places, bool sign)
+
+moving_coil_meter Voltmeter ( LCD_COLOR_BLACK, // Frame / body colour
+ LCD_COLOR_WHITE, // Dial face colour
+ LCD_COLOR_RED, // Moving needle colour
+ LCD_COLOR_BLUE, // Text colour for Units e.g. 'V' and e.g. '32.7'
+ LCD_COLOR_MAGENTA, // Scale graduations colour
+ VOLTMETER_X, // X co-ord, centre of meter
+ VOLTMETER_Y, // Y co-ord, centre of meter
+ V_A_SIZE, // Meter is square with rounded corners. This is meter dial face radius
+ 22.0, // Scale not limited to e.g. 0 to 10. This is reading at anti-clock limit
+ 59.0, // This is reading at full scale deflection
+ 1.25 * PI, // Angle of needle at anti-clockwise limit
+ -0.25 * PI , // Angle of needle at full scale deflection (clockwise max)
+ 30, // Number of scale graduation marks drwan
+ "V", // Text for Units, e.g. 'V' or 'MPH'
+ ONE_DP, // NO_DPS or ONE_DP - supports only no decimal places or one
+ false) ; // true to show '+' or '-', false to supress sign display
+
+*/
+class moving_coil_meter
+{
+ int meter_radius, cent_x, cent_y, needle_len, scale_ticks,
+ disc_colour, needle_colour, scale_colour, text_colour, body_colour, dec_places;
+ int corner_rad, // = (meter_radius / 6),
+ screw_hole_offset, // = (meter_radius * 92 / 100),
+ screw_rad ; // = (meter_radius / 13);
+ char unit_txt[8];
+ double start_angle, end_angle, old_angle, value_min, value_max, swept_angle, value_range;
+ bool draw_sign;
+
+ void DrawNeedle (double alpha, int colour) ;
+ double get_pointer_angle (double value) ;
+ int get_font_size () ;
+
+public:
+ moving_coil_meter () {} ;// default constructor
+ moving_coil_meter (int, int, int, int, int, int, int, int, double, double, double, double, int, char *, int, bool) ;
+ void set_value (double v) ;
+ void redraw () ;
+ void LED (int, int) ;
+} ;
+
+#endif
--- a/qspi_mem.cpp Sat Jun 23 09:37:41 2018 +0000
+++ b/qspi_mem.cpp Mon Jan 14 16:39:41 2019 +0000
@@ -1,9 +1,10 @@
#include "mbed.h"
#include "Electric_Loco.h"
-#ifdef QSPI // Defined in Electric_Loco.h
#include "QSPI_DISCO_F746NG.h"
+extern error_handling_Jan_2019 Controller_Error ;
+
struct log_element {
uint32_t pulsetot; // Total distance ever in metres
uint16_t powerW; // during previous second
@@ -28,29 +29,24 @@
(pQSPI_Info.ProgPagesNumber != N25Q128A_SECTOR_SIZE))
{
error("Get informations FAILED\r\n");
- return false;
+ return false; // Controller_Error.set gets called from main ;
}
else
{
- pc.printf("Get informations PASSED\r\n");
+/* pc.printf("Get N25Q128A QSPI mem informations PASSED\r\n");
pc.printf ("FLASH_SIZE\t\t%d\r\n", N25Q128A_FLASH_SIZE);
pc.printf ("ERASE_SECTOR_SIZE\t%d\r\n", N25Q128A_SUBSECTOR_SIZE);
pc.printf ("PROG_PAGE_SIZE\t\t%d\r\n", N25Q128A_PAGE_SIZE);
pc.printf ("Erase sectors number\t%d\r\n", N25Q128A_SUBSECTOR_SIZE);
- pc.printf ("N25Q128A_SECTOR_SIZE\t%d\r\n", N25Q128A_SECTOR_SIZE);
+ pc.printf ("N25Q128A_SECTOR_SIZE\t%d\r\n", N25Q128A_SECTOR_SIZE); */
return true;
}
}
-int ask_QSPI_OK () {
- return QSPI_OK;
-}
-
-int qspiinit () {
- int r = qspi.Init();
- if (r != QSPI_OK)
- pc.printf ("QSPI Init failed.\r\n");
- return r;
+bool test_qspi () {
+ if ((qspi.Init() == QSPI_OK) && (qspimemcheck ()))
+ return true;
+ return false;
}
void show_bank (uint32_t bank) {
@@ -58,6 +54,7 @@
struct log_element * p = (log_element *)bu;
if (qspi.Read(bu, bank << 12, 4096) != QSPI_OK) {
pc.printf ("Error reading qspi mem in show_bank\r\n");
+ Controller_Error.set (FAULT_QSPI, 1);
return ;
}
pc.printf ("Listing records in bank %d\r\n", bank);
@@ -94,14 +91,15 @@
bank = START_BANK;
while (bank < START_BANK + BANKS_4K && !free_elem_found) {
if (qspi.Read(buff, bank << 12, 4096) != QSPI_OK) {
- pc.printf ("Error reading qspi mem\r\n");
+ Controller_Error.set (FAULT_QSPI, 1);
+// pc.printf ("Error reading qspi mem\r\n");
}
for (ptr = 0; !free_elem_found && ptr < 4096; ptr += sizeof(struct log_element)) {
free_elem_found = test_element_free (&buff[ptr]);
if (free_elem_found) {
obank = bank;
optr = ptr;
- pc.printf ("Found free element at bank %d, ptr %x\r\n", bank, ptr);
+// pc.printf ("Found free element at bank %d, ptr %x\r\n", bank, ptr);
}
else { // Not free element found
lptr = ptr;
@@ -116,7 +114,7 @@
// historic_distance = p->pulsetot; // This needs replacing
total_distance = p->pulsetot; // New May 2018, total_distance is metres. Update info arrives in mm.
- pc.printf ("Constructor found free elem at bank %d, position %d, previous total %ld, volts %.3f\r\n", bank, ptr, p->pulsetot, ((double)p->volts) / 500.0);
+// pc.printf ("Constructor found free elem at bank %d, position %d, previous total %ld, volts %.3f\r\n", bank, ptr, p->pulsetot, ((double)p->volts) / 500.0);
} // endof constructor
} odometer;
@@ -208,4 +206,3 @@
uint32_t odometer_out () {
return odometer.out();
}
-#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/touch_handler.cpp Mon Jan 14 16:39:41 2019 +0000
@@ -0,0 +1,269 @@
+#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 ();
+ }
+