Commit | Line | Data |
---|---|---|
710937e4 KA |
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 | ||
35efcc9f | 37 | // Setup twi to run at 100kHz or 400kHz (see ./i2c.h SCL_CLOCK) |
710937e4 KA |
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 |