Here is the header file to accompany the posted example on how to use the Master Synchronous Serial Port (MSSP). Have not tested on other devices or generalized the InitI2C sub to handle other devices if so required? Comments welcome.
'--------------------------------------------------------
'Header file name MasterI2c.h
'Master I2C Mode (Hardware-Synchronous)
'By Kent Schafer June 21, 2007
'This is a port of Microchip Tutorial "I2C Master Mode"
'and AN976
'
'This header is currently set up for word size addressing
'like in the larger size eeproms.
'No polling is taking place after writes or reads
'A suitable delay is required (5 ms or ??)
'--------------------------------------------------------
sub InitI2C 'Disable slew rate control (SMP)
SSPSTAT = b'1000000' 'ClockValue or SSPADD = (FOSC / (4 * Baud)) - 1
SSPADD = ClockValue 'Clear the register, only cleared on POR
SSPCON2 = 0 'Set SSPEN and SSPMx bits for SSP Enable and Master I2C mode
SSPCON = b'00101000'
SET PIR1.SSPIF OFF
SET PIR2.BCLIF OFF
end sub
sub Start
Set PIR1.SSPIF Off 'Clear SSP interrupt flag
Set SSPCON2.SEN On 'set start condition
waitstart:
If PIR1.SSPIF Off Then 'Check if operation complete
goto waitstart
end if
end sub
sub Restart
Set PIR1.SSPIF Off 'Clear SSP interrupt flag
Set SSPCON2.RSEN On 'set stop condition
waitRestart:
If PIR1.SSPIF Off Then 'Check if operation complete
goto waitRestart
end if
end sub
sub TxI2C(SendByte)
Set PIR1.SSPIF Off
SSPBUF = SendByte
'Wait for acknowledge
waitTx:
If PIR1.SSPIF Off Then 'Check if operation complete
goto waitTx
end if
waitAck:
If SSPCON2.ACKSTAT On Then
goto waitAck
end if
end sub
sub RxI2C
Set PIR1.SSPIF Off
Set SSPCON2.RCEN On 'Initiate reception
waitRx1:
If PIR1.SSPIF Off Then 'Check if operation complete
goto waitRx1
end if
ReadByte = SSPBUF 'Read byte in buffer
'Wait for Not acknowledge
Set PIR1.SSPIF Off
Set SSPCON2.ACKEN On 'Select to send NO ACK
waitNoAck:
If PIR1.SSPIF Off Then 'Check if operation complete
goto waitNoAck
end if
end sub
sub Stop
Set PIR1.SSPIF Off
Set SSPCON2.PEN On 'Set Stop condition
waitStop:
If PIR1.SSPIF Off Then 'Check if operation complete
goto waitStop
end if
end sub
'sub Poll
' For pollcount = 1 to 40
' Restart
' TxI2C(eeprom_write)
'end sub
sub I2CWrite(WriteID,HighAddr,LowAddr,I2Cdata) '#NR
Start
TxI2C(WriteID) 'Device ID write byte
TxI2C(HighAddr) 'Send word address high byte
TxI2C(LowAddr) 'Send word address low byte
TxI2C(I2Cdata) 'Send out data byte to slave
Stop
'Poll
end sub
Function I2CRead(WriteID,HighAddr,LowAddr,ReadID)
Start
TxI2C(WriteID) 'Device ID read byte
TxI2C(HighAddr) 'Send word address high byte
TxI2C(LowAddr) 'Send word address low byte
Restart
TxI2C(ReadID) 'Device ID read byte
Set SSPCON2.ACKDT On 'Send no acknowledge
RxI2C 'Receive data byte from slave
I2CRead = ReadByte
Stop
end function
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
First, the address could be passed as a word variable. For example:
sub I2CWrite(WriteID,Addr As Word,I2Cdata) '#NR
Start
TxI2C(WriteID) 'Device ID write byte
TxI2C(Addr_H) 'Send word address high byte
TxI2C(Addr) 'Send word address low byte
TxI2C(I2Cdata) 'Send out data byte to slave
Stop
'Poll
end sub
Also, it may be possible to take away the names of the registers, and just use the name of the bit. "Set SSPCON2.RCEN On" may be able to be changed to "Set RCEN On". This depends on whether there are multiple bits in different registers with the same name. If this is possible, it improves the portability of the code between devices.
The code looks good, I'll try it out on a few chips sometime in the next couple of days and let you know how it goes.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hugh,
The Addr as word suggestion works as expected, so less variables to keep track of, thanks!
Using just the bit values, rather than register and bit values, also works. Little unsure on how to procede on device migration, seems like a big task, but maybe you could point to an appnote, or use your method for testing code on different device family's.
One method of device migration might be to do a parametric search for MSSP on the Microchip site. Then you could group those results into device family's, for which I believe there is an AppNote? Currently don't have any other 16fxxx or 18Fxxxx devices with MSSP to test with.
Lots more work can be done on the header file. Like adding 100KHz, and 400KHz modes, like in your SPI library. Also, need 8bit address handling routines. Have tried to make my Real Time Clock code work in the 8bit mode, to no avail. The RTC code worked fine in the software I2C library, so its a mystery. Need a couple of aspirin and a fresh view on things I guess.
Kent
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Normally when I'm writing code for several devices, I first concentrate on getting it to work on one. Then, I look through the datasheets to find other PICs that I have with the same feature, and try and make it work on them. Even without a given PIC, it's usually possible to assume that the program will work fine if it'll assemble in MPASM.
Another thing - there is a small script in the ssp.h file that will automatically calculate the SSPADD value for a given baud rate:
That sets the constant I2CBaudTemp to the correct value, based on I2CBaud and the chip speed. I2CBaud is in KHz, hence the multiplication by 4000 rather than 4.
Perhaps it would be worth combining your routines with the code already in ssp.h?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Here is the header file to accompany the posted example on how to use the Master Synchronous Serial Port (MSSP). Have not tested on other devices or generalized the InitI2C sub to handle other devices if so required? Comments welcome.
'--------------------------------------------------------
'Header file name MasterI2c.h
'Master I2C Mode (Hardware-Synchronous)
'By Kent Schafer June 21, 2007
'This is a port of Microchip Tutorial "I2C Master Mode"
'and AN976
'
'This header is currently set up for word size addressing
'like in the larger size eeproms.
'No polling is taking place after writes or reads
'A suitable delay is required (5 ms or ??)
'--------------------------------------------------------
sub InitI2C 'Disable slew rate control (SMP)
SSPSTAT = b'1000000' 'ClockValue or SSPADD = (FOSC / (4 * Baud)) - 1
SSPADD = ClockValue 'Clear the register, only cleared on POR
SSPCON2 = 0 'Set SSPEN and SSPMx bits for SSP Enable and Master I2C mode
SSPCON = b'00101000'
SET PIR1.SSPIF OFF
SET PIR2.BCLIF OFF
end sub
sub Start
Set PIR1.SSPIF Off 'Clear SSP interrupt flag
Set SSPCON2.SEN On 'set start condition
waitstart:
If PIR1.SSPIF Off Then 'Check if operation complete
goto waitstart
end if
end sub
sub Restart
Set PIR1.SSPIF Off 'Clear SSP interrupt flag
Set SSPCON2.RSEN On 'set stop condition
waitRestart:
If PIR1.SSPIF Off Then 'Check if operation complete
goto waitRestart
end if
end sub
sub TxI2C(SendByte)
Set PIR1.SSPIF Off
SSPBUF = SendByte
'Wait for acknowledge
waitTx:
If PIR1.SSPIF Off Then 'Check if operation complete
goto waitTx
end if
waitAck:
If SSPCON2.ACKSTAT On Then
goto waitAck
end if
end sub
sub RxI2C
Set PIR1.SSPIF Off
Set SSPCON2.RCEN On 'Initiate reception
waitRx1:
If PIR1.SSPIF Off Then 'Check if operation complete
goto waitRx1
end if
ReadByte = SSPBUF 'Read byte in buffer
'Wait for Not acknowledge
Set PIR1.SSPIF Off
Set SSPCON2.ACKEN On 'Select to send NO ACK
waitNoAck:
If PIR1.SSPIF Off Then 'Check if operation complete
goto waitNoAck
end if
end sub
sub Stop
Set PIR1.SSPIF Off
Set SSPCON2.PEN On 'Set Stop condition
waitStop:
If PIR1.SSPIF Off Then 'Check if operation complete
goto waitStop
end if
end sub
'sub Poll
' For pollcount = 1 to 40
' Restart
' TxI2C(eeprom_write)
'end sub
sub I2CWrite(WriteID,HighAddr,LowAddr,I2Cdata) '#NR
Start
TxI2C(WriteID) 'Device ID write byte
TxI2C(HighAddr) 'Send word address high byte
TxI2C(LowAddr) 'Send word address low byte
TxI2C(I2Cdata) 'Send out data byte to slave
Stop
'Poll
end sub
Function I2CRead(WriteID,HighAddr,LowAddr,ReadID)
Start
TxI2C(WriteID) 'Device ID read byte
TxI2C(HighAddr) 'Send word address high byte
TxI2C(LowAddr) 'Send word address low byte
Restart
TxI2C(ReadID) 'Device ID read byte
Set SSPCON2.ACKDT On 'Send no acknowledge
RxI2C 'Receive data byte from slave
I2CRead = ReadByte
Stop
end function
A couple of suggestions:
First, the address could be passed as a word variable. For example:
sub I2CWrite(WriteID,Addr As Word,I2Cdata) '#NR
Start
TxI2C(WriteID) 'Device ID write byte
TxI2C(Addr_H) 'Send word address high byte
TxI2C(Addr) 'Send word address low byte
TxI2C(I2Cdata) 'Send out data byte to slave
Stop
'Poll
end sub
Also, it may be possible to take away the names of the registers, and just use the name of the bit. "Set SSPCON2.RCEN On" may be able to be changed to "Set RCEN On". This depends on whether there are multiple bits in different registers with the same name. If this is possible, it improves the portability of the code between devices.
The code looks good, I'll try it out on a few chips sometime in the next couple of days and let you know how it goes.
Hugh,
The Addr as word suggestion works as expected, so less variables to keep track of, thanks!
Using just the bit values, rather than register and bit values, also works. Little unsure on how to procede on device migration, seems like a big task, but maybe you could point to an appnote, or use your method for testing code on different device family's.
One method of device migration might be to do a parametric search for MSSP on the Microchip site. Then you could group those results into device family's, for which I believe there is an AppNote? Currently don't have any other 16fxxx or 18Fxxxx devices with MSSP to test with.
Lots more work can be done on the header file. Like adding 100KHz, and 400KHz modes, like in your SPI library. Also, need 8bit address handling routines. Have tried to make my Real Time Clock code work in the 8bit mode, to no avail. The RTC code worked fine in the software I2C library, so its a mystery. Need a couple of aspirin and a fresh view on things I guess.
Kent
Normally when I'm writing code for several devices, I first concentrate on getting it to work on one. Then, I look through the datasheets to find other PICs that I have with the same feature, and try and make it work on them. Even without a given PIC, it's usually possible to assume that the program will work fine if it'll assemble in MPASM.
Another thing - there is a small script in the ssp.h file that will automatically calculate the SSPADD value for a given baud rate:
#define I2CBaud 100 'Change to desired frequency
#script
I2CBaudTemp = int((ChipMhz * 1000000) / (4000 * I2CBaud)) - 1
#endscript
That sets the constant I2CBaudTemp to the correct value, based on I2CBaud and the chip speed. I2CBaud is in KHz, hence the multiplication by 4000 rather than 4.
Perhaps it would be worth combining your routines with the code already in ssp.h?