Electric Locomotive control system. Touch screen driver control, includes regenerative braking, drives 4 brushless motors, displays speed MPH, system volts and power
Dependencies: BSP_DISCO_F746NG FastPWM LCD_DISCO_F746NG SD_DISCO_F746NG TS_DISCO_F746NG mbed
Revision 1:8ef34deb5177, committed 2017-11-13
- Comitter:
- JonFreeman
- Date:
- Mon Nov 13 09:53:00 2017 +0000
- Parent:
- 0:23cc72b18e74
- Commit message:
- Brushless Motor electric locomotive congtrol system; Drives 4 motors using touch-screen control.; Displays speed MPH, system volts and power
Changed in this revision
--- a/BSP_DISCO_F746NG.lib Sun Nov 12 06:26:29 2017 +0000 +++ b/BSP_DISCO_F746NG.lib Mon Nov 13 09:53:00 2017 +0000 @@ -1,1 +1,1 @@ -https://developer.mbed.org/teams/ST/code/BSP_DISCO_F746NG/#5f0817e857ea +https://os.mbed.com/users/JonFreeman/code/BSP_DISCO_F746NG/#5f0817e857ea
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Electric_Loco.h Mon Nov 13 09:53:00 2017 +0000 @@ -0,0 +1,80 @@ +/* Updated 12 Nov 2017 + Jon Freeman + + 5" and 7.25" gauge Electric Locomotive Controller - ST DISCO-F746NG +Uses built in display and touch screen. + +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. + +Touch screen has three 'buttons', these are currently unused, and are where the meter movements show. +Idea is to use two for two horns, + +Display has 'slider' touch control. This drives the loco. +Control in central position when not driving or drfting. +Moving towards bottom of screen applies regenerative braking - move further down applies harder braking. +Moving towards top of screen powers drive motors, move further up applies more torque (current controller implemented) +Take finger off and control drifts down to central 'neutral' position. +*/ +#define MAX_TOUCHES 6 // Touch screen can decode up to this many simultaneous finger press positions +#define NEUTRAL_VAL 150 // Number of pixels + +#define SLIDERX 418 // slider graphic x position +#define SLIDERY 2 // slider graphic y position +#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 + +const int + + NUMBER_OF_MOTORS = 4, // 1 to 6 motors + + 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; + +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 + ; + +const double LOCO_HANDBRAKE_ESCAPE_SPEED = 0.5; + +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}; + +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 rect { struct point a, b; } ; +struct key { int keynum; int x; int y; bool pressed; } ; +struct ky_bd { int count, slider_y; key ky[MAX_TOUCHES + 1]; bool sli; } ; +
--- a/costab.cpp Sun Nov 12 06:26:29 2017 +0000 +++ b/costab.cpp Mon Nov 13 09:53:00 2017 +0000 @@ -1,4 +1,8 @@ #include "mbed.h" +/* +Purpose of this file is to provide sin and cos functions. Yes, C++ has these already, but using inbuilt sin and cos on +DISCO-F746NG causes display to flicker! Interrupt getting missed or whatever, these look-up functions solve the flicker ! +*/ static const double HALF_PI = 2.0 * atan(1.0); static const double TWO_PI = 8.0 * atan(1.0);
--- a/dro.h Sun Nov 12 06:26:29 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/* Updated 12 Nov 2017 - Jon Freeman - - 5" and 7.25" gauge Electric Locomotive Controller - ST DISCO-F746NG -Uses built in display and touch screen. - -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. - -Touch screen has three 'buttons', these are currently unused, and are where the meter movements show. -Idea is to use two for two horns, - -Display has 'slider' touch control. This drives the loco. -Control in central position when not driving or drfting. -Moving towards bottom of screen applies regenerative braking - move further down applies harder braking. -Moving towards top of screen powers drive motors, move further up applies more torque (current controller implemented) -Take finger off and control drifts down to central 'neutral' position. -*/ -#define MAX_TOUCHES 6 // Touch screen can decode up to this many simultaneous finger press positions -#define NEUTRAL_VAL 150 // Number of pixels - -#define SLIDERX 418 // slider graphic x position -#define SLIDERY 2 // slider graphic y position -#define SLIDERW 50 // pixel width of slider -#define SLIDERH 268 // pixel height of slider -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; - -static const double PI = 4.0 * atan(1.0); -const double LOCO_HANDBRAKE_ESCAPE_SPEED = 0.5; - -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}; - -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 rect { struct point a, b; } ; -struct key { int keynum; int x; int y; bool pressed; } ; -struct ky_bd { int count, slider_y; key ky[MAX_TOUCHES + 1]; bool sli; } ; -
--- a/graphics.cpp Sun Nov 12 06:26:29 2017 +0000 +++ b/graphics.cpp Mon Nov 13 09:53:00 2017 +0000 @@ -1,7 +1,7 @@ #include "mbed.h" #include "TS_DISCO_F746NG.h" #include "LCD_DISCO_F746NG.h" -#include "dro.h" +#include "Electric_Loco.h" #define VOLTMETER_X 68 // Voltmeter screen position #define VOLTMETER_Y 68 @@ -331,9 +331,11 @@ Powermeter.setup (AMMETER_X, AMMETER_Y, V_A_SIZE, -1400.0, 1400.0, 1.25 * PI, -0.25 * PI , 14, "Watt", NO_DPS); } -void update_meters (double speed, double current, double voltage) +//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(voltage * current); + Powermeter.set_value(power); Voltmeter.set_value (voltage); Speedo.set_value (speed); }
--- a/main.cpp Sun Nov 12 06:26:29 2017 +0000 +++ b/main.cpp Mon Nov 13 09:53:00 2017 +0000 @@ -1,7 +1,8 @@ // Electric Locomotive Controller // Jon Freeman B. Eng Hons -// Last Updated 12 April 2017 +// Last Updated 13 November 2017 + // Touch Screen Loco 2017 - WITH SD card data logger functions // This code runs on STM 32F746NG DISCO module, high performance ARM Cortex with touch screen @@ -16,15 +17,12 @@ // NOTE that when braking, the motor supply rail voltage will be lifted. Failure to design-in some type of 'surplus power dump' // may result in over-voltage damage to batteries or power electronics. - #include "mbed.h" #include "FastPWM.h" #include "TS_DISCO_F746NG.h" #include "LCD_DISCO_F746NG.h" -#include "SD_DISCO_F746NG.h" -#include "dro.h" - - +//#include "SD_DISCO_F746NG.h" // SD card stuff now in separate file sd_card.cpp +#include "Electric_Loco.h" // Design Topology // This F746NG is the single loco control computer. @@ -57,7 +55,7 @@ LCD_DISCO_F746NG lcd; TS_DISCO_F746NG touch_screen; -SD_DISCO_F746NG sd; +//SD_DISCO_F746NG sd; // SD card stuff now in sd_card.cpp FastPWM maxv (D12, 1), maxi (D11, 1); // pin, prescaler value @@ -70,7 +68,8 @@ DigitalOut led_grn (LED1); // the only on board user led DigitalIn f_r_switch (D0); // Reads position of centre-off ignition switch -DigitalIn spareio_d8 (D8); +//DigitalIn spareio_d8 (D8); +//DigitalOut throttle_servo_pulse_out (D8); // now defined in throttle.cpp DigitalIn spareio_d9 (D9); DigitalIn spareio_d10 (D10); // D8, D9, D10 wired to jumper on pcb - not used to Apr 2017 @@ -100,50 +99,25 @@ extern void update_meters (double, double, double) ; extern void command_line_interpreter () ; -static const int NUMBER_OF_MOTORS = 4, - SD_BLOCKSIZE = 512, /* SD card data Block Size in Bytes */ +extern int throttle (double, double) ; // called from main every 31ms + +extern void update_SD_card () ; // Hall pulse total updated once per sec and saved in blocks of 128 to SD card +extern bool read_SD_state () ; +extern bool mainSDtest(); + +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 - PWM_HZ = 13000, + MAF_PTS = 140, // Moving Average Filter points. Filters reduce noise on volatage and current readings + PWM_HZ = 16000, // chosen to be above cutoff frequency of average human ear // PWM_HZ = 2000, // Used this to experiment on much bigger motor MAX_PWM_TICKS = 108000000 / PWM_HZ, // 108000000 for F746N, due to cpu clock = 216 MHz FWD = 0, REV = ~FWD; -static const double - 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, - 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 - ; - -// Assume SD card size is 4Gbyte, might be 8 Gbyte -// Then can use 8388608 blocks (8 * 1024 * 1024) - -uint64_t SD_blockptr = 0; -uint32_t SDBuffer[(SD_BLOCKSIZE >> 2)]; // = space for (512 / 4) uint32_t -uint8_t SD_state = SD_OK, sd_jf = 0; - -static const uint64_t GIGAB = 1024 * 1024 * 1024; -//static const uint64_t SDBLOCKS = (GIGAB / SD_BLOCKSIZE) * 4; // software drives SD up to 4Gbyte only - 8 M block -static const uint64_t SDBLOCKS = (GIGAB / SD_BLOCKSIZE) * 2; // software drives SD up to 4Gbyte only - 8 M block -// If data logger takes 2 minutes to fill 1 block, a 4G card takes 32 years run-time to fill -// If system generates approx 320 pulses per metre travelled, max distance recordable in uint32_t is 65536 * 65536 / 320 = 13421.772 km - -//from dro.h struct slide { int position; int oldpos; int state; int direction; bool recalc_run; bool handbrake_slipping; double handbrake_effort; double loco_speed } ; +//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 ; - -//static const double mph_2_mm_per_sec = 447.04; // exact - int V_maf[MAF_PTS + 2], I_maf[MAF_PTS + 2], maf_ptr = 0; //uint32_t Hall_pulse[8] = {0,0,0,0,0,0,0,0}; // more than max number of motors uint32_t Hall_pulse[8] = {1,1,1,1,1,1,1,1}; // more than max number of motors @@ -155,163 +129,6 @@ double last_pwm = 0.0; -bool sd_error () { // Test and Clear error code sd_jf, return true if any error bits set, false on 0 - bool retval = false; - if (sd_jf != 0) { - retval = true; - sd_jf = 0; - } - return retval; -} - -bool check_SD_block_clear (uint32_t block) { - uint32_t b[(SD_BLOCKSIZE >> 2)]; - SD_state = sd.ReadBlocks(b, (uint64_t)(SD_BLOCKSIZE * block), SD_BLOCKSIZE, 1); - if(SD_state != SD_OK) { - sd_jf = 1; - pc.printf ("Failed, not SD_OK, erasing block %d\r\n", block); - return false; - } - for (int i = 0; i < (SD_BLOCKSIZE >> 2); i++) - if (b[i] != 0) - return false; - return true; -} - -/*bool erase_block (uint32_t block2erase) { - uint64_t addr = SD_BLOCKSIZE * (uint64_t)block2erase; - SD_state = sd.Erase(addr, addr + SD_BLOCKSIZE); - if (SD_state != SD_OK) { - sd_jf = 1; // Assert error flag - pc.printf ("Failed, not SD_OK, erasing block %d\r\n", block2erase); - return false; - } - return check_SD_block_clear (block2erase); -}*/ - -bool SD_find_next_clear_block (uint64_t * blok) { // Successive approximation algorithm to quickly find next vacant SD card 512 byte block - uint64_t toaddsub = SDBLOCKS / 2, stab = SDBLOCKS - 1; - pc.printf ("At SD_find_next_clear_block \r\n"); - while (toaddsub) { - pc.printf ("stab = %lld, toadsub = %lld\r\n", stab, toaddsub); // lld for long long int - bool clear_block = true; - SD_state = sd.ReadBlocks(SDBuffer, SD_BLOCKSIZE * stab, SD_BLOCKSIZE, 1); - if(SD_state != SD_OK) { - sd_jf = 1; - pc.printf ("SD error in SD_find_next_clear_block, returning -1\r\n"); - return false; - } - for (int i = 0; i < (SD_BLOCKSIZE >> 2); i++) { - if (SDBuffer[i] != 0) { - clear_block = false; - pc.printf ("Buff at %d contains %x\r\n", i, SDBuffer[i]); - i = SD_BLOCKSIZE; // to exit loop - } - } - if (clear_block) - stab -= toaddsub; - else - stab += toaddsub; - toaddsub >>= 1; - } - if (!check_SD_block_clear(stab)) - stab++; - if (sd_error()) { // sd_error() tests and clears error bits - pc.printf ("check_SD_block_clear(%ld)returned ERROR in SD_find_next_clear_block\r\n", stab); - sd_jf = 1; // reassert error flag - return false; - } - pc.printf ("Completed find_next, stab = %d\r\n", stab); - *blok = stab; // block number of next free block - return true; -} - -bool SD_card_erase_all (void) { // assumes sd card is 4 Gbyte, erases 4 Gbyte. Called from CLI - uint64_t EndAddr = GIGAB * 4, - StartAddr = 0LL; - sd_jf = 0; - pc.printf ("Erasing SD card ... "); - // uint8_t Erase(uint64_t StartAddr, uint64_t EndAddr); - SD_state = sd.Erase(StartAddr, EndAddr); - if (SD_state != SD_OK) { - pc.printf ("SD_card_erase_all FAILED\r\n"); - sd_jf = 1; - return false; - } - pc.printf ("no error detected\r\n"); - return true; -} - - -bool mainSDtest() -{ - SD_state = sd.Init(); - if(SD_state != SD_OK) { - pc.printf ("sd.Init set SD_state to %0x\r\n", SD_state); - if(SD_state == MSD_ERROR_SD_NOT_PRESENT) { - pc.printf("SD shall be inserted before running test\r\n"); - } else { - pc.printf("SD Initialization : FAIL.\r\n"); - } - pc.printf("SD Test Aborted.\r\n"); - return false; - } -// else { // SD_state is SD_OK - pc.printf("SD Initialization : OK.\r\n"); - - - -// SD_card_erase_all(); -// if (sd_error()) -// pc.printf ("SD_card_erase_all() reports ERROR"); - - - - SD_find_next_clear_block(& SD_blockptr); - pc.printf ("SD_find_next_clear_block returned %lld\r\n\n\n", SD_blockptr); - if (sd_error()) { - pc.printf ("***** ERROR returned from SD_find_next_clear_block ***** SD ops aborted\r\n"); - return false; - } - pc.printf("SD_find_next_clear_block() returned %ld\r\n", SD_blockptr); - if (SD_blockptr < 1) { - pc.printf ("Looks like card newly erased, SD_blockptr value of %d\r\n", SD_blockptr); - SD_blockptr = 0; - historic_distance = 0; - } - else { - SD_state = sd.ReadBlocks(SDBuffer, SD_BLOCKSIZE * (SD_blockptr - 1), SD_BLOCKSIZE, 1); - if (SD_state != SD_OK) { - pc.printf ("Error reading last block from SD block %d\r\n", SD_blockptr - 1); - return false; - } - for (int i = 0; i < (SD_BLOCKSIZE >> 2); i++) - pc.printf ("%lx\t", SDBuffer[i]); - historic_distance = SDBuffer[(SD_BLOCKSIZE >> 2) - 1]; - pc.printf ("\r\nAbove, data read from last filled SD block %lld, using historic_distance = %lx\r\n", SD_blockptr - 1, historic_distance); - } - if (SD_blockptr > 2) { - for (int i = SD_blockptr - 2; i < SD_blockptr + 2; i++) { - pc.printf ("check_SD_block_clear (%d) ", i); - if (check_SD_block_clear(i)) - pc.printf ("block %ld is CLEAR\r\n", i); - else - pc.printf ("block %ld is NOT clear\r\n", i); - if (sd_error()) { - pc.printf ("ERROR from check_SD_block_clear ()\r\n"); - } - } - } - return true; -} - - - - - - - - class speed_measurement // Interrupts at qtr sec cause read of Hall_pulse counters which are incremented by transitions of Hall inputs { static const int SPEED_AVE_PTS = 9; // AVE_PTS - points in moving average filters @@ -393,6 +210,10 @@ return historic_distance + Hall_pulse[0] + Hall_pulse[1] + Hall_pulse[2] + Hall_pulse[3]; } +uint32_t get_pulse_total () { // called by SD card code + return speed.pulse_total(); +} + void set_V_limit (double p) // Sets max motor voltage { if (p < 0.0) @@ -458,22 +279,33 @@ { Hall_pulse[3]++; } -/*void ISR_mot5_hall_handler () +void ISR_mot5_hall_handler () // If only 4 motors this never gets used, there is no fifth motor { Hall_pulse[4]++; } -void ISR_mot6_hall_handler () +void ISR_mot6_hall_handler () // As one above { Hall_pulse[5]++; } -*/ + void ISR_current_reader (void) // FIXED at 250us { + 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) { + ms32 = 0; + trigger_32ms = true; + ms250++; + if (ms250 > 7) { + ms250 = 0; + qtrsec_trig = true; + } + } } -void ISR_tick_32ms (void) // +/*void ISR_tick_32ms (void) // { trigger_32ms = true; } @@ -481,7 +313,7 @@ { qtrsec_trig = true; } - +*/ // End of Interrupt Service Routines @@ -521,7 +353,7 @@ 1 1 1 Regen Braking */ 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) { @@ -560,36 +392,21 @@ } } -void update_SD_card () { // Hall pulse total updated once per sec and saved in blocks of 128 to SD card - static int index = 0; - static uint32_t buff[(SD_BLOCKSIZE >> 2) + 2]; - buff[index++] = speed.pulse_total(); // pulse_total for all time, add this to buffer to write to SD - if (index >= (SD_BLOCKSIZE >> 2)) { - pc.printf ("Writing new SD block %d ... ", SD_blockptr); - SD_state = sd.WriteBlocks(buff, SD_BLOCKSIZE * SD_blockptr, SD_BLOCKSIZE, 1); - SD_blockptr++; - if (SD_state == SD_OK) - pc.printf ("OK, distance %d\r\n", buff[index - 1] / (int)PULSES_PER_METRE); - else - pc.printf ("ERROR\r\n"); - index = 0; - } -} - int main() { int c_5 = 0, seconds = 0, minutes = 0; + double electrical_power_Watt = 0.0; ky_bd kybd_a, kybd_b; memset (&kybd_a, 0, sizeof(kybd_a)); memset (&kybd_b, 0, sizeof(kybd_b)); - spareio_d8.mode (PullUp); +// spareio_d8.mode (PullUp); now output driving throttle servo spareio_d9.mode (PullUp); spareio_d10.mode(PullUp); Ticker tick250us; - Ticker tick32ms; - Ticker tick250ms; +// Ticker tick32ms; +// Ticker tick250ms; // Setup User Interrupt Vectors mot1hall.fall (&ISR_mot1_hall_handler); @@ -601,9 +418,10 @@ mot4hall.fall (&ISR_mot4_hall_handler); mot4hall.rise (&ISR_mot4_hall_handler); - tick250us.attach_us (&ISR_current_reader, 250); // set to longer time to test - tick32ms.attach_us (&ISR_tick_32ms, 32001); - tick250ms.attach_us (&ISR_tick_250ms, 250002); + tick250us.attach_us (&ISR_current_reader, 250); // count 125 of these to trig 31.25ms +// tick32ms.attach_us (&ISR_tick_32ms, 32001); +// tick32ms.attach_us (&ISR_tick_32ms, 31250); // then count 8 pulses per 250ms +// tick250ms.attach_us (&ISR_tick_250ms, 250002); pc.baud (9600); GfetT1 = 0; GfetT2 = 0; // two output bits for future use driving horns @@ -650,6 +468,7 @@ mainSDtest(); + double torque_req = 0.0; bool toggle32ms = false; // Main loop while(1) { // @@ -660,6 +479,10 @@ if (trigger_32ms == true) { // Stuff to do every 32 milli secs trigger_32ms = false; + +// CALL THROTTLE HERE - why here ? Ah yes, this initiates servo pulse. Need steady stream of servo pulses even when nothing changes. + throttle (torque_req, 2.3); + toggle32ms = !toggle32ms; if (toggle32ms) { present_kybd = &kybd_a; @@ -690,7 +513,7 @@ else { // nice slow non-jerky glidey movement required dbl = (double)(k - slider.position); - dbl /= 13.179; + dbl /= 13.179; // Where did 13.179 come from ? if (dbl < 0.0) dbl -= 1.0; if (dbl > 0.0) @@ -767,7 +590,7 @@ if (slider.recalc_run) { // range of slider.position in RUN mode is min_pos_() to NEUTRAL_VAL - 1 slider.recalc_run = false; // All RUN power and pwm calcs done here int b = slider.position; - double torque_req; +// double torque_req; // now declared above to be used as parameter for throttle if (b > NEUTRAL_VAL) b = NEUTRAL_VAL; if (b < MIN_POS) // if finger position is above top of slider limit @@ -793,7 +616,8 @@ //static const double mph_2_mm_per_sec = 447.04; // exact // double mm_travelled_in_qtrsec = speedmph * mph_2_mm_per_sec / 4.0; slider.loco_speed = speedmph; - update_meters (speedmph, amps, volts) ; + electrical_power_Watt = volts * amps; // visible throughout main + update_meters (speedmph, electrical_power_Watt, volts) ; // displays speed, volts and power (volts times amps) // update_meters (7.5, amps, volts) ; led_grn = !led_grn; if (slider.state == PARK) { @@ -820,7 +644,8 @@ minutes++; // do once per minute stuff here } // fall back into once per second - if(SD_state == SD_OK) { +// if(SD_state == SD_OK) { + if(read_SD_state() == true) { uint32_t distance = speed.metres_travelled(); char dist[20]; sprintf (dist, "%05d m", distance);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sd_card.cpp Mon Nov 13 09:53:00 2017 +0000 @@ -0,0 +1,204 @@ +#include "mbed.h" +#include "Electric_Loco.h" +#include "SD_DISCO_F746NG.h" +/* +SD card used only to keep log of total distance travelled. +Odometer is trivial. +This file treats SD card as random access memory. +A better implementation would use library functions for FAT file system etc. + +May revisit this. + +*/ +SD_DISCO_F746NG sd; +extern Serial pc; +extern uint32_t historic_distance; +extern uint32_t get_pulse_total () ; + +static const int + SD_BLOCKSIZE = 512; /* SD card data Block Size in Bytes */ +// Assume SD card size is 4Gbyte, might be 8 Gbyte +// Then can use 8388608 blocks (8 * 1024 * 1024) + +uint64_t SD_blockptr = 0; +uint32_t SDBuffer[(SD_BLOCKSIZE >> 2)]; // = space for (512 / 4) uint32_t +uint8_t SD_state = SD_OK, sd_jf = 0; + +static const uint64_t GIGAB = 1024 * 1024 * 1024; +//static const uint64_t SDBLOCKS = (GIGAB / SD_BLOCKSIZE) * 4; // software drives SD up to 4Gbyte only - 8 M block +static const uint64_t SDBLOCKS = (GIGAB / SD_BLOCKSIZE) * 2; // software drives SD up to 4Gbyte only - 8 M block +// If data logger takes 2 minutes to fill 1 block, a 4G card takes 32 years run-time to fill +// If system generates approx 320 pulses per metre travelled, max distance recordable in uint32_t is 65536 * 65536 / 320 = 13421.772 km +bool sd_error () { // Test and Clear error code sd_jf, return true if any error bits set, false on 0 + bool retval = false; + if (sd_jf != 0) { + retval = true; + sd_jf = 0; + } + return retval; +} + +bool check_SD_block_clear (uint32_t block) { + uint32_t b[(SD_BLOCKSIZE >> 2)]; + SD_state = sd.ReadBlocks(b, (uint64_t)(SD_BLOCKSIZE * block), SD_BLOCKSIZE, 1); + if(SD_state != SD_OK) { + sd_jf = 1; + pc.printf ("Failed, not SD_OK, erasing block %d\r\n", block); + return false; + } + for (int i = 0; i < (SD_BLOCKSIZE >> 2); i++) + if (b[i] != 0) + return false; + return true; +} + +bool read_SD_state () { + if (SD_state == SD_OK) + return true; + return false; +} +/*bool erase_block (uint32_t block2erase) { + uint64_t addr = SD_BLOCKSIZE * (uint64_t)block2erase; + SD_state = sd.Erase(addr, addr + SD_BLOCKSIZE); + if (SD_state != SD_OK) { + sd_jf = 1; // Assert error flag + pc.printf ("Failed, not SD_OK, erasing block %d\r\n", block2erase); + return false; + } + return check_SD_block_clear (block2erase); +}*/ + +bool SD_find_next_clear_block (uint64_t * blok) { // Successive approximation algorithm to quickly find next vacant SD card 512 byte block + uint64_t toaddsub = SDBLOCKS / 2, stab = SDBLOCKS - 1; + pc.printf ("At SD_find_next_clear_block \r\n"); + while (toaddsub) { + pc.printf ("stab = %lld, toadsub = %lld\r\n", stab, toaddsub); // lld for long long int + bool clear_block = true; + SD_state = sd.ReadBlocks(SDBuffer, SD_BLOCKSIZE * stab, SD_BLOCKSIZE, 1); + if(SD_state != SD_OK) { + sd_jf = 1; + pc.printf ("SD error in SD_find_next_clear_block, returning -1\r\n"); + return false; + } + for (int i = 0; i < (SD_BLOCKSIZE >> 2); i++) { + if (SDBuffer[i] != 0) { + clear_block = false; + pc.printf ("Buff at %d contains %x\r\n", i, SDBuffer[i]); + i = SD_BLOCKSIZE; // to exit loop + } + } + if (clear_block) + stab -= toaddsub; + else + stab += toaddsub; + toaddsub >>= 1; + } + if (!check_SD_block_clear(stab)) + stab++; + if (sd_error()) { // sd_error() tests and clears error bits + pc.printf ("check_SD_block_clear(%ld)returned ERROR in SD_find_next_clear_block\r\n", stab); + sd_jf = 1; // reassert error flag + return false; + } + pc.printf ("Completed find_next, stab = %d\r\n", stab); + *blok = stab; // block number of next free block + return true; +} + +bool SD_card_erase_all (void) { // assumes sd card is 4 Gbyte, erases 4 Gbyte. Called from CLI + uint64_t EndAddr = GIGAB * 4, + StartAddr = 0LL; + sd_jf = 0; + pc.printf ("Erasing SD card ... "); + // uint8_t Erase(uint64_t StartAddr, uint64_t EndAddr); + SD_state = sd.Erase(StartAddr, EndAddr); + if (SD_state != SD_OK) { + pc.printf ("SD_card_erase_all FAILED\r\n"); + sd_jf = 1; + return false; + } + pc.printf ("no error detected\r\n"); + return true; +} + + +bool mainSDtest() +{ + SD_state = sd.Init(); + if(SD_state != SD_OK) { + pc.printf ("sd.Init set SD_state to %0x\r\n", SD_state); + if(SD_state == MSD_ERROR_SD_NOT_PRESENT) { + pc.printf("SD shall be inserted before running test\r\n"); + } else { + pc.printf("SD Initialization : FAIL.\r\n"); + } + pc.printf("SD Test Aborted.\r\n"); + return false; + } +// else { // SD_state is SD_OK + pc.printf("SD Initialization : OK.\r\n"); + + + +// SD_card_erase_all(); +// if (sd_error()) +// pc.printf ("SD_card_erase_all() reports ERROR"); + + + + SD_find_next_clear_block(& SD_blockptr); + pc.printf ("SD_find_next_clear_block returned %lld\r\n\n\n", SD_blockptr); + if (sd_error()) { + pc.printf ("***** ERROR returned from SD_find_next_clear_block ***** SD ops aborted\r\n"); + return false; + } + pc.printf("SD_find_next_clear_block() returned %ld\r\n", SD_blockptr); + if (SD_blockptr < 1) { + pc.printf ("Looks like card newly erased, SD_blockptr value of %d\r\n", SD_blockptr); + SD_blockptr = 0; + historic_distance = 0; + } + else { + SD_state = sd.ReadBlocks(SDBuffer, SD_BLOCKSIZE * (SD_blockptr - 1), SD_BLOCKSIZE, 1); + if (SD_state != SD_OK) { + pc.printf ("Error reading last block from SD block %d\r\n", SD_blockptr - 1); + return false; + } + for (int i = 0; i < (SD_BLOCKSIZE >> 2); i++) + pc.printf ("%lx\t", SDBuffer[i]); + historic_distance = SDBuffer[(SD_BLOCKSIZE >> 2) - 1]; + pc.printf ("\r\nAbove, data read from last filled SD block %lld, using historic_distance = %lx\r\n", SD_blockptr - 1, historic_distance); + } + if (SD_blockptr > 2) { + for (int i = SD_blockptr - 2; i < SD_blockptr + 2; i++) { + pc.printf ("check_SD_block_clear (%d) ", i); + if (check_SD_block_clear(i)) + pc.printf ("block %ld is CLEAR\r\n", i); + else + pc.printf ("block %ld is NOT clear\r\n", i); + if (sd_error()) { + pc.printf ("ERROR from check_SD_block_clear ()\r\n"); + } + } + } + return true; +} + +void update_SD_card () { // Hall pulse total updated once per sec and saved in blocks of 128 to SD card + static int index = 0; + static uint32_t buff[(SD_BLOCKSIZE >> 2) + 2]; +// buff[index++] = speed.pulse_total(); // pulse_total for all time, add this to buffer to write to SD + buff[index++] = get_pulse_total(); // pulse_total for all time, add this to buffer to write to SD + if (index >= (SD_BLOCKSIZE >> 2)) { + pc.printf ("Writing new SD block %d ... ", SD_blockptr); + SD_state = sd.WriteBlocks(buff, SD_BLOCKSIZE * SD_blockptr, SD_BLOCKSIZE, 1); + SD_blockptr++; + if (SD_state == SD_OK) + pc.printf ("OK, distance %d\r\n", buff[index - 1] / (int)PULSES_PER_METRE); + else + pc.printf ("ERROR\r\n"); + index = 0; + } +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/throttle.cpp Mon Nov 13 09:53:00 2017 +0000 @@ -0,0 +1,56 @@ +/* +This file about using model control servo to drive the throttle of Honda GX120 engine + +Using a model control servo to set the throttle of Honda GX120 petrol engine +First thoughts 12 Nov 2017. + +Servo driven by positive pulse of duration 1000 to 2000 micro sec. Repetition rate 50Hz +Will first try repeating in 32ms loop giving repetirion rate approx 30 Hz. +Pos pulse generated by setting to 1 in 32ms handler and starting a 'timeout' + + ... as part of 32ms code + if (torque_demand < 0.01) { + throttle (TICKOVER); + } + else { // got positive torque demand + throttle (MID_REVS); + } + throttle_servo_pulse_out = 1; + throttle_servo_pulse_width.attach_us (&servo_pulse_lo, global_throttle_us); +*/ +#include "mbed.h" +#include "Electric_Loco.h" + +static const int + SERVO_RANGE = 1000, // micro sec difference between max and min + SERVO_OFFSET = 1000, // min servo pulse width micro sec + TICKOVER = SERVO_OFFSET + 0, + MID_REVS = SERVO_OFFSET + 500, + RANGE_MULTIPLIER = SERVO_RANGE + SERVO_OFFSET - MID_REVS; + +DigitalOut throttle_servo_pulse_out (D8); // now defined in throttle.cpp + +Timeout throttle_servo_pulse_width; + +void throttle_servo_pulse_lo () { // Interrupt handler called at end of 'Timeout' + throttle_servo_pulse_out = 0; // end servo positive pulse +} + +int throttle (double torque_demand, double speed_mph) { // called from main every 31ms + int duration_us; + if (throttle_servo_pulse_out != 0) + return -1; // pulse positive already + if (torque_demand < 0.01) { + duration_us = TICKOVER; + } + else { // got positive torque demand + double tmpd = torque_demand * RANGE_MULTIPLIER; + duration_us = MID_REVS + (int) tmpd; + } + throttle_servo_pulse_out = 1; // start servo positive pulse + throttle_servo_pulse_width.attach_us (&throttle_servo_pulse_lo, duration_us); + return 0; +} +// endof New Nov 2017 Controlling throttle of Honda engine + +