i2c port lockups in 20X2 Slave

inglewoodpete

Senior Member
I have been using a 20X2 (Firmware C.2) as an i2c slave and have been plagued by random lockups in the slave.

I have used PICAXEs as i2c slaves (28X1, 28X2, 40X1, & 40X2) for several years, so consider myself fairly familiar with them.

I understand that care must be taken with 'blocking' commands when it comes to time-critical background tasks like hSerial and hi2c. I accept that data corruptions can occur with incoming data if the kernel is not given enough time to clear the serial buffers in time for subsequent incoming data.

However, I believe the slave chip should be 100% stable and be able to recover from i2c overruns/overwrites. Which leads to the problem that I am encountering: occasional and random lockups of the i2c port of the slave. The timing of the lockup is random: it can take anything from 10 seconds to 10 minutes for the lockup to occur.

I have developed the following software to highlight the problem. I admit that it 'pushes the limits', with its extensive use of SerTxd commands. The 'bit-banged' serial output to the PE terminal disables internal interrupts, meaning that there is a risk that the i2c receive buffer may not be cleared, resulting is data corruption. However, this should not cause unrecoverable lockups of the i2c serial port. The code in the slave also regularly writes data to the scratchpad (in case this is the cause of the lockups - I suspect not).

When the lockup occurs, the master chip only reads '255' values from the slave. The only way to recover i2c communications is to power cycle the slave chip. The master chip does not need resetting, indicating that the problem is in the 20X2 i2c slave.

The circuit is a basic one: download circuits, indicator LEDs and 2 x 4.7kohm pullup resistors on the two i2c lines. The power pins of both chips have 100nF decoupling capacitors. The power supply is a 5-volt regulated bench supply. PE version 5.5.5

