|
| 1 | +#include <util/twi.h> |
| 2 | +#include <avr/io.h> |
| 3 | +#include <stdlib.h> |
| 4 | +#include <avr/interrupt.h> |
| 5 | +#include <util/twi.h> |
| 6 | +#include <stdbool.h> |
| 7 | +#include "i2c.h" |
| 8 | + |
| 9 | +#ifdef USE_I2C |
| 10 | + |
| 11 | +// Limits the amount of we wait for any one i2c transaction. |
| 12 | +// Since were running SCL line 100kHz (=> 10μs/bit), and each transactions is |
| 13 | +// 9 bits, a single transaction will take around 90μs to complete. |
| 14 | +// |
| 15 | +// (F_CPU/SCL_CLOCK) => # of μC cycles to transfer a bit |
| 16 | +// poll loop takes at least 8 clock cycles to execute |
| 17 | +#define I2C_LOOP_TIMEOUT (9+1)*(F_CPU/SCL_CLOCK)/8 |
| 18 | + |
| 19 | +#define BUFFER_POS_INC() (slave_buffer_pos = (slave_buffer_pos+1)%SLAVE_BUFFER_SIZE) |
| 20 | + |
| 21 | +volatile uint8_t i2c_slave_buffer[SLAVE_BUFFER_SIZE]; |
| 22 | + |
| 23 | +static volatile uint8_t slave_buffer_pos; |
| 24 | +static volatile bool slave_has_register_set = false; |
| 25 | + |
| 26 | +// Wait for an i2c operation to finish |
| 27 | +inline static |
| 28 | +void i2c_delay(void) { |
| 29 | + uint16_t lim = 0; |
| 30 | + while(!(TWCR & (1<<TWINT)) && lim < I2C_LOOP_TIMEOUT) |
| 31 | + lim++; |
| 32 | + |
| 33 | + // easier way, but will wait slightly longer |
| 34 | + // _delay_us(100); |
| 35 | +} |
| 36 | + |
| 37 | +// Setup twi to run at 100kHz |
| 38 | +void i2c_master_init(void) { |
| 39 | + // no prescaler |
| 40 | + TWSR = 0; |
| 41 | + // Set TWI clock frequency to SCL_CLOCK. Need TWBR>10. |
| 42 | + // Check datasheets for more info. |
| 43 | + TWBR = ((F_CPU/SCL_CLOCK)-16)/2; |
| 44 | +} |
| 45 | + |
| 46 | +// Start a transaction with the given i2c slave address. The direction of the |
| 47 | +// transfer is set with I2C_READ and I2C_WRITE. |
| 48 | +// returns: 0 => success |
| 49 | +// 1 => error |
| 50 | +uint8_t i2c_master_start(uint8_t address) { |
| 51 | + TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTA); |
| 52 | + |
| 53 | + i2c_delay(); |
| 54 | + |
| 55 | + // check that we started successfully |
| 56 | + if ( (TW_STATUS != TW_START) && (TW_STATUS != TW_REP_START)) |
| 57 | + return 1; |
| 58 | + |
| 59 | + TWDR = address; |
| 60 | + TWCR = (1<<TWINT) | (1<<TWEN); |
| 61 | + |
| 62 | + i2c_delay(); |
| 63 | + |
| 64 | + if ( (TW_STATUS != TW_MT_SLA_ACK) && (TW_STATUS != TW_MR_SLA_ACK) ) |
| 65 | + return 1; // slave did not acknowledge |
| 66 | + else |
| 67 | + return 0; // success |
| 68 | +} |
| 69 | + |
| 70 | + |
| 71 | +// Finish the i2c transaction. |
| 72 | +void i2c_master_stop(void) { |
| 73 | + TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); |
| 74 | + |
| 75 | + uint16_t lim = 0; |
| 76 | + while(!(TWCR & (1<<TWSTO)) && lim < I2C_LOOP_TIMEOUT) |
| 77 | + lim++; |
| 78 | +} |
| 79 | + |
| 80 | +// Write one byte to the i2c slave. |
| 81 | +// returns 0 => slave ACK |
| 82 | +// 1 => slave NACK |
| 83 | +uint8_t i2c_master_write(uint8_t data) { |
| 84 | + TWDR = data; |
| 85 | + TWCR = (1<<TWINT) | (1<<TWEN); |
| 86 | + |
| 87 | + i2c_delay(); |
| 88 | + |
| 89 | + // check if the slave acknowledged us |
| 90 | + return (TW_STATUS == TW_MT_DATA_ACK) ? 0 : 1; |
| 91 | +} |
| 92 | + |
| 93 | +// Read one byte from the i2c slave. If ack=1 the slave is acknowledged, |
| 94 | +// if ack=0 the acknowledge bit is not set. |
| 95 | +// returns: byte read from i2c device |
| 96 | +uint8_t i2c_master_read(int ack) { |
| 97 | + TWCR = (1<<TWINT) | (1<<TWEN) | (ack<<TWEA); |
| 98 | + |
| 99 | + i2c_delay(); |
| 100 | + return TWDR; |
| 101 | +} |
| 102 | + |
| 103 | +void i2c_reset_state(void) { |
| 104 | + TWCR = 0; |
| 105 | +} |
| 106 | + |
| 107 | +void i2c_slave_init(uint8_t address) { |
| 108 | + TWAR = address << 0; // slave i2c address |
| 109 | + // TWEN - twi enable |
| 110 | + // TWEA - enable address acknowledgement |
| 111 | + // TWINT - twi interrupt flag |
| 112 | + // TWIE - enable the twi interrupt |
| 113 | + TWCR = (1<<TWIE) | (1<<TWEA) | (1<<TWINT) | (1<<TWEN); |
| 114 | +} |
| 115 | + |
| 116 | +ISR(TWI_vect); |
| 117 | + |
| 118 | +ISR(TWI_vect) { |
| 119 | + uint8_t ack = 1; |
| 120 | + switch(TW_STATUS) { |
| 121 | + case TW_SR_SLA_ACK: |
| 122 | + // this device has been addressed as a slave receiver |
| 123 | + slave_has_register_set = false; |
| 124 | + break; |
| 125 | + |
| 126 | + case TW_SR_DATA_ACK: |
| 127 | + // this device has received data as a slave receiver |
| 128 | + // The first byte that we receive in this transaction sets the location |
| 129 | + // of the read/write location of the slaves memory that it exposes over |
| 130 | + // i2c. After that, bytes will be written at slave_buffer_pos, incrementing |
| 131 | + // slave_buffer_pos after each write. |
| 132 | + if(!slave_has_register_set) { |
| 133 | + slave_buffer_pos = TWDR; |
| 134 | + // don't acknowledge the master if this memory loctaion is out of bounds |
| 135 | + if ( slave_buffer_pos >= SLAVE_BUFFER_SIZE ) { |
| 136 | + ack = 0; |
| 137 | + slave_buffer_pos = 0; |
| 138 | + } |
| 139 | + slave_has_register_set = true; |
| 140 | + } else { |
| 141 | + i2c_slave_buffer[slave_buffer_pos] = TWDR; |
| 142 | + BUFFER_POS_INC(); |
| 143 | + } |
| 144 | + break; |
| 145 | + |
| 146 | + case TW_ST_SLA_ACK: |
| 147 | + case TW_ST_DATA_ACK: |
| 148 | + // master has addressed this device as a slave transmitter and is |
| 149 | + // requesting data. |
| 150 | + TWDR = i2c_slave_buffer[slave_buffer_pos]; |
| 151 | + BUFFER_POS_INC(); |
| 152 | + break; |
| 153 | + |
| 154 | + case TW_BUS_ERROR: // something went wrong, reset twi state |
| 155 | + TWCR = 0; |
| 156 | + default: |
| 157 | + break; |
| 158 | + } |
| 159 | + // Reset everything, so we are ready for the next TWI interrupt |
| 160 | + TWCR |= (1<<TWIE) | (1<<TWINT) | (ack<<TWEA) | (1<<TWEN); |
| 161 | +} |
| 162 | +#endif |
0 commit comments