I2C: distinguish between individual transmissions.
So far calling code had to wait long enough between individual transmissions to make sure they end up in distinc ones. Now calling code can stuff as fast as it wants, i2c_write() takes care of the distinction.
This commit is contained in:
parent
d088930664
commit
2298973343
42
i2c.c
42
i2c.c
|
|
@ -67,9 +67,17 @@
|
|||
|
||||
// Address of the device that is communicated with.
|
||||
uint8_t i2c_address;
|
||||
|
||||
// State of TWI component of MCU.
|
||||
volatile uint8_t i2c_state = I2C_MODE_FREE;
|
||||
|
||||
/**
|
||||
Wether transmission should be terminated on buffer drain. This also means
|
||||
no new bytes get stuffed into the buffer until this drain happened. It's
|
||||
used to allow distinct transmissions.
|
||||
*/
|
||||
volatile uint8_t i2c_should_end = 0;
|
||||
|
||||
#ifdef I2C_EEPROM_SUPPORT
|
||||
// For SAWSARP mode (see ENHA in i2c.h).
|
||||
uint8_t i2c_page_address[I2C_PAGE_ADDRESS_SIZE];
|
||||
|
|
@ -140,23 +148,35 @@ void i2c_init(uint8_t address) {
|
|||
|
||||
\param data The byte to be buffered/sent.
|
||||
|
||||
\param last_byte Wether this is the last byte of a transaction.
|
||||
\param last_byte Wether this is the last byte of a transmission.
|
||||
|
||||
Unlike many other protocols (serial, SPI), I2C has an explicite transmission
|
||||
start and transmission end. Transmission start is detected automatically,
|
||||
but end of the transmission has to be told by the invoking code.
|
||||
start and transmission end. Invoking code has to tell wether the given byte
|
||||
is the last byte of a transmission, so sending code can properly end it.
|
||||
|
||||
This function has been tested to properly distinguish between individual
|
||||
transmissions separated by last_byte. Other than setting this flag, invoking
|
||||
code doesn't have to care about distinction, but may experience substantial
|
||||
delays (up to several milliseconds) if the bus is already busy with a
|
||||
distinct previous transmission.
|
||||
|
||||
Data is buffered, so this returns quickly for small amounts of data. Large
|
||||
amounts don't get lost, but this function has to wait until sufficient
|
||||
previous data was sent.
|
||||
|
||||
TODO: for now this function assumes that the buffer drains between distinct
|
||||
transmissions. This means, last_byte is ignored and transmission is
|
||||
ended as soon as the buffer drains.
|
||||
Note that calling code has to send bytes quickly enough to not drain the
|
||||
buffer. It looks like the I2C protocol doesn't, unlike e.g. SPI, allow
|
||||
to pause sending without dropping the transmission. Positive of this
|
||||
limitation is, one can end a transmisson by simply not writing for a while,
|
||||
until it's sure the buffer became empty.
|
||||
*/
|
||||
void i2c_write(uint8_t address, uint8_t data, uint8_t last_byte) {
|
||||
|
||||
if ( ! (i2c_state & I2C_MODE_BUSY)) {
|
||||
while (i2c_should_end || ! buf_canwrite(send)) {
|
||||
delay_us(10);
|
||||
}
|
||||
|
||||
if (i2c_state & I2C_MODE_FREE) {
|
||||
// No transmission ongoing, start one.
|
||||
i2c_address = address;
|
||||
|
||||
|
|
@ -168,11 +188,9 @@ void i2c_write(uint8_t address, uint8_t data, uint8_t last_byte) {
|
|||
i2c_state |= I2C_MODE_BUSY;
|
||||
}
|
||||
|
||||
while ( ! buf_canwrite(send))
|
||||
delay_us(10);
|
||||
|
||||
ATOMIC_START
|
||||
buf_push(send, data);
|
||||
i2c_should_end = last_byte;
|
||||
ATOMIC_END
|
||||
}
|
||||
|
||||
|
|
@ -261,8 +279,10 @@ ISR(TWI_vect) {
|
|||
buf_pop(send, TWDR);
|
||||
TWCR = (1<<TWINT)|(I2C_MODE<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|(1<<TWEN)|(1<<TWIE);
|
||||
} else {
|
||||
// Last byte, send stop condition.
|
||||
// Buffer drained because transmission is completed.
|
||||
i2c_state = I2C_MODE_FREE;
|
||||
i2c_should_end = 0;
|
||||
// Send stop condition.
|
||||
TWCR = (1<<TWINT)|(I2C_MODE<<TWEA)|(0<<TWSTA)|(1<<TWSTO)|(1<<TWEN)|(0<<TWIE);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
21
i2c_test.c
21
i2c_test.c
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
#include <string.h>
|
||||
#include "config_wrapper.h"
|
||||
#include "delay.h"
|
||||
#include "i2c.h"
|
||||
|
||||
|
||||
|
|
@ -157,15 +156,15 @@ SYMBOL font_8x4[] = {
|
|||
|
||||
#define FONT_SYMBOLS_SPACE 1
|
||||
|
||||
|
||||
static void i2c_test(void) {
|
||||
uint16_t i;
|
||||
const char* message = "Welcome to Teacup";
|
||||
|
||||
for (i = 0; i < sizeof(display_init); i++) {
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, display_init[i], 0);
|
||||
// Send last byte with 'last_byte' set.
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, display_init[i],
|
||||
(i == sizeof(display_init) - 1));
|
||||
}
|
||||
delay_ms(500);
|
||||
|
||||
/**
|
||||
Clear the screen. As this display supports many sophisticated commands,
|
||||
|
|
@ -175,21 +174,18 @@ static void i2c_test(void) {
|
|||
// Set horizontal adressing mode.
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, 0x00, 0);
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, 0x20, 0);
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, 0x00, 0);
|
||||
delay_ms(100);
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, 0x00, 1);
|
||||
|
||||
// Write 512 zeros.
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, 0x40, 0);
|
||||
for (i = 0; i < 512; i++) {
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, 0x00, 0);
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, 0x00, (i == 511));
|
||||
}
|
||||
delay_ms(2000);
|
||||
|
||||
// Return to page adressing mode.
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, 0x00, 0);
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, 0x20, 0);
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, 0x02, 0);
|
||||
delay_ms(100);
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, 0x02, 1);
|
||||
|
||||
/**
|
||||
Setup cursor on display.
|
||||
|
|
@ -202,8 +198,7 @@ static void i2c_test(void) {
|
|||
i2c_write(DISPLAY_I2C_ADDRESS, 0xB0 | 1, 0);
|
||||
// Column 32.
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, 0x00 | (32 & 0x0F), 0);
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, 0x10 | ((32 >> 4) & 0x0F), 0);
|
||||
delay_ms(100);
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, 0x10 | ((32 >> 4) & 0x0F), 1);
|
||||
|
||||
// Render text to bitmap to display.
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, 0x40, 0);
|
||||
|
|
@ -221,6 +216,8 @@ static void i2c_test(void) {
|
|||
|
||||
message++;
|
||||
}
|
||||
// Send another space for transmission end.
|
||||
i2c_write(DISPLAY_I2C_ADDRESS, 0x00, 1);
|
||||
}
|
||||
|
||||
#endif /* I2C_TEST */
|
||||
|
|
|
|||
Loading…
Reference in New Issue