Jon Freeman / Mbed 2 deprecated Alternator2020_06

Dependencies:   mbed BufferedSerial Servo2 PCT2075 I2CEeprom FastPWM

Revision:
0:77803b3ee157
Child:
1:450090bdb6f4
diff -r 000000000000 -r 77803b3ee157 i2c_bit_banged.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/i2c_bit_banged.cpp	Fri Jun 28 19:32:51 2019 +0000
@@ -0,0 +1,374 @@
+#include "mbed.h"
+#include "Alternator.h"
+extern  Serial  pc;
+DigitalInOut    SDA (D4);       //  Horrible bodge to get i2c working using bit banging.
+DigitalInOut    SCL (D5);       //  DigitalInOut do not work as you might expect. Fine if used only as OpenDrain opuputs though!
+DigitalIn       SDA_IN  (A4);   //  That means paralleling up with two other pins as inputs
+DigitalIn       SCL_IN  (A5);   //  This works but is a pain. Inbuilt I2C should have worked but never does on small boards with 32 pin cpu.
+
+const int _24LC_rd = 0xa1;  //  set bit 0 for read, clear bit 0 for write
+const int _24LC_wr = 0xa0;  //  set bit 0 for read, clear bit 0 for write
+const int ACK     = 0;  //  but acknowledge is 0, NAK is 1
+
+
+/*struct  optpar  {
+    int min, max, def;  //  min, max, default
+    const char * t;     //  description
+}   ;*/
+struct  optpar option_list2[] = {
+    {0, 100, 10, "max pwm% @ Eng RPM 0, 0 to 100"},
+    {0, 100, 10, "max pwm% @ Eng RPM 1000, 0 to 100"},
+    {0, 100, 20, "max pwm% @ Eng RPM 2000, 0 to 100"},
+    {0, 100, 30, "max pwm% @ Eng RPM 3000, 0 to 100"},
+    {0, 100, 40, "max pwm% @ Eng RPM 4000, 0 to 100"},
+    {0, 100, 50, "max pwm% @ Eng RPM 5000, 0 to 100"},
+    {0, 100, 50, "max pwm% @ Eng RPM 6000, 0 to 100"},
+    {0, 100, 50, "max pwm% @ Eng RPM 7000, 0 to 100"},
+    {0, 100, 50, "max pwm% @ Eng RPM 8000, 0 to 100"},
+    {0, 100, 50, "Set Overall PWM Scale Factor percent"},
+    {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);
+
+bool    wr_24LC64  (int start_addr, char * source, int length)   ;  //  think this works
+bool    rd_24LC64  (int start_addr, char * source, int length)   ;  //  think this works
+
+
+
+eeprom_settings mode  ;
+
+eeprom_settings::eeprom_settings    ()  {}
+
+bool    eeprom_settings::set_defaults   ()  {
+    for (int i = 0; i < numof_eeprom_options2; i++)
+        settings[i] = option_list2[i].def;       //  Load defaults and 'Save Settings'
+    return  save    ();
+}
+
+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", i);
+        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;
+}
+
+int     eeprom_settings::get_pwm (int rpm)   {
+    int p = rpm * lut_size;
+    p /= 8000;  //  8000 is upper RPM limit
+    if  (p < 0) p = 0;                    //  point to first
+    if  (p >= lut_size) p = lut_size - 1; //  point to last
+//    pc.printf   ("In get_pwm, rpm = %d, lut entry = %d, pwm = %d\r\n", rpm, p, max_pwm_lut[p]);
+    return  max_pwm_lut[p];
+}
+
+void    eeprom_settings::build_lut   ()  {
+    int ptr = 0;
+    int range, i;
+    int base = mode.rd(RPM0) * PWM_PERIOD_US;
+    double  acc, incr;
+    base /= 100;    //  got pwm_pulsewidth of 0 RPM
+    acc = (double) base;
+    pc.printf   ("pwm_period_us ar 0 RPM = %d\r\n", base);
+    for (i = 0; i < 8; i++) {
+        range = mode.rd(i+1) - mode.rd(i);  //  range now change in percent between two 'n'000 RPMs
+        range *= mode.rd(PWM_SCALE);        //  range now 10000 times factor due to percentage twice
+        range *= PWM_PERIOD_US;
+        incr = (double)range;
+        incr /= 10000.0;
+        incr /= lut_seg_size;
+        for(int j = 0; j < lut_seg_size; j++)   {
+            max_pwm_lut[ptr++] = (int)acc;
+            acc += incr;
+        }
+    }
+    max_pwm_lut[ptr] = (int)acc;
+    pc.printf   ("At end of build_lut ptr=%d\r\n", ptr);
+    range = 0;
+//    while   (range < ptr)   {
+//        for (i = 0; i < 10; i++)    {
+//            pc.printf   ("%d\t", max_pwm_lut[range++]);
+//        }
+//        pc.printf   ("\r\n");
+//    }
+    pc.printf   ("lut_size = %d\r\n", lut_size);
+}
+
+bool    eeprom_settings::load    ()  {               //  Get 'settings' buffer from EEPROM
+    bool    rv  ;
+    rv =    rd_24LC64   (eeprom_page * 32, settings, 32);   //  Can now build lookup table
+    build_lut   ();
+    return  rv;
+}
+
+bool    eeprom_settings::save    ()  {               //  Write 'settings' buffer to EEPROM
+    return  wr_24LC64   (eeprom_page * 32, settings, 32);
+}
+
+
+
+/**
+*   bool    i2c_init(void) {
+*
+*   Init function. Needs to be called once in the beginning.
+*   Returns false if SDA or SCL are low, which probably means 
+*   a I2C bus lockup or that the lines are not pulled up.
+*/
+bool    i2c_init(void) {
+    SDA.output();
+    SCL.output();
+    SDA.mode(OpenDrain);
+    SCL.mode(OpenDrain);    //  Device may pull clock lo to indicate to master
+    SDA = 0;
+    SCL = 0;
+    wait_us   (1);
+    SDA = 1;
+    wait_us (1);
+    SCL = 1;
+    wait_us (1);
+    if  (SCL_IN == 0 || SDA_IN == 0)    return  false;
+  return true;
+}
+
+/**
+*   During data transfer, the data line must remain 
+*   stable whenever the clock line is high. Changes in 
+*   the data line while the clock line is high will be 
+*   interpreted as a Start or Stop condition
+*
+*   A high-to-low transition of the SDA line while the clock
+*   (SCL)   is   high   determines   a   Start   condition.   All
+*   commands must be preceded by a Start condition.
+*/
+int i2c_start  ()  {   // Should be Both hi, start takes SDA low
+    int    rv = 0;
+    if  (SDA_IN == 0 )  {
+        rv |= 1;    //  Fault - SDA was lo on entry
+        SDA = 1;
+        wait_us (1);
+    }
+    if  (SCL == 0 )  {
+        rv |= 2;    //  Fault - SCL was lo on entry
+        SCL = 1;
+        wait_us (1);
+    }
+    SDA = 0;                    //  Take SDA lo
+    wait_us (1);
+    SCL = 0;
+    wait_us (1);
+    return  rv;     //  Returns 0 on success, 1 with SDA fault, 2 with SCL fault, 3 with SDA and SCL fault
+}
+
+/**
+*   During data transfer, the data line must remain 
+*   stable whenever the clock line is high. Changes in 
+*   the data line while the clock line is high will be 
+*   interpreted as a Start or Stop condition
+*
+*   A low-to-high transition of the SDA line while the clock
+*   (SCL)   is   high   determines   a   Stop   condition.   All
+*   operations must be ended with a Stop condition.
+*/
+int i2c_stop  ()  {   //  Should be SDA=0, SCL=1, start takes SDA hi
+    int    rv = 0;
+    SDA = 0;    //  Pull SDA to 0
+    wait_us (1);
+    if  (SCL_IN != 0)   {
+        pc.printf   ("SCL 1 on entry to stop\r\n");
+        SCL = 0;    //  pull SCL to 0 if not there already
+        wait_us (1);
+    }
+    SCL = 1;
+    wait_us (1);
+    if  (SCL_IN == 0)
+        pc.printf   ("SCL stuck lo in stop\r\n");
+    SDA = 1;
+    wait_us (1);
+    if  (SDA_IN == 0)
+        pc.printf   ("SDA stuck lo in stop\r\n");
+    return  rv;     //  Returns 0 on success, 1 with SDA fault, 2 with SCL fault, 3 with SDA and SCL fault
+}
+
+void    jclk    (int bit)   {
+    SCL = bit;
+    wait_us (1);
+}
+
+void    jclkout ()  {
+    wait_us (1);
+    SCL = 1;
+    wait_us (1);
+    SCL = 0;
+    wait_us (1);
+}
+
+int i2c_write (int    d)  {
+    int ackbit = 0;
+    if  (SCL_IN != 0)   {
+        pc.printf   ("SCL hi on entry to write\r\n");
+        jclk    (0);
+    }
+    for (int i = 0x80; i != 0; i >>= 1) {        //  bit out msb first
+        if  ((d & i) == 0)  SDA = 0;
+        else                SDA = 1;
+        jclkout ();     //  SCL ____---____
+    }
+    SDA = 1;    //  Release data to allow remote device to pull lo for ACK or not
+    jclk    (1);    //  SCL = 1
+    ackbit = SDA_IN;   //  read in ack bit
+    jclk    (0);    //  SCL = 0
+//    pc.printf   ("wr 0x%x %s\r\n", d, ackbit == 0 ? "ACK" : "nak");
+    return  ackbit; //      0 for acknowledged ACK, 1 for NAK
+}
+
+
+
+
+int i2c_read    (int acknak)  {     //  acknak  indicates if the byte is to be acknowledged (0 = acknowledge)
+    int result = 0;                 //  SCL should be 1 on entry
+    SDA = 1;        //  Master released SDA
+    if  (SCL_IN != 0)   pc.printf   ("SCL hi arriving at read\r\n");
+    wait_us (2);
+    for (int i = 0; i < 8; i++) {
+        result <<= 1;
+        jclk    (1);
+        if  (SDA_IN != 0)  result |= 1;
+        jclk    (0);
+    }
+    if  (acknak != 0 && acknak != 1)
+        pc.printf   ("Bad acknak in 12c_read %d\r\n", acknak);
+    if  (acknak == 0)   SDA = 0;
+    else                SDA = 1;
+    jclkout ();    //  clock out the ACK bit __--__
+//    pc.printf   ("rd 0x%x %s\r\n", result, acknak == 0 ? "ACK" : "nak");
+    return  result;             //  Always ? nah
+}
+
+int check_24LC64   ()  {     //  Call from near top of main() to init i2c bus
+    int last_found = 0, q, e;      //  Note address bits 3-1 to match addr pins on device
+    for (int i = 0; i < 255; i += 2)    {   //  Search for devices at all possible i2c addresses
+        e = i2c_start();
+        if  (e) pc.putc(',');
+        q = i2c_write(i);   //  may return error code 2 when no start issued
+        if  (q == ACK)  {
+            pc.printf   ("I2C device found at 0x%x\r\n", i);
+            last_found = i;
+            wait_ms (5);
+        }
+        i2c_stop();
+    }
+    return  last_found;
+}
+
+
+
+
+
+
+
+
+bool    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(_24LC_wr) == ACK)
+            i2cfree = true;
+        else
+            wait_ms   (1);
+    }
+//    pc.printf   ("ack_poll, count = %d, i2cfree = %s\r\n", poll_count, i2cfree ? "true" : "false");
+    return  i2cfree;
+}
+
+/**bool    set_24LC64_internal_address (int    start_addr)   {
+*
+*
+*
+*/
+bool    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;
+    }
+//    pc.printf   ("GOOD set_24LC64_internal_address %d\r\n", start_addr);
+    return  true;
+}
+
+bool    wr_24LC64  (int start_addr, char * source, int length)   {  //  think this works
+    int err = 0;
+    if(length < 1 || length > 32)   {
+        pc.printf   ("Length out of range %d in wr_24LC64\r\n", length);
+        return  false;
+    }
+    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 += 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;
+}
+
+bool 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(_24LC_rd) != ACK) {
+        pc.printf   ("Errors in rd_24LC64 sending 24LC_rd\r\n");
+        return  false;
+    }
+    while(length--) {
+        if(length == 0)
+            acknak = 1;
+        *dest++ = i2c_read(acknak);
+    }
+    i2c_stop();
+    return  true;
+}
+
+
+
+
+
+
+
+
+
+
+
+