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/>.
24 #include "build_config.h"
41 static void i2c_er_handler(void);
42 static void i2c_ev_handler(void);
43 static void i2cUnstick(void);
45 typedef struct i2cDevice_s
{
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)
75 void I2C1_EV_IRQHandler(void)
80 void I2C2_ER_IRQHandler(void)
85 void I2C2_EV_IRQHandler(void)
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)
107 // reinit peripheral + clock out garbage
112 bool i2cWriteBuffer(uint8_t addr_
, uint8_t reg_
, uint8_t len_
, uint8_t *data
)
114 uint32_t timeout
= I2C_DEFAULT_TIMEOUT
;
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
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) { ; }
143 return i2cHandleHardwareFailure();
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
;
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
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) { ; }
184 return i2cHandleHardwareFailure();
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
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
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
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
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
245 (void)I2Cx
->SR2
; // clear ADDR after ACK is turned off
246 I2C_GenerateSTOP(I2Cx
, ENABLE
); // program the stop
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
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
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
272 I2C_GenerateSTOP(I2Cx
, ENABLE
); // program the Stop
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
)) {
282 I2C_GenerateSTOP(I2Cx
, ENABLE
); // program the Stop
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
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
320 void i2cInit(I2CDevice index
)
322 NVIC_InitTypeDef nvic
;
325 if (index
> I2CDEV_MAX
)
328 // Turn on peripheral clock, save device and index
329 I2Cx
= i2cHardwareMap
[index
].dev
;
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
340 // Init I2C peripheral
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
;
350 i2c
.I2C_ClockSpeed
= 800000; // 800khz Maximum speed tested on various boards without issues
352 i2c
.I2C_ClockSpeed
= 400000; // 400khz Operation according specs
355 I2C_Cmd(I2Cx
, ENABLE
);
356 I2C_Init(I2Cx
, &i2c
);
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
;
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
);
372 uint16_t i2cGetErrorCounter(void)
374 return i2cErrorCount
;
377 static void i2cUnstick(void)
385 gpio
= i2cHardwareMap
[I2Cx_index
].gpio
;
386 scl
= i2cHardwareMap
[I2Cx_index
].scl
;
387 sda
= i2cHardwareMap
[I2Cx_index
].sda
;
389 digitalHi(gpio
, 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);
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
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
422 cfg
.speed
= Speed_2MHz
;
423 cfg
.mode
= Mode_AF_OD
;
424 gpioInit(gpio
, &cfg
);