LCOV - code coverage report
Current view: top level - uart/src - stm32f4_uart2.c (source / functions) Hit Total Coverage
Test: filtered.info Lines: 83 86 96.5 %
Date: 2025-12-18 00:00:35 Functions: 8 8 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /**
       2             :  * @file stm32f4_uart2.c
       3             :  * @brief Implements serial communication over UART2.
       4             :  *
       5             :  * Copyright (c) 2025 Cory McKiel.
       6             :  * Licensed under the MIT License. See LICENSE file in the project root.
       7             :  */
       8             : #ifdef DESKTOP_BUILD
       9             : #include "registers.h"
      10             : #include "nvic.h"
      11             : #else
      12             : #include "stm32f4xx.h"
      13             : #endif
      14             : 
      15             : #include "stm32f4_hal.h"
      16             : #include "stm32f4_uart_util.h"
      17             : #include "stm32f4_uart2.h"
      18             : #include "circular_buffer.h"
      19             : 
      20             : #define UART_BAUDRATE 115200
      21             : 
      22             : #define UART_BUFFER_RX_SIZE CIRCULAR_BUFFER_MAX_SIZE
      23             : #define UART_BUFFER_TX_SIZE CIRCULAR_BUFFER_MAX_SIZE
      24             : 
      25             : static bool uart2_initialized = false;
      26             : 
      27             : static circular_buffer_ctx rx_ctx;
      28             : static circular_buffer_ctx tx_ctx;
      29             : 
      30             : static void configure_gpio_pins();
      31             : static void configure_uart();
      32             : static void configure_interrupt();
      33             : 
      34        4824 : void USART2_IRQHandler(void)
      35             : {
      36        4824 :         if (USART2->SR & USART_SR_RXNE)
      37             :         {
      38             :                 // A received byte is waiting in data register.
      39        3012 :                 uint8_t byte = USART2->DR & 0xFF;
      40        3012 :                 circular_buffer_push_with_overwrite(&rx_ctx, byte);
      41             :         }
      42             : 
      43        4824 :         if (USART2->SR & USART_SR_TXE)
      44             :         {
      45             :                 // Transmit register is empty. Ready for a new byte.
      46        1812 :                 uint8_t byte = 0;
      47        1812 :                 if (circular_buffer_pop(&tx_ctx, &byte))
      48             :                 {
      49        1809 :                         USART2->DR = byte;
      50             :                 }
      51             :                 else
      52             :                 {
      53             :                         // Buffer empty — stop TXE interrupt to prevent ISR from firing again
      54           3 :                         USART2->CR1 &= ~USART_CR1_TXEIE;
      55             :                 }
      56             :         }
      57        4824 : }
      58             : 
      59             : /**
      60             :  * @brief Initialize the UART2. (Connected to the USB)
      61             :  */
      62          26 : hal_status_t stm32f4_uart2_init()
      63             : {
      64             :         // Prevent multiple initialization
      65          26 :         if (uart2_initialized)
      66             :         {
      67           1 :                 return HAL_STATUS_ERROR;
      68             :         }
      69             : 
      70          25 :         if (!circular_buffer_init(&rx_ctx, UART_BUFFER_RX_SIZE) ||
      71          25 :                 !circular_buffer_init(&tx_ctx, UART_BUFFER_TX_SIZE))
      72             :         {
      73           0 :                 return HAL_STATUS_ERROR;
      74             :         }
      75             : 
      76          25 :         configure_gpio_pins();
      77          25 :         configure_uart();
      78          25 :         configure_interrupt();
      79             : 
      80          25 :         uart2_initialized = true; // Mark as initialized only after success.
      81          25 :         return HAL_STATUS_OK;
      82             : }
      83             : 
      84          46 : hal_status_t stm32f4_uart2_deinit()
      85             : {
      86          46 :     if (!uart2_initialized)
      87             :         {
      88          22 :         return HAL_STATUS_ERROR;
      89             :     }
      90             : 
      91             :     // Disable interrupts
      92          24 :     USART2->CR1 &= ~(USART_CR1_RXNEIE | USART_CR1_TXEIE);
      93          24 :     NVIC_DisableIRQ(USART2_IRQn);
      94             : 
      95             :     // Disable UART
      96          24 :     USART2->CR1 &= ~USART_CR1_UE;
      97             : 
      98             :     // Disable clock
      99          24 :         RCC->APB1ENR &= ~RCC_APB1ENR_USART2EN;
     100             : 
     101          24 :     uart2_initialized = false;
     102          24 :     return HAL_STATUS_OK;
     103             : }
     104             : 
     105             : /**
     106             :  * @brief Reads data from UART2 register.
     107             : */
     108          13 : hal_status_t stm32f4_uart2_read(uint8_t *data, size_t len, size_t *bytes_read)
     109             : {
     110          13 :         hal_status_t res = HAL_STATUS_ERROR;
     111             : 
     112          13 :         if (data && bytes_read && uart2_initialized)
     113             :         {
     114          10 :                 *bytes_read = 0;
     115          10 :                 res = HAL_STATUS_OK;
     116             : 
     117          10 :                 uint8_t byte = 0;
     118             : 
     119             :                 // While there are still bytes in the buffer and we still have space to store them.
     120        2943 :                 while (!circular_buffer_is_empty(&rx_ctx) && *bytes_read < len)
     121             :                 {
     122             :                         // ***************************************************
     123             :                         // CRITICAL_SECTION_ENTER
     124        2933 :                         NVIC_DisableIRQ(USART2_IRQn);
     125        2933 :                         bool popped = circular_buffer_pop(&rx_ctx, &byte);
     126        2933 :                         NVIC_EnableIRQ(USART2_IRQn);
     127             :                         // CRITICAL_SECTION_EXIT
     128             :                         // ***************************************************
     129             : 
     130        2933 :                         if (popped)
     131             :                         {
     132        2933 :                                 data[*bytes_read] = byte;
     133        2933 :                                 *bytes_read = *bytes_read + 1;
     134             :                         }
     135             :                         else
     136             :                         {
     137             :                                 // Error
     138           0 :                                 res = HAL_STATUS_ERROR;
     139           0 :                                 break;
     140             :                         }
     141             :                 }
     142             :         }
     143             : 
     144          13 :     return res;
     145             : }
     146             : 
     147             : /**
     148             :  * @brief Writes data to UART2 register.
     149             : */
     150          14 : hal_status_t stm32f4_uart2_write(const uint8_t *data, size_t len, size_t *bytes_written)
     151             : {
     152          14 :         hal_status_t res = HAL_STATUS_ERROR;
     153          14 :         bool push_success = true;
     154             : 
     155          14 :         if (uart2_initialized && bytes_written && data && len > 0)
     156             :         {
     157          10 :                 *bytes_written = 0;
     158        2855 :                 for (size_t i = 0; i < len; i++)
     159             :                 {
     160             :                         // ***************************************************
     161             :                         // CRITICAL_SECTION_ENTER
     162        2846 :                         NVIC_DisableIRQ(USART2_IRQn);
     163        2846 :                         push_success = circular_buffer_push_no_overwrite(&tx_ctx, data[i]);
     164        2846 :                         NVIC_EnableIRQ(USART2_IRQn);
     165             :                         // CRITICAL_SECTION_EXIT
     166             :                         // ***************************************************
     167             : 
     168        2846 :                         if (push_success)
     169             :                         {
     170        2845 :                                 *bytes_written += 1;
     171             :                         }
     172             :                         else
     173             :                         {
     174           1 :                                 break;
     175             :                         }
     176             :                 }
     177             : 
     178             :                 // If bytes were written successfully to buffer, then enable the transmit
     179             :                 // interrupt because those bytes need to be sent out.
     180          10 :                 if (*bytes_written > 0)
     181             :                 {
     182          10 :                         USART2->CR1 |= USART_CR1_TXEIE;  // Enable TXE interrupt
     183             :                 }
     184             : 
     185             :                 // If we successfully wrote all bytes to buffer, then the function was an
     186             :                 // overall success.
     187          10 :                 res = (*bytes_written == len) ? HAL_STATUS_OK : HAL_STATUS_ERROR;
     188             :         }
     189             : 
     190          14 :         return res;
     191             : }
     192             : 
     193          25 : static void configure_gpio_pins()
     194             : {
     195             :         // Enable Bus.
     196          25 :         RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
     197             : 
     198             :         // Set PA2 (usart2 tx pin) mode to alternate function.
     199          25 :         GPIOA->MODER &= ~BIT_4;
     200          25 :         GPIOA->MODER |= BIT_5;
     201             : 
     202             :         // Set PA3 (usart2 rx pin) mode to alternate function.
     203          25 :         GPIOA->MODER &= ~BIT_6;
     204          25 :         GPIOA->MODER |= BIT_7;
     205             : 
     206             :         // Set PA2 alternate function type to UART_TX (AF07)
     207          25 :         GPIOA->AFR[0] &= ~(0xF << (PIN_2 * AF_SHIFT_WIDTH)); // clear bits 11-8
     208          25 :         GPIOA->AFR[0] |=  (AF7_MASK << (PIN_2 * AF_SHIFT_WIDTH)); // set bits 11-8 as 0111 aka AF07 for PA2.
     209             : 
     210             :         // Set PA3 alternate function type to UART_RX (AF07)
     211          25 :         GPIOA->AFR[0] &= ~(0xF << (PIN_3 * AF_SHIFT_WIDTH)); // clear bits 15-12
     212          25 :         GPIOA->AFR[0] |= (AF7_MASK << (PIN_3 * AF_SHIFT_WIDTH)); // set bits 15-12 as 0111 aka AF07 for PA3.
     213          25 : }
     214             : 
     215          25 : static void configure_uart()
     216             : {
     217             :         // Enable the bus.
     218          25 :         RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
     219             : 
     220             :         // Program the M bit in USART_CR1 to define the word length.
     221          25 :         USART2->CR1 &= ~(USART_CR1_M);
     222             : 
     223             :         // Select the desired baud rate using the USART_BRR register.
     224          25 :         USART2->BRR = stm32f4_hal_compute_uart_bd(APB1_CLK, UART_BAUDRATE);
     225             : 
     226             :         // Set the TE bit in USART_CR1 to send an idle frame as first transmission.
     227          25 :         USART2->CR1 = USART_CR1_TE; // No OR, sets the UART to a default state.
     228          25 :         USART2->CR1 |= USART_CR1_RE; // Enable receiver bit.
     229             : 
     230             :         // Set the CR2 register to a default state.
     231          25 :         USART2->CR2 = 0;
     232             : 
     233             :         // Enable the USART by writing the UE bit in USART_CR1 register to 1.
     234          25 :         USART2->CR1 |= USART_CR1_UE;
     235          25 : }
     236             : 
     237          25 : static void configure_interrupt()
     238             : {
     239             :         // Enable RXNE Interrupt.
     240          25 :         USART2->CR1 |= USART_CR1_RXNEIE;
     241             : 
     242             :         // Enable NVIC Interrupt.
     243          25 :         NVIC_EnableIRQ(USART2_IRQn);
     244          25 : }

Generated by: LCOV version 1.14