├── .gitignore ├── Makefile ├── twi.h ├── README.md └── twi.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET = twi 2 | MCU = atmega328p 3 | F_CPU = 8000000 4 | CFLAGS = -D F_CPU=$(F_CPU) -mmcu=$(MCU) -g -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall 5 | 6 | %.a: %.o 7 | avr-ar ru $(TARGET).a $< $@ 8 | 9 | %.o: %.c 10 | avr-gcc -c $(CFLAGS) $< -o $@ 11 | 12 | all: $(TARGET).a 13 | 14 | clean: 15 | rm -f *.o 16 | rm -f *.a 17 | 18 | .PHONY: all clean 19 | -------------------------------------------------------------------------------- /twi.h: -------------------------------------------------------------------------------- 1 | #ifndef TWI_H 2 | #define TWI_H 3 | 4 | #include 5 | 6 | #ifndef TWI_FREQ 7 | #define TWI_FREQ 100000UL 8 | #endif 9 | 10 | #ifndef TWI_BUFFER_LENGTH 11 | #define TWI_BUFFER_LENGTH 32 12 | #endif 13 | 14 | void twi_init(); 15 | void twi_write(uint8_t address, uint8_t* data, uint8_t length, void (*callback)(uint8_t, uint8_t *)); 16 | void twi_read(uint8_t address, uint8_t length, void (*callback)(uint8_t, uint8_t *)); 17 | uint8_t *twi_wait(); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # avr-twi 2 | 3 | Nonblocking TWI/I2C master driver for Atmel AVR 4 | 5 | ## API 6 | 7 | ###### `void twi_init()` 8 | 9 | Initializes the driver. Should be called once before calling any other TWI functions. 10 | 11 | ###### `void twi_write(uint8_t address, uint8_t* data, uint8_t length, void (*callback)(uint8_t address, uint8_t *data))` 12 | 13 | Writes data to the given address. 14 | 15 | * `address` - TWI slave address 16 | * `data` - pointer to data buffer 17 | * `length` - numer of bytes to write from the given data buffer 18 | * `callback` - function pointer to callback (called when write completes) 19 | 20 | The callback should accept two arguments: 21 | 22 | * `address` - TWI slave address 23 | * `data` - pointer to data buffer that was written 24 | 25 | ###### `void twi_read(uint8_t address, uint8_t length, void (*callback)(uint8_t address, uint8_t *data))` 26 | 27 | Reads data from the given address. 28 | 29 | * `address` - TWI slave address 30 | * `length` - number of bytes to read 31 | * `callback` - function pointer to callback (called when read is complete) 32 | 33 | The callback should accept two arguments: 34 | 35 | * `address` - TWI slave address 36 | * `data` - pointer to data buffer that was written 37 | 38 | ###### `uint8_t *twi_wait()` 39 | 40 | This will block until the current operation (read or write) completes or return immediately if there is no operation in progress. It returns a pointer to the internal TWI buffer. This is useful for performing initialization read/writes where asynchrony may be unnecessary. Call it between read/write calls. 41 | 42 | twi_write(address, &data, 2, NULL); 43 | twi_wait(); 44 | twi_read(address, 2, NULL); 45 | uint8_t *result = twi_wait(); 46 | 47 | ## Definitions 48 | 49 | * `F_CPU` - you should define this before including this library 50 | * `TWI_FREQ` - defaults to 100kHz if left undefined 51 | * `TWI_BUFFER_LENGTH` - defaults to 32 if left undefined 52 | -------------------------------------------------------------------------------- /twi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "twi.h" 7 | 8 | static volatile uint8_t busy; 9 | static struct { 10 | uint8_t buffer[TWI_BUFFER_LENGTH]; 11 | uint8_t length; 12 | uint8_t index; 13 | void (*callback)(uint8_t, uint8_t *); 14 | } transmission; 15 | 16 | void twi_init() { 17 | TWBR = ((F_CPU / TWI_FREQ) - 16) / 2; 18 | TWSR = 0; // prescaler = 1 19 | 20 | busy = 0; 21 | 22 | sei(); 23 | 24 | TWCR = _BV(TWEN); 25 | } 26 | 27 | uint8_t *twi_wait() { 28 | while (busy); 29 | return &transmission.buffer[1]; 30 | } 31 | 32 | void twi_start(void) { 33 | TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA); 34 | } 35 | 36 | void twi_stop(void) { 37 | TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO); 38 | } 39 | 40 | void twi_ack() { 41 | TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE) | _BV(TWEA); 42 | } 43 | 44 | void twi_nack() { 45 | TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWIE); 46 | } 47 | 48 | void twi_send(uint8_t data) { 49 | TWDR = data; 50 | } 51 | 52 | void twi_recv() { 53 | transmission.buffer[transmission.index++] = TWDR; 54 | } 55 | 56 | void twi_reply() { 57 | if (transmission.index < (transmission.length - 1)) { 58 | twi_ack(); 59 | } else { 60 | twi_nack(); 61 | } 62 | } 63 | 64 | void twi_done() { 65 | uint8_t address = transmission.buffer[0] >> 1; 66 | uint8_t *data = &transmission.buffer[1]; 67 | 68 | busy = 0; 69 | 70 | if (transmission.callback != NULL) { 71 | transmission.callback(address, data); 72 | } 73 | } 74 | 75 | void twi_write(uint8_t address, uint8_t* data, uint8_t length, void (*callback)(uint8_t, uint8_t *)) { 76 | twi_wait(); 77 | 78 | busy = 1; 79 | 80 | transmission.buffer[0] = (address << 1) | TW_WRITE; 81 | transmission.length = length + 1; 82 | transmission.index = 0; 83 | transmission.callback = callback; 84 | memcpy(&transmission.buffer[1], data, length); 85 | 86 | twi_start(); 87 | } 88 | 89 | void twi_read(uint8_t address, uint8_t length, void (*callback)(uint8_t, uint8_t *)) { 90 | twi_wait(); 91 | 92 | busy = 1; 93 | 94 | transmission.buffer[0] = (address << 1) | TW_READ; 95 | transmission.length = length + 1; 96 | transmission.index = 0; 97 | transmission.callback = callback; 98 | 99 | twi_start(); 100 | } 101 | 102 | ISR(TWI_vect) { 103 | switch (TW_STATUS) { 104 | case TW_START: 105 | case TW_REP_START: 106 | case TW_MT_SLA_ACK: 107 | case TW_MT_DATA_ACK: 108 | if (transmission.index < transmission.length) { 109 | twi_send(transmission.buffer[transmission.index++]); 110 | twi_nack(); 111 | } else { 112 | twi_stop(); 113 | twi_done(); 114 | } 115 | break; 116 | 117 | case TW_MR_DATA_ACK: 118 | twi_recv(); 119 | twi_reply(); 120 | break; 121 | 122 | case TW_MR_SLA_ACK: 123 | twi_reply(); 124 | break; 125 | 126 | case TW_MR_DATA_NACK: 127 | twi_recv(); 128 | twi_stop(); 129 | twi_done(); 130 | break; 131 | 132 | case TW_MT_SLA_NACK: 133 | case TW_MR_SLA_NACK: 134 | case TW_MT_DATA_NACK: 135 | default: 136 | twi_stop(); 137 | twi_done(); 138 | break; 139 | } 140 | } 141 | --------------------------------------------------------------------------------