Menu

I2C Slave Operational Solution

Anobium
2016-11-04
2017-01-23
  • Anobium

    Anobium - 2016-11-04

    Over the last few days I have developed a operational solution for an I2C Hardware Slave for Microchip microcontrollers.

    This is extremely easy to use and makes the implementation of a slave very easy.

    This implementation is an ISR. It is therefore highly reliant on processing time and the adherence with the I2C specification. There is an I2C handler and this is not intended to be user configurable and it is not intended to edited by the user.

    As an ISR if the user turns on/off the interrupts in the user routines this WILL cause I2C protocol errors and overruns. Do not use interrupts without considering the impact on the I2C protocol.

    When developing solutions adding serial or LCD output must be fully optimised to ensure I2C protocol, in terms of timing, is maintained. Longs strings will cause I2C overruns.

    Interfacing to Slave attached devices is relatively simple. This is completed in the user code.

    How does this work?

    You have a Master I2C device. It writes and reads using the I2C protocol.

    The Slave responds to the writes and read. The Slave code exposed the message in a simple message queue. The user can examine the message queue and take appropriate action or return values to the Master.

    Hardware I2C ISR Handler Library

    This library provides an ISR to implement a stateful I2C hardware slave.

    This is a GCB implementation of Microchip Application Note AN734. According to AN734, and, Sebastien Lelong and Joep Suijs who created the JAL implementation, there are 5 possible I2C states. During ISR, each of this states are detected. This ISR provides a standard skeleton to implement an I2C hardware slaves, while client code must implement several callbacks the ISR is expecting to call while processing states.

    Callbacks in the library:

    HI2CSlave_State_1 ( in I2Ctemp as byte ) - called when I2C address matches (master starts a talk)
    HI2CSlave_State_2 ( in I2CByte as byte) - called when master is writing a byte. Slave is thus receiving this byte. This callback takes this bytes as argument
    HI2CSlave_State_3 called when master wants to read a byte from slave. Thus, slave should send a byte using HI2C2Send
    HI2CSlave_State_4 called when master still wants to read a byte from slave. That is, master required to read (state 3) and now still want to read a byte slave should send a byte using HI2C2Send
    HI2CSlave_State_5 called when master does not want to talk to slave anymore usually a good place to reset data or slave's logic
    HI2CSlave_State_Error called when something wrong happens. You can do what you want in this case, like resetting the PIC, log some information using usart, ... called any cases other than states 1, 2, 3, 4 or 5
    Also, there is an enable the line below to enable testing of the clock stretching feature it will add an additional delay of 200us in the interrupt handler so were sure that clock stretching is required for 100 KHz I2C operation

    The I2C Message Handler Library

    The I2C Message Handler library provides the interface for the user to supply a procedure to process the received message.

    Basically, this I2C slave waits for a full message to arrive. Then it calls the User procedures to process the message and (optional) prepare a response. Subsequently, this library will pass the response data over to the master, if it wants to have them.

    Callbacks:

    HI2C_Process_In_Message( HI2CIndex as byte ) called when I2C a Master starts a talk. HI2CIndex is the length of the incoming packet.
    HI2C_Process_Outgoing_Message called when I2C an Master needs to a talk.
    The HI2CBUFFER should be populated with the data to be sent.
    All other methods are Callbacks from the ISR routine.
    Demonstration Code
    The demonstration code creates I2C Slave on a specific I2C address.
    The slave responds to a write of 4 bytes to I2C address 0x4C (which is really Read Address 0x4D) and the Slave responds with two data bytes – from two pots. The results are shown on a terminal.
    Callbacks:
    HI2C_Process_In_Message ( in HI2CMESSAGESIZE as byte ) called when slave has a full buffer.
    HI2C_Process_Out_Message called when slave is requested to respond to a masters request.
    Users should redirect all or any of the standard callbacks like HI2CSlave_State_Error, HI2CSlave_State_1-5 using #define to call specific routines. #define HI2CSlave_State_Error MyHI2CSlave_State_Error

    Code in the package
    The tests to showcase the new GCB capabilities are:
    1. Demonstrate how to configure the I2C Slave address and to configure the I2C queue
    2. Respond to I2C discovery with the response from the Write and Read addresses
    3. Demonstration code on using how to use the I2C Slave using GCB Message Queue
    a. Receive I2C data from an I2C Master to this I2C Slave – with the I2C Slave handling the incoming via a Message Queue, and,
    b. Send I2C data from this I2C Slave to the I2C Master. The data being sent should be sensor data sourced from the Slave.

    Software Configuration
    1. GCB v0.95.010
    2. HW I2C ISR Library ( new .h ). This file is not intended to be edited by a user.
    3. HW I2C Message Queue Library ( new .h ). This file is not intended to be edited by a user.
    4. Example 16F demonstration code ( new )

    Installation of new capabilities
    Install GCB v0.95.010, copy the two .h into INCLUDE folder. These files are not intended to be edited by a user.
    Using GCB v0.95.010, copy the hwI2C .h into INCLUDE\LOWLEVEL folder. This file is not intended to be edited by a user.

    Install the Demo user GCB file into the any suitable code development folder. This file is intended to be edited by the user.

    Message Queue Structure

    Essentially the I2C messages are exposed in the Message Queue. The Message Queue is a buffer. The size of the buffer is user determined but this will be based on the maxiumum I2C message to be communicated.

    The methods expose the buffer and with very simple GCB logic users can respond to the specifics of the I2C message.

    HI2CSend(TargetGCBI2CAddress)   ;WriteOp                  
    HI2CSend(outvar)       ;first  value  = 1
    HI2CSend(outvar+1)    ;then the value = 2
    HI2CSend(outvar+2)    ;then the value
    HI2CSend(outvar+3)    ;then the value
    HI2CSend(outvar+4)    ;then the value
    HI2CSend(outvar+5)    ;then the value=6
    HI2CStop    
    

    Is exposed as 83:01:02:03:04:05:06: in the buffer array

    The queue is user specified. And addressed via HI2CBUFFER(1) thru to HI2CBUFFER(N) where N is HI2CMESSAGESIZE. HI2CMESSAGESIZE will confirm message size.

    HI2CBUFFER(0) will contain the first sent byte (the Write instruction)

    Master Write then Read (aka an EEPROM addressing scheme) All Master messages of this type MUST be the same structure in terms of the length to the Send/Write component of the message.

    HI2CStart  ;generate a start signal
    HI2CSend(TargetGCBI2CAddress)   ;WriteOp                  
    HI2CSend(outvar)       ;first  value  = 1
    HI2CReStart   ;generate a restart signal
    HI2CSend(TargetGCBI2CAddress XOR 1) ;inidicate a read
    HI2CReceive(Val1, ACK)    ;read one byte
    HI2CReceive(Val2, NACK)  ;read one byte and conclude
    HI2CStop    82
    

    Would provide a queue of 1 bytes long.

    Reading the Incoming Queue

    HI2C_Process_In_Message ( in HI2CMESSAGESIZE as byte ) is called when slave has a full buffer.
    The variable HI2CMESSAGESIZE will specify the buffer input queue length.

    If the demo code the incoming queue is copied to a local queue for display purpose.

    A use case could be read the byte pattern or first byte and respond with specific sensors results – which would be placed in the output queue.

    Writing to the outgoing Queue

    HI2C_Process_Out_Message is called when slave is requested to respond to a masters request for data.
    Data bytes should be placed in the buffer in the correct order where buffer element 0 is the first data byte transmitted.

    The supporting libraries are intended to hide the complexities of the state engine, and the buffer is intended to make things easy.

    Use Case #1
    Emulate DS1307 where code would be

        do
            HI2CReStart                          ;generate a start signal
            HI2CSend(AddrWrite)                     ;inidcate a write
        loop While HI2CAckPollState
        HI2CSend(0)                      ;begin with address 0
        HI2CSend(DecToBcd(DS_Sec))       ;then set the three
        HI2CSend(DecToBcd(DS_Min))       ;consecutive values
        HI2CSend(DecToBcd(DS_Hour))
        HI2CStop
    

    This could update the 3 memory addresses in the slave device. Slave code would be….

    Sub HI2C_Process_In_Message ( in HI2CMESSAGESIZE as byte )
    Select CASE HI2CBUFFER[0]
       Case 0x00
          if (HI2CMESSAGESIZE = 4) then
    SecVar = HI2CBUFFER[1]           
    MinVar = HI2CBUFFER[2]
    HourVar= HI2CBUFFER[3]           
          end if
    ….
    ….
    

    Use Case #2

    Response to various message types. This is working/tested code.

    sub HI2C_Process_In_Message ( in HI2CMESSAGESIZE )
          'We have data!!
        Select CASE HI2CBUFFER(0)
           Case 0x80
              if (HI2CMESSAGESIZE = 1) then
                 ' cmd 0x80 - request version
                 HI2CBUFFER(1)   = REV_HI_BYTE     'application code version, write one
                 HI2CBUFFER(2)   = REV_LO_BYTE     'byte, read three bytes
              end if
           CASE  0x81
              if (HI2CMESSAGESIZE = 1) then
                 ' cmd 0x81 - turn on power to temperature sensors
                 I2C_TS_EN = on            'power to temp sensors "on"
                 HI2CBUFFER(1) = 0xFF   'report status of power to TS
              end if
           CASE 0x82
              if (HI2CMESSAGESIZE =  1) then
                 ' cmd 0x82 - turn off power to temperature sensors
                 I2C_TS_EN = off          ' power to temp sensors "off"
                 HI2CBUFFER(1) = 0x00
              end if
           CASE  0x55
                ' cmd 0x55 - set PWM duty on 7 channels
              if (HI2CMESSAGESIZE = 8) then          'write 8 bytes, first command,
                    BB = HI2CBUFFER(1)            ' seven bytes with values from
                    GG = HI2CBUFFER(2)            '0 to 100 for setting PWM in persents.
                    HR = HI2CBUFFER(3)           '85,xx,xx,xx,xx,xx,xx,xx"
                    EQW = HI2CBUFFER(4)       '   read 8 bytes for verification
                    FR = HI2CBUFFER(5)         '
                    WW = HI2CBUFFER(6)    '
                    UV = HI2CBUFFER(7)      '
              end if
                 ' cmd 0x83 - request User ID
           CASE 0x83
    
              if (HI2CMESSAGESIZE = 8) then
    '              User_ID ()            'Call procedure reading User ID to write 1 byte
                    BB = HI2CBUFFER(1)            ' seven bytes with values from
                    GG = HI2CBUFFER(2)            '0 to 100 for setting PWM in persents.
                    HR = HI2CBUFFER(3)           '85,xx,xx,xx,xx,xx,xx,xx"
                    EQW = HI2CBUFFER(4)       '   read 8 bytes for verification
                    FR = HI2CBUFFER(5)         '
                    WW = HI2CBUFFER(6)    '
                    UV = HI2CBUFFER(7)      '
              end if
        End Select
        end sub
    

    The GCB code related to this is as follows:

      do
        HI2CStart                              ;generate a start signal
        HI2CSend(TargetGCBI2CAddress)                       ;inidcate a write
        I2CRetry++
      loop While HI2CAckPollState and I2CRetry <> 255
      HI2CSend(0X55)               
      HI2CSend(outvar)                    ;then the value
      HI2CSend(outvar+1)                    ;then the value
      HI2CSend(outvar+2)                    ;then the value
      HI2CSend(outvar+3)                    ;then the value
      HI2CSend(outvar+4)                    ;then the value
      HI2CSend(outvar+5)                    ;then the value
      HI2CSend(outvar+6)                    ;then the value
      HI2CStop
      outvar++
    
      ‘init the vars
      eepromVal1 = 0X55
      eepromVal2 = 0X55
      do
        HI2CReStart                          ;generate a start signal
        HI2CSend(TargetGCBI2CAddress)                       ;indicate a write
      loop While HI2CAckPollState
      HI2CSend(0x80)  
      HI2CReStart
      HI2CSend(TargetGCBI2CAddress XOR 1) ;set the read flag
      HI2CReceive(eepromVal1, ACK)        ;read one byte
      HI2CReceive(eepromVal2, ACK)        ;read one byte
      HI2CReceive(eepromVal3, NACK)       ;read one byte and conclude
      HI2CStop
    
      HSerPrint eepromVal1
      HSerPrint ":"
      HSerPrint eepromVal2
      HSerPrint ":"
      HSerPrint eepromVal3
      HSerPrintCRLF
    
      do
        HI2CReStart                          ;generate a start signal
        HI2CSend(TargetGCBI2CAddress)                       ;indicate a write
      loop While HI2CAckPollState
      HI2CSend(0x81)  ;number of bytes to request or low address
      HI2CReStart
      HI2CSend(TargetGCBI2CAddress XOR 1) ;set the read flag
      HI2CReceive(eepromVal1, ACK)        ;read one byte
      HI2CReceive(eepromVal2, NACK)        ;read one byte
      HI2CStop
    
      HSerPrint eepromVal1
      HSerPrint ":"
      HSerPrint eepromVal2
      HSerPrintCRLF
    
      do
        HI2CReStart                          ;generate a start signal
        HI2CSend(TargetGCBI2CAddress)                       ;indicate a write
      loop While HI2CAckPollState
      HI2CSend(0x82)  
      HI2CReStart
      HI2CSend(TargetGCBI2CAddress XOR 1) ;set the read flag
      HI2CReceive(eepromVal1, ACK)        ;read one byte
      HI2CReceive(eepromVal2, NACK)        ;read one byte
      HI2CStop
    
      HSerPrint eepromVal1
      HSerPrint ":"
      HSerPrint eepromVal2
      HSerPrintCRLF
    
      do
        HI2CStart                              ;generate a start signal
        HI2CSend(TargetGCBI2CAddress)                       ;indicate a write
        I2CRetry++
      loop While HI2CAckPollState and I2CRetry <> 255
      HI2CSend(0X83)                      
      HI2CSend(outvar)                    ;then the value
      HI2CSend(outvar+1)                    ;then the value
      HI2CSend(outvar+2)                    ;then the value
      HI2CSend(outvar+3)                    ;then the value
      HI2CSend(outvar+4)                    ;then the value
      HI2CSend(outvar+5)                    ;then the value
      HI2CSend(outvar+6)                    ;then the value
      HI2CStop
      outvar++
    

    I will post a package to SVN very soon.

     

    Last edit: Anobium 2016-11-04
  • Anobium

    Anobium - 2016-11-04

    Documentation attached.

     
  • Chris Roper

    Chris Roper - 2016-11-04

    I tested the above with a PIC16F690 on a Low Pin Count demo board talking to the XPress board and it worked first time. I flashed and Powered the Master with a PICKit 2 and had GCBasic set to Program the XPress board directly. The 3rd USB cable is a TTL / Serial bridge that I used to run I2C detection code on the PIC16F690 Prior to loading the host code:

    Well done Anobium, a very useful library.
    I already have some ideas I want to try with it.

    Cheers
    Chris

     

    Last edit: Chris Roper 2016-11-04
  • Anobium

    Anobium - 2016-11-04

    Slaves implementEd during the testing of this code are:

    16f18855
    16f1939
    16f88
    16f690

    These libraries should work on all 16f chips with SSP or MSSP modules. 18f will require someone to test.

     

    Last edit: Anobium 2016-11-04
  • Anobium

    Anobium - 2016-11-05

    And, a Youtube video. See here

    Enjoy.

     
  • viscomjim

    viscomjim - 2017-01-21

    Evan, this is awesome stuff. This is a great solution to create "custom" I2C peripherals using PICs. I have a couple of questions about the in message sub..

    In your video demo using the xpress board as a slave, the sub uses this code...

    sub HI2C_Process_In_Message ( in HI2CMESSAGESIZE )
    
        'We have data!!  Just send some the First byte back
           Select CASE HI2CBUFFER(0)
    
                 Case 0x23
                    if (HI2CMESSAGESIZE = 2) then
                        porta = HI2CBUFFER(1)
                    end if
    
            end Select
    
        end sub
    

    In the demo file for the 16f88 the in message sub uses this code...

    sub HI2C_Process_In_Message ( in HI2CMESSAGESIZE )
    
        'We have data!!
           Select CASE HI2CBUFFER(0)
    
                 Case 0x00
                    LED0 = HI2CBUFFER(1)
                 Case 0x01
                    LED1 = HI2CBUFFER(1)
                 Case 0x02
                    LED2 = HI2CBUFFER(1)
    
            end Select
    
        end sub
    

    On the xpress demo, what is the reason behind sending the 0x23 after the address? Is this just a "double check" or does it represent an I2C register? In the 16f88 code, you just read the byte after the address and use that as data.

    Also, I want to use this for a simple experiment using a 12f1840 which has a mssp, but I noticed in the above post that you say these libaries should work with all 16f chips. Is it possible to use the 12f1840 with this library?

    Thanks again for a GREAT example!

     
  • Anobium

    Anobium - 2017-01-22

    Thank you. If you can go to Youtube - select like and thumbs up please. The new Youtube video scoring means visits with no rating will not show in the searches! So, everyone - it is critical to select like and give thumbs etc. Take a few moments every time to help.

    Back the question:

    The diagram above show an i2c packet. Essentially, an address and data (data...data). The library handles everything and the routine exposes presents the data )data...data) in the buffer.

    In one example I used 0x23 as a 'command' byte, as shiwn in this diagram . I could have therefore have 255 [0 to 255] commands and your CASE statement could complete 255 different commands.

    In the other example - I simply read the data and did an action based upon the incoming data.

    Think of the first example as simulation an EEPROM with the second example as being a trivial example to set some LEDs.

    Regarding the 12f. Should work. However, if not, the library functions are documented. I think there is one place where I had to adapt the code for 16f or 18f - so, this is the area that may need adapation.

    Please ENSURE you have the latest library from the v0.96.00 build. I did fix an issue in this release.

     
  • viscomjim

    viscomjim - 2017-01-22

    Thats a great explanation. I will try the code with a 16f1825 first just to see it work and then try the 12f. I downloaded the entire great cow basic yesterday so can I assume it has the latest liabrary? If not, where do I find that library? Will keep you posted on progress and thanks again for the awesome work. I will go back to you tube and respond also.

     
    • Anobium

      Anobium - 2017-01-22

      The latest is v0.96.00. If you downloaded yesterday then this is ok.

      Next release v0.96.01 will be out very soon - with patches for 18f, plus revised .dat files to simplify support.

       
  • viscomjim

    viscomjim - 2017-01-22

    Hi Evan, I am having difficulty making this work. So I basically took the code from the 16f1939 example and changed the relevant pins to use the 16f1825's I2C and changed the led pins. No issues when compiling and flashing. I did not see any serial prints when I sent data to the pic from the in message sub. I do have pullups on the I2C bus.

    I hooked up another micro to scan the I2C bus and the 16f does not show up. So I attaced a 24lc32 eeprom to the same bus and it shows up at address (h50) which is correct. I tried 100 and 400khz bus speed (in the scanner and pic). The leds flash and hserprint works when pic is powered up, so pretty sure pic is working. No error leds light up. Sending and receiving I2C data to the eeprom work as expected. When I send a byte of data to address 0x30 from a master device, it should print the data on the serial terminal from the in message sub.

    I hooked up a logic analyser to the bus and see what happens when the scanner sees the eeprom, but no response from the pic. Eeprom shows read from 50 with ACK. Pic shows read from 30 with NAK.

    I am not sure if my config has anything to do with it. I am using portC.1 (pin 9) for SDA and port C.0 (pin 10) for SCK. Here is the code I am using...

    '
    '''********************************************************************************
    
    ; ---'''Configuration
    
    #chip 16F1825, 20
    #config OSC = HS, Pllen = OFF
    
       #include <HWI2C_MessageInterface.h>  'Defines a set of callbacks - you do NOT need to define HI2CSlave_State_1-5
    #define HI2CSlaveEnableStartStopInterrupts
        'Redirect standard Error handler to my Error handler
        #define HI2CSlave_State_Error  MyHI2CSlave_State_Error
    
        'Buffer size
        #define HI2CBUFFERSIZE 16
    
        ' error messaging LEDs
        #define HI2CSlaveSSPOVOverflowErrorLED LED0
        #define HI2CSlaveStateNotHandledErrorLED  LED1
    
        ' set up alert LEDs
        #ifdef HI2CSlaveStateNotHandledErrorLED
              Dir HI2CSlaveStateNotHandledErrorLED out
        #endif
        #ifdef HI2CSlaveSSPOVOverflowErrorLED
              Dir HI2CSlaveSSPOVOverflowErrorLED out
        #endif
    
     ' Required I2C settings - CHANGE PORTS if required
        #define hi2c_BAUD_RATE 400
        #define hi2c_DATA PORTc.1
        #define hi2c_CLOCK PORTc.0
        'Initialise I2C Master
        'I2C pins need to be input for SSP2 module
        Dir hi2c_DATA in
        Dir hi2c_CLOCK in
    
        HI2CSetAddress 0x30
        HI2CMode Slave
        HI2CSlave_ISR_Init   'This is required to initialise the library
    
     ' THIS CONFIG OF THE SERIAL PORT WORKS WITH max232 THEN TO PC
     ' USART settings
        #define USART_BAUD_RATE 9600
        #define USART_TX_BLOCKING
        dir portc.4 out
    
    ; ---'''Define Hardware settings
    
        #define LED0  porta.0          ;pin 1
        #define LED1  porta.1          ;pin 2
        #define LED2  porta.2          ;pin 3
    
        dir LED0 out                  ;0, 1 and 2 are outputs (LEDs)
        dir LED1 out                  ;0, 1 and 2 are outputs (LEDs)
        dir LED2 out                  ;0, 1 and 2 are outputs (LEDs
    
    ; ---'''Variables
        dim HI2CForLoop as byte
    
    ; ---'''Main body of program commences here.
    
        Repeat 20
    
            LED0 = !LED0
            LED1 = !LED1
            LED2 = !LED2
            wait 100 ms
    
        end Repeat
    
        hserprint "before interrupt setup"
        hserprintcrlf
    
        On Interrupt  SSP1Ready call HI2CSlave_ISR_Handler
    
        do Forever
    
          'This terminal output WILL cause timing issues. This NOT a Recommend method to show the data stream - use HserSend in HEX!
    
            'Do stuff
        Loop
    
        sub HI2C_Process_In_Message ( in HI2CMESSAGESIZE )
    
        'We have data!!
    
          hserprint "I2C data = "
          hserprint HI2CBUFFER(0)
          hserprintcrlf
    
        end sub
    
        sub HI2C_Process_Out_Message
          'Want to post process the data? do it here.
    
        end sub
    
        #define HI2CSlave_State_Error  MyHI2CSlave_State_Error
    
        sub MyHI2CSlave_State_Error ( in HI2CErrorCode )
    
            dim I2Ctemp as byte
            ' HI2CErrorCode = What caused the error...
            ' SSPSTAT will tell you the status
            'Recommend to consume the Buffer
            Do While  BF = 1
                I2Ctemp = SSPBUF
                hserprint "I2Ctemp = "
                hserprint I2Ctemp
                hserprintcrlf
            Loop
            HSerSend 0
            Select Case HI2CErrorCode
              #ifdef HI2CSlaveSSPOVOverflowErrorLED
                Case HI2CSlaveSSPOVOverflowError
                    HSerSend 1
              #endif
              #ifdef HI2CSlaveStateNotHandledErrorLED
                Case HI2CSlaveStateNotHandledError
                    HSerSend 2
              #endif
            End Select
    
        end Sub
    
        #define  HI2CMessageHandler_State_On_Start myHI2CMessageHandler_State_On_Start
        sub myHI2CMessageHandler_State_On_Start
    
           ' let user process buffer
    
        end sub
    
        #define  HI2CMessageHandler_State_On_Stop myHI2CMessageHandler_State_On_Stop
        sub myHI2CMessageHandler_State_On_Stop
    
           ' let user process buffer
    
        end sub
    

    P.S. I am using the pic16f1825 with a 20mHz crystal. I can run other test programs and the setup is working (hserprint, leds flash etc.). Not sure if clock speed has anything to do with it.

    Also, what is the minumum code setup that will work for this, i.e. I just want to send a byte of data from the master once in a while and perform a small task on the pic with that data byte. I don't need any response or error handeling. Can I simply strip those things out of the program or does the library need all these things in the program to work correctly?

    I will not give up!

    Thanks!

     

    Last edit: viscomjim 2017-01-22
  • Anobium

    Anobium - 2017-01-22

    Add #define SSPIF SSP1IF to your main program.

    This will is because the code is using SSPIF as the interrupt, and guess what, your chip needs SSP1IF.

    Test please.

    You should try this as an alternaitve method. This is what I will put in the library to resolve.

    #script
    
        If nobit(SSPIF) then
    
            If bit(SSP1IF) then
            SSPIF=SSP1IF
            end if
    
        if nobit(SSP1IF) then
                Warning "SSPIF not mapped to SSP1IF ….library is likely to fail"
            End if
        End if
    
    #endscript
    
     

    Last edit: Anobium 2017-01-22
  • Anobium

    Anobium - 2017-01-22

    And, you may have issues with

          hserprint "I2C data = "
          hserprint HI2CBUFFER(0)
          hserprintcrlf
    

    use this as an alternative, and, use Terminal in HEX mode. Then, you can see lots of data without the serial overheads.

            hsersend HI2CBUFFER(0)
    
     
  • viscomjim

    viscomjim - 2017-01-22

    Hi Evan, as I could not get the 16f1825 working, I switched over to the xpress board just to see and it is working great, so at least now I know for sure that I am sending the data correctly. This is working fine using the 4 leds on the xpress board, and I can see it working. I can send the data pretty quickly to the point where you cant see the leds changing, counting from 0 to 15. I have to add a 3ms delay between counts just to see the last led flashing quickly. So, seems to work very well. I will switch back to the 16f1825 and try your code addition by adding...

    #define SSPIF SSP1IF
    

    I have never used scripts before, so where does the script actually go?

    As far as the xpress board or other pic, and timing seems to be critical, I wonder how to go about doing something like this...

    I want to be able to send a data byte to the pic slave I2C device, so I will just send the address and the data and the data will be HI2CBUFFER(0). I want to use that byte for another operation that the pic will do. I will be sending the i2c data to the pic every 50ms or so. During this 50ms period, I need to perform a simple operation with the HI2CBUFFER(0) byte and send a serial stream that takes no longer than 2 ms to send in completion (just calculating the amount of data being sent at a high baud rate). Unfortunately, i have to use the hserprint command as I am sending ascii text to another unit that requires it.

    I noticed if I use just the...

    hserprint "I2C data = "
    hserprint HI2CBUFFER(0)
     hserprintcrlf
    

    in the "in message" sub as before, on the xpress board setup, it does seem to hose things up timing wise when I try to print the byte to the terminal. Does the hserprint command have a large overhead or what is causing the timing issues? I am assuming that the i2c interrupt only occurs when new data is sent from a master and the library code throws the data (assuming one byte is sent) to the HI2CBUFFER(0). Is there a limit to the amount or speed at which you can receive the byte and do some processing before the next byte is received? Should the extra operations not be done in the "in message" sub? Sorry for all the questions.

    Thanks!

     
  • Anobium

    Anobium - 2017-01-23

    Try the attachment. I have updated the library to cater for chips with SSP1IF. You can look at the script in the file to review how this is resolved programmatically.

    Timing. It is important to understand the timing. 'A small delay is required to give time for the eeprom to save the data' I just took that from a page off a Maxim I2C EEProm device, and, if you search the same page it states 'It is important that we give the 24C04 enough time to write. This typically takes several milliseconds after the "stop-condition." Consult the data sheet of your IC to make sure that you use the correct timing.'. This is no different for your implementation. The timing of the 'process in message' buffer needs to understood and the master i2c device needed to handle appropiately.

    Using HSerPrint and the other output commands all take time and the master is simply sending data onto the i2c bus. You have choices.
    - On the master check for a NAK from the slave. A NAK would indicate the master is busy, I think we do this in the EEProm using HI2CAckPollState (a Great Cow BASIC HWI2C method).
    - Add an appropiate delay to the master, in a similar manner to the writing to an EEprom.
    - We could enhance the library to include clock line stretching, see below.
    -
    But, using HSer routine are not slow they just take time. Consider using HSerSend and get the master to do the hard work. Sending a raw data stream removes the use of String handling in the slave device. So, rather then sending just the one data byte, send the compete string (as bytes) and then use HserSend to essentially print raw ASCII to the terminal.

    Do try clock line stretching. Simply add 'CKP = 0' as the first command within your HI2C_Process_In_Message method. CKP will be handled by the library appropiately after your code has completed. Set CKP and let me know the results.

    Re Interrupts. There is only one interrupt defined. The interrupt supports the I2C routines. So what is happening? The master is overrunning the input buffer. There are error routines in the library to show that this has occurred, in your code this is a routine called 'MyHI2CSlave_State_Error ( in HI2CErrorCode ). I am 100% certain you are getting buffer overruns because the master is simply sending data whilst the slave is send Hser data. This is the same problem as any EEProm and this is not specific to these libraries - timing is important with I2C.

    Pleasure.

     
  • viscomjim

    viscomjim - 2017-01-23

    I will give these suggestions a try and consider the amount of time required for the actions. I am still playing with the xpress board currently, but will be switching back to the 16f1825 using the new handler code. It seems the 12f1840 has the same SSP1IF so it may work also as a small I2C "custom" controller. I may break up my operation between two pics, one to read the i2c from the master (i'm only reading the lower 4 bits from the master (0 - F)) and continously outputting them on 4 gpios. Then the second can constantly poll the 4 gpio bits and create the constant serial string needed. This would be totally async. but should work for what I am doing. Will keep trucking along. This is a great addition to the GCB arsenol, and I appreciate your work! This will have lots of uses for sure.

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.