Skyward boardcore
Loading...
Searching...
No Matches
I2CDriver-f4.cpp
Go to the documentation of this file.
1/* Copyright (c) 2022 Skyward Experimental Rocketry
2 * Author: Emilio Corigliano
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#ifdef _ARCH_CORTEXM4_STM32F4
24
25#include <assert.h>
26#include <kernel/scheduler/scheduler.h>
27#include <utils/ClockUtils.h>
28#include <utils/Debug.h>
29
30#include "I2CDriver.h"
31
32namespace I2CConsts
33{
34static Boardcore::I2CDriver* ports[N_I2C_PORTS] =
35 {};
37static const int MAX_N_POLLING =
38 2000;
39static const int N_SCL_BITBANG =
40 16;
41static const uint8_t I2C_PIN_ALTERNATE_FUNCTION =
42 4;
43static uint8_t f;
44} // namespace I2CConsts
45
46#ifdef I2C1
50void __attribute__((naked)) I2C1_EV_IRQHandler()
51{
52 saveContext();
53 asm volatile("bl _Z15I2C1HandlerImplv");
54 restoreContext();
55}
56
60void __attribute__((used)) I2C1HandlerImpl()
61{
62 auto* port = I2CConsts::ports[0];
63 if (port)
64 port->IRQhandleInterrupt();
65}
66
70void __attribute__((naked)) I2C1_ER_IRQHandler()
71{
72 saveContext();
73 asm volatile("bl _Z18I2C1errHandlerImplv");
74 restoreContext();
75}
76
80void __attribute__((used)) I2C1errHandlerImpl()
81{
82 auto* port = I2CConsts::ports[0];
83 if (port)
84 port->IRQhandleErrInterrupt();
85}
86#endif
87
88#ifdef I2C2
92void __attribute__((naked)) I2C2_EV_IRQHandler()
93{
94 saveContext();
95 asm volatile("bl _Z15I2C2HandlerImplv");
96 restoreContext();
97}
98
102void __attribute__((used)) I2C2HandlerImpl()
103{
104 auto* port = I2CConsts::ports[1];
105 if (port)
106 port->IRQhandleInterrupt();
107}
108
112void __attribute__((naked)) I2C2_ER_IRQHandler()
113{
114 saveContext();
115 asm volatile("bl _Z18I2C2errHandlerImplv");
116 restoreContext();
117}
118
122void __attribute__((used)) I2C2errHandlerImpl()
123{
124 auto* port = I2CConsts::ports[1];
125 if (port)
126 port->IRQhandleErrInterrupt();
127}
128#endif
129
130#ifdef I2C3
134void __attribute__((naked)) I2C3_EV_IRQHandler()
135{
136 saveContext();
137 asm volatile("bl _Z15I2C3HandlerImplv");
138 restoreContext();
139}
140
144void __attribute__((used)) I2C3HandlerImpl()
145{
146 auto* port = I2CConsts::ports[2];
147 if (port)
148 port->IRQhandleInterrupt();
149}
150
154void __attribute__((naked)) I2C3_ER_IRQHandler()
155{
156 saveContext();
157 asm volatile("bl _Z18I2C3errHandlerImplv");
158 restoreContext();
159}
160
164void __attribute__((used)) I2C3errHandlerImpl()
165{
166 auto* port = I2CConsts::ports[2];
167 if (port)
168 port->IRQhandleErrInterrupt();
169}
170#endif
171
172#ifdef I2C4
176void __attribute__((naked)) I2C4_EV_IRQHandler()
177{
178 saveContext();
179 asm volatile("bl _Z15I2C4HandlerImplv");
180 restoreContext();
181}
182
186void __attribute__((used)) I2C4HandlerImpl()
187{
188 auto* port = I2CConsts::ports[3];
189 if (port)
190 port->IRQhandleInterrupt();
191}
192
196void __attribute__((naked)) I2C4_ER_IRQHandler()
197{
198 saveContext();
199 asm volatile("bl _Z18I2C4errHandlerImplv");
200 restoreContext();
201}
202
206void __attribute__((used)) I2C4errHandlerImpl()
207{
208 auto* port = I2CConsts::ports[3];
209 if (port)
210 port->IRQhandleErrInterrupt();
211}
212#endif
213
214namespace Boardcore
215{
216
217I2CDriver::I2CDriver(I2C_TypeDef* i2c, miosix::GpioPin scl, miosix::GpioPin sda)
218 : i2c(i2c), scl(scl), sda(sda), transaction()
219{
220 // Setting the id and irqn of the right i2c peripheral
221 switch (reinterpret_cast<uint32_t>(i2c))
222 {
223#ifdef I2C1
224 case I2C1_BASE:
225 this->id = 1;
226 irqnEv = I2C1_EV_IRQn;
227 irqnErr = I2C1_ER_IRQn;
228 break;
229#endif
230#ifdef I2C2
231 case I2C2_BASE:
232 this->id = 2;
233 irqnEv = I2C2_EV_IRQn;
234 irqnErr = I2C2_ER_IRQn;
235 break;
236#endif
237#ifdef I2C3
238 case I2C3_BASE:
239 this->id = 3;
240 irqnEv = I2C3_EV_IRQn;
241 irqnErr = I2C3_ER_IRQn;
242 break;
243#endif
244#ifdef I2C4
245 case I2C4_BASE:
246 this->id = 4;
247 irqnEv = I2C4_EV_IRQn;
248 irqnErr = I2C4_ER_IRQn;
249 break;
250#endif
251 default:
252 // Checking that the peripheral is present in this architecture
253 D(assert(false &&
254 "I2C peripheral not present in this architecture"));
255 break;
256 }
257
258 {
259 miosix::FastInterruptDisableLock dLock;
260
261 // Initializing the alternate function and mode of the pins so we won't
262 // forget the open-drain mode, avoiding eventual short-circuits between
263 // master and slaves when they both drive the same bus on two different
264 // logical values.
265 scl.alternateFunction(I2CConsts::I2C_PIN_ALTERNATE_FUNCTION);
266 sda.alternateFunction(I2CConsts::I2C_PIN_ALTERNATE_FUNCTION);
267 scl.mode(miosix::Mode::ALTERNATE_OD_PULL_UP);
268 sda.mode(miosix::Mode::ALTERNATE_OD_PULL_UP);
269 }
270
271 // Checking that this particular I2C port hasn't been already instantiated
272 D(assert(id > 0));
273 D(assert(I2CConsts::ports[id - 1] == nullptr));
274
275 // Enabling the peripheral's clock
276 ClockUtils::enablePeripheralClock(i2c);
277
278 init();
279
280 // Add to the array of i2c peripherals so that the interrupts can see it
281 I2CConsts::ports[id - 1] = this;
282
283 // Enabling the interrupts (Ev and Err) in the NVIC
284 NVIC_SetPriority(irqnEv, 15);
285 NVIC_ClearPendingIRQ(irqnEv);
286 NVIC_EnableIRQ(irqnEv);
287 NVIC_SetPriority(irqnErr, 15);
288 NVIC_ClearPendingIRQ(irqnErr);
289 NVIC_EnableIRQ(irqnErr);
290}
291
292I2CDriver::~I2CDriver()
293{
294 // Removing the relative i2c port from the array
295 I2CConsts::ports[id - 1] = nullptr;
296
297 // Disabling the interrupts (Ev and Err) in the NVIC
298 NVIC_DisableIRQ(irqnEv);
299 NVIC_DisableIRQ(irqnErr);
300
301 // Disabling the peripheral
302 i2c->CR1 &= ~I2C_CR1_PE;
303
304 // Disabling the peripheral on the bus
305 ClockUtils::disablePeripheralClock(i2c);
306}
307
308void I2CDriver::init()
309{
310 // Resetting the I2C peripheral before setting the registers
311 i2c->CR1 = I2C_CR1_SWRST;
312 i2c->CR1 = 0; // cppcheck-suppress redundantAssignment
313
314 // Retrieving the frequency of the APB relative to the I2C peripheral
315 // [MHz] (I2C peripherals are always connected to APB1, Low speed bus)
316 I2CConsts::f =
317 ClockUtils::getAPBPeripheralsClock(ClockUtils::APB::APB1) / 1000000;
318
319 // Frequency higher than 50MHz not allowed by I2C peripheral
320 D(assert(I2CConsts::f <= 50));
321
322 // Programming the input clock in order to generate correct timings +
323 // enabling generation of all interrupts
324 i2c->CR2 = (I2CConsts::f & I2C_CR2_FREQ) | // setting FREQ bits
325 I2C_CR2_ITBUFEN; // enabling interrupts for rx/tx byte
326}
327
328void I2CDriver::setupPeripheral(const I2CSlaveConfig& slaveConfig)
329{
330 // Frequency < 2MHz in standard mode or < 4MHz in fast mode not allowed by
331 // the peripheral
332 D(assert((slaveConfig.speed == STANDARD && I2CConsts::f >= 2) ||
333 (slaveConfig.speed == FAST && I2CConsts::f >= 4)));
334
335 // Disabling the I2C peripheral before setting the registers
336 i2c->CR1 &= ~I2C_CR1_PE;
337
338 // Configuring the Clock Control Register
339 if (slaveConfig.speed == Speed::STANDARD)
340 {
341 // If STANDARD mode, this is the divider to the peripheral clock to
342 // reach the wanted frequency. It's divided by 2 because in reality
343 // it uses this value to calculate the time that the clock needs to
344 // be in the "set" state. [* 1000 KHz / (100 KHz * 2) = *5]
345 i2c->CCR =
346 I2CConsts::f * 5; // Setting the CCR bits (implicit Standard mode)
347 }
348 else if (slaveConfig.speed == Speed::FAST)
349 {
350 // [WARNING] Hardcoded to use DUTY = 0
351 i2c->CCR = I2C_CCR_FS | // Selecting Fast mode
352 I2CConsts::f * 5 / 6; // Setting the CCR bits
353
354 // For DUTY = 1
355 // i2c->CCR = I2C_CCR_FS | // Selecting Fast mode
356 // I2C_CCR_DUTY | // Selecting duty cycle of 9 - 16
357 // f * 2 / 5; // Setting the CCR bits (f * 10 / 25)
358 }
359 else
360 {
361 D(assert(false && "speed not supported!"));
362 }
363
364 // Configuring the TRISE
365 i2c->TRISE = (I2CConsts::f & I2C_CR2_FREQ) + 1;
366
367 // Setting the addressing mode
368 i2c->OAR1 = (slaveConfig.addressing << 15);
369
370 // Finally enabling the peripheral
371 i2c->CR1 |= I2C_CR1_PE;
372}
373
374bool I2CDriver::read(const I2CSlaveConfig& slaveConfig, void* buffer,
375 const size_t& nBytes)
376{
377 // Setting up the read transaction
378 transaction.operation = Operation::READ;
379 transaction.buffWrite = nullptr;
380 transaction.buffRead = static_cast<uint8_t*>(buffer);
381 transaction.nBytes = nBytes;
382 transaction.nBytesDone = 0;
383 transaction.generateStop = true;
384
385 // Disabling the generation of the ACK if reading only 1 byte, otherwise
386 // enable it
387 i2c->CR1 =
388 ((nBytes <= 1) ? (i2c->CR1 & ~I2C_CR1_ACK) : (i2c->CR1 | I2C_CR1_ACK));
389
390 // Sending doOperation when the channel isn't busy
391 return doOperation(slaveConfig);
392};
393
394bool I2CDriver::write(const I2CSlaveConfig& slaveConfig, const void* buffer,
395 const size_t& nBytes, bool generateStop)
396{
397 // Setting up the write transaction
398 transaction.operation = Operation::WRITE;
399 transaction.buffWrite = static_cast<const uint8_t*>(buffer);
400 transaction.buffRead = nullptr;
401 transaction.nBytes = nBytes;
402 transaction.nBytesDone = 0;
403 transaction.generateStop = generateStop;
404
405 // Sending doOperation when the channel isn't busy
406 return doOperation(slaveConfig);
407};
408
409bool I2CDriver::doOperation(const I2CSlaveConfig& slaveConfig)
410{
411 // Not yet supported
412 D(assert(slaveConfig.addressing == Addressing::BIT7 &&
413 "Only 7-bit addressing supported!"));
414
415 // If already detected a locked state return directly without loosing time
416 if (lastError & Errors::BUS_LOCKED)
417 {
418 reStarting = false;
419 return false;
420 }
421
422 // If starting a new transaction (so STOP bit sent in previous transaction),
423 // wait for the bus to be clear
424 if (!reStarting)
425 {
426 // Waiting for the bus to be clear
427 uint32_t i{0};
428 for (; (i < I2CConsts::MAX_N_POLLING) && (i2c->SR2 & I2C_SR2_BUSY); ++i)
429 ;
430
431 // Locked state detected after N polling cycles
432 if (i == I2CConsts::MAX_N_POLLING)
433 {
434 lastError = Errors::BUS_LOCKED;
435 LOG_ERR(logger, fmt::format("I2C{} bus locked state detected", id));
436 return false;
437 }
438
439 // Setting up the peripheral when the bus is clear in order to
440 // communicate in the mode wanted by the slave device
441 setupPeripheral(slaveConfig);
442 }
443
444 reStarting = false;
445
446 // Starting the transaction with the START bit
447 // From the wait till the end of transaction it will all be executed in the
448 // event ISR
449 {
450 miosix::PauseKernelLock dLock;
451 uint32_t i{0};
452
453 // Sending the start condition
454 // We are waiting on the Start Bit using polling because using
455 // interrupts would have lead to a messy and less reliable code. The
456 // only downside regards the usage of polling instead of interrupts; the
457 // maximum number of cycles used in the tests for waiting for a start
458 // condition were more or less 950. Anyway this is preferred to the risk
459 // of having a deadlock.
460 i2c->CR1 |= I2C_CR1_START;
461
462 // Waiting for START condition to be sent
463 for (; (i < I2CConsts::MAX_N_POLLING) &&
464 (!(i2c->SR1 & I2C_SR1_SB) || !(i2c->SR2 & I2C_SR2_MSL));
465 ++i)
466 ;
467
468 // START condition not sent after N polling cycles
469 if (i == I2CConsts::MAX_N_POLLING)
470 {
471 lastError |= Errors::SB_NOT_SENT;
472 LOG_ERR(logger,
473 fmt::format("I2C{} bus didn't send the start bit", id));
474 return false;
475 }
476 }
477
478 transaction.nBytesDone = 0;
479
480 // Sending slave address
481 {
482 miosix::FastInterruptDisableLock dLock;
483
484 // Setting the LSB if we want to enter receiver mode
485 i2c->DR = ((slaveConfig.slaveAddress << 1) | transaction.operation);
486
487 // Making the thread wait for the operation completion. The next steps
488 // will be performed in the ISR while the thread stays in waiting state.
489 // The only way the thread will be waken up are the completion of the
490 // operation or an error during the transaction.
491 return IRQwaitForOperationCompletion(dLock);
492 }
493}
494
495void I2CDriver::flushBus()
496{
497 // If there isn't any locked state return immediately
498 if (!(lastError & Errors::BUS_LOCKED))
499 return;
500
501 // Set the period of the bit-banged clock (Default to standard mode)
502 uint8_t toggleDelay = 5;
503
504 {
505 miosix::FastInterruptDisableLock dLock;
506
507 // Recovery from the locked state due to a stuck Slave.
508 // We bit-bang 16 clocks on the scl line in order to restore pending
509 // packets of the slaves.
510 scl.mode(miosix::Mode::OPEN_DRAIN_PULL_UP);
511 }
512
513 for (size_t c = 0; c < I2CConsts::N_SCL_BITBANG; c++)
514 {
515 scl.low();
516 miosix::delayUs(toggleDelay);
517 scl.high();
518 miosix::delayUs(toggleDelay);
519 }
520
521 {
522 miosix::FastInterruptDisableLock dLock;
523
524 // We set again the scl pin to the correct Alternate function
525 scl.mode(miosix::Mode::ALTERNATE_OD_PULL_UP);
526 scl.alternateFunction(I2CConsts::I2C_PIN_ALTERNATE_FUNCTION);
527 }
528
529 // Re-initializing the peripheral in order to avoid inconsistent state
530 init();
531
532 // Assuming the locked state is solved. If it is not the case, only when
533 // it will be the case it will be detected again
534 lastError = Errors::NO_ERROR;
535
536 LOG_WARN(logger, fmt::format("I2C{} Bus flushed", id));
537}
538
539uint16_t I2CDriver::getLastError() { return lastError; }
540
541inline bool I2CDriver::IRQwaitForOperationCompletion(
542 miosix::FastInterruptDisableLock& dLock)
543{
544 // Saving the current thread in order to be waken up by interrupts
545 waiting = miosix::Thread::IRQgetCurrentThread();
546
547 // flag thread as waiting, enable interrupts in I2C peripheral and yield
548 // till an interrupt doesn't wake up the thread
549 while (waiting)
550 {
551 waiting->IRQwait();
552 i2c->CR2 |= I2C_CR2_ITEVTEN | I2C_CR2_ITERREN;
553 miosix::FastInterruptEnableLock eLock(dLock);
554 waiting->yield();
555 }
556
557 // If error occurred, parse it to notify the error(s); otherwise reset
558 // lastError parameter
559 if (error)
560 {
561 lastError |= ((error & I2C_SR1_OVR) ? Errors::OVR : 0) |
562 ((error & I2C_SR1_BERR) ? Errors::BERR : 0) |
563 ((error & I2C_SR1_ARLO) ? Errors::ARLO : 0) |
564 ((error & I2C_SR1_AF) ? Errors::AF : 0);
565 error = 0;
566 return false;
567 }
568 else
569 {
570 lastError = Errors::NO_ERROR;
571 }
572
573 return true;
574}
575
576inline void I2CDriver::IRQwakeUpWaitingThread()
577{
578 // Disabling the regeneration of the interrupt; if we don't disable the
579 // interrupts we will enter in an infinite loop of interrupts
580 i2c->CR2 &= ~(I2C_CR2_ITEVTEN | I2C_CR2_ITERREN);
581
582 // invalidating the buffer pointers (avoiding to keep a pointer to an old
583 // memory location)
584 transaction.buffRead = nullptr;
585 transaction.buffWrite = nullptr;
586
587 if (waiting)
588 {
589 waiting->IRQwakeup();
590
591 if (waiting->IRQgetPriority() >
592 miosix::Thread::IRQgetCurrentThread()->IRQgetPriority())
593 {
594 miosix::Scheduler::IRQfindNextThread();
595 }
596
597 waiting = nullptr;
598 }
599}
600
601void I2CDriver::IRQhandleInterrupt()
602{
603 // Address sending
604 if ((i2c->SR1 & I2C_SR1_ADDR))
605 {
606 // Clearing ADDR flag
607 if (!(i2c->SR2 & I2C_SR2_BUSY) || // Channel should be busy
608 !(i2c->SR2 & I2C_SR2_MSL) || // Should be in Master mode
609 !((i2c->SR2 & I2C_SR2_TRA) != transaction.operation))
610 {
611 // "reserved" bit in the SR1 register, so we don't collide with
612 // other fields
613 error = 1 << 13;
614 lastError |= ADDR_ERROR;
615 IRQwakeUpWaitingThread();
616 return;
617 }
618 }
619
620 // Performing the read/write
621 if (transaction.operation == Operation::READ)
622 {
623 // READ
624 if (i2c->SR1 & (I2C_SR1_BTF | I2C_SR1_RXNE))
625 {
626 // Clearing the ACK flag in order to send a NACK on the last byte
627 // that will be read
628 if (transaction.nBytesDone >= transaction.nBytes - 2)
629 i2c->CR1 &= ~I2C_CR1_ACK;
630
631 if (transaction.nBytesDone < transaction.nBytes)
632 {
633 transaction.buffRead[transaction.nBytesDone] = i2c->DR;
634 transaction.nBytesDone++;
635 }
636 }
637 }
638 else
639 {
640 // WRITE
641 if (i2c->SR1 & (I2C_SR1_BTF | I2C_SR1_TXE))
642 {
643 if (transaction.nBytesDone < transaction.nBytes)
644 {
645 i2c->DR = transaction.buffWrite[transaction.nBytesDone];
646 transaction.nBytesDone++;
647 return;
648 }
649 }
650 }
651
652 // Sending STOP condition and wake up thread
653 if (transaction.nBytesDone >= transaction.nBytes)
654 {
655 // If we are on the last byte, generate the stop condition if we have to
656 // end the communication
657 if (transaction.generateStop)
658 {
659 i2c->CR1 |= I2C_CR1_STOP;
660 reStarting = false;
661 }
662 else
663 {
664 reStarting = true;
665 }
666
667 // waking up the waiting thread
668 IRQwakeUpWaitingThread();
669 }
670}
671
672void I2CDriver::IRQhandleErrInterrupt()
673{
674 error = i2c->SR1;
675
676 // Clearing all the errors in the register
677 i2c->SR1 = 0;
678
679 // In case of arbitration lost, the hardware releases automatically the
680 // lines. Do not send STOP condition. In the other cases, the software must
681 // issue the STOP condition.
682 if (!(error & I2C_SR1_ARLO))
683 i2c->CR1 |= I2C_CR1_STOP;
684
685 // Waking up the waiting thread
686 IRQwakeUpWaitingThread();
687}
688
689} // namespace Boardcore
690
691#endif // _ARCH_CORTEXM4_STM32F4
void __attribute__((naked)) CAN1_RX0_IRQHandler()
#define D(x)
Definition Debug.h:57
#define LOG_WARN(logger,...)
#define LOG_ERR(logger,...)
Boardcore::USART * ports[N_USART_PORTS]
< Pointer to serial port classes to let interrupts access the classes
Definition USART.cpp:39
Low level driver for I2C peripherals.
Definition I2CDriver.h:57
I2CDriver(I2C_TypeDef *i2c, miosix::GpioPin scl, miosix::GpioPin sda)
Constructor for the I2C low-level driver.
This file includes all the types the logdecoder script will decode.