Slave code:
Code:
'20X2 i2c Slave code Firmware is C.2
Symbol Version = 3 'v0.3 24/06/2013  284 bytes Lockup Demo-3 Cut Down version Locks up.
'
'#COM 1                    'AXE026 Cable
#PICAXE 20X2
#Terminal 38400            'SerTxd default at 32MHz
#No_Table
'
' **** Hardware Connections - i2c Slave 20X2
'
Symbol oLED               = C.4      ' Indicator LED
'
Symbol bCommsStatus       = b0       '<<<b0 is reserved for bit flags
Symbol ti2cInMode         = Bit7     'Flag: Data received via i2c
'
Symbol bDebugi2c          = b10      'w5
Symbol wIdleCounter       = w6       'b12/13
Symbol bi2cAddress        = b14      'w7
Symbol bInChar            = b21      'w10
Symbol bCounter           = b54      'w27
'
' **** Constants
'
Symbol False              = 0
Symbol True               = 1
'
Symbol cEmpty             = 250      'Indicates empty location.
'
Symbol ci2cSlaveAddress   = 5
'
' **** Scratchpad (20X2 - Bytes 0 to 127d)
'
Symbol sSerInBuffStart    = 0
'Symbol sSerInBuffEnd     = 63
'
'
' ****************************************************
' ****        I n i t i a l i s a t i o n         ****
' ****************************************************
'
Init: Output oLED
      '
      For bCounter = 1 to 8      'Bootup indicator
         Toggle oLED
         Pause 60
      Next bCounter
      '
      SetFreq M32
      Pause 100                  'Allow speed to settle before outputting next line
      SerTxd(CR, LF, CR, LF, "i2c Lockup test 20X2 32MHz v.", #Version, CR, LF)
      '
      'Initialise scratchpad
      ptr = 0
      Do 
         @ptrInc = cEmpty         'Fill scratchpad with "Empty" marker bytes (cEmpty=250)
      Loop Until ptr = sSerInBuffStart
      '
      Flags = 0
      SetIntFlags %01000000, %01000000 'Set hi2c to interrupt
      '
      'Initialise i2c as slave
      bi2cAddress = ci2cSlaveAddress * 2
      hi2cSetup i2cSlave, bi2cAddress
      '
      SerTxd("Entering main loop", CR, LF)
'      
' ****************************************************
' ****     Main program loop, runs at 32MHz       ****
' ****************************************************
'
      'Enters with ptr pointing to sSerInBuffStart
      Do
         If ti2cInMode = True Then     'i2c data has been received
                                       'Ptr points to the location of the next byte to be read
            GoSub FetchData            'Returns bInChar from scratchpad sequential data buffer
            '
            If bInChar = cEmpty Then   'Once past the available characters
               bCommsStatus = 0        'Clear the i2c Status bits
               '
               SerTxd(CR, LF)          'Last debugging until next i2c packet received
               Do                      'Backfill scratchpad buffer with "empty" markers
                  @PtrDec = cEmpty
               Loop Until Ptr = sSerInBuffStart
               @Ptr = cEmpty           'Mark the first scratchpad location with "empty" marker
                                       'First scratchpad location is poller by i2c Master
            Else
               Inc Ptr
            EndIf
         Else                          'No i2c data received: indicate every 500 loops
            Inc wIdleCounter
            If wIdleCounter = 500 Then
               wIdleCounter = 0
               Inc bDebugi2c           'Should regularly get reset in the Interrupt routine
               SerTxd("~", #bDebugi2c)
               Do While bDebugi2c = 20: Loop   'Code loops here when i2c comms stops
            EndIf
         EndIf
      Loop
'
' ****************************************************
' ****           S u b r o u t i n e s            ****
' ****************************************************
'
' **** FetchData: Get a byte of data from Lower Scratchpad area
'
'    Exit: bInChar         b21   Received Character
'
FetchData:  SerTxd("@", #Ptr, ":")          'The location
            bInChar = @Ptr                  'The location's contents
            If bInChar < 128 And bInChar > 31 Then
               SerTxd(bInChar, " ")         'Standard ASCII characters
            ElseIf bInChar = cEmpty Then    'Byte value 250
               SerTxd("[EoD]")
            Else               'Any "non-print" character 0-31, 128-249, 251-255
               SerTxd("[", #bInChar, "] ")
            EndIf
            Return
'
' ************************************************************
'  Interrupt handler
' ************************************************************
'  
Interrupt:  bDebugi2c = 0                     'Reset watchdog counter
            ti2cInMode = True                 'Remains set
            hi2cFlag = False
            SetIntFlags %01000000, %01000000  'Set hi2c to interrupt again
            Return
' ************************************************************
Software for the i2c master chip follows in the next post....
 

inglewoodpete

Senior Member
Code for the i2c Master:
Code:
'Test Program for 20X2 Master sending data into 20X2 i2c Slave
'
'Version 0.4-1 406 bytes 23-Jun-2013 Modified for basic i2c testing
'
'#COM 1                  'AXE026
#PICAXE 20X2
#Terminal 9600
'#No_Data
#No_Table
#Define i2cComms
'
' **** Variables
'
Symbol bCounter           = b5
Symbol bOutChar           = b6
Symbol bCharCnt           = b7
Symbol bi2cAddress        = b8        'Used during initialisation only
Symbol bi2cStatus         = b9
Symbol bDebugi2c          = b10
'
' **** Hardware Allocation
'
Symbol oLED               = C.5       'Indicator LED 20X2
'
' **** Constants
'
Symbol cEmpty             = 250       'Used to indicate an empty location.
Symbol ci2cOLEDAddress    = 5
'
' **** Scratchpad (20X2 - Bytes 0 to 127d)
'
Symbol sSendBufferStart   = 0
'
' ****************************************************
' ****        I n i t i a l i s a t i o n         ****
' ****************************************************
'
Init:   bCharCnt = 0
      SetFreq M8
      Pause 1000
      '
      For bCounter = 1 to 20
         Toggle oLED
         Pause 60
      Next bCounter
      '
      SerTxd("Test Data Generator v.4-1" , CR, LF)
      '
      'Initialise i2c
      bi2cAddress = ci2cOLEDAddress * 2
      hi2cSetup i2cMaster, bi2cAddress, i2cSlow_16, i2cByte
      '
      SerTxd("Wait i2c")
      GoSub Waiti2c
      SerTxd(": i2c ok!", CR, LF)
      '
' ****************************************************
' ****      Main program loop, runs at 8MHz       ****
' ****************************************************
      '
      Do
         '
         bCharCnt = 0
         ptr = sSendBufferStart
         For bOutChar = "A" To "Z"
            GoSub CollateAndSendi2c
         Next bOutChar
         '
         Pause 2000
         '
         bCharCnt = 0
         ptr = sSendBufferStart
         For bOutChar = "a" To "z"
            GoSub CollateAndSendi2c
         Next bOutChar
         '
         Pause 2000
      Loop
      '
' ****************************************************
' ****           S u b r o u t i n e s            ****
' ****************************************************
'
Waiti2c:High oLED
        SerTxd("^", CR, LF)
        bDebugi2c = 0
        Do
           Pause 20
           hi2cIn 0, (bi2cStatus)
           If bi2cStatus < 128 And bi2cStatus > 31 Then
              SerTxd(bi2cStatus, ";")
           ElseIf bi2cStatus = 250 Then
              'SerTxd("^")
           Else
              SerTxd("[", #bi2cStatus, "]")
           EndIf
           Inc bDebugi2c
           'Do While bDebugi2c = 20: Loop   'Lock up if required
        Loop Until bi2cStatus = cEmpty
        'SerTxd(CR, LF)
        Low oLED
        Return
        '
        '
CollateAndSendi2c:
       If bCharCnt = 0 Then
          SerTxd("Pkt1:")
       EndIf
       Inc bCharCnt
       '
       @ptrInc = bOutChar          'Save character to local scratchpad
       SerTxd(bOutChar, " ")
       If bCharCnt = 8 Then        'First packet of 8 characters is ready to be sent
          ptr = 0
          GoSub Waiti2c
          hi2cOut 0, (@ptrInc, @ptrInc, @ptrInc, @ptrInc, @ptrInc, @ptrInc, @ptrInc, @ptrInc)
          ptr = 0
          SerTxd("Pkt2:")
       ElseIf  bCharCnt = 16 Then  'Second packet of 8 characters is ready to be sent
          ptr = 0
          GoSub Waiti2c
          hi2cOut 0, (@ptrInc, @ptrInc, @ptrInc, @ptrInc, @ptrInc, @ptrInc, @ptrInc, @ptrInc)
          ptr = 0
          SerTxd("Pkt3:")
       ElseIf bCharCnt = 24 Then   'Third packet of 8 characters is ready to be sent
          ptr = 0
          GoSub Waiti2c
          hi2cOut 0, (@ptrInc, @ptrInc, @ptrInc, @ptrInc, @ptrInc, @ptrInc, @ptrInc, @ptrInc)
          ptr = 0
          SerTxd("Pkt4:")
       ElseIf  bCharCnt = 26 Then  'Fourth packet: Y Z or y z
          ptr = 0
          GoSub Waiti2c
          hi2cOut 0, (@ptrInc, @ptrInc)
       EndIf
       Return
' ************************************************************
 

inglewoodpete

Senior Member
Buzby, Both you and Peter Mather are referring to different situations to mine.

You refer to i2c problems with an i2c master (a 40X2). matherp refers to the situation where an i2c master resets during an i2c data transfer.

I'm referring to PICAXE slave lockups in a 20X2.

I have run an i2c network of 32 slaves (all PICAXE 28X1s using the 2010 firmware) for several weeks with hardly a hitch. I can barely keep the i2c running in an otherwise stable network of two devices (Master and Slave) for more than a few minutes.
 

nick12ab

Senior Member
Does the problem still happen if ALL blocking commands (including sertxd) and ALL other interrupt based commands including setint, setintflags, hsersetup are removed?
 

inglewoodpete

Senior Member
Does the problem still happen if ALL blocking commands (including sertxd) and ALL other interrupt based commands including setint, setintflags, hsersetup are removed?
A bit hard to tell. Of the commands you list, I'm only using sertxd and setintflags. 'SetIntFlags' is designed to work in an i2c slave.

As you can probably tell, the code posted is just a minimised skeleton to demonstrate the problem. There are reasons why I'm using i2c interrupts.

I'm saying that the i2c port should be stable. I'm not particularly concerned about data loss/corruption. I can deal with data quality issues in code when they arise. However, I can't reopen a locked port of a slave, especially if I have 80 of them in a network as I'm planning.
 

Technical

Technical Support
Staff member
In industry if a micro is used as an i2c slave it is generally dedicated to the task, processing the i2c interrupts and doing the required task processing in between.


The PICAXE, as a much more general device, can be programmed in an infinite number of ways, and has various other commands that can affect this behaviour. In particular, as Nick suggests, you should not use *any* commands that can disable interrupts (such as sertxd). If you do then the i2c silicon module (not the PICAXE firmware) can start to receive errors which can then cause issues. The fact that sertxd is bit busted is not relevant - it still disables the hardware interrupts to prevent framing errors.


So a PICAXE i2c slave should be dedicated to that task, not expected to process other commands that affect interrupts simulatenously. Obviously it can process 'normal' commands between interrupts, but avoid anything that turns the internal silicon interrupts on and off.
 

inglewoodpete

Senior Member
Comparison with a 28X2

I modified the code for the slave 20X2 so that SerTxds only occurr at the conclusion of i2c reception. The master code was modified to send an "End of Transmission" byte before entering the 500mS pause. As expected, the 20X2 slave did not experience any i2c port lockups.

Performance of the 28X2 as an i2c slave is superior.

I then replaced the 20X2 slave with a 28X2. I used identical code to that posted above for both the master and slave chips (with 28X2 slave's SetFreq changed to EM32 with an 8Mhz resonator). That configuration ran continually on the bench for several hours yesterday evening, outputting debugging data via the SerTxds shown in post #1, above.

As I expected, there were occasional data drops with the 28X2 but there were no i2c port lockups. And as an educated guess, I'd say that the frequency of the 28X2's data drops were equivalent to the 20X2's port lockups.

So, either the 20X2 has some silicon issues or the 20X2's current firmware is not handling its internal interrupts the same way as the 28X2.

In my project I need to use SerTxds to debug the proposed slave's software. As mentioned previously, the proposed network will have about 80 slaves and we do not want to go to the physically larger and more expensive 28X2. PICAXE Basic is quicker to develop in than other languages and I'd prefer to stay with PICAXE. Fortunately we are in an early stage of the design so need to keep our options open.
 

inglewoodpete

Senior Member
@Technical, I was wondering if there has been an opportunity to test the i2c slave firmware in the 20X2.

I have tried a search of www.picaxe.com and cannot find a copy of Firmware.txt to see if your investigations have resulted in a firmware change.

As mentioned previously, my team is nearing a point at which microcontroller technology to run with in our current project. The 20X2 is ideal, apart from the bug we are seeing when it is used as an i2c slave.
 
Just to add that I also have an interest in this thread. I have not yet knowingly experienced the same problem but I do have two application building up for production development, both of which use 20x2 as i2c slaves.
 

Technical

Technical Support
Staff member

inglewoodpete

Senior Member
Firmware revision history is posted on the 'revision history' tab for each individual PICAXE chip page e.g.
http://www.picaxe.com/Hardware/PICAXE-Chips/PICAXE-20X2-microcontroller/
I thought I had seen revision history on the website previously. Just a matter of searching in the right place or using the right terminology in the "search" function.
The PICAXE firmware is the same in each case so we are currently investigating if this is a silicon issue. So unfortunately there will be no quick fix here.
Thanks for the follow up. So there's a possibility that the PIC18F14K22 may not be the best solution for any programmable i2c slave, in its PICAXE guise or as a raw PIC programmed in C with a PICKit.

I'll do some experimenting with the latter option although it's obviously not my preferred solution. We may need to make some space on the PCB (and our budget!) for a 28X2 yet.
 
Top