News:

;) This forum is the property of Proton software developers

Main Menu

Buffer for I2C

Started by John Drew, May 04, 2021, 01:30 AM

Previous topic - Next topic

John Drew

A project involving a graphic display is being handicapped by the slow write to screen so I'm losing characters coming in the I2C slave.
Here's the I2C slave code:
' Here begins the interrupt routine
' I2C slave subroutine

BringInI2C:
    Context Save
    If SSP1IF <> 1 Then GoTo EndIntRoutine
    SSP1IF = 0                               'Clear interrupt flag
    If R_W1 = 1 Then GoTo i2cWr              'Data for bus (not address)
    If BF1 = 0 Then GoTo EndIntRoutine       'Nothing in buffer so exit
    If D_A1 = 1 Then
       GoTo i2cRd                           'Read data from bus
    EndIf
    If SSP1BUF <> ssp1add Then EndIntRoutine     'Clear the address from the buffer
    GoTo EndIntRoutine
'------------------------------------------------------------------------------------
' I2C write data to bus
i2cWr:
    btmp2 = ssp1buf
    SSP1BUF = DataKey                            'Put data into datain
    DataKey = 0
    CKP1 = 1                                     'Release SCL line
    GoTo EndIntRoutine
'------------------------------------------------------------------------------------
' I2C read data from bus
i2cRd:
    DataIn = SSP1BUF                             'Get data from datain
    CKP1 = 1                                     'added may not be needed
    Command = DataIn
    NewData = 1
EndIntRoutine:
    Context Restore
'------------------------------------------------------------------------------------
'end of high interrupt routine

The master sends data in bursts and currently I have slowed the sent data to avoid losing screen writes. Unfortunately this has an affect on the speed of the main program that uses Busout. As the data is sent in bursts I should be able to write a buffer routine in the slave and then read it at my leisure and keep up.

Here's the hard part. I've looked at the Buffered USART code in the includes and don't really understand it, although it is well documented. Is it possible to convert it to I2C?
Has anyone created an I2C buffer? Or alternatively have some idea  how to do it.
John

TimB


If you are looking to receive/send data via i2c and you can do it via the hardware regs then your almost there.

The interrupt code is essentially filling a ring buffer controlling the adding and removal with in and out pointers

Make the array and mark the in and out pointers to 0
There after receive data and save it to the address pointed to by the inbufferpointer. Now inc the inbufferpointer and check it is not = buffer size, if so reset pointer to 0 also check its not = the outbufferpointer as that indicates the buffer is full.

You can at this point inc a datainbuffer counter

To read the data out work with the outbufferpointer. eg read the data from the array at the end pointer. Inc and keepin bounds wrap around etc and dec the datainbuffer counter

Easy way for main code to tell if there is data to read is to check the datainbuffer var >0 then there is data.

Its not supper clear but I hope you get the idea. After that you can do all kinds of tricks to replace the built in commands with your own.

Tim

John Drew

Thanks Tim, I'll have a go and let you know how I get on.
I'm not clear about what happens if there's an interrupt caused by I2C while I'm reading and changing pointers in the array.
Anyway I'll see what happens.
John

TimB


Hi John

Yes you need to think about variables being messed with as your reading the data. Simple solution is to turn off/on the interrupt around making a copy of the interrupt var.

Tim

top204

The MSSP peripheral has a single byte buffer, so if the interrupt is disabled while the bufer array is being manipulated, as soon as interrupts are re-enabled, it will fire with what is in the peripheral's buffer.

However, the better method is to have the buffer fill within the interrupt itself. Then the reading/writing part in the main program just needs to look at the previous index value and the new index value of the buffer and if they do not match, there is something in it. Take a look at the method I used for the USART interrupt buffer handling and you will see what I mean. The same method is used, but from a different peripheral filling the buffer array when its address matches.

John Drew

G'day all,
Some success. I used Les's code in the samples folder and made a small change or two. It seems to be working OK so far.
Here's my shot at using it with I2C. I haven't shown the setup of I2C or the initial setting of the counters. It's using a PIC18F25K22 which requires a read of the buffer before writing a character to the peripheral. As the PIC has two MSSP peripherals the registers are a little different too.

' Here begins the interrupt routine
' I2C slave subroutine

BringInI2C:
    Context Save
    If SSP1IF <> 1 Then GoTo EndIntRoutine
    SSP1IF = 0                               'Clear interrupt flag
    If R_W1 = 1 Then GoTo i2cWr              'Data for bus (not address)
    If BF1 = 0 Then GoTo EndIntRoutine       'Nothing in buffer so exit
    If D_A1 = 1 Then
       GoTo i2cRd                           'Read data from bus
    EndIf
    If SSP1BUF <> ssp1add Then EndIntRoutine     'Clear the address from the buffer
    GoTo EndIntRoutine
'------------------------------------------------------------------------------------
' I2C write data to bus
i2cWr:
    btmp2 = ssp1buf                              'required for a PIC18F25K22
    SSP1BUF = DataKey                            'Put data into data to be sent
    ckp1 = 1
    DataKey = 0
    GoTo EndIntRoutine
'------------------------------------------------------------------------------------
' I2C read data from bus
i2cRd:
    Index_in = (Index_in + 1)                   ' Increment index_in pointer (0 to 63)
    If Index_in > (Buffer_size - 1) Then Index_in = 0    'Reset pointer if outside of buffer
    If Index_in = Index_out Then Buffer_error   ' Check for buffer overrun
    temp = SSP1BUF                             'Get data from datain
    ckp1 = 1
    if temp> 0 then Buffer[Index_in] = Temp                     ' Read USART and store character to next empty location
    If ssp1IF = 1 Then goto i2crd               ' Check for another character while we're here
    GoTo endintroutine                          ' Return to program
   Buffer_error:
    ErrFlag = 1                               ' Set the error flag for software
' Move pointer back to avoid corrupting the buffer. Min insures that it ends up within the buffer.
    Index_in = (Index_in - 1) Min (Buffer_size - 1)
    Buffer[Index_in]=ssp1buf                      ' Overwrite the last character stored (resets the interrupt flag)
EndIntRoutine:
    ssp1if = 0
Context Restore
'------------------------------------------------------------------------------------
'end of high interrupt routine

And here is how I read it.

Proc GetBuf()                                 ' Move the next character in buffer to bufchar
    if index_in = index_out then return
    Index_out = (Index_out + 1)         ' Increment index_out pointer (0 to 63)
    If Index_out > (Buffer_size - 1) Then Index_out = 0    ' Reset pointer if outside of buffer
    command = Buffer[Index_out]         ' Read buffer location
    newdata = 1
EndProc
 

Thanks everyone for your advice.
The PIC is very busy. It has an interrupt running on the touch screen, an interrupt for a timer which turns off the screen and also the I2C reading and writing interrupt which communicates with the main board controlling three radio repeaters. In amongst that the 18F25K22 writes to a 320*240 graphic display and controls an external recorder/playback.

No wonder I love using your compiler Les.
John