In December 2017 I have developed an operational solution for an I2C Hardware Slave for Microchip 18FK42 range of microcontrollers. The 18FK42 range of microcontrollers have I2C implemented using a different I2C hardware module to the ‘Legacy’ I2C.
See here for operational solution for an I2C Hardware Slave for Microchip Legacy microcontrollers.
This is an extremely simple solution and makes the implementation of a slave very easy.
Interfacing to Slave attached devices is relatively simple. This is completed in the user code - I have provided demonstration code here
How does this work?
You have a Master I2C device. It writes and reads using the I2C protocol. Your master could be any microcontroller that supports hardware or software i2C.
The Slave responds to the writes and read. The Slave code exposes the I2C message sent by the Master 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 very loosy based on Microchip Application Notes AN734 and TB3159. According to AN734, and, Sebastien Lelong and Joep Suijs who created the JAL implementation, there are 5+1 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 the write request is completed. The user call back is called at this point as we have received the complete I2C packet.
HI2CSlave_State_4 called when master wants to read a byte from slave.
HI2CSlave_State_5 called when master still wants to more bytes from slave. That is, master required to read and now still want to read a byte slave should send a byte(s) until the Master sends a NACK.
HI2CSlave_State_6 called when master does not want to talk to slave anymore usually a good place to reset data or slave's logic
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 0x70 and the Slave responds with two data bytes – from a pot.
Callbacks:
HI2C_Process_In_Message ( in HI2CMESSAGESIZE as byte ) called when slave has a received a complete I2C packet.
HI2C_Process_Out_Message called when slave is requested to respond to a Master request.
Demonstration and Library Code
To showcase the new GCB capabilities are:
Demonstrate how to configure the I2C Slave address and to configure the I2C queue
Respond to I2C discovery with the response from the Write and Read addresses
Demonstration code on using how to use the I2C Slave using GCB Message Queue
Receive I2C data from an I2C Master to this I2C Slave – with the I2C Slave handling the incoming via a Message Queue, and,
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
GCB v0.98.+
HW I2C ISR KMode Library. This file is not intended to be edited by a user.
HW I2C Message Queue Kmode Library. This file is not intended to be edited by a user.
Example 16F demonstration code.
Installation of new capabilities
Install GREAT COW BASIC, copy the libraries into INCLUDE folder, if not part of your installation.
Using GREAT COW BASIC 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 GREAT COW BASIC 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 GREAT COW BASIC logic users can respond to the specifics of the I2C message.
HI2CSend( Target I2CAddress ) ;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( Target I2CAddress) ;WriteOp
HI2CSend(outvar) ;first value = 1
HI2CReStart ;generate a restart signal
HI2CSend(Target I2CAddress XOR 1) ;inidicate a read
HI2CReceive(Val2, NACK) ;read one byte and conclude
HI2CStop
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 GREAT COW BASIC code related to this is as follows:
do
HI2CStart ;generate a start signal
HI2CSend(TargetGREAT COW BASICI2CAddress) ;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(TargetGREAT COW BASICI2CAddress) ;indicate a write
loop While HI2CAckPollState
HI2CSend(0x80)
HI2CReStart
HI2CSend(TargetGREAT COW BASICI2CAddress 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(TargetGREAT COW BASICI2CAddress) ;indicate a write
loop While HI2CAckPollState
HI2CSend(0x81) ;number of bytes to request or low address
HI2CReStart
HI2CSend(TargetGREAT COW BASICI2CAddress 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(TargetGREAT COW BASICI2CAddress) ;indicate a write
loop While HI2CAckPollState
HI2CSend(0x82)
HI2CReStart
HI2CSend(TargetGREAT COW BASICI2CAddress 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(TargetGREAT COW BASICI2CAddress) ;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++
Enjoy
Last edit: Anobium 2017-12-31
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
In December 2017 I have developed an operational solution for an I2C Hardware Slave for Microchip 18FK42 range of microcontrollers. The 18FK42 range of microcontrollers have I2C implemented using a different I2C hardware module to the ‘Legacy’ I2C.
See here for operational solution for an I2C Hardware Slave for Microchip Legacy microcontrollers.
This is an extremely simple solution and makes the implementation of a slave very easy.
Interfacing to Slave attached devices is relatively simple. This is completed in the user code - I have provided demonstration code here
How does this work?
You have a Master I2C device. It writes and reads using the I2C protocol. Your master could be any microcontroller that supports hardware or software i2C.
The Slave responds to the writes and read. The Slave code exposes the I2C message sent by the Master 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 very loosy based on Microchip Application Notes AN734 and TB3159. According to AN734, and, Sebastien Lelong and Joep Suijs who created the JAL implementation, there are 5+1 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:
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:
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 0x70 and the Slave responds with two data bytes – from a pot.
Callbacks:
Demonstration and Library Code
To showcase the new GCB capabilities are:
Software Configuration
Installation of new capabilities
Install GREAT COW BASIC, copy the libraries into INCLUDE folder, if not part of your installation.
Using GREAT COW BASIC 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 GREAT COW BASIC 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 GREAT COW BASIC logic users can respond to the specifics of the I2C message.
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.
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
This could update the 3 memory addresses in the slave device. Slave code would be….
Use Case #2
Response to various message types. This is working/tested code.
The GREAT COW BASIC code related to this is as follows:
Enjoy
Last edit: Anobium 2017-12-31