LCOV - code coverage report
Current view: top level - i2c/src - stm32f4_i2c.c (source / functions) Hit Total Coverage
Test: filtered.info Lines: 181 187 96.8 %
Date: 2025-12-18 00:00:35 Functions: 11 11 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /**
       2             :  * @file stm32f4_i2c.c
       3             :  * @brief STM32F4 I2C HAL implementation
       4             :  *
       5             :  * Copyright (c) 2025 Cory McKiel.
       6             :  * Licensed under the MIT License. See LICENSE file in the project root.
       7             :  */
       8             : 
       9             : #ifdef DESKTOP_BUILD
      10             : #include "registers.h"
      11             : #include "nvic.h"
      12             : #else
      13             : #include "stm32f4xx.h"
      14             : #endif
      15             : 
      16             : #include "i2c.h"
      17             : #include "i2c_transaction_queue.h"
      18             : #include "stm32f4_hal.h"
      19             : 
      20             : #include <string.h>
      21             : #include <stdbool.h>
      22             : 
      23             : #define SYS_FREQ_MHZ 16
      24             : #define I2C_DIRECTION_WRITE 0
      25             : #define I2C_DIRECTION_READ  1
      26             : 
      27             : static void configure_gpio();
      28             : static void configure_peripheral();
      29             : static void configure_interrupts();
      30             : static bool load_new_transaction();
      31             : static bool current_transaction_is_valid();
      32             : 
      33             : /* Private variables */
      34             : static hal_i2c_txn_t   *current_i2c_transaction = NULL;
      35             : 
      36             : /* ISR variables */
      37             : static volatile hal_i2c_txn_t _current_i2c_transaction;
      38             : static volatile size_t        _tx_position = 0;
      39             : static volatile size_t        _rx_position = 0;
      40             : static volatile bool          _tx_last_byte_written = false;
      41             : static volatile bool          _rx_last_byte_read = false;
      42             : static volatile bool          _tx_in_progress = false;
      43             : static volatile bool          _rx_in_progress = false;
      44             : static volatile bool          _error_occurred = false;
      45             : 
      46             : #define _SET_ERROR_FLAG_AND_ABORT_TRANSACTION() \
      47             : _error_occurred = true; \
      48             : I2C1->CR2 &= ~I2C_CR2_ITBUFEN; \
      49             : I2C1->CR1 |= I2C_CR1_STOP; \
      50             : _tx_in_progress = false; \
      51             : _rx_in_progress = false;
      52             : 
      53          35 : void I2C1_EV_IRQHandler(void)
      54             : {
      55             :     // ************** START Phase **************
      56             :     // Start condition has been generated on the line.
      57             :     // Start bit has been set. It must be cleared and the
      58             :     // device address written to DR.
      59             :     // *****************************************
      60          35 :     if (I2C1->SR1 & I2C_SR1_SB)
      61             :     {
      62             :         // Reading SR1 clears SB.
      63           9 :         (void)I2C1->SR1;
      64             : 
      65             :         // If we're transmitting data, append the WRITE bit to address. Otherwise,
      66             :         // append the READ bit.
      67           9 :         if (_tx_in_progress && !_rx_in_progress)
      68             :         {
      69           4 :             I2C1->DR = (_current_i2c_transaction.target_addr << 1) | I2C_DIRECTION_WRITE;
      70             :         }
      71           5 :         else if (_rx_in_progress && !_tx_in_progress)
      72             :         {
      73           5 :             I2C1->DR = (_current_i2c_transaction.target_addr << 1) | I2C_DIRECTION_READ;
      74             :         }
      75             :         else
      76             :         {
      77             :             // Error with mutual exclusion of _tx_in_progress and _rx_in_progress.
      78             :             // Set error flag and bail.
      79           0 :             _SET_ERROR_FLAG_AND_ABORT_TRANSACTION();
      80             :         }
      81             :     }
      82             : 
      83             :     // ************** ADDRESS Phase **************
      84             :     // The target address has been sent on the line and
      85             :     // the target has acknowledged the address. Need to
      86             :     // set up the rest of the transaction and clear the
      87             :     // ADDR bit.
      88             :     // *****************************************
      89          35 :     if (I2C1->SR1 & I2C_SR1_ADDR)
      90             :     {
      91             :         // Set up ACK hardware base on reception size.
      92           8 :         if (_rx_in_progress)
      93             :         {
      94           5 :             if (_current_i2c_transaction.expected_bytes_to_rx == 1)
      95             :             {
      96             :                 // Reset ACK bit so that NACK is sent on the next byte reception.
      97           1 :                 I2C1->CR1 &= ~I2C_CR1_ACK;
      98             :             }
      99           4 :             else if (_current_i2c_transaction.expected_bytes_to_rx == 2)
     100             :             {
     101             :                 // Reset ACK bit.
     102           2 :                 I2C1->CR1 &= ~I2C_CR1_ACK;
     103             :                 // Only set POS for 2-byte reception.
     104             :                 // ACK bit controls the NACK of the next byte which is received in the shift register.
     105             :                 // Therefore, the first byte will be ACK'd and the second NACK'd automatically.
     106           2 :                 I2C1->CR1 |= I2C_CR1_POS;
     107             :             }
     108           2 :             else if (_current_i2c_transaction.expected_bytes_to_rx > 2)
     109             :             {
     110             :                 // Set ACK bit to acknowledge received bytes until further notice.
     111           2 :                 I2C1->CR1 |= I2C_CR1_ACK;
     112             :             }
     113             :             else
     114             :             {
     115             :                 // In this scenario, an error occurred. Expected bytes to receive should be one or
     116             :                 // greater if an rx is in progress.
     117             :                 // Set STOP and reset ACK.
     118           0 :                 I2C1->CR1 |= I2C_CR1_STOP;
     119           0 :                 I2C1->CR1 &= ~I2C_CR1_ACK;
     120           0 :                 _rx_in_progress = false;
     121           0 :                 _tx_in_progress = false;
     122           0 :                 _error_occurred = true;
     123             :             }
     124             :         }
     125             : 
     126             :         // Reading SR1 followed by SR1 clears ADDR.
     127             :         // SCL stretched low until this cleared.
     128           8 :         (void)I2C1->SR1;
     129           8 :         (void)I2C1->SR2;
     130             : 
     131             :         // Setup STOP condition for single byte rx.
     132           8 :         if (_rx_in_progress && _current_i2c_transaction.expected_bytes_to_rx == 1)
     133             :         {
     134             :             // If ADDR was cleared by reading SR1 and SR2, then the clock is no longer stretched low and
     135             :             // the reception of the single byte should be happening right now as we process this instruction.
     136             :             // Stop bit needs to be set while byte is still in flight so hardware can generate STOP on time.
     137           1 :             I2C1->CR1 |= I2C_CR1_STOP;
     138             :             // BTF will never be set for a single byte, in which case we must enable RxNE interrupt to
     139             :             // receive our byte.
     140           1 :             I2C1->CR2 |= I2C_CR2_ITBUFEN;
     141             :         }
     142             : 
     143           8 :         if (_tx_in_progress)
     144             :         {
     145             :             // Enable TxE interrupts for the transmit phase.
     146           3 :             I2C1->CR2 |= I2C_CR2_ITBUFEN;
     147             :         }
     148             :     }
     149             : 
     150             :     // ************** DATA Phase **************
     151             :     // The connection has been established and the
     152             :     // hardware configured for either the tx or rx
     153             :     // phase. Need to monitor BTF, RxNE, and TxE.
     154             :     // *****************************************
     155          35 :     if (I2C1->SR1 & I2C_SR1_BTF)
     156             :     {
     157           9 :         if (_rx_in_progress)
     158             :         {
     159           7 :             if (_current_i2c_transaction.expected_bytes_to_rx == 2)
     160             :             {
     161             :                 // For the case of 2-byte reception and BTF set, byte 1 is
     162             :                 // in the DR and byte 2 is in the shift register and SCL is stretched
     163             :                 // low. Set the STOP bit and then read the two bytes.
     164             :                 // Reset POS for 2-byte read.
     165           2 :                 I2C1->CR1 |= I2C_CR1_STOP;
     166           2 :                 I2C1->CR1 &= ~I2C_CR1_POS;
     167           2 :                 _current_i2c_transaction.rx_data[_rx_position] = I2C1->DR;
     168           2 :                 _rx_position++;
     169             :                 // Wait for the last byte to be read in the RxNE interrupt
     170             :                 // to give hardware time to move it from shift register.
     171           2 :                 I2C1->CR2 |= I2C_CR2_ITBUFEN;
     172             :             }
     173           5 :             else if (_current_i2c_transaction.expected_bytes_to_rx > 2)
     174             :             {
     175             :                 // Count starting from 1 instead of zero indexed array.
     176           5 :                 size_t byte_number = _rx_position + 1;
     177             :                 // Assuming rx bytes numbered 1, 2, ..., N
     178             :                 // If byte N-2 is in the DR, then byte N-1 is in the shift register since BTF
     179             :                 // bit is set. Target is waiting to send byte N while SCL is stretch low by our micro.
     180           5 :                 if (byte_number == (_current_i2c_transaction.expected_bytes_to_rx - 2))
     181             :                 {
     182             :                     // Reset ACK bit before byte N is sent so the hardware can NACK in time.
     183           2 :                     I2C1->CR1 &= ~I2C_CR1_ACK;
     184             :                     // Reading the DR clears BTF and unstretches the clock. Byte N should be on
     185             :                     // its way.
     186           2 :                     _current_i2c_transaction.rx_data[_rx_position] = I2C1->DR;
     187           2 :                     _rx_position++;
     188             :                     // Arm the flag. Next BTF will mean byte N-1 in DR and N in shift register.
     189           2 :                     _rx_last_byte_read = true;
     190             :                 }
     191           3 :                 else if (_rx_last_byte_read)
     192             :                 {
     193             :                     // Byte N-1 in DR and byte N in shift register. SCL stretched low.
     194             :                     // Time to set STOP and read last two bytes.
     195           2 :                     I2C1->CR1 |= I2C_CR1_STOP;
     196             : 
     197             :                     // Read byte N-1.
     198           2 :                     _current_i2c_transaction.rx_data[_rx_position] = I2C1->DR;
     199           2 :                     _rx_position++;
     200             :                     // Wait for the last byte to be read in the RxNE interrupt
     201             :                     // to give hardware time to move it from shift register.
     202           2 :                     I2C1->CR2 |= I2C_CR2_ITBUFEN;
     203             : 
     204             :                     // Reset the flag.
     205           2 :                     _rx_last_byte_read = false;
     206             :                 }
     207             :                 else
     208             :                 {
     209             :                     // Normal read somewhere in the beginning or middle of the transaction.
     210           1 :                     _current_i2c_transaction.rx_data[_rx_position] = I2C1->DR;
     211           1 :                     _rx_position++;
     212             :                 }
     213             :             }
     214             :         }
     215           2 :         else if (_tx_in_progress && _tx_last_byte_written)
     216             :         {
     217             :             // With BTF set during the transmit phase, and the last byte already written,
     218             :             // then both DR and shift register are empty and SCL is stretched low. Time to determine
     219             :             // whether to begin a read phase or end the transaction. Either way, the transmit phase is over.
     220             : 
     221             :             // Reset transmit control variables
     222           2 :             _tx_in_progress = false;
     223           2 :             _tx_last_byte_written = false;
     224             : 
     225             :             // Determine next phase.
     226           2 :             if (_current_i2c_transaction.i2c_op == HAL_I2C_OP_WRITE)
     227             :             {
     228             :                 // There is no read phase. End the transaction.
     229           1 :                 I2C1->CR1 |= I2C_CR1_STOP;
     230             :             }
     231           1 :             else if (_current_i2c_transaction.i2c_op == HAL_I2C_OP_WRITE_READ)
     232             :             {
     233             :                 // There is a read phase. Generate a re-start.
     234           1 :                 _rx_in_progress = true;
     235           1 :                 I2C1->CR1 |= I2C_CR1_START;
     236             :             }
     237             : 
     238             :             // Clear BTF to prevent immediate refire.
     239           2 :             (void)I2C1->DR;
     240             :             // Disable TxE and RxNE interrupts.
     241           2 :             I2C1->CR2 &= ~I2C_CR2_ITBUFEN;
     242             :         }
     243             :     }
     244             : 
     245          35 :     if (I2C1->SR1 & I2C_SR1_TXE)
     246             :     {
     247          10 :         if (_tx_in_progress)
     248             :         {
     249           4 :             if (_tx_position < _current_i2c_transaction.expected_bytes_to_tx)
     250             :             {
     251           3 :                 I2C1->DR = _current_i2c_transaction.tx_data[_tx_position];
     252           3 :                 _tx_position++;
     253           3 :                 if (_tx_position == _current_i2c_transaction.expected_bytes_to_tx)
     254             :                 {
     255             :                     // we just queued the final byte; arm BTF to finish
     256           2 :                     _tx_last_byte_written = true;
     257             :                 }
     258             :             }
     259           1 :             else if (_current_i2c_transaction.expected_bytes_to_tx == 0)
     260             :             {
     261             :                 // Zero length write.
     262             :                 // Disable TxE interrupt, generate STOP, and close out transaction.
     263           1 :                 I2C1->CR2 &= ~I2C_CR2_ITBUFEN;
     264           1 :                 I2C1->CR1 |= I2C_CR1_STOP;
     265           1 :                 _tx_in_progress = false;
     266             :             }
     267             :         }
     268             :     }
     269             : 
     270          35 :     if (I2C1->SR1 & I2C_SR1_RXNE)
     271             :     {
     272             :         // In receive mode we only use RxNE to pick up the last byte.
     273           8 :         if (_rx_in_progress && _rx_position == (_current_i2c_transaction.expected_bytes_to_rx - 1))
     274             :         {
     275             :             // We are about to receive our last byte.
     276           5 :             _current_i2c_transaction.rx_data[_rx_position] = I2C1->DR;
     277           5 :             _rx_position++;
     278             : 
     279             :             // Turn off buffer interrupt.
     280           5 :             I2C1->CR2 &= ~I2C_CR2_ITBUFEN;
     281             : 
     282             :             // Close out the transaction.
     283           5 :             _rx_in_progress = false;
     284             :         }
     285             :     }
     286          35 : }
     287             : 
     288           1 : void I2C1_ER_IRQHandler()
     289             : {
     290           1 :     uint32_t sr1 = I2C1->SR1;  // volatile read ok
     291             : 
     292           1 :     if (sr1 & I2C_SR1_AF)
     293             :     {
     294             :         // The target failed to acknowledge either address or data.
     295             :         // Reset flag.
     296           1 :         I2C1->SR1 &= ~I2C_SR1_AF;
     297           1 :         _SET_ERROR_FLAG_AND_ABORT_TRANSACTION();
     298             :     }
     299           1 : }
     300             : 
     301          15 : hal_status_t hal_i2c_init()
     302             : {
     303          15 :     configure_gpio();
     304          15 :     configure_peripheral();
     305          15 :     configure_interrupts();
     306             : 
     307          15 :     return HAL_STATUS_OK;
     308             : }
     309             : 
     310          12 : hal_status_t hal_i2c_submit_transaction(hal_i2c_txn_t *txn)
     311             : {
     312             :     // @todo: Some transaction validation here.
     313          12 :     return (i2c_transaction_queue_add(txn) == I2C_QUEUE_STATUS_SUCCESS) ? HAL_STATUS_OK : HAL_STATUS_ERROR;
     314             : }
     315             : 
     316          23 : hal_status_t hal_i2c_transaction_servicer()
     317             : {
     318          23 :     hal_status_t status = HAL_STATUS_BUSY;
     319             : 
     320             :     // CRITICAL SECTION ENTER
     321          23 :     NVIC_DisableIRQ(I2C1_EV_IRQn);
     322          23 :     NVIC_DisableIRQ(I2C1_ER_IRQn);
     323             : 
     324             :     // Check if there is currently no transaction in progress.
     325          23 :     if (!_tx_in_progress && !_rx_in_progress)
     326             :     {
     327          21 :         status = HAL_STATUS_OK;
     328             : 
     329             :         // Finish transaction that just completed.
     330          21 :         if (current_i2c_transaction)
     331             :         {
     332             :             // Transfer the results back to the client's transaction object.
     333           8 :             current_i2c_transaction->actual_bytes_transmitted = _tx_position;
     334           8 :             current_i2c_transaction->actual_bytes_received = _rx_position;
     335           8 :             memcpy(current_i2c_transaction->rx_data, (const void*)_current_i2c_transaction.rx_data, current_i2c_transaction->actual_bytes_received);
     336           8 :             current_i2c_transaction->transaction_result = (_error_occurred) ? HAL_I2C_TXN_RESULT_FAIL : HAL_I2C_TXN_RESULT_SUCCESS;
     337             : 
     338             :             // Complete the transaction.
     339           8 :             current_i2c_transaction->processing_state = HAL_I2C_TXN_STATE_COMPLETED;
     340             : 
     341             :             // Reset our pointer away from the completed transaction.
     342           8 :             current_i2c_transaction = NULL;
     343             :         }
     344             : 
     345             :         // Load in a new transaction if there is one.
     346          21 :         if (load_new_transaction())
     347             :         {
     348          12 :             if (current_transaction_is_valid())
     349             :             {
     350             :                 // Set state to processing.
     351          11 :                 current_i2c_transaction->processing_state = HAL_I2C_TXN_STATE_PROCESSING;
     352             : 
     353             :                 // Copy the transaction to memory that belongs to the ISR.
     354          11 :                 _current_i2c_transaction = *current_i2c_transaction;
     355             : 
     356             :                 // Set up the control variables.
     357          11 :                 _error_occurred = false;
     358          11 :                 _tx_position = 0;
     359          11 :                 _rx_position = 0;
     360             : 
     361          11 :                 if (current_i2c_transaction->i2c_op == HAL_I2C_OP_WRITE ||
     362           5 :                     current_i2c_transaction->i2c_op == HAL_I2C_OP_WRITE_READ)
     363             :                 {
     364           7 :                     _tx_in_progress = true;
     365           7 :                     _rx_in_progress = false;
     366             :                 }
     367           4 :                 else if (current_i2c_transaction->i2c_op == HAL_I2C_OP_READ)
     368             :                 {
     369           4 :                     _tx_in_progress = false;
     370           4 :                     _rx_in_progress = true;
     371             :                 }
     372             : 
     373             :                 // Send start
     374          11 :                 I2C1->CR1 |= I2C_CR1_START;
     375             :             }
     376             :             else
     377             :             {
     378             :                 // Close out the invalid transaction and set error.
     379           1 :                 status = HAL_STATUS_ERROR;
     380           1 :                 current_i2c_transaction->actual_bytes_transmitted = 0;
     381           1 :                 current_i2c_transaction->actual_bytes_received = 0;
     382           1 :                 current_i2c_transaction->transaction_result = HAL_I2C_TXN_RESULT_FAIL;
     383           1 :                 current_i2c_transaction->processing_state = HAL_I2C_TXN_STATE_COMPLETED;
     384           1 :                 current_i2c_transaction = NULL;
     385             :             }
     386             :         }
     387             :     }
     388             : 
     389          23 :     NVIC_EnableIRQ(I2C1_EV_IRQn);
     390          23 :     NVIC_EnableIRQ(I2C1_ER_IRQn);
     391             :     // CRITICAL SECTION EXIT
     392             : 
     393          23 :     return status;
     394             : }
     395             : 
     396             : /// @brief Just for testing.
     397             : /// @warning Grave consequences if used in production code.
     398          15 : void _test_fixture_hal_i2c_reset_internals()
     399             : {
     400          15 :     current_i2c_transaction = NULL;
     401             : 
     402          15 :     _current_i2c_transaction.target_addr = 0;
     403          15 :     _current_i2c_transaction.i2c_op = HAL_I2C_OP_WRITE;
     404          15 :     _current_i2c_transaction.expected_bytes_to_tx = 0;
     405          15 :     _current_i2c_transaction.expected_bytes_to_rx = 0;
     406          15 :     _current_i2c_transaction.processing_state = HAL_I2C_TXN_STATE_CREATED;
     407          15 :     _current_i2c_transaction.transaction_result = HAL_I2C_TXN_RESULT_NONE;
     408          15 :     _current_i2c_transaction.actual_bytes_received = 0;
     409          15 :     _current_i2c_transaction.actual_bytes_transmitted = 0;
     410          15 :     memset((void*)_current_i2c_transaction.tx_data, 0, sizeof(_current_i2c_transaction.tx_data));
     411          15 :     memset((void*)_current_i2c_transaction.rx_data, 0, sizeof(_current_i2c_transaction.rx_data));
     412             : 
     413          15 :     _tx_position = 0;
     414          15 :     _rx_position = 0;
     415          15 :     _tx_last_byte_written = false;
     416          15 :     _rx_last_byte_read = false;
     417          15 :     _tx_in_progress = false;
     418          15 :     _rx_in_progress = false;
     419          15 :     _error_occurred = false;
     420          15 : }
     421             : 
     422             : // These pins are broken out right next to each other on the dev board.
     423             : // And no interference from other peripherals.
     424             : // PB8 - I2C1 SCL
     425             : // PB9 - I2C1 SDA
     426             : // Need to bring up bus from port B, set these pins in AF4.
     427          15 : static void configure_gpio()
     428             : {
     429             :     // Enable Bus.
     430          15 :     RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
     431             : 
     432             :     // Set PB8 (i2c1 SCL pin) mode to alternate function.
     433          15 :     GPIOB->MODER &= ~BIT_16;
     434          15 :     GPIOB->MODER |= BIT_17;
     435             : 
     436             :     // Set PB9 (i2c1 SDA pin) mode to alternate function.
     437          15 :     GPIOB->MODER &= ~BIT_18;
     438          15 :     GPIOB->MODER |= BIT_19;
     439             : 
     440             :     // Set PB8 alternate function type to I2C (AF04)
     441          15 :     GPIOB->AFR[1] &= ~(0xF << (PIN_0 * AF_SHIFT_WIDTH));
     442          15 :     GPIOB->AFR[1] |= (AF4_MASK << (PIN_0 * AF_SHIFT_WIDTH));
     443             : 
     444             :     // Set PB9 alternate function type to I2C (AF04)
     445          15 :     GPIOB->AFR[1] &= ~(0xF << (PIN_1 * AF_SHIFT_WIDTH));
     446          15 :     GPIOB->AFR[1] |= (AF4_MASK << (PIN_1 * AF_SHIFT_WIDTH));
     447             : 
     448             :     // Open drain
     449          15 :     GPIOB->OTYPER |= (GPIO_OTYPER_OT_8 | GPIO_OTYPER_OT_9);
     450          15 : }
     451             : 
     452          15 : static void configure_peripheral()
     453             : {
     454             :     // Send the clock to I2C1
     455          15 :     RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
     456             : 
     457             :     // Need to set the APB1 Clock frequency in the CR2 register.
     458             :     // With no dividers, it is the same as the System Frequency of 16 MHz.
     459          15 :     I2C1->CR2 &= ~(I2C_CR2_FREQ);
     460          15 :     I2C1->CR2 |= (SYS_FREQ_MHZ & I2C_CR2_FREQ);
     461             : 
     462             :     // Time to rise (TRISE) register. Set to 17 via the calculation
     463             :     // Assumed 1000 ns SCL clock rise time (maximum permitted for I2C Standard Mode)
     464             :     // Peripheral's clock period (1 / SYSTEM_FREQ_MHZ)
     465             :     // (1000ns / 62.5) = 17 OR SYSTEM_FREQ_MHZ + 1 = 17. Either calc works.
     466          15 :     size_t trise_reg_val = SYS_FREQ_MHZ + 1;
     467          15 :     I2C1->TRISE &= ~(I2C_TRISE_TRISE);
     468          15 :     I2C1->TRISE |= (trise_reg_val & I2C_TRISE_TRISE);
     469             : 
     470             :     // Set CCR.
     471             :     // We want to setup CCR so that the peripheral can count up ticks of the
     472             :     // peripheral bus clock, and use that count to create the SCL clock at 100kHz for
     473             :     // Standard Mode.
     474             :     // 100 kHz SCL means 1 / 100kHz period of 10 microseconds.
     475             :     // So we need to transition the SCL clock every ~5 microseconds.
     476             :     // On a 16 MHz bus clock with a tick every 62.5 nanoseconds, this means
     477             :     // we need to transition the SCL line every 80 ticks to achieve 100kHz SCL line.
     478          15 :     size_t ticks_between_scl_transitions = 80;
     479          15 :     I2C1->CCR &= ~(I2C_CCR_CCR);
     480          15 :     I2C1->CCR |= (ticks_between_scl_transitions & I2C_CCR_CCR);
     481             : 
     482             :     // Standard mode
     483          15 :     I2C1->CCR &= ~I2C_CCR_FS;
     484             : 
     485             :     // Enable the peripheral.
     486          15 :     I2C1->CR1 |= I2C_CR1_PE;
     487          15 : }
     488             : 
     489          15 : static void configure_interrupts()
     490             : {
     491          15 :     I2C1->CR2 |= I2C_CR2_ITEVTEN;
     492          15 :     I2C1->CR2 |= I2C_CR2_ITERREN;
     493          15 :     NVIC_EnableIRQ(I2C1_EV_IRQn);
     494          15 :     NVIC_EnableIRQ(I2C1_ER_IRQn);
     495          15 : }
     496             : 
     497          12 : static bool current_transaction_is_valid()
     498             : {
     499          24 :     return (current_i2c_transaction &&
     500          23 :             ENUM_IN_RANGE(current_i2c_transaction->i2c_op, _HAL_I2C_OP_MIN, _HAL_I2C_OP_MAX) &&
     501          11 :             current_i2c_transaction->processing_state == HAL_I2C_TXN_STATE_QUEUED);
     502             : }
     503             : 
     504          21 : static bool load_new_transaction()
     505             : {
     506          21 :     return (I2C_QUEUE_STATUS_SUCCESS == i2c_transaction_queue_get_next(&current_i2c_transaction) &&
     507             :             current_i2c_transaction);
     508             : }

Generated by: LCOV version 1.14