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

24LC64_eeprom.cpp

Committer:
JonFreeman
Date:
2019-11-30
Revision:
14:acaa1add097b
Parent:
13:ef7a06fa11de
Child:
16:d1e4b9ad3b8b

File content as of revision 14:acaa1add097b:

#include "mbed.h"
#include "STM3_ESC.h"
#include "BufferedSerial.h"
extern  BufferedSerial pc;
extern  error_handling_Jan_2019     ESC_Error    ;         //  Provides array usable to store error codes.
    //  Code for 24LC64 8k x 8 bit eeprom
    //  Code based on earlier work using memory FM24W256, also at i2c address 0xa0;

const int addr_rd = 0xa1;  //  set bit 0 for read, clear bit 0 for write 24LC64
const int addr_wr = 0xa0;  //  set bit 0 for read, clear bit 0 for write 24LC64
const int ACK     = 1;

struct  optpar  {
    int min, max, def;  //  min, max, default
    const char * t;     //  description
}   ;
struct  optpar option_list2[] = {
    {0, 1, 1, "MotorA direction 0 or 1"},
    {0, 1, 0, "MotorB direction 0 or 1"},
    {4, 8, 8, "MotorA poles 4 or 6 or 8"},
    {4, 8, 8, "MotorB poles 4 or 6 or 8"},
    {1, 4, 1, "MotorA 0R05 ohm current shunt resistors 1 to 4"},
    {1, 4, 1, "MotorB 0R05 ohm current shunt resistors 1 to 4"},
    {0, 1, 0, "Servo1 0 = Not used, 1= Output"},
    {0, 1, 0, "Servo2 0 = Not used, 1= Output"},
    {0, 1, 0, "RC Input1 0 = Not used, 1= Output"},
    {0, 1, 0, "RC Input2 0 = Not used, 1= Output"},
    {2, 5, 2, "Command source 2= COM2 (Touch Screen), 3= Pot, 4= RC Input1, 5= RC Input2"},
    {'1', '9', '0', "Alternative ID ascii '1' to '9'"}, //  defaults to '0' before eerom setup for first time
    {10, 250, 60,   "Top speed MPH * 10 range 1.0 to 25.0"},    //  New Jan 2019 TOP_SPEED
    {50, 253, 98,   "Wheel diameter mm"},   //  New 01/06/2018
    {10, 253, 27,   "Motor pinion"},   //  New 01/06/2018
    {50, 253, 85,   "Wheel gear"},   //  New 01/06/2018
    {0, 100, 0, "Future 1"},
    {0, 100, 0, "Future 2"},
    {0, 100, 0, "Future 3"},
    {0, 100, 0, "Future 4"},
    {0, 100, 0, "Future 5"},
}   ;

const   int    numof_eeprom_options2    = sizeof(option_list2) / sizeof (struct optpar);




/*
class   eeprom_settings {
    I2C i2c;
    uint32_t    errors;
    uint32_t    i2c_device_count;
    uint32_t    i2c_device_list[12];    //  max 12 i2c devices
    char        settings    [36];
    bool        rd_24LC64  (int start_addr, char * dest, int length)    ;
    bool        wr_24LC64  (int start_addr, char * dest, int length)    ;
    bool        set_24LC64_internal_address (int    start_addr) ;
    bool        ack_poll    ()  ;
  public:
    eeprom_settings (PinName sda, PinName scl); //  Constructor
    bool        do_we_have_i2c  (uint32_t x)    ;
    char        rd  (uint32_t)  ;           //  Read one setup char value from private buffer 'settings'
    bool        wr  (char, uint32_t)  ;     //  Write one setup char value to private buffer 'settings'
    bool        save    ()  ;               //  Write 'settings' buffer to EEPROM
    uint32_t    errs    ()  ;               //  Return errors
}   ;
*/

bool        eeprom_settings::set_defaults    () {         //  Put default settings into EEPROM and local buffer
    for (int i = 0; i < numof_eeprom_options2; i++)
        settings[i] = option_list2[i].def;       //  Load defaults and 'Save Settings'
    return  save    ();
}

uint32_t    eeprom_settings::errs   ()  {
    return  errors;
}

bool    eeprom_settings::do_we_have_i2c  (uint32_t x)    {
    for (int i = 0; i < i2c_device_count; i++)  {
        if  (i2c_device_list[i] == x)
            return  true;
    }
    return  false;
}

//  Use :   eeprom_settings (SDA_PIN, SCL_PIN);
eeprom_settings::eeprom_settings    (PinName sda, PinName scl) : i2c(sda, scl)  //  Constructor
{
    errors = i2c_device_count = 0;
    for (int i = 0; i < 36; i++)
        settings[i] = 0;
    for (int i = 0; i < 12; i++)
        i2c_device_list[i] = 0;
    i2c.frequency(400000);      //  Speed 400000 max.
//    int last_found = 0, q;      //  Note address bits 3-1 to match addr pins on device
    int q;
    for (int i = 0; i < 255; i += 2)    {   //  Search for devices at all possible i2c addresses
        i2c.start();
        q = i2c.write(i);   //  may return error code 2 when no start issued
        switch  (q) {
            case    ACK:
                pc.printf   ("I2C device found at 0x%x\r\n", i);
//                last_found = i;
                i2c_device_list[i2c_device_count++] = i;
            case    2:      //  Device not seen at this address
            break;
            default:
                pc.printf   ("Unknown error %d from i2c.write while looking for i2c devices\r\n", q);
                errors |= 512;
            break;
        }
    }
    i2c.stop();
//    if  (errors || last_found != 0xa0)    {
    if  (errors || !do_we_have_i2c(0xa0))    {
        pc.printf   ("Error - EEPROM not seen %d\r\n", errors);
        errors |= 0xa0;
        ESC_Error.set   (FAULT_EEPROM, errors);                      //  Set FAULT_EEPROM bits if 24LC64 problem
    }
    else    {   //  Found 24LC64 memory on I2C. Attempt to load settings from EEPROM
        errors = 0;
        if  (!rd_24LC64   (0, settings, 32))
            ESC_Error.set   (FAULT_EEPROM, 2);                      //  Set FAULT_EEPROM bit 1 if 24LC64 problem
        for (int i = 0; i < numof_eeprom_options2; i++) {
            if  ((settings[i] < option_list2[i].min) || (settings[i] > option_list2[i].max))  {
                pc.printf   ("EEROM error with %s\r\n", option_list2[i].t);
                errors++;
            }
        }
    }
    ESC_Error.set   (FAULT_EEPROM, errors);  //  sets nothing if 0
    if  (errors > 1) {
        pc.printf   ("Bad settings found at startup. Restoring defaults\r\n");
        for (int i = 0; i < numof_eeprom_options2; i++)
            settings[i] = option_list2[i].def;       //  Load defaults and 'Save Settings'
        if  (!wr_24LC64  (0, settings, 32))         //  Save settings
            pc.printf   ("Error saving EEPROM in mode19\r\n");
    }
    else    //  0 or 1 error max found
        pc.printf   ("At startup, settings errors = %d\r\n", errors);
}       //  endof constructor
    
bool    eeprom_settings::ack_poll    ()  {   //  wait short while for any previous memory operation to complete
    const int poll_tries    = 40;
    int poll_count = 0;
    bool    i2cfree = false;
    while   (poll_count++ < poll_tries && !i2cfree)  {
        i2c.start   ();
        if  (i2c.write(addr_wr) == ACK)
            i2cfree = true;
        else
            wait_ms   (1);
    }
    return  i2cfree;
}

bool    eeprom_settings::set_24LC64_internal_address (int    start_addr)   {
    if  (!ack_poll())
    {
        pc.printf   ("Err in set_24LC64_internal_address, no ACK writing device address byte\r\n");
        i2c.stop();
        return  false;
    }
    int err = 0;
    if  (i2c.write(start_addr >> 8)   != ACK) err++;
    if  (i2c.write(start_addr & 0xff) != ACK) err++;
    if  (err)   {
        pc.printf   ("In set_24LC64_internal_address, Believe Device present, failed in writing 2 mem addr bytes %d\r\n", err);
        i2c.stop();
        return  false;
    }
    return  true;
}

bool eeprom_settings::rd_24LC64  (int start_addr, char * dest, int length)   {
    int acknak = ACK;
    if(length < 1)
        return false;
    if  (!set_24LC64_internal_address   (start_addr))   {
        pc.printf   ("In rd_24LC64, failed to set_ramaddr\r\n");
        return  false;
    }
    i2c.start();
    if  (i2c.write(addr_rd) != ACK) {
        pc.printf   ("Errors in rd_24LC64 sending addr_rd\r\n");
        return  false;
    }
    while(length--) {
        if(length == 0)
            acknak = 0;
        *dest++ = i2c.read(acknak);
    }
    i2c.stop();
    return  true;
}

bool    eeprom_settings::wr_24LC64  (int start_addr, char * source, int length)   {
    int err = 0;
    if(length < 1 || length > 32)   {
        pc.printf   ("Length out of range %d in wr_24LC64\r\n", length);
        return  false;
    }
    ack_poll    ();
    if  (!set_24LC64_internal_address   (start_addr))   {
        pc.printf   ("In wr_24LC64, Believe Device present, failed in writing 2 mem addr bytes %d\r\n", err);
        return  false;
    }
    while(length--)
        err += ACK - i2c.write(*source++);
    i2c.stop();
    if  (err)   {
        pc.printf   ("in wr_24LC64, device thought good, mem addr write worked, failed writing string\r\n");
        return  false;
    }
//    pc.printf   ("In wr_24LC64 No Errors Found!\r\n");
    return  true;
}

char    eeprom_settings::rd  (uint32_t i)  {           //  Read one setup char value from private buffer 'settings'
    if  (i > 31)    {
        pc.printf   ("ERROR Attempt to read setting %d\r\n");
        return  0;
    }
    return  settings[i];
}

bool    eeprom_settings::wr  (char c, uint32_t i)  {           //  Read one setup char value from private buffer 'settings'
    if  (i > 31)
        return  false;
    settings[i] = c;
    return  true;
}

bool    eeprom_settings::save    ()  {               //  Write 'settings' buffer to EEPROM
    return  wr_24LC64   (0, settings, 32);
}