]> git.donarmstrong.com Git - qmk_firmware.git/blob - drivers/avr/i2c_master.c
Update KBD67 readme so that it mentions the KBD65 PCB (#5143)
[qmk_firmware.git] / drivers / avr / i2c_master.c
1 /* Library made by: g4lvanix
2  * Github repository: https://github.com/g4lvanix/I2C-master-lib
3  */
4
5 #include <avr/io.h>
6 #include <util/twi.h>
7
8 #include "i2c_master.h"
9 #include "timer.h"
10 #include "wait.h"
11
12 #ifndef F_SCL
13 #  define F_SCL 400000UL  // SCL frequency
14 #endif
15 #define Prescaler 1
16 #define TWBR_val ((((F_CPU / F_SCL) / Prescaler) - 16) / 2)
17
18 void i2c_init(void) {
19   TWSR = 0; /* no prescaler */
20   TWBR = (uint8_t)TWBR_val;
21 }
22
23 i2c_status_t i2c_start(uint8_t address, uint16_t timeout) {
24   // reset TWI control register
25   TWCR = 0;
26   // transmit START condition
27   TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
28
29   uint16_t timeout_timer = timer_read();
30   while (!(TWCR & (1 << TWINT))) {
31     if ((timeout != I2C_TIMEOUT_INFINITE) && ((timer_read() - timeout_timer) >= timeout)) {
32       return I2C_STATUS_TIMEOUT;
33     }
34   }
35
36   // check if the start condition was successfully transmitted
37   if (((TW_STATUS & 0xF8) != TW_START) && ((TW_STATUS & 0xF8) != TW_REP_START)) {
38     return I2C_STATUS_ERROR;
39   }
40
41   // load slave address into data register
42   TWDR = address;
43   // start transmission of address
44   TWCR = (1 << TWINT) | (1 << TWEN);
45
46   timeout_timer = timer_read();
47   while (!(TWCR & (1 << TWINT))) {
48     if ((timeout != I2C_TIMEOUT_INFINITE) && ((timer_read() - timeout_timer) >= timeout)) {
49       return I2C_STATUS_TIMEOUT;
50     }
51   }
52
53   // check if the device has acknowledged the READ / WRITE mode
54   uint8_t twst = TW_STATUS & 0xF8;
55   if ((twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK)) {
56     return I2C_STATUS_ERROR;
57   }
58
59   return I2C_STATUS_SUCCESS;
60 }
61
62 i2c_status_t i2c_write(uint8_t data, uint16_t timeout) {
63   // load data into data register
64   TWDR = data;
65   // start transmission of data
66   TWCR = (1 << TWINT) | (1 << TWEN);
67
68   uint16_t timeout_timer = timer_read();
69   while (!(TWCR & (1 << TWINT))) {
70     if ((timeout != I2C_TIMEOUT_INFINITE) && ((timer_read() - timeout_timer) >= timeout)) {
71       return I2C_STATUS_TIMEOUT;
72     }
73   }
74
75   if ((TW_STATUS & 0xF8) != TW_MT_DATA_ACK) {
76     return I2C_STATUS_ERROR;
77   }
78
79   return I2C_STATUS_SUCCESS;
80 }
81
82 int16_t i2c_read_ack(uint16_t timeout) {
83   // start TWI module and acknowledge data after reception
84   TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA);
85
86   uint16_t timeout_timer = timer_read();
87   while (!(TWCR & (1 << TWINT))) {
88     if ((timeout != I2C_TIMEOUT_INFINITE) && ((timer_read() - timeout_timer) >= timeout)) {
89       return I2C_STATUS_TIMEOUT;
90     }
91   }
92
93   // return received data from TWDR
94   return TWDR;
95 }
96
97 int16_t i2c_read_nack(uint16_t timeout) {
98   // start receiving without acknowledging reception
99   TWCR = (1 << TWINT) | (1 << TWEN);
100
101   uint16_t timeout_timer = timer_read();
102   while (!(TWCR & (1 << TWINT))) {
103     if ((timeout != I2C_TIMEOUT_INFINITE) && ((timer_read() - timeout_timer) >= timeout)) {
104       return I2C_STATUS_TIMEOUT;
105     }
106   }
107
108   // return received data from TWDR
109   return TWDR;
110 }
111
112 i2c_status_t i2c_transmit(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) {
113   i2c_status_t status = i2c_start(address | I2C_WRITE, timeout);
114
115   for (uint16_t i = 0; i < length && status >= 0; i++) {
116     status = i2c_write(data[i], timeout);
117   }
118
119   i2c_stop();
120
121   return status;
122 }
123
124 i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) {
125   i2c_status_t status = i2c_start(address | I2C_READ, timeout);
126
127   for (uint16_t i = 0; i < (length - 1) && status >= 0; i++) {
128     status = i2c_read_ack(timeout);
129     if (status >= 0) {
130       data[i] = status;
131     }
132   }
133
134   if (status >= 0) {
135     status = i2c_read_nack(timeout);
136     if (status >= 0) {
137       data[(length - 1)] = status;
138     }
139   }
140
141   i2c_stop();
142
143   return (status < 0) ? status : I2C_STATUS_SUCCESS;
144 }
145
146 i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) {
147   i2c_status_t status = i2c_start(devaddr | 0x00, timeout);
148   if (status >= 0) {
149     status = i2c_write(regaddr, timeout);
150
151     for (uint16_t i = 0; i < length && status >= 0; i++) {
152       status = i2c_write(data[i], timeout);
153     }
154   }
155
156   i2c_stop();
157
158   return status;
159 }
160
161 i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) {
162   i2c_status_t status = i2c_start(devaddr, timeout);
163   if (status < 0) {
164     goto error;
165   }
166
167   status = i2c_write(regaddr, timeout);
168   if (status < 0) {
169     goto error;
170   }
171
172   status = i2c_start(devaddr | 0x01, timeout);
173
174   for (uint16_t i = 0; i < (length - 1) && status >= 0; i++) {
175     status = i2c_read_ack(timeout);
176     if (status >= 0) {
177       data[i] = status;
178     }
179   }
180
181   if (status >= 0) {
182     status = i2c_read_nack(timeout);
183     if (status >= 0) {
184       data[(length - 1)] = status;
185     }
186   }
187
188 error:
189   i2c_stop();
190
191   return (status < 0) ? status : I2C_STATUS_SUCCESS;
192 }
193
194 void i2c_stop(void) {
195   // transmit STOP condition
196   TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
197 }