Create set-home.md
[u360gts.git] / src / main / drivers / bus_i2c_stm32f10x.c
blob591843c52883451524580cc87324e5d1c30e2908
1 /*
2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
18 #include <stdbool.h>
19 #include <stdint.h>
20 #include <stdlib.h>
22 #include <platform.h>
24 #include "build_config.h"
26 #include "gpio.h"
27 #include "system.h"
29 #include "bus_i2c.h"
30 #include "nvic.h"
32 #ifndef SOFT_I2C
34 // I2C2
35 // SCL PB10
36 // SDA PB11
37 // I2C1
38 // SCL PB6
39 // SDA PB7
41 static void i2c_er_handler(void);
42 static void i2c_ev_handler(void);
43 static void i2cUnstick(void);
45 typedef struct i2cDevice_s {
46 I2C_TypeDef *dev;
47 GPIO_TypeDef *gpio;
48 uint16_t scl;
49 uint16_t sda;
50 uint8_t ev_irq;
51 uint8_t er_irq;
52 uint32_t peripheral;
53 } i2cDevice_t;
55 static const i2cDevice_t i2cHardwareMap[] = {
56 { I2C1, GPIOB, Pin_6, Pin_7, I2C1_EV_IRQn, I2C1_ER_IRQn, RCC_APB1Periph_I2C1 },
57 { I2C2, GPIOB, Pin_10, Pin_11, I2C2_EV_IRQn, I2C2_ER_IRQn, RCC_APB1Periph_I2C2 },
60 // Copy of peripheral address for IRQ routines
61 static I2C_TypeDef *I2Cx = NULL;
62 // Copy of device index for reinit, etc purposes
63 static I2CDevice I2Cx_index;
64 static bool i2cOverClock;
66 void i2cSetOverclock(uint8_t OverClock) {
67 i2cOverClock = (OverClock) ? true : false;
70 void I2C1_ER_IRQHandler(void)
72 i2c_er_handler();
75 void I2C1_EV_IRQHandler(void)
77 i2c_ev_handler();
80 void I2C2_ER_IRQHandler(void)
82 i2c_er_handler();
85 void I2C2_EV_IRQHandler(void)
87 i2c_ev_handler();
90 #define I2C_DEFAULT_TIMEOUT 30000
91 static volatile uint16_t i2cErrorCount = 0;
93 static volatile bool error = false;
94 static volatile bool busy;
96 static volatile uint8_t addr;
97 static volatile uint8_t reg;
98 static volatile uint8_t bytes;
99 static volatile uint8_t writing;
100 static volatile uint8_t reading;
101 static volatile uint8_t* write_p;
102 static volatile uint8_t* read_p;
104 static bool i2cHandleHardwareFailure(void)
106 i2cErrorCount++;
107 // reinit peripheral + clock out garbage
108 i2cInit(I2Cx_index);
109 return false;
112 bool i2cWriteBuffer(uint8_t addr_, uint8_t reg_, uint8_t len_, uint8_t *data)
114 uint32_t timeout = I2C_DEFAULT_TIMEOUT;
116 addr = addr_ << 1;
117 reg = reg_;
118 writing = 1;
119 reading = 0;
120 write_p = data;
121 read_p = data;
122 bytes = len_;
123 busy = 1;
124 error = false;
126 if (!I2Cx)
127 return false;
129 if (!(I2Cx->CR2 & I2C_IT_EVT)) { // if we are restarting the driver
130 if (!(I2Cx->CR1 & 0x0100)) { // ensure sending a start
131 while (I2Cx->CR1 & 0x0200 && --timeout > 0) { ; } // wait for any stop to finish sending
132 if (timeout == 0) {
133 return i2cHandleHardwareFailure();
135 I2C_GenerateSTART(I2Cx, ENABLE); // send the start for the new job
137 I2C_ITConfig(I2Cx, I2C_IT_EVT | I2C_IT_ERR, ENABLE); // allow the interrupts to fire off again
140 timeout = I2C_DEFAULT_TIMEOUT;
141 while (busy && --timeout > 0) { ; }
142 if (timeout == 0) {
143 return i2cHandleHardwareFailure();
146 return !error;
149 bool i2cWrite(uint8_t addr_, uint8_t reg_, uint8_t data)
151 return i2cWriteBuffer(addr_, reg_, 1, &data);
154 bool i2cRead(uint8_t addr_, uint8_t reg_, uint8_t len, uint8_t* buf)
156 uint32_t timeout = I2C_DEFAULT_TIMEOUT;
158 addr = addr_ << 1;
159 reg = reg_;
160 writing = 0;
161 reading = 1;
162 read_p = buf;
163 write_p = buf;
164 bytes = len;
165 busy = 1;
166 error = false;
168 if (!I2Cx)
169 return false;
171 if (!(I2Cx->CR2 & I2C_IT_EVT)) { // if we are restarting the driver
172 if (!(I2Cx->CR1 & 0x0100)) { // ensure sending a start
173 while (I2Cx->CR1 & 0x0200 && --timeout > 0) { ; } // wait for any stop to finish sending
174 if (timeout == 0)
175 return i2cHandleHardwareFailure();
176 I2C_GenerateSTART(I2Cx, ENABLE); // send the start for the new job
178 I2C_ITConfig(I2Cx, I2C_IT_EVT | I2C_IT_ERR, ENABLE); // allow the interrupts to fire off again
181 timeout = I2C_DEFAULT_TIMEOUT;
182 while (busy && --timeout > 0) { ; }
183 if (timeout == 0)
184 return i2cHandleHardwareFailure();
186 return !error;
189 static void i2c_er_handler(void)
191 // Read the I2Cx status register
192 uint32_t SR1Register = I2Cx->SR1;
194 if (SR1Register & 0x0F00) { // an error
195 error = true;
198 // If AF, BERR or ARLO, abandon the current job and commence new if there are jobs
199 if (SR1Register & 0x0700) {
200 (void)I2Cx->SR2; // read second status register to clear ADDR if it is set (note that BTF will not be set after a NACK)
201 I2C_ITConfig(I2Cx, I2C_IT_BUF, DISABLE); // disable the RXNE/TXE interrupt - prevent the ISR tailchaining onto the ER (hopefully)
202 if (!(SR1Register & 0x0200) && !(I2Cx->CR1 & 0x0200)) { // if we dont have an ARLO error, ensure sending of a stop
203 if (I2Cx->CR1 & 0x0100) { // We are currently trying to send a start, this is very bad as start, stop will hang the peripheral
204 // TODO - busy waiting in highest priority IRQ. Maybe only set flag and handle it from main loop
205 while (I2Cx->CR1 & 0x0100) { ; } // wait for any start to finish sending
206 I2C_GenerateSTOP(I2Cx, ENABLE); // send stop to finalise bus transaction
207 while (I2Cx->CR1 & 0x0200) { ; } // wait for stop to finish sending
208 i2cInit(I2Cx_index); // reset and configure the hardware
209 } else {
210 I2C_GenerateSTOP(I2Cx, ENABLE); // stop to free up the bus
211 I2C_ITConfig(I2Cx, I2C_IT_EVT | I2C_IT_ERR, DISABLE); // Disable EVT and ERR interrupts while bus inactive
215 I2Cx->SR1 &= ~0x0F00; // reset all the error bits to clear the interrupt
216 busy = 0;
219 void i2c_ev_handler(void)
221 static uint8_t subaddress_sent, final_stop; // flag to indicate if subaddess sent, flag to indicate final bus condition
222 static int8_t index; // index is signed -1 == send the subaddress
223 uint8_t SReg_1 = I2Cx->SR1; // read the status register here
225 if (SReg_1 & 0x0001) { // we just sent a start - EV5 in ref manual
226 I2Cx->CR1 &= ~0x0800; // reset the POS bit so ACK/NACK applied to the current byte
227 I2C_AcknowledgeConfig(I2Cx, ENABLE); // make sure ACK is on
228 index = 0; // reset the index
229 if (reading && (subaddress_sent || 0xFF == reg)) { // we have sent the subaddr
230 subaddress_sent = 1; // make sure this is set in case of no subaddress, so following code runs correctly
231 if (bytes == 2)
232 I2Cx->CR1 |= 0x0800; // set the POS bit so NACK applied to the final byte in the two byte read
233 I2C_Send7bitAddress(I2Cx, addr, I2C_Direction_Receiver); // send the address and set hardware mode
234 } else { // direction is Tx, or we havent sent the sub and rep start
235 I2C_Send7bitAddress(I2Cx, addr, I2C_Direction_Transmitter); // send the address and set hardware mode
236 if (reg != 0xFF) // 0xFF as subaddress means it will be ignored, in Tx or Rx mode
237 index = -1; // send a subaddress
239 } else if (SReg_1 & 0x0002) { // we just sent the address - EV6 in ref manual
240 // Read SR1,2 to clear ADDR
241 __DMB(); // memory fence to control hardware
242 if (bytes == 1 && reading && subaddress_sent) { // we are receiving 1 byte - EV6_3
243 I2C_AcknowledgeConfig(I2Cx, DISABLE); // turn off ACK
244 __DMB();
245 (void)I2Cx->SR2; // clear ADDR after ACK is turned off
246 I2C_GenerateSTOP(I2Cx, ENABLE); // program the stop
247 final_stop = 1;
248 I2C_ITConfig(I2Cx, I2C_IT_BUF, ENABLE); // allow us to have an EV7
249 } else { // EV6 and EV6_1
250 (void)I2Cx->SR2; // clear the ADDR here
251 __DMB();
252 if (bytes == 2 && reading && subaddress_sent) { // rx 2 bytes - EV6_1
253 I2C_AcknowledgeConfig(I2Cx, DISABLE); // turn off ACK
254 I2C_ITConfig(I2Cx, I2C_IT_BUF, DISABLE); // disable TXE to allow the buffer to fill
255 } else if (bytes == 3 && reading && subaddress_sent) // rx 3 bytes
256 I2C_ITConfig(I2Cx, I2C_IT_BUF, DISABLE); // make sure RXNE disabled so we get a BTF in two bytes time
257 else // receiving greater than three bytes, sending subaddress, or transmitting
258 I2C_ITConfig(I2Cx, I2C_IT_BUF, ENABLE);
260 } else if (SReg_1 & 0x004) { // Byte transfer finished - EV7_2, EV7_3 or EV8_2
261 final_stop = 1;
262 if (reading && subaddress_sent) { // EV7_2, EV7_3
263 if (bytes > 2) { // EV7_2
264 I2C_AcknowledgeConfig(I2Cx, DISABLE); // turn off ACK
265 read_p[index++] = (uint8_t)I2Cx->DR; // read data N-2
266 I2C_GenerateSTOP(I2Cx, ENABLE); // program the Stop
267 final_stop = 1; // required to fix hardware
268 read_p[index++] = (uint8_t)I2Cx->DR; // read data N - 1
269 I2C_ITConfig(I2Cx, I2C_IT_BUF, ENABLE); // enable TXE to allow the final EV7
270 } else { // EV7_3
271 if (final_stop)
272 I2C_GenerateSTOP(I2Cx, ENABLE); // program the Stop
273 else
274 I2C_GenerateSTART(I2Cx, ENABLE); // program a rep start
275 read_p[index++] = (uint8_t)I2Cx->DR; // read data N - 1
276 read_p[index++] = (uint8_t)I2Cx->DR; // read data N
277 index++; // to show job completed
279 } else { // EV8_2, which may be due to a subaddress sent or a write completion
280 if (subaddress_sent || (writing)) {
281 if (final_stop)
282 I2C_GenerateSTOP(I2Cx, ENABLE); // program the Stop
283 else
284 I2C_GenerateSTART(I2Cx, ENABLE); // program a rep start
285 index++; // to show that the job is complete
286 } else { // We need to send a subaddress
287 I2C_GenerateSTART(I2Cx, ENABLE); // program the repeated Start
288 subaddress_sent = 1; // this is set back to zero upon completion of the current task
291 // TODO - busy waiting in ISR
292 // we must wait for the start to clear, otherwise we get constant BTF
293 while (I2Cx->CR1 & 0x0100) { ; }
294 } else if (SReg_1 & 0x0040) { // Byte received - EV7
295 read_p[index++] = (uint8_t)I2Cx->DR;
296 if (bytes == (index + 3))
297 I2C_ITConfig(I2Cx, I2C_IT_BUF, DISABLE); // disable TXE to allow the buffer to flush so we can get an EV7_2
298 if (bytes == index) // We have completed a final EV7
299 index++; // to show job is complete
300 } else if (SReg_1 & 0x0080) { // Byte transmitted EV8 / EV8_1
301 if (index != -1) { // we dont have a subaddress to send
302 I2Cx->DR = write_p[index++];
303 if (bytes == index) // we have sent all the data
304 I2C_ITConfig(I2Cx, I2C_IT_BUF, DISABLE); // disable TXE to allow the buffer to flush
305 } else {
306 index++;
307 I2Cx->DR = reg; // send the subaddress
308 if (reading || !bytes) // if receiving or sending 0 bytes, flush now
309 I2C_ITConfig(I2Cx, I2C_IT_BUF, DISABLE); // disable TXE to allow the buffer to flush
312 if (index == bytes + 1) { // we have completed the current job
313 subaddress_sent = 0; // reset this here
314 if (final_stop) // If there is a final stop and no more jobs, bus is inactive, disable interrupts to prevent BTF
315 I2C_ITConfig(I2Cx, I2C_IT_EVT | I2C_IT_ERR, DISABLE); // Disable EVT and ERR interrupts while bus inactive
316 busy = 0;
320 void i2cInit(I2CDevice index)
322 NVIC_InitTypeDef nvic;
323 I2C_InitTypeDef i2c;
325 if (index > I2CDEV_MAX)
326 index = I2CDEV_MAX;
328 // Turn on peripheral clock, save device and index
329 I2Cx = i2cHardwareMap[index].dev;
330 I2Cx_index = index;
331 RCC_APB1PeriphClockCmd(i2cHardwareMap[index].peripheral, ENABLE);
333 // diable I2C interrrupts first to avoid ER handler triggering
334 I2C_ITConfig(I2Cx, I2C_IT_EVT | I2C_IT_ERR, DISABLE);
336 // clock out stuff to make sure slaves arent stuck
337 // This will also configure GPIO as AF_OD at the end
338 i2cUnstick();
340 // Init I2C peripheral
341 I2C_DeInit(I2Cx);
342 I2C_StructInit(&i2c);
344 I2C_ITConfig(I2Cx, I2C_IT_EVT | I2C_IT_ERR, DISABLE); // Disable EVT and ERR interrupts - they are enabled by the first request
345 i2c.I2C_Mode = I2C_Mode_I2C;
346 i2c.I2C_DutyCycle = I2C_DutyCycle_2;
347 i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
349 if (i2cOverClock) {
350 i2c.I2C_ClockSpeed = 800000; // 800khz Maximum speed tested on various boards without issues
351 } else {
352 i2c.I2C_ClockSpeed = 400000; // 400khz Operation according specs
355 I2C_Cmd(I2Cx, ENABLE);
356 I2C_Init(I2Cx, &i2c);
358 // I2C ER Interrupt
359 nvic.NVIC_IRQChannel = i2cHardwareMap[index].er_irq;
360 nvic.NVIC_IRQChannelPreemptionPriority = NVIC_PRIORITY_BASE(NVIC_PRIO_I2C_ER);
361 nvic.NVIC_IRQChannelSubPriority = NVIC_PRIORITY_SUB(NVIC_PRIO_I2C_ER);
362 nvic.NVIC_IRQChannelCmd = ENABLE;
363 NVIC_Init(&nvic);
365 // I2C EV Interrupt
366 nvic.NVIC_IRQChannel = i2cHardwareMap[index].ev_irq;
367 nvic.NVIC_IRQChannelPreemptionPriority = NVIC_PRIORITY_BASE(NVIC_PRIO_I2C_EV);
368 nvic.NVIC_IRQChannelSubPriority = NVIC_PRIORITY_SUB(NVIC_PRIO_I2C_EV);
369 NVIC_Init(&nvic);
372 uint16_t i2cGetErrorCounter(void)
374 return i2cErrorCount;
377 static void i2cUnstick(void)
379 GPIO_TypeDef *gpio;
380 gpio_config_t cfg;
381 uint16_t scl, sda;
382 int i;
384 // prepare pins
385 gpio = i2cHardwareMap[I2Cx_index].gpio;
386 scl = i2cHardwareMap[I2Cx_index].scl;
387 sda = i2cHardwareMap[I2Cx_index].sda;
389 digitalHi(gpio, scl | sda);
391 cfg.pin = scl | sda;
392 cfg.speed = Speed_2MHz;
393 cfg.mode = Mode_Out_OD;
394 gpioInit(gpio, &cfg);
396 for (i = 0; i < 8; i++) {
397 // Wait for any clock stretching to finish
398 while (!digitalIn(gpio, scl))
399 delayMicroseconds(10);
401 // Pull low
402 digitalLo(gpio, scl); // Set bus low
403 delayMicroseconds(10);
404 // Release high again
405 digitalHi(gpio, scl); // Set bus high
406 delayMicroseconds(10);
409 // Generate a start then stop condition
410 // SCL PB10
411 // SDA PB11
412 digitalLo(gpio, sda); // Set bus data low
413 delayMicroseconds(10);
414 digitalLo(gpio, scl); // Set bus scl low
415 delayMicroseconds(10);
416 digitalHi(gpio, scl); // Set bus scl high
417 delayMicroseconds(10);
418 digitalHi(gpio, sda); // Set bus sda high
420 // Init pins
421 cfg.pin = scl | sda;
422 cfg.speed = Speed_2MHz;
423 cfg.mode = Mode_AF_OD;
424 gpioInit(gpio, &cfg);
427 #endif