I had some fun experimenting with the STM8 I2C peripheral. Working around its quirks and designing a code efficient yet reliable and easy to use I2C driver was a challenge. In my previous log entry I mentioned improvements for mixing control structures with optimized assembler code in ISRs.
After testing some alternatives I ended up with a very simple approach:
- Device initialization and buffer definition in user code in user code
- An ISR with a simple API
- variable I2ISR as configuration "registers" for the ISR
- I2S ( c -- ) start I2C transmission sequence with device address c
- I2W ( -- ) wait for end of transmission (optional)
The ISR is now really easy to use. Here is the code for a 24C64 EEPROM:
\res MCU: STM8S103
\ Initialization code, e.g. for 100kBit
#require I2I
\ ISR based I2C "Master transmitter/receiver"
#require I2CMA
\ temporary constants for the I2C user code
I2ISR 2 + CONSTANT TCOUNT \ char number of bytes TX
I2ISR 3 + CONSTANT RCOUNT \ char number of bytes RX
I2ISR 4 + CONSTANT TPOINT \ points to TX buffer, starting with CMD/ADDR
I2ISR 6 + CONSTANT RPOINT \ points to RX buffr
80 CONSTANT SA24C64
NVM
VARIABLE EADDR
VARIABLE BUFFER 6 ALLOT
: write ( a c -- )
\ BUFFER follows EADDR, c=0 at least writes the address
( c ) 2+ TCOUNT C! \ TCOUNT, # bytes incl. EADDR
( a ) EADDR ! \ set EEPROM address
EADDR TPOINT ! \ initialize transfer pointer
SA24C64 I2S
;
: read ( a c -- )
BUFFER RPOINT ! \ set read pointer to buffer
( c ) RCOUNT C! \ RCOUNT
( a ) 0 write \ zero-write sets EADDR and starts the read sequence
;
RAM
\\ Example
I2I
$AA55 BUFFER !
$0011 2 write
$0011 2 read
The ISR deals with the different even sequences in the reference manual and it also catches errors.
Using the error event writing a simple I2C scanner is very easy:
\res MCU: STM8S103
\ Initialization code, e.g. for 100kBit
#require I2I
\ ISR based I2C "Master transmitter/receiver"
#require I2ISR
: scan ( -- )
I2I
127 FOR
I 16 MOD 15 = IF CR THEN
I2S I2W \ send address and wait for ACK/NACK
I2ISR @ 0< IF ." --" ELSE I . THEN
NEXT
;
scan
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 80
79 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ok
A negative value in I2ISR indicates that there was a transmission error (the MSB also contains the error event flags). In the listing above shows replies from a DS1621 at A2:0=7 (79) and from a 24C64 EEPROM at A2:0=0 (80).
I also wrote test code for the DS1621 temperature sensor (which has a 8 bit command instead of the 16 bit address in the case of the EEPROM) which was just a few lines of code. Accessing any 7bit I2C device should be really easy now.
Edit: @Eelco did some serious testing with the well known SSD1306 128x64 OLED display. After discussing a slightly more involved API (extra command interface) it was decided to keep the original simple API. Working code for the SSD1306 is in this GitHub Gist comment.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.