3654d0f0 |
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 or 400kHz (see ./i2c.h SCL_CLOCK) |
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 |