en:pfw:i2c
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| en:pfw:i2c [2023-09-04 18:14] – gelöscht - Externe Bearbeitung (Unbekanntes Datum) 127.0.0.1 | en:pfw:i2c [2023-09-05 06:33] (current) – ↷ Links angepasst, weil Seiten im Wiki verschoben wurden 157.90.7.32 | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | {{pfw: | ||
| + | ====== I2C, The I2C protocol ====== | ||
| + | |||
| + | **The PFW universal I2C drivers** The I2C protocol is a must-have for any microcontroller. It opens up access to IO-expanders, | ||
| + | |||
| + | With these drivers, the use of I2C should not be a problem for anyone. Have fun exploring and using I2C-devices! | ||
| + | |||
| + | ===== The Idea of I2C ===== | ||
| + | |||
| + | |{{https:// | ||
| + | |SDA | ||
| + | |SCL | ||
| + | |||
| + | **Note that the I2C-protocol uses 7-bits addresses and a read/write bit, but in some cases an 8-bit address is mentioned. Even the original designer of the protocol, Philips, sometimes falls into this trap.** | ||
| + | |||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== The low level I2C states for a single master are in short: ==== | ||
| + | |||
| + | * Start condition | ||
| + | * Address a device (for read or write) A device may stretch the clock cycle to allow for a slow response\\ | ||
| + | When a device exists and is ready, it responds with an ACK | ||
| + | * Read or write one or more data bytes After each byte an ACK is received, a NAK is received when it is the last byte | ||
| + | * Stop condition | ||
| + | |||
| + | {{https:// | ||
| + | |||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Pseudo code for low level bitbang I2C ==== | ||
| + | |||
| + | This pseudo code is without the use of clock stretching. This is only necessary when you use a multi master system or I2C slaves implemented on slow microcontrollers. Note that the used example works on on a chip with a push/pull output ports | ||
| + | |||
| + | < | ||
| + | Reserve RAM memory cells named: DEV SUM NACK? | ||
| + | Function: WAIT ( -- ) | ||
| + | Delay for about 5 µsec. | ||
| + | |||
| + | Function: I2START | ||
| + | Clock line high, wait, generate low flank on data line, wait | ||
| + | |||
| + | Function: I2ACK ( -- ) | ||
| + | Clock line low, data line low, wait, | ||
| + | clock line high, wait | ||
| + | |||
| + | Function: I2NACK | ||
| + | Clock line low, data line high, wait, | ||
| + | clock line high, wait | ||
| + | |||
| + | Function: I2ACK@ | ||
| + | Clock line low, data line high, wait, clock line high, wait | ||
| + | Read status of data line, store true in NACK? if it is a nack, otherwise false | ||
| + | |||
| + | Function: BUS! ( x -- ) | ||
| + | 8 loop | ||
| + | clock line low | ||
| + | write bit-7 level of x to data line, wait | ||
| + | clock line high, wait | ||
| + | shift x left | ||
| + | Discard byte, perform i2ack@ | ||
| + | |||
| + | Function: {I2C-ADDR | ||
| + | store +n + 1 in SUM, perform i2start | ||
| + | read dev, perform bus! | ||
| + | |||
| + | |||
| + | Higher level I2C access, hides internal details! | ||
| + | |||
| + | Function: I2C-ON | ||
| + | Setup I/O-bits for two bidirectional | ||
| + | open collector lines with pull-up | ||
| + | |||
| + | Function: I2C} ( -- ) | ||
| + | Clock line high, wait, generate high flank on data line, wait | ||
| + | |||
| + | Function: BUS@ ( -- y ) | ||
| + | Initialise y at zero | ||
| + | 8 loop | ||
| + | shift y left | ||
| + | Clock line low, data line high, wait, clock line high, wait | ||
| + | read data line to bit-0 position of y | ||
| + | Decrease SUM | ||
| + | Sum not zero IF perform i2ack ELSE perform i2nack | ||
| + | |||
| + | Function: | ||
| + | Multiply dev by 2, AND result with 0xFE and store in DEV | ||
| + | |||
| + | Function: {I2C-WRITE | ||
| + | Discard +n, perform i2start, read DEV, (bus! | ||
| + | Read nack? issue error message when true | ||
| + | |||
| + | Function: {I2C-READ | ||
| + | Store +n in SUM, perform i2start, read DEV and set lowest bit, | ||
| + | Perform bus!, read nack? issue error message when true | ||
| + | |||
| + | Function: {DEVICE-OK? | ||
| + | Perform {i2c-addr, perform i2c} | ||
| + | Read nack?, leave true when result is zero | ||
| + | |||
| + | |||
| + | \ Waiting for an EEPROM write to succeed is named acknowledge polling. | ||
| + | Function: {POLL} | ||
| + | Function: {I2C-OUT | ||
| + | Function: {I2C-IN | ||
| + | Function: BUS!} ( b -- ) Perform bus!, perform i2c} | ||
| + | Function: BUS@} ( -- b ) Perform bus@, perform i2c} | ||
| + | Function: BUS-MOVE | ||
| + | </ | ||
| + | |||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== When looked to I2C from a higher level it's access is: ==== | ||
| + | |||
| + | 1) Simple write action - Open I2C-bus for write access to bus-address and output one byte to I2C-bus - Close I2C-bus access | ||
| + | |||
| + | 2) Multiple write action to a devices register or address: - Open I2C-bus for write access to bus-address and output one byte to I2C-bus - Output one ore more byte(s) to I2C-bus (with auto increment) - Close I2C-bus access | ||
| + | |||
| + | 3) A read action from a devices register or address: - Open I2C-bus for write access to bus-address and output the address byte to I2C-bus - Open I2C-bus for reading (Repeated start) - Read one byte from I2C-bus - Close I2C-bus access | ||
| + | |||
| + | 4) Multiple read action from a devices register or address: - Open I2C-bus for write access to bus-address and output the address byte to I2C-bus - Open I2C-bus for reading (Repeated start) - Read one or more byte(s) from I2C-bus (with auto increment) - Close I2C-bus access | ||
| + | |||
| + | ==== I2C pseudo code with high level factorisation ==== | ||
| + | |||
| + | < | ||
| + | Function: > | ||
| + | perform device! | ||
| + | |||
| + | Function: PCF8574> | ||
| + | perform device! | ||
| + | </ | ||
| + | |||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Generic Forth low level part of bitbang example ==== | ||
| + | |||
| + | This example has the I2C interface pins connected like this. | ||
| + | |||
| + | < | ||
| + | SDA (Serial DAta line) = bit-7 | ||
| + | SCL (Serial CLock line) = bit-6 | ||
| + | </ | ||
| + | |||
| + | **The used addresses are for port-1 of the MSP430G2553: | ||
| + | |||
| + | Note that the MSP430 controller series does not have the easiest I/O structure to implement a bitbang version of I2C! This is because it only has push/pull outputs and I2C needs an open collector (or open drain) output. So this example code mimics open collector ports. | ||
| + | |||
| + | {{https:// | ||
| + | **Read byte from I2C EEPROM** %%**%%* | ||
| + | |||
| + | ===== Generic Forth example ===== | ||
| + | |||
| + | <code forth> | ||
| + | Extra words: ABORT" | ||
| + | |||
| + | Words with hardware dependencies: | ||
| + | : *BIS ( mask addr -- ) tuck c@ or swap c! ; | ||
| + | : *BIC ( mask addr -- ) >r invert | ||
| + | : BIT* ( mask addr -- b ) c@ and ; | ||
| + | |||
| + | 20 constant P1IN \ Port-1 input register | ||
| + | 21 constant P1OUT \ Port-1 output register | ||
| + | 22 constant P1DIR \ Port-1 direction register | ||
| + | 26 constant P1SEL \ Port-1 function select | ||
| + | 27 constant P1REN \ Port-1 resistor enable (pullup/ | ||
| + | 42 constant P1SEL2 | ||
| + | |||
| + | 40 constant SCL \ I2C clock line | ||
| + | 80 constant SDA \ I2C data line | ||
| + | SCL SDA or constant IO \ I2C bus lines | ||
| + | |||
| + | : WAIT ( -- ) \ Delay of 5 µsec. must be trimmed! | ||
| + | ( true drop ) ; | ||
| + | |||
| + | : I2START | ||
| + | scl p1out *bis scl p1dir *bic wait | ||
| + | sda p1dir *bis sda p1out *bic wait ; | ||
| + | |||
| + | : I2ACK ( -- ) | ||
| + | scl p1out *bic scl p1dir *bis | ||
| + | sda p1out *bic sda p1dir *bis wait | ||
| + | scl p1out *bis scl p1dir *bic wait ; | ||
| + | |||
| + | : I2NACK | ||
| + | scl p1out *bic scl p1dir *bis | ||
| + | sda p1out *bis sda p1dir *bic wait | ||
| + | scl p1out *bis scl p1dir *bic wait ; | ||
| + | |||
| + | : I2ACK@ | ||
| + | scl p1out *bic scl p1dir *bis | ||
| + | sda p1out *bis sda p1dir *bic wait | ||
| + | scl p1out *bis scl p1dir *bic wait | ||
| + | sda p1in bit* nack? ! ; | ||
| + | |||
| + | : BUS! ( byte -- ) | ||
| + | 8 0 do | ||
| + | scl p1out *bic scl p1dir *bis | ||
| + | dup 80 and if | ||
| + | sda p1out *bis sda p1dir *bic | ||
| + | else | ||
| + | sda p1out *bic sda p1dir *bis | ||
| + | then | ||
| + | wait 2* | ||
| + | scl p1out *bis scl p1dir *bic wait | ||
| + | loop drop i2ack@ ; | ||
| + | |||
| + | : {I2C-ADDR | ||
| + | drop i2start | ||
| + | |||
| + | |||
| + | \ Higher level I2C access, hides internal details! | ||
| + | |||
| + | \ Note that this setup is valid for an MSP430 with external pull-up resistors attached! | ||
| + | \ On hardware which is able to use an open collector (or open source) with pull-up | ||
| + | \ resistor, you should initialise this mode! | ||
| + | : I2C-ON | ||
| + | io p1ren *bic \ Deactivate pull-up/ | ||
| + | io p1dir *bic \ SDA & SCL are inputs | ||
| + | io p1out *bis \ Which start high | ||
| + | io p1sel *bic \ Guarantee normal i/o on MSP430 | ||
| + | io p1sel2 *bic ; | ||
| + | |||
| + | : BUS@ ( -- byte ) | ||
| + | 0 8 0 do | ||
| + | 2* | ||
| + | scl p1out *bic scl p1dir *bis | ||
| + | sda p1out *bis sda p1dir *bic wait | ||
| + | sda p1in bit* 0= 0= 1 and or | ||
| + | scl p1out *bis scl p1dir *bic wait | ||
| + | loop -1 sum +! | ||
| + | sum @ if i2ack else i2nack | ||
| + | |||
| + | : I2C} ( -- ) | ||
| + | scl p1out *bic scl p1dir *bis | ||
| + | sda p1out *bic sda p1dir *bis wait | ||
| + | scl p1out *bis scl p1dir *bic wait | ||
| + | sda p1out *bis sda p1dir *bic ; | ||
| + | |||
| + | : DEVICE! | ||
| + | : {DEVICE-OK? | ||
| + | : {I2C-WRITE | ||
| + | | ||
| + | : {I2C-READ | ||
| + | sum ! i2start | ||
| + | nack? @ abort" Ack error" ; | ||
| + | |||
| + | |||
| + | \ Waiting for an EEPROM write to succeed is named acknowledge polling. | ||
| + | : {POLL} | ||
| + | : {I2C-OUT | ||
| + | : {I2C-IN | ||
| + | : BUS!} ( b -- ) bus! i2c} ; | ||
| + | : BUS@} ( -- b ) bus@ i2c} ; | ||
| + | : BUS-MOVE | ||
| + | </ | ||
| + | |||
| + | ==== I2C implementation examples ==== | ||
| + | |||
| + | This example is for an 8-bit PCF8574 like I/ | ||
| + | |||
| + | <code forth> | ||
| + | : > | ||
| + | device! | ||
| + | |||
| + | : PCF8574> | ||
| + | device! | ||
| + | </ | ||
| + | |||
| + | More examples can be found in the file i2c-examples.f, | ||
| + | See the list of example words below. | ||
| + | |||
| + | ^Word ^Stack | ||
| + | |'' | ||
| + | |'' | ||
| + | |'' | ||
| + | |'' | ||
| + | |'' | ||
| + | |'' | ||
| + | |'' | ||
| + | |'' | ||
| + | |'' | ||
| + | |'' | ||
| + | |||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Dedicated implementations ==== | ||
| + | |||
| + | Have a look at the sub directories for implementations for different systems. | ||
| + | |||
| + | * [[en: | ||
| + | * [[en: | ||
| + | * [[https:// | ||
| + | * [[en: | ||