Spent the weekend appreciating the wonders that are PICAXE!

bpowell

Senior Member
I've been trying to implement Software I2C on a raw PIC chip (18f24k22)...it's been so frustrating trying to figure out delays, routing, etc...

If I were using a PICAXE, it'd be a simple setup command and away I'd go.

I need to control a WS2811 light strip via SPI...otherwise, I WOULD be using a PICAXE! :)

Oh well...thanks to the developers and maintainers...amazing platform.
 

bpowell

Senior Member
Very nice; thanks Bill!

I've got the SPI figured out (using hardware)....it's the software I2C that is giving me grief! :)
 

Rick100

Senior Member
it's the software I2C that is giving me grief! :)
What language are you programming in ? I think there is a software i2c library for C. I used the assembly routines from Microchip several years ago for the 16f parts.

Good luck,
Rick
 

bpowell

Senior Member
I'm using C...Microchip does have a library....but it's not "plug and play"...it's taking some massaging....
 

Rick100

Senior Member
but it's not "plug and play"...it's taking some massaging....
That's been my experience also . Even doing simple things can involve googling odd error messages. It does make you appreciate the built in Picaxe commands.

Good luck,
Rick
 

bpowell

Senior Member
I'll be firing up my trusty PICAXE tonight to grab some waveforms of good I2C traffic so I can "massage" my bit-banged waveforms into compliance!
 

womai

Senior Member
Below is bit banged I2C code that I converted a while ago to MikroC. Should run with minimum changes on pretty much any C compiler for the PIC. The .Fx syntax accesses bit x of a byte or word, this is the only part that is not standard ANSI C. (e.g. LATB.F6 = 1 sets bit 6 of LATB to 1, while my_var = PORTB.F6 reads the state of bit 6 of PORTB into variable my_var).

Works fine for me. Note that PIC16 devices use PORT for both input an output, while PIC18 or dsPIC devices use LAT for output and PORT for input.

You may need to adapt the pin assignments to the specific PIC you use.

Code:
#define MySoft_I2C_Scl           PORTB.F6
#define MySoft_I2C_Sda           PORTB.F7
#define MySoft_I2C_Scl_Direction TRISB.F6
#define MySoft_I2C_Sda_Direction TRISB.F7
#define SCL_DRIVE                LATB.F6
#define SDA_DRIVE                LATB.F7

#define H_DEL Delay_us(5)
#define Q_DEL Delay_us(5)

#define SCL_LOW  MySoft_I2C_Scl_Direction = 0
#define SCL_HIGH MySoft_I2C_Scl_Direction = 1
#define SDA_LOW  MySoft_I2C_Sda_Direction = 0
#define SDA_HIGH MySoft_I2C_Sda_Direction = 1

#define SCL_READ MySoft_I2C_Scl
#define SDA_READ MySoft_I2C_Sda

void MySoftI2CInit()
{
   SCL_HIGH;
   SDA_HIGH;
   
   SCL_DRIVE = 0;
   SDA_DRIVE = 0;
}
void MySoftI2CStart()
{
   SCL_HIGH;
   H_DEL;

   SDA_LOW;
   H_DEL;
}

void MySoftI2CStop()
{
   SDA_LOW;
   H_DEL;
   SCL_HIGH;
   Q_DEL;
   SDA_HIGH;
   H_DEL;
}

char MySoftI2CWriteByte(char write_data)
{
   char i;
   char ack;
   
   for (i = 0;i < 8;i++)
   {
      SCL_LOW;
      Q_DEL;

      if(write_data & 0x80)
         SDA_HIGH;
      else
         SDA_LOW;

      H_DEL;

      SCL_HIGH;
      H_DEL;

      while (SCL_READ == 0)
      {
      }

      write_data = write_data << 1;
   }

   //The 9th clock (ACK Phase)
   SCL_LOW;
   Q_DEL;

   SDA_HIGH;
   H_DEL;

   SCL_HIGH;
   H_DEL;

   ack = !MySoft_I2C_Sda;
   
   SCL_LOW;
   H_DEL;

   return ack;
}

char MySoftI2CReadByte(char ack)
{
   char read_data = 0x00;
   char i;

   for (i = 0; i < 8;i++)
   {
      SCL_LOW;
      H_DEL;
      SCL_HIGH;
      H_DEL;

      while (SCL_READ == 0)
      {
      }

      if (SDA_READ) read_data |= (0x80 >> i);
   }

   SCL_LOW;
   Q_DEL; 
   
   //MySoft_I2C_Put_Ack
   if (ack)
   {
      SDA_LOW;
   }
   else
   {
      SDA_HIGH;
   }
   
   H_DEL;

   SCL_HIGH;
   H_DEL;

   SCL_LOW;
   H_DEL;

   return read_data;
}
 

womai

Senior Member
And here is some sample code that accesses standard I2C EEPROM:

Code:
void write_byte_to_eeprom (char dev_addr, char mem_addr, char write_data)
{
   //my_data [mem_addr] = write_data;
   //return;

   MySoftI2CStart ();                      // I2C start signal
   MySoftI2CWriteByte (dev_addr & 0b11111110); // device address + W
   MySoftI2CWriteByte (mem_addr);              // address
   MySoftI2CWriteByte (write_data);            // data
   MySoftI2CStop ();                       // issue I2C stop signal

   Delay_ms (EEPROM_WRITE_DELAY);      // give device sufficient time to write
}

// read byte from EXTERNAL EEPROM (Microchip 24LC08, 4 x 256 bytes)
char read_byte_from_eeprom (char dev_addr, char mem_addr)
{
   char read_data = 0xff;

   //read_data = my_data [mem_addr];
   //return (read_data);

   MySoftI2CStart ();                      // I2C start signal
   MySoftI2CWriteByte (dev_addr & 0b11111110); // device address + W
   MySoftI2CWriteByte (mem_addr);              // address
   MySoftI2CStart ();                      // issue I2C signal repeated start
   MySoftI2CWriteByte (dev_addr | 0b00000001); // device address + R
   read_data = MySoftI2CReadByte (0u);         // Read the data (NO acknowledge)
   MySoftI2CStop ();                       // issue I2C stop signal

   return (read_data);
}
 

bpowell

Senior Member
Thanks Womai! I feel like I'm close on the code I'm working with ... but if I don't have it working in the next day or so, I'll likely switch on over to your code and start working that!
 

srnet

Senior Member
Thanks Womai! I feel like I'm close on the code I'm working with ... but if I don't have it working in the next day or so, I'll likely switch on over to your code and start working that!
A better place to ask would be the Microchip forums, where you stand a chance of getting advice specific to the the actual processor you are using, i.e a PIC.

A Google on 'pic bit bang i2c' points to several Microchip forum threads .............................
 

bpowell

Senior Member
Thanks srnet, you're spot-on.

Mchip has SW_I2C libraries for the PIC18 chips already...however, they still require a great deal of work...there's no telling what frequency they're built for, what I2C frequency they're communicating at, etc...it's all manageable, but just makes one appreciate PICAXE!

All of that being said....I looked and realized PICAXE doesn't have a software I2C library! I'd thought they had, but PICAXE uses the hardware I2C...STILL...issuing a "Hi2csetup" is 100% easier than the config bits and setup required to get I2C up and running (even on the hardware) on a raw PIC.

It's all a learning experience, so it's all good!
 

Technical

Technical Support
Staff member
....I looked and realized PICAXE doesn't have a software I2C library!
Bit bashed i2c example below.
But using hi2c commands are much easier and faster!

Code:
; *****************************************************************************
; *                                                                           *
; *     I2C Test Program for the PICAXE-08M                     I2C6.BAS      *
; *                                     ^^^                                   *
; *****************************************************************************
; *                                                                           *
; *     This is an version of I2C3.BAS specifically designed to run on the    *
; *     PICAXE-08M using its bi-directional port abilities.                   *
; *                                                                           *
; *     The core I2C routine suse just 140 bytes of code.                     *
; *                                                                           *
; *****************************************************************************


;                              --.---.-- +V
;                               .|. .|.
;                               | | | | 2 x 4K7
;                               |_| |_|
;        PICAXE-08M              |   |           I2C Eeprom / Ram
;       .----------.             |   |             .----------.
;       |       X1 |-------------^---|-------------| SDA   A2 |---.
;       |          |                 |             |       A1 |---{
;       |       X4 |-----------------^-------------| SCL   A0 |---{
;       `----------'                               |       WP |---{
;                                                  `----------'  _|_ 0V


; *****************************************************************************
; *                                                                           *
; *     Define the I2C device being used                                      *
; *                                                                           *
; *****************************************************************************


                                                ; 24LC256, Word Addressed


        Symbol  I2C_ADDRESS     = %10100000     ; $A0


; *****************************************************************************
; *                                                                           *
; *     Variables                                                             *
; *                                                                           *
; *****************************************************************************


        Symbol  tstAdr          = b0
        Symbol  tstVal          = b1


        Symbol  i2cAdr          = w1    ' b3:b2
        Symbol  i2cAdrLsb       = b2
        Symbol  i2cAdrMsb       = b3


        Symbol  i2cVal          = b4


        Symbol  i2cDataByte     = b5
        Symbol  i2cAckBit       = b6
        Symbol  i2cBitCount     = b7


; *****************************************************************************
; *                                                                           *
; *     Bit-Banged I/O definitions                                            *
; *                                                                           *
; *****************************************************************************


        Symbol  SDA             = 1
        Symbol  SCL             = 4


        Symbol  SDA_PIN         = pin1
        Symbol  SCL_PIN         = pin4


; *****************************************************************************
; *                                                                           *
; *     Main Program Code                                                     *
; *                                                                           *
; *****************************************************************************


        ; Wait for Terminal Window Pop-Up


        Pause 1000


        ; Initialise the I2C comms


        Gosub InitI2cDevice


        ; Test I2C


        For i2cAdr = $00 To $3F
          i2cVal = i2cAdr+3
          Gosub WriteI2cData
          Gosub ReadI2cData
          i2cVal = i2cVal-3
          If i2cVal <> i2cAdr Then
            SerTxd( "Er" )
          End If
        Next


        SerTxd( "Ok" )


        End


; *****************************************************************************
; *                                                                           *
; *     High-Level I2C Interface Routines                                     *
; *                                                                           *
; *****************************************************************************


WriteI2cData:


        Gosub I2cSendHeader


        i2cDataByte = i2cVal
        Gosub I2cSendByte


        Gosub InitI2cDevice


        Pause 20                        ; 20mS to allow write


        Return


ReadI2cData:


        Gosub I2cSendHeader


        Gosub I2cStart


        i2cDataByte = I2C_ADDRESS | $01
        Gosub I2cSendByte


        Input SDA                       ; SDA = 1 / Tri-State SDA
        For i2cBitCount = 0 To 7
          Gosub I2cStrobeScl
          i2cVal= i2cVal * 2 | i2cAckBit
        Next


InitI2cDevice:


        Low SCL                         ; SCL = 0
        Low SDA                         ; SDA = 0
        Input SCL                       ; SCL = 1
        Input SDA                       ; SDA = 1
        Return


; *****************************************************************************
; *                                                                           *
; *     Low-Level I2C Interface                                               *
; *                                                                           *
; *****************************************************************************


I2cSendHeader:


        Gosub I2cStart


        i2cDataByte = I2C_ADDRESS & $FE
        Gosub I2cSendByte


        i2cDataByte = i2cAdrMsb
        Gosub I2cSendByte


        i2cDataByte = i2cAdrLsb


I2cSendByte:


        For i2cBitCount = 0 TO 7
          If i2cDataByte < $80 Then
            Low SDA                     ; SDA = 0
          Else
            Input SDA                   ; SDA = 1
          End If
          Gosub I2cStrobeScl
          i2cDataByte = i2cDataByte * 2
        Next
        Input SDA                       ; SDA = 1 / Tri-State SDA
        Gosub I2cStrobeScl
; ---   if i2cAckBit = 1 Then
; ---     SerTxd( "ACK FAILED",CR,LF )
; ---   end if
        Return


I2cStart:


        Input SDA                       ; SDA = 1
        Input SCL                       ; SCL = 1
        Low SDA                         ; SDA = 0
        Low SCL                         ; SCL = 0
        Return


I2cStrobeScl:


        Input SCL                       ; SCL = 1
        Do
        Loop Until SCL_PIN = 1
        i2cAckBit = SDA_PIN
        Low SCL                         ; SCL = 0
        Return


; *****************************************************************************
; *                                                                           *
; *     End of program                                                        *
; *                                                                           *
; *****************************************************************************
 
Top