Dual Brushless Motor ESC, 10-62V, up to 50A per motor. Motors ganged or independent, multiple control input methods, cycle-by-cycle current limit, speed mode and torque mode control. Motors tiny to kW. Speed limit and other parameters easily set in firmware. As used in 'The Brushless Brutalist' locomotive - www.jons-workshop.com. See also Model Engineer magazine June-October 2019.
Dependencies: mbed BufferedSerial Servo PCT2075 FastPWM
Update 17th August 2020 Radio control inputs completed
Revision 6:f289a49c1eae, committed 2018-06-05
- Comitter:
- JonFreeman
- Date:
- Tue Jun 05 07:19:39 2018 +0000
- Parent:
- 5:ca86a7848d54
- Child:
- 7:6deaeace9a3e
- Commit message:
- Migrating towards code for both STM32F401RET (64 pin) and STM32F446ZET7 (144 pin). Should resolve IO conflicts for larger device - getting servo ins and outs working
Changed in this revision
--- a/DualBLS.h Tue May 29 16:36:34 2018 +0000
+++ b/DualBLS.h Tue Jun 05 07:19:39 2018 +0000
@@ -21,6 +21,7 @@
const double PI = 4.0 * atan(1.0),
TWOPI = 8.0 * atan(1.0);
+enum {MOTADIR, MOTBDIR, GANG, SVO1, SVO2, COMM_SRC, ID, WHEELDIA, MOTPIN, WHEELGEAR} ; // Identical in TS and DualBLS
struct optpar {
int min, max, def; // min, max, default
const char * t; // description
@@ -33,6 +34,12 @@
{0, 2, 2, "Servo2 0, 1, 2 = Not used, Input, Output"},
{1, 5, 2, "Command source 0 Invalid, 1 COM1, 2 COM2, 3 Pot, 4 Servo1, 5 Servo2"},
{'1', '9', '0', "Alternative ID ascii '1' to '9'"}, // defaults to '0' before eerom setup for first time
+ {50, 250, 98, "Wheel diameter mm"}, // New 01/06/2018
+ {10, 250, 27, "Motor pinion"}, // New 01/06/2018
+ {50, 250, 85, "Wheel gear"}, // New 01/06/2018
} ;
-const int numofopts = sizeof(option_list) / sizeof (struct optpar);
+const int numof_eeprom_options = sizeof(option_list) / sizeof (struct optpar);
+struct single_bogie_options {
+ char motoradir, motorbdir, gang, svo1, svo2, comm_src, id, wheeldia, motpin, wheelgear, spare;
+} ;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cli_BLS_nortos.cpp Tue Jun 05 07:19:39 2018 +0000
@@ -0,0 +1,334 @@
+// DualBLS2018_03
+#include "mbed.h"
+#include "BufferedSerial.h"
+
+#include <cctype>
+#include "DualBLS.h"
+using namespace std;
+
+extern int I_Am () ; // Returns boards id number as ASCII char '0', '1' etc. Code for Broadcast = '\r'
+extern int WatchDog;
+
+const int BROADCAST = '\r';
+const int MAX_PARAMS = 20;
+struct parameters {
+ int32_t position_in_list, // set but not used Apr 2018, contains i for i'th menu item
+// last_time, // gets reading from clock() ; not known to be useful or reliable
+ numof_dbls,
+ target_unit;
+ double dbl[MAX_PARAMS];
+ bool respond;
+} ;
+
+// WithOUT RTOS
+extern BufferedSerial com2, pc;
+extern void send_test () ;
+extern void setVI (double v, double i) ;
+extern void setV (double v) ;
+extern void setI (double i) ;
+extern void read_last_VI (double * val) ; // only for test from cli
+
+BufferedSerial * com;
+
+void null_cmd (struct parameters & a)
+{
+ if (a.respond)
+ com->printf ("At null_cmd, board ID %c, parameters : First %.3f, second %.3f\r\n", I_Am(), a.dbl[0], a.dbl[1]);
+}
+
+extern void mode_set (int mode, double val) ; // called from cli to set fw, re, rb, hb
+extern void read_supply_vi (double * val) ;
+
+void rdi_cmd (struct parameters & a) // read motor currents
+{
+ if (a.respond) {
+ double r[4];
+ read_supply_vi (r); // get MotorA.I.ave, MotorB.I.ave, Battery volts
+ com->printf ("rdi%.0f %.0f %.1f\r", r[0], r[1], r[2]); // Format good to be unpicked by cli in touch screen controller
+ }
+}
+
+void rvi_cmd (struct parameters & a) // read last normalised values sent to pwms
+{
+ if (a.respond) {
+ double r[6];
+ read_last_VI (r);
+ com->printf ("rvi%.2f %.2f %.2f %.2f\r", r[0], r[1], r[2], r[3]);
+ }
+}
+
+void fw_cmd (struct parameters & a) // Forward command
+{
+ mode_set (FORWARD, 0.0);
+}
+
+void re_cmd (struct parameters & a) // Reverse command
+{
+ mode_set (REVERSE, 0.0);
+}
+
+void rb_cmd (struct parameters & a) // Regen brake command
+{
+ double b = a.dbl[0] / 100.0;
+// com->printf ("Applying brake %.3f\r\n", b);
+ mode_set (REGENBRAKE, b);
+// apply_brake (b);
+}
+
+extern bool wr_24LC64 (int mem_start_addr, char * source, int length) ;
+extern bool rd_24LC64 (int mem_start_addr, char * dest, int length) ;
+
+void erase_cmd (struct parameters & a) // Sets eeprom contents to all 0xff. 256 pages of 32 bytes to do
+{
+ char t[36];
+ for (int i = 0; i < 32; i++)
+ t[i] = 0xff;
+ for (int i = 0; i < 8191; i += 32) {
+ com->printf (".");
+ if (!wr_24LC64 (i, t, 32))
+ com->printf ("eeprom write prob\r\n");
+ }
+}
+/*struct motorpairoptions { // This to be user settable in eeprom, 32 bytes
+ uint8_t MotA_dir, // 0 or 1
+ MotB_dir, // 0 or 1
+ gang, // 0 for separate control (robot mode), 1 for ganged loco bogie mode
+ serv1, // 0, 1, 2 = Not used, Input, Output
+ serv2, // 0, 1, 2 = Not used, Input, Output
+ cmd_source, // 0 Invalid, 1 COM1, 2 COM2, 3 Pot, 4 Servo1, 5 Servo2
+ {'1', '9', '0', "Alternative ID ascii '1' to '9'"}, // defaults to '0' before eerom setup for first time
+ {50, 250, 98, "Wheel diameter mm"}, // New 01/06/2018
+ {10, 250, 27, "Motor pinion"}, // New 01/06/2018
+ {50, 250, 85, "Wheel gear"}, // New 01/06/2018
+// last;
+} ;
+*/
+extern char mode_bytes[];
+
+void mode_cmd (struct parameters & a) // With no params, reads eeprom contents. With params sets eeprom contents
+{
+ if (a.target_unit == BROADCAST) {
+// com->printf ("At mode_cmd, can not use BROADCAST with mode_cmd\r\n");
+ } else {
+ char t[36];
+ com->printf ("At mode_cmd with node %d\r\n", a.target_unit);
+ rd_24LC64 (0, t, 32);
+ com->printf ("Numof params=%d\r\n", a.numof_dbls);
+ for (int i = 0; i < numof_eeprom_options; i++)
+ com->printf ("%2x\t%s\r\n", t[i], option_list[i].t);
+ if (a.numof_dbls == 0) { // Read present contents, do not write
+ com->printf ("That's it\r\n");
+ } else { // Write new shit to eeprom
+ com->printf ("\r\n");
+ if (a.numof_dbls != numof_eeprom_options) {
+ com->printf ("params required = %d, you offered %d\r\n", numof_eeprom_options, a.numof_dbls);
+ } else { // Have been passed correct number of parameters
+ int b;
+ com->printf("Ready to write params to eeprom\r\n");
+ for (int i = 0; i < numof_eeprom_options; i++) {
+ b = (int)a.dbl[i]; // parameter value to check against limits
+ if (i == 6) // Alternative ID must be turned to ascii
+ b |= '0';
+ if ((b < option_list[i].min) || (b > option_list[i].max)) { // if parameter out of range
+ com->printf("Warning - Parameter = %d, out of range, setting to default %d\r\n", b, option_list[i].def);
+ b = option_list[i].def;
+ }
+ com->printf ("0x%2x\t%s\r\n", (t[i] = b), option_list[i].t);
+ }
+ wr_24LC64 (0, t, numof_eeprom_options);
+ memcpy (mode_bytes,t,32);
+ com->printf("Parameters set in eeprom\r\n");
+ }
+ }
+ }
+}
+/*void coast_cmd (struct parameters & a) { // Coast
+
+}
+*/
+void hb_cmd (struct parameters & a)
+{
+ if (a.respond) {
+ com->printf ("numof params = %d\r\n", a.numof_dbls);
+ com->printf ("Hand Brake : First %.3f, second %.3f\r\n", a.dbl[0], a.dbl[1]);
+ }
+ mode_set (HANDBRAKE, 0.0);
+}
+
+extern uint32_t last_temp_count;
+void temperature_cmd (struct parameters & a) {
+ if (a.respond) {
+ com->printf ("tem%c %d\r\n", mode_bytes[ID], (last_temp_count / 16) - 50);
+ }
+}
+
+void bogie_constants_report_cmd (struct parameters & a) {
+ if (a.respond) {
+ com->printf ("bc%c %d %d %d\r\n", mode_bytes[ID], mode_bytes[WHEELDIA], mode_bytes[MOTPIN], mode_bytes[WHEELGEAR]);
+ }
+}
+
+extern void read_RPM (uint32_t * dest) ;
+void rpm_cmd (struct parameters & a) // to report e.g. RPM 1000 1000 ; speed for both motors
+{
+ if (a.respond) {
+ uint32_t dest[3];
+ read_RPM (dest);
+ com->printf ("rpm%d %d\r", dest[0], dest[1]);
+ }
+}
+
+void menucmd (struct parameters & a);
+
+void vi_cmd (struct parameters & a)
+{
+// if (a.respond)
+// com->printf ("In setVI, setting V to %.2f, I %.2f\r\n", a.dbl[0], a.dbl[1]);
+ setVI (a.dbl[0] / 100.0, a.dbl[1] / 100.0);
+}
+
+void v_cmd (struct parameters & a)
+{
+// if (a.respond)
+// com->printf ("In setV, setting V to %.2f\r\n", a.dbl[0]);
+ setV (a.dbl[0] / 100.0);
+}
+
+void i_cmd (struct parameters & a)
+{
+// if (a.respond)
+// com->printf ("In setI, setting I to %.2f\r\n", a.dbl[0]);
+ setI (a.dbl[0] / 100.0);
+}
+
+void kd_cmd (struct parameters & a) // kick the watchdog
+{
+ WatchDog = WATCHDOG_RELOAD + (I_Am() & 0x0f);
+// com->printf ("Poked %d up Dog\r\n", WatchDog);
+}
+
+void who_cmd (struct parameters & a)
+{
+ int i = I_Am ();
+ if (I_Am() == a.target_unit)
+ com->printf ("who%c\r\n", a.target_unit);
+}
+
+struct kb_command {
+ const char * cmd_word; // points to text e.g. "menu"
+ const char * explan;
+ void (*f)(struct parameters &); // points to function
+} ;
+
+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},
+ {"tem", "report temperature", temperature_cmd},
+ {"kd", "kick the dog, reloads WatchDog", kd_cmd},
+ {"rpm", "read motor pair speeds", rpm_cmd},
+ {"rvi", "read most recent values sent to pwms", rvi_cmd},
+ {"rdi", "read motor currents and power voltage", rdi_cmd},
+ {"bc", "bogie constants - wheel dia, motor pinion, wheel gear", bogie_constants_report_cmd},
+ {"nu", "do nothing", null_cmd},
+};
+
+const int numof_menu_items = sizeof(command_list) / sizeof(kb_command);
+void menucmd (struct parameters & a)
+{
+ if (a.respond) {
+ com->printf("\r\n\nDouble Brushless Motor Driver 2018\r\nAt menucmd function - listing commands:-\r\n");
+ for(int i = 0; i < numof_menu_items; i++)
+ com->printf("[%s]\t\t%s\r\n", command_list[i].cmd_word, command_list[i].explan);
+ com->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.
+Commands without prefix digit - broadcast to all units, all to obey but none to respond.
+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;
+ int ch, IAm = I_Am();
+ char * pEnd, * cmd_line_ptr;
+ static struct parameters param_block ;
+ com = &com2;
+ while (com->readable()) {
+// ch = tolower(com->getc());
+ ch = com->getc();
+ if (cl_index > MAX_CMD_LEN) { // trap out stupidly long command lines
+ com->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
+ else { // key was CR, may or may not be command to lookup
+ param_block.target_unit = BROADCAST; // Set to BROADCAST default once found command line '\r'
+ 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'
+ //com->printf ("Got prefix %c\r\n", 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 (int k = 0; k < MAX_PARAMS; k++) {
+ param_block.dbl[k] = 0.0;
+ }
+ param_block.position_in_list = i;
+// param_block.last_time = clock ();
+ param_block.numof_dbls = 0;
+ pEnd = cmd_line_ptr + wrdlen;
+ while (*pEnd) { // Assemble all numerics as doubles
+ param_block.dbl[param_block.numof_dbls++] = strtod (pEnd, &pEnd);
+ while (*pEnd && !isdigit(*pEnd) && '-' != *pEnd && '+' != *pEnd) {
+ pEnd++;
+ }
+ }
+ //com->printf ("\r\n"); // Not allowed as many may output this.
+ //for (int k = 0; k < param_block.numof_dbls; k++)
+ // com->printf ("Read %.3f\r\n", param_block.dbl[k]);
+// param_block.times[i] = clock();
+ param_block.respond = false;
+ if (((param_block.target_unit == BROADCAST) && (IAm == '0')) || (IAm == param_block.target_unit))
+ param_block.respond = true; // sorted 26/4/18
+ // All boards to obey BROADCAST command, only specific board to obey number prefixed command
+ if ((param_block.target_unit == BROADCAST) || (IAm == param_block.target_unit))
+ command_list[i].f(param_block); // execute command if addressed to this unit
+ i = numof_menu_items + 1; // to exit for loop
+ } // end of match found
+ } // End of for numof_menu_items
+ if(i == numof_menu_items)
+ com->printf("No Match Found for CMD [%s]\r\n", cmd_line);
+ } // End of If have got some chars to lookup
+ //com->printf("\r\n>");
+ cl_index = 0;
+ } // End of else key was CR, may or may not be command to lookup
+ } // End of while (com->readable())
+// Thread::wait(20); // Using RTOS on this project
+// }
+}
+
+
--- a/cli_nortos.cpp Tue May 29 16:36:34 2018 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,313 +0,0 @@
-// DualBLS2018_03
-#include "mbed.h"
-#include "BufferedSerial.h"
-
-#include <cctype>
-#include "DualBLS.h"
-using namespace std;
-
-extern int I_Am () ; // Returns boards id number as ASCII char '0', '1' etc. Code for Broadcast = '\r'
-extern int WatchDog;
-
-const int BROADCAST = '\r';
-const int MAX_PARAMS = 20;
-struct parameters {
- int32_t position_in_list, // set but not used Apr 2018, contains i for i'th menu item
-// last_time, // gets reading from clock() ; not known to be useful or reliable
- numof_dbls,
- target_unit;
- double dbl[MAX_PARAMS];
- bool respond;
-} ;
-
-// WithOUT RTOS
-extern BufferedSerial com2, pc;
-extern void send_test () ;
-extern void setVI (double v, double i) ;
-extern void setV (double v) ;
-extern void setI (double i) ;
-extern void read_last_VI (double * val) ; // only for test from cli
-
-BufferedSerial * com;
-
-void null_cmd (struct parameters & a)
-{
- if (a.respond)
- com->printf ("At null_cmd, board ID %c, parameters : First %.3f, second %.3f\r\n", I_Am(), a.dbl[0], a.dbl[1]);
-}
-
-extern void mode_set (int mode, double val) ;
-extern void read_supply_vi (double * val) ;
-
-void rdi_cmd (struct parameters & a) // read motor currents
-{
- if (a.respond) {
- double r[4];
- read_supply_vi (r); // get MotorA.I.ave, MotorB.I.ave, Battery volts
- com->printf ("rdi%.0f %.0f %.1f\r", r[0], r[1], r[2]); // Format good to be unpicked by cli in touch screen controller
- }
-}
-
-void rvi_cmd (struct parameters & a) // read last normalised values sent to pwms
-{
- if (a.respond) {
- double r[6];
- read_last_VI (r);
- com->printf ("rvi%.2f %.2f %.2f %.2f\r", r[0], r[1], r[2], r[3]);
- }
-}
-
-void fw_cmd (struct parameters & a) // Forward command
-{
- mode_set (FORWARD, 0.0);
-}
-
-void re_cmd (struct parameters & a) // Reverse command
-{
- mode_set (REVERSE, 0.0);
-}
-
-void rb_cmd (struct parameters & a) // Regen brake command
-{
- double b = a.dbl[0] / 100.0;
-// com->printf ("Applying brake %.3f\r\n", b);
- mode_set (REGENBRAKE, b);
-// apply_brake (b);
-}
-
-extern bool wr_24LC64 (int mem_start_addr, char * source, int length) ;
-extern bool rd_24LC64 (int mem_start_addr, char * dest, int length) ;
-
-void erase_cmd (struct parameters & a) // Sets eeprom contents to all 0xff. 256 pages of 32 bytes to do
-{
- char t[36];
- for (int i = 0; i < 32; i++)
- t[i] = 0xff;
- for (int i = 0; i < 8191; i += 32) {
- com->printf (".");
- if (!wr_24LC64 (i, t, 32))
- com->printf ("eeprom write prob\r\n");
- }
-}
-/*struct motorpairoptions { // This to be user settable in eeprom, 32 bytes
- uint8_t MotA_dir, // 0 or 1
- MotB_dir, // 0 or 1
- gang, // 0 for separate control (robot mode), 1 for ganged loco bogie mode
- serv1, // 0, 1, 2 = Not used, Input, Output
- serv2, // 0, 1, 2 = Not used, Input, Output
- cmd_source, // 0 Invalid, 1 COM1, 2 COM2, 3 Pot, 4 Servo1, 5 Servo2
- last;
-} ;
-*/
-
-void mode_cmd (struct parameters & a) // With no params, reads eeprom contents. With params sets eeprom contents
-{
- if (a.target_unit == BROADCAST) {
-// com->printf ("At mode_cmd, can not use BROADCAST with mode_cmd\r\n");
- } else {
- char t[36];
- com->printf ("At mode_cmd with node %d\r\n", a.target_unit);
- rd_24LC64 (0, t, 32);
- com->printf ("Numof params=%d\r\n", a.numof_dbls);
- for (int i = 0; i < numofopts; i++)
- com->printf ("%2x\t%s\r\n", t[i], option_list[i].t);
- if (a.numof_dbls == 0) { // Read present contents, do not write
- com->printf ("That's it\r\n");
- } else { // Write new shit to eeprom
- com->printf ("\r\n");
- if (a.numof_dbls != numofopts) {
- com->printf ("params required = %d, you offered %d\r\n", numofopts, a.numof_dbls);
- } else { // Have been passed correct number of parameters
- int b;
- com->printf("Ready to write params to eeprom\r\n");
- for (int i = 0; i < numofopts; i++) {
- b = (int)a.dbl[i]; // parameter value to check against limits
- if (i == 6) // Alternative ID must be turned to ascii
- b |= '0';
- if ((b < option_list[i].min) || (b > option_list[i].max)) { // if parameter out of range
- com->printf("Warning - Parameter = %d, out of range, setting to default %d\r\n", b, option_list[i].def);
- b = option_list[i].def;
- }
- com->printf ("0x%2x\t%s\r\n", (t[i] = b), option_list[i].t);
- }
- wr_24LC64 (0, t, numofopts);
- com->printf("Parameters set in eeprom\r\n");
- }
- }
- }
-}
-/*void coast_cmd (struct parameters & a) { // Coast
-
-}
-*/
-void hb_cmd (struct parameters & a)
-{
- if (a.respond) {
- com->printf ("numof params = %d\r\n", a.numof_dbls);
- com->printf ("Hand Brake : First %.3f, second %.3f\r\n", a.dbl[0], a.dbl[1]);
- }
- mode_set (HANDBRAKE, 0.0);
-}
-
-extern void read_RPM (uint32_t * dest) ;
-void rpm_cmd (struct parameters & a) // to report e.g. RPM 1000 1000 ; speed for both motors
-{
- if (a.respond) {
- uint32_t dest[3];
- read_RPM (dest);
- com->printf ("rpm%d %d\r", dest[0], dest[1]);
- }
-}
-
-void menucmd (struct parameters & a);
-
-void vi_cmd (struct parameters & a)
-{
-// if (a.respond)
-// com->printf ("In setVI, setting V to %.2f, I %.2f\r\n", a.dbl[0], a.dbl[1]);
- setVI (a.dbl[0] / 100.0, a.dbl[1] / 100.0);
-}
-
-void v_cmd (struct parameters & a)
-{
-// if (a.respond)
-// com->printf ("In setV, setting V to %.2f\r\n", a.dbl[0]);
- setV (a.dbl[0] / 100.0);
-}
-
-void i_cmd (struct parameters & a)
-{
-// if (a.respond)
-// com->printf ("In setI, setting I to %.2f\r\n", a.dbl[0]);
- setI (a.dbl[0] / 100.0);
-}
-
-void kd_cmd (struct parameters & a) // kick the watchdog
-{
- WatchDog = WATCHDOG_RELOAD + (I_Am() & 0x0f);
-// com->printf ("Poked %d up Dog\r\n", WatchDog);
-}
-
-void who_cmd (struct parameters & a)
-{
- int i = I_Am ();
- if (I_Am() == a.target_unit)
- com->printf ("Hi there, I am %c\r\n", a.target_unit);
-}
-
-struct kb_command {
- const char * cmd_word; // points to text e.g. "menu"
- const char * explan;
- void (*f)(struct parameters &); // points to function
-} ;
-
-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},
- {"kd", "kick the dog, reloads WatchDog", kd_cmd},
- {"rpm", "read motor pair speeds", rpm_cmd},
- {"rvi", "read most recent values sent to pwms", rvi_cmd},
- {"rdi", "read motor currents and power voltage", rdi_cmd},
- {"nu", "do nothing", null_cmd},
-};
-
-const int numof_menu_items = sizeof(command_list) / sizeof(kb_command);
-void menucmd (struct parameters & a)
-{
- if (a.respond) {
- com->printf("\r\n\nDouble Brushless Motor Driver 2018\r\nAt menucmd function - listing commands:-\r\n");
- for(int i = 0; i < numof_menu_items; i++)
- com->printf("[%s]\t\t%s\r\n", command_list[i].cmd_word, command_list[i].explan);
- com->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.
-Commands without prefix digit - broadcast to all units, all to obey but none to respond.
-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;
- int ch, IAm = I_Am();
- char * pEnd, * cmd_line_ptr;
- static struct parameters param_block ;
- com = &com2;
- while (com->readable()) {
-// ch = tolower(com->getc());
- ch = com->getc();
- if (cl_index > MAX_CMD_LEN) { // trap out stupidly long command lines
- com->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
- else { // key was CR, may or may not be command to lookup
- param_block.target_unit = BROADCAST; // Set to BROADCAST default once found command line '\r'
- 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'
- //com->printf ("Got prefix %c\r\n", 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 (int k = 0; k < MAX_PARAMS; k++) {
- param_block.dbl[k] = 0.0;
- }
- param_block.position_in_list = i;
-// param_block.last_time = clock ();
- param_block.numof_dbls = 0;
- pEnd = cmd_line_ptr + wrdlen;
- while (*pEnd) { // Assemble all numerics as doubles
- param_block.dbl[param_block.numof_dbls++] = strtod (pEnd, &pEnd);
- while (*pEnd && !isdigit(*pEnd) && '-' != *pEnd && '+' != *pEnd) {
- pEnd++;
- }
- }
- //com->printf ("\r\n"); // Not allowed as many may output this.
- //for (int k = 0; k < param_block.numof_dbls; k++)
- // com->printf ("Read %.3f\r\n", param_block.dbl[k]);
-// param_block.times[i] = clock();
- param_block.respond = false;
- if (((param_block.target_unit == BROADCAST) && (IAm == '0')) || (IAm == param_block.target_unit))
- param_block.respond = true; // sorted 26/4/18
- // All boards to obey BROADCAST command, only specific board to obey number prefixed command
- if ((param_block.target_unit == BROADCAST) || (IAm == param_block.target_unit))
- command_list[i].f(param_block); // execute command if addressed to this unit
- i = numof_menu_items + 1; // to exit for loop
- } // end of match found
- } // End of for numof_menu_items
- if(i == numof_menu_items)
- com->printf("No Match Found for CMD [%s]\r\n", cmd_line);
- } // End of If have got some chars to lookup
- //com->printf("\r\n>");
- cl_index = 0;
- } // End of else key was CR, may or may not be command to lookup
- } // End of while (com->readable())
-// Thread::wait(20); // Using RTOS on this project
-// }
-}
-
-
--- a/main.cpp Tue May 29 16:36:34 2018 +0000
+++ b/main.cpp Tue Jun 05 07:19:39 2018 +0000
@@ -6,12 +6,12 @@
/*
New 29th May 2018 - YET TO CODE FOR - Fwd/Rev line from possible remote hand control box has signal routed to T5
- Also new LMT01 temperature sensor routed to T1
+ Also new LMT01 temperature sensor routed to T1 - and rerouted to PC_13 at InterruptIn on T1 (ports A and B I think) not workable
*/
/* STM32F401RE - compile using NUCLEO-F401RE
-// PROJECT - Dual Brushless Motor Controller - Jon Freeman April 2018.
+// PROJECT - Dual Brushless Motor Controller - Jon Freeman June 2018.
AnalogIn to read each motor current
@@ -28,6 +28,8 @@
*/
+//#if defined (TARGET_NUCLEO_F446ZE)
+#if defined (TARGET_NUCLEO_F401RE)
// Hoped to select servo functions from user info stored on EEROM. Too difficult. Do not define servo as in and out
@@ -80,7 +82,11 @@
PortOut MotB (PortB, PORT_B_MASK);
// Pin 1 VBAT NET +3V3
-DigitalIn J3 (PC_13, PullUp);// Pin 2 Jumper pulls to GND, R floats Hi
+
+//DigitalIn J3 (PC_13, PullUp);// Pin 2 Jumper pulls to GND, R floats Hi
+InterruptIn Temperature_pin (PC_13);// Pin 2 June 2018 - taken for temperature sensor - hard wired to T1 due to wrong thought T1 could be InterruptIn
+
+
// Pin 3 PC14-OSC32_IN NET O32I
// Pin 4 PC15-OSC32_OUT NET O32O
// Pin 5 PH0-OSC_IN NET PH1
@@ -132,7 +138,15 @@
//BufferedSerial extra_ser (PA_11, PA_12); // Pins 44, 45 tx, rx to XBee module
DigitalOut T2 (PA_11); // Pin 44
// was DigitalOut T1 (PA_12); // Pin 45
-InterruptIn T1 (PA_12); // Pin 45 now input counting pulses from LMT01 temperature sensor
+
+
+//InterruptIn T1 (PA_12); // Pin 45 now input counting pulses from LMT01 temperature sensor
+// InterruptIn DOES NOT WORK ON PA_12. Boards are being made, will have to wire link PA12 to PC13
+DigitalIn T1 (PA_12);
+////InterruptIn T1 (PC_13); // Pin 45 now input counting pulses from LMT01 temperature sensor
+
+
+
// Pin 46 SWDIO
// Pin 47 VSS
// Pin 48 VDD
@@ -160,7 +174,9 @@
// Pin 64 VDD
// SYSTEM CONSTANTS
-
+#endif
+#if defined (TARGET_NUCLEO_F446ZE)
+#endif
/* Global variable declarations */
volatile uint32_t fast_sys_timer = 0; // gets incremented by our Ticker ISR every VOLTAGE_READ_INTERVAL_US
int WatchDog = WATCHDOG_RELOAD + 80; // Allow extra few seconds at powerup
@@ -171,13 +187,31 @@
int IAm;
bool loop_flag = false; // made true in ISR_loop_timer, picked up and made false again in main programme loop
bool flag_8Hz = false; // As loop_flag but repeats 8 times per sec
+bool temp_sensor_exists = false;
+char mode_bytes[36];
+uint32_t temp_sensor_count = 0, // incremented by every rising edge from LMT01
+ last_temp_count = 0; // global updated approx every 100ms after each LMT01 conversion completes
+// struct single_bogie_options bogie;
/* End of Global variable declarations */
Ticker tick_vread; // Device to cause periodic interrupts, used to time voltage readings etc
Ticker loop_timer; // Device to cause periodic interrupts, used to sync iterations of main programme loop
+Ticker temperature_find_ticker;
+Timer temperature_timer;
// Interrupt Service Routines
+void ISR_temperature_find_ticker () { // every 960 us, i.e. slightly faster than once per milli sec
+ static bool readflag = false;
+ int t = temperature_timer.read_ms ();
+ if ((t == 5) && (!readflag)) {
+ last_temp_count = temp_sensor_count;
+ temp_sensor_count = 0;
+ readflag = true;
+ }
+ if (t == 6)
+ readflag = false;
+}
/** void ISR_loop_timer ()
* This ISR responds to Ticker interrupts at a rate of (probably) 32 times per second (check from constant declarations above)
@@ -240,7 +274,7 @@
return rx_active;
}
-void RControl_In::rise ()
+void RControl_In::rise () // These may not work as use of PortB as port may bugger InterruptIn use
{
t.stop ();
period_us = t.read_us ();
@@ -505,11 +539,14 @@
Hindex[1] = Hindex[0];
}
- uint32_t temp_sensor_count = 0; // global
- bool temp_count_in_progress = false;
-void temp_sensor_isr () { // got rising edge from LMT01
+void temp_sensor_isr () { // got rising edge from LMT01. ALMOST CERTAIN this misses some
+ int t = temperature_timer.read_us (); // Must be being overrun by something, most likely culprit A-D reading ?
+ temperature_timer.reset ();
temp_sensor_count++;
+ if (t > 18) // Yes proved some interrupts get missed, this fixes temperature reading
+ temp_sensor_count++;
+// T2 = !T2; // scope hanger
}
void MAH_isr ()
@@ -576,29 +613,6 @@
d[3] = MotorB.last_I;
}
-/*void sincostest () {
- sinv = sin(angle); // to set speed and direction of MotorA
- cosv = cos(angle); // to set speed and direction of MotorB
- Servos[0]->write ((sinv + 1.0) / 2.0);
- Servos[1]->write ((cosv + 1.0) / 2.0);
- angle += angle_step;
- if (angle > TWOPI)
- angle -= TWOPI;
- if (sinv > 0.0)
- MotorA.set_mode (FORWARD);
- else {
- MotorA.set_mode (REVERSE);
- sinv = -sinv;
- }
- MotorA.set_V_limit (0.01 + (sinv / 1.3));
- if (cosv > 0.0)
- MotorB.set_mode (FORWARD);
- else {
- MotorB.set_mode (REVERSE);
- cosv = -cosv;
- }
- MotorB.set_V_limit (0.01 + (cosv / 1.3));
-}*/
/**
void AtoD_reader () // Call to here every VOLTAGE_READ_INTERVAL_US = 50 once loop responds to flag set in isr
@@ -606,17 +620,8 @@
*/
void AtoD_reader () // Call to here every VOLTAGE_READ_INTERVAL_US = 50 once loop responds to flag set in isr
{
- static uint32_t i = 0, tab_ptr = 0, local_temperature_count = 0;
+ static uint32_t i = 0, tab_ptr = 0;
-// sincostest ();
-// uint32_t temp_sensor_count = 0; // global
-// bool temp_count_in_progress = false;
- if (local_temperature_count == temp_sensor_count)
- temp_count_in_progress = false;
- else {
- temp_count_in_progress = true;
- local_temperature_count = temp_sensor_count;
- }
if (MotorA.tickleon)
MotorA.high_side_off ();
if (MotorB.tickleon)
@@ -711,10 +716,6 @@
} ;
*/
int I_Am () { // Returns boards id number as ASCII char
-// int i = J3;
-// if (i != 0)
-// i = 1;
-// return i | '0';
return IAm;
}
@@ -728,7 +729,8 @@
MotPtr[0] = &MotorA; // Pointers to motor class objects
MotPtr[1] = &MotorB;
- T1.rise (&temp_sensor_isr);
+ Temperature_pin.fall (&temp_sensor_isr);
+ Temperature_pin.mode (PullUp);
MAH1.rise (& MAH_isr); // Set up interrupt vectors
MAH1.fall (& MAH_isr);
@@ -756,14 +758,16 @@
// Setup system timers to cause periodic interrupts to synchronise and automate volt and current readings, loop repeat rate etc
tick_vread.attach_us (&ISR_voltage_reader, VOLTAGE_READ_INTERVAL_US); // Start periodic interrupt generator
loop_timer.attach_us (&ISR_loop_timer, MAIN_LOOP_REPEAT_TIME_US); // Start periodic interrupt generator
+ temperature_find_ticker.attach_us (&ISR_temperature_find_ticker, 960);
// Done setting up system interrupt timers
+ temperature_timer.start ();
- const int TXTBUFSIZ = 36;
- char buff[TXTBUFSIZ];
+// const int TXTBUFSIZ = 36;
+// char buff[TXTBUFSIZ];
pc.baud (9600);
com3.baud (1200);
com2.baud (19200);
-
+
if (check_24LC64() != 0xa0) { // searches for i2c devices, returns address of highest found
pc.printf ("Check for 24LC64 eeprom FAILED\r\n");
com2.printf ("Check for 24LC64 eeprom FAILED\r\n");
@@ -777,15 +781,15 @@
// k = rd_24LC64 (0x1240, buff, strlen(ramtst));
// pc.printf("Ram test returned [%s], wr ret'd [%s], rd ret'd [%s]\r\n", buff, j ? "true" : "false", k ? "true" : "false");
// com2.printf("Ram test returned [%s], wr ret'd [%s], rd ret'd [%s]\r\n", buff, j ? "true" : "false", k ? "true" : "false");
- k = rd_24LC64 (0, buff, 32);
+ k = rd_24LC64 (0, mode_bytes, 32);
// if (k)
// com2.printf ("Good read from eeprom\r\n");
if (!k)
com2.printf ("Error reading from eeprom\r\n");
int err = 0;
- for (int i = 0; i < numofopts; i++) {
- if ((buff[i] < option_list[i].min) || (buff[i] > option_list[i].max)) {
+ for (int i = 0; i < numof_eeprom_options; i++) {
+ if ((mode_bytes[i] < option_list[i].min) || (mode_bytes[i] > option_list[i].max)) {
com2.printf ("EEROM error with %s\r\n", option_list[i].t);
err++;
}
@@ -794,14 +798,14 @@
}
IAm = '0';
if (err == 0) {
- MotorA.direction_set (buff[0]);
- MotorB.direction_set (buff[1]);
- IAm = buff[6];
+ MotorA.direction_set (mode_bytes[MOTADIR]);
+ MotorB.direction_set (mode_bytes[MOTBDIR]);
+ IAm = mode_bytes[ID];
}
// Alternative ID 1 to 9
// com2.printf ("Alternative ID = 0x%2x\r\n", buff[6]);
}
-// T1 = 0; Now interruptIn counting pulses from LMT01 temperature sensor
+// T1 = 0; Now WRONGLY hoped to be InterruptIn counting pulses from LMT01 temperature sensor
T2 = 0; // T2, T3, T4 As yet unused pins
T3 = 0;
T4 = 0;
@@ -821,6 +825,10 @@
Servos[0] = & Servo1;
Servo Servo2 (PB_9) ;
Servos[1] = & Servo2;
+
+ pc.printf ("last_temp_count = %d\r\n", last_temp_count); // Has had time to do at least 1 conversion
+ if ((last_temp_count > 160) && (last_temp_count < 2400)) // in range -40 to +100 degree C
+ temp_sensor_exists = true;
/*
// Setup Complete ! Can now start main control forever loop.
// March 16th 2018 thoughts !!!
@@ -843,6 +851,7 @@
break;
}
*/
+ pc.printf ("Ready to go!, wheel gear in position %d\r\n", WHEELGEAR);
while (1) { // Loop forever, repeats synchroised by waiting for ticker Interrupt Service Routine to set 'loop_flag' true
while (!loop_flag) { // Most of the time is spent in this loop, repeatedly re-checking for commands from pc port
command_line_interpreter () ; // Proceed beyond here once loop_timer ticker ISR has set loop_flag true
@@ -866,8 +875,14 @@
eighth_sec_count++;
if (eighth_sec_count > 6) { // Send some status info out of serial port every second and a bit or thereabouts
eighth_sec_count = 0;
- MotorA.current_calc (); // Updates readings in MotorA.I.min, MotorA.I.ave and MotorA.I.max
- MotorB.current_calc ();
+ MotorA.current_calc (); // Updates readings in MotorA.I.min, MotorA.I.ave and MotorA.I.max
+ MotorB.current_calc ();
+ if (temp_sensor_exists) {
+ double tmprt = (double) last_temp_count;
+ tmprt /= 16.0;
+ tmprt -= 50.0;
+ pc.printf ("Temp %.2f\r\n", tmprt);
+ }
// com2.printf ("V=%+.2f, Pot=%+.2f, HA %d, HB %d, IAmin %d, IAave %d, IAmax %d, IB %d, Arpm %d, Brpm %d\r\n", Read_BatteryVolts(), Read_DriverPot(), MotorA.read_Halls (), MotorB.read_Halls (), MotorA.I.min, MotorA.I.ave, MotorA.I.max, MotorB.I.ave, (Apps * 60) / 24, (Bpps * 60) / 24);
}
} // End of if(flag_8Hz)