LCOV - code coverage report
Current view: top level - src - app.c (source / functions) Coverage Total Hit
Test: stm32-async-1wire core coverage Lines: 100.0 % 53 53
Test Date: 2026-09-10 04:33:53 Functions: 100.0 % 6 6
Branches: 78.6 % 28 22

             Branch data     Line data    Source code
       1                 :             : /**
       2                 :             :  * @file app.c
       3                 :             :  * @brief Shared application layer implementation (UART TX, system clock, LED)
       4                 :             :  */
       5                 :             : 
       6                 :             : #include "app.h"
       7                 :             : #if defined(OW_PORT_FAMILY_F0)
       8                 :             : #include "stm32f0xx.h"
       9                 :             : #elif defined(OW_PORT_FAMILY_G0)
      10                 :             : #include "stm32g0xx.h"
      11                 :             : #else
      12                 :             : #include "stm32f1xx.h"
      13                 :             : #endif
      14                 :             : 
      15                 :             : // ======== USART1 TX ring buffer ========
      16                 :             : static uint32_t uart_tx_head = 0; // write index - points to next free slot
      17                 :             : static uint32_t uart_tx_tail = 0; // read index - points to oldest data
      18                 :             : static uint8_t uart_tx_buf[UART_TX_BUF_SIZE]; // circular buffer for UART transmission
      19                 :             : 
      20                 :             : /**
      21                 :             :  * @brief Advance USART1 transmission by at most one byte (non-blocking)
      22                 :             :  * @note Must be called periodically to feed the UART from the ring buffer
      23                 :             :  */
      24                 :        1410 : void uart_poll_tx(void) {
      25                 :             : #if defined(OW_PORT_FAMILY_G0)
      26                 :             :     // G0 uses the modern USART naming: TXE/TXFNF lives in ISR, data in TDR
      27   [ +  -  +  - ]:         470 :     if ((USART1->ISR & USART_ISR_TXE_TXFNF) && (uart_tx_tail != uart_tx_head)) {
      28                 :         470 :         uint8_t b = uart_tx_buf[uart_tx_tail];
      29                 :         470 :         uart_tx_tail = (uart_tx_tail + 1u) & UART_TX_IDX_MASK;
      30                 :         470 :         USART1->TDR = b;
      31                 :             :     }
      32                 :             : #elif defined(OW_PORT_FAMILY_F0)
      33                 :             :     // F0 unified USART naming: TXE lives in ISR, data goes to TDR
      34   [ +  -  +  - ]:         470 :     if ((USART1->ISR & USART_ISR_TXE) && (uart_tx_tail != uart_tx_head)) {
      35                 :             :         // Get byte from buffer at tail position
      36                 :         470 :         uint8_t b = uart_tx_buf[uart_tx_tail];
      37                 :             :         // Advance tail pointer with wrap-around
      38                 :         470 :         uart_tx_tail = (uart_tx_tail + 1u) & UART_TX_IDX_MASK;
      39                 :             :         // Write byte to UART data register for transmission
      40                 :         470 :         USART1->TDR = b;
      41                 :             :     }
      42                 :             : #else
      43                 :             :     // Check if UART is ready to transmit (TXE flag set) and buffer not empty
      44   [ +  -  +  - ]:         470 :     if ((USART1->SR & USART_SR_TXE) && (uart_tx_tail != uart_tx_head)) {
      45                 :             :         // Get byte from buffer at tail position
      46                 :         470 :         uint8_t b = uart_tx_buf[uart_tx_tail];
      47                 :             :         // Advance tail pointer with wrap-around
      48                 :         470 :         uart_tx_tail = (uart_tx_tail + 1u) & UART_TX_IDX_MASK;
      49                 :             :         // Write byte to UART data register for transmission
      50                 :         470 :         USART1->DR = b;
      51                 :             :     }
      52                 :             : #endif
      53                 :        1410 : }
      54                 :             : 
      55                 :             : /**
      56                 :             :  * @brief Block until every enqueued byte has been transmitted
      57                 :             :  * @note Blocking, intended for diagnostic/blocking code paths only; the
      58                 :             :  *       demos keep the non-blocking uart_poll_tx() discipline.
      59                 :             :  */
      60                 :          54 : void uart_flush(void) {
      61         [ +  + ]:        1266 :     while (uart_tx_tail != uart_tx_head) {
      62                 :        1212 :         uart_poll_tx();
      63                 :             :     }
      64                 :          54 : }
      65                 :             : 
      66                 :             : /**
      67                 :             :  * @brief Enqueue a single byte into the UART transmit buffer (non-blocking)
      68                 :             :  * @param[in] b Byte to enqueue
      69                 :             :  * @return 1 if enqueued, 0 if the buffer is full (byte dropped)
      70                 :             :  * @note Never blocks: when the buffer is full the byte is dropped so the
      71                 :             :  *       caller's code path stays non-blocking.
      72                 :             :  */
      73                 :        1428 : int uart_tx_enqueue_byte(int b) {
      74                 :        1428 :     uint32_t head = uart_tx_head;
      75                 :             :     // Calculate next head position with wrap-around using power-of-two mask
      76                 :        1428 :     uint32_t next = (head + 1u) & UART_TX_IDX_MASK;
      77         [ +  + ]:        1428 :     if (next != uart_tx_tail) { // Room is available
      78                 :        1410 :         uart_tx_buf[head] = (uint8_t)b; // Store byte at current head position
      79                 :        1410 :         uart_tx_head = next; // Update head pointer
      80                 :        1410 :         return 1;
      81                 :             :     }
      82                 :          18 :     return 0; // Buffer full - drop the byte to stay non-blocking
      83                 :             : }
      84                 :             : 
      85                 :             : /**
      86                 :             :  * @brief Enqueue an entire null-terminated string (non-blocking)
      87                 :             :  * @param[in] s Null-terminated string to enqueue
      88                 :             :  * @return Number of characters actually enqueued (may be less than strlen)
      89                 :             :  */
      90                 :         144 : int uart_write_str(const char* s) {
      91                 :         144 :     const char* start = s;
      92         [ +  + ]:         594 :     while (*s) {
      93         [ +  + ]:         456 :         if (uart_tx_enqueue_byte(*s)) {
      94                 :         450 :             s++;
      95                 :             :         } else {
      96                 :           6 :             break; // Buffer full - stop to stay non-blocking
      97                 :             :         }
      98                 :             :     }
      99                 :         144 :     return (int)(s - start);
     100                 :             : }
     101                 :             : 
     102                 :             : /**
     103                 :             :  * @brief Convert integer to string and enqueue for UART transmission
     104                 :             :  * @param[in] value Integer value to convert and transmit
     105                 :             :  * @return Number of characters enqueued
     106                 :             :  * @note Buffer holds 12 chars, enough for the full int32 range (-2147483648)
     107                 :             :  */
     108                 :          84 : int uart_write_int(int value) {
     109                 :             :     char buf[12]; // enough for -2147483648 and '\0'
     110                 :          84 :     char* p = buf + sizeof(buf) - 1;
     111                 :          84 :     *p = '\0';
     112                 :             : 
     113         [ +  + ]:          84 :     if (value == 0) { // Special case for zero
     114                 :           6 :         *(--p) = '0';
     115                 :             :     } else {
     116                 :          78 :         int is_negative = 0;
     117                 :             :         unsigned int uvalue;
     118                 :             : 
     119         [ +  + ]:          78 :         if (value < 0) { // Handle negative numbers
     120                 :          12 :             is_negative = 1;
     121                 :          12 :             uvalue = (unsigned int)-(value + 1) + 1;
     122                 :             :         } else {
     123                 :          66 :             uvalue = (unsigned int)value;
     124                 :             :         }
     125                 :             : 
     126                 :             :         do { // Convert digits from least significant to most significant
     127                 :         216 :             *(--p) = '0' + (uvalue % 10);
     128                 :         216 :             uvalue /= 10;
     129         [ +  + ]:         216 :         } while (uvalue);
     130                 :             : 
     131         [ +  + ]:          78 :         if (is_negative) *(--p) = '-'; // Add negative sign if needed
     132                 :             :     }
     133                 :          84 :     return uart_write_str(p);
     134                 :             : }
     135                 :             : 
     136                 :             : /**
     137                 :             :  * @brief Enqueue one byte as two uppercase hexadecimal digits (non-blocking)
     138                 :             :  * @param[in] b Byte to convert and transmit
     139                 :             :  * @return Number of characters actually enqueued (0, 1 or 2)
     140                 :             :  */
     141                 :          54 : int uart_write_hex(uint8_t b) {
     142                 :             :     static const char hex[] = "0123456789ABCDEF";
     143                 :          54 :     int n = 0;
     144                 :          54 :     n += uart_tx_enqueue_byte(hex[(b >> 4) & 0x0F]);
     145                 :          54 :     n += uart_tx_enqueue_byte(hex[b & 0x0F]);
     146                 :          54 :     return n;
     147                 :             : }
     148                 :             : 
     149                 :             : #if !defined(DS18B20_TEST_HARNESS)
     150                 :             : /* Hardware bring-up (system clock, USART1 TX, LED GPIO) with full register
     151                 :             :  *-level access.  Excluded from the host test build, which only exercises the
     152                 :             :  * non-blocking UART ring buffer above. */
     153                 :             : 
     154                 :             : /**
     155                 :             :  * @brief Configure system clock
     156                 :             :  * @note The source is derived from OW_PORT_SYSCLK_MHZ (see onewire.h).
     157                 :             :  *       F1: 72MHz via HSE+PLL x9, or raw HSI at 8MHz. F030x6 has no HSE:
     158                 :             :  *       48MHz via HSI/2+PLL x12, or raw HSI at 8MHz. G031x6 has no HSE:
     159                 :             :  *       64MHz via HSI16+PLL (M=1, N=8, R=2), or raw HSI16 at 16MHz.
     160                 :             :  */
     161                 :             : __STATIC_FORCEINLINE void configure_system_clock(void) {
     162                 :             : #if defined(OW_PORT_FAMILY_G0)
     163                 :             : #if (OW_PORT_SYSCLK_MHZ) == 64
     164                 :             :     // HSI16 is on and stable right after reset. PLL source must be selected
     165                 :             :     // explicitly: on this family PLLSRC=00 means "no clock sent to the PLL",
     166                 :             :     // HSI16 encodes as 10 (RCC_PLLCFGR_PLLSRC_HSI). M(=1) xN(=8) R(=2) then
     167                 :             :     // gives 64MHz; PLLREN enables the PLLR output the SYSCLK mux uses.
     168                 :             :     RCC->PLLCFGR = RCC_PLLCFGR_PLLSRC_HSI | RCC_PLLCFGR_PLLN_3 |
     169                 :             :                    RCC_PLLCFGR_PLLR_0 | RCC_PLLCFGR_PLLREN;
     170                 :             :     RCC->CR |= RCC_CR_PLLON;
     171                 :             :     while (!(RCC->CR & RCC_CR_PLLRDY))
     172                 :             :         ;
     173                 :             :     // Flash latency: 2 wait states above 48MHz (RM0444)
     174                 :             :     FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_LATENCY_1;
     175                 :             :     // Switch system clock to PLLRCLK
     176                 :             :     RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_PLLRCLK;
     177                 :             :     while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLLRCLK)
     178                 :             :         ;
     179                 :             : #elif (OW_PORT_SYSCLK_MHZ) == 16
     180                 :             :     // Raw HSI16: the MCU already runs on the internal 16MHz RC after reset —
     181                 :             :     // nothing to configure
     182                 :             : #else
     183                 :             : #error "Unsupported OW_PORT_SYSCLK_MHZ for G0: use 64 (HSI16+PLL) or 16 (raw HSI16)"
     184                 :             : #endif
     185                 :             : #elif defined(OW_PORT_FAMILY_F0)
     186                 :             : #if (OW_PORT_SYSCLK_MHZ) == 48
     187                 :             :     // PLL input is HSI/2 = 4MHz; x12 gives 48MHz. Configure the multiplier
     188                 :             :     // before enabling the PLL so it locks on a valid clock (per RM0360).
     189                 :             :     RCC->CFGR = RCC_CFGR_PLLMUL12;
     190                 :             :     RCC->CR |= RCC_CR_PLLON;
     191                 :             :     while (!(RCC->CR & RCC_CR_PLLRDY))
     192                 :             :         ;
     193                 :             :     // Flash latency for 48MHz operation (1 wait state)
     194                 :             :     FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;
     195                 :             :     // Switch system clock to PLL
     196                 :             :     RCC->CFGR |= RCC_CFGR_SW_PLL;
     197                 :             :     while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL)
     198                 :             :         ;
     199                 :             : #elif (OW_PORT_SYSCLK_MHZ) == 8
     200                 :             :     // Raw HSI: the MCU already runs on the internal 8MHz RC after reset —
     201                 :             :     // nothing to configure
     202                 :             : #else
     203                 :             : #error "Unsupported OW_PORT_SYSCLK_MHZ for F0: use 48 (HSI+PLL) or 8 (raw HSI)"
     204                 :             : #endif
     205                 :             : #else /* F1 */
     206                 :             : #if (OW_PORT_SYSCLK_MHZ) == 72
     207                 :             :     // Enable HSI and HSE oscillators
     208                 :             :     RCC->CR = RCC_CR_HSION | RCC_CR_HSEON;
     209                 :             :     // Wait for HSE to stabilize - HSERDY is the hardware stabilization
     210                 :             :     // indicator, so no fixed delay is required
     211                 :             :     while (!(RCC->CR & RCC_CR_HSERDY))
     212                 :             :         ;
     213                 :             :     // Configure PLL: HSE source, multiply by 9, APB1 prescaler /2
     214                 :             :     RCC->CFGR = RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL9 | RCC_CFGR_PPRE1_DIV2;
     215                 :             :     // Enable PLL only after HSE is confirmed stable, so the PLL locks on a
     216                 :             :     // valid clock (per RM0008: HSE must be ready before enabling the PLL)
     217                 :             :     RCC->CR |= RCC_CR_PLLON;
     218                 :             :     // Wait for the PLL to lock
     219                 :             :     while (!(RCC->CR & RCC_CR_PLLRDY))
     220                 :             :         ;
     221                 :             :     // Configure flash latency for 72MHz operation
     222                 :             :     FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY_2;
     223                 :             :     // Switch system clock to PLL
     224                 :             :     RCC->CFGR = RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL9 | RCC_CFGR_PPRE1_DIV2 | RCC_CFGR_SW_PLL;
     225                 :             :     // Wait for system clock switch to PLL
     226                 :             :     while ((RCC->CFGR & RCC_CFGR_SWS_PLL) != RCC_CFGR_SWS_PLL)
     227                 :             :         ;
     228                 :             :     // Disable HSI oscillator
     229                 :             :     RCC->CR &= ~RCC_CR_HSION;
     230                 :             : #elif (OW_PORT_SYSCLK_MHZ) == 8
     231                 :             :     // Raw HSI: the MCU already runs on the internal 8MHz RC after reset —
     232                 :             :     // nothing to configure
     233                 :             : #else
     234                 :             : #error "Unsupported OW_PORT_SYSCLK_MHZ for F1: use 72 (HSE+PLL) or 8 (raw HSI)"
     235                 :             : #endif
     236                 :             : #endif
     237                 :             : }
     238                 :             : 
     239                 :             : /**
     240                 :             :  * @brief Initialize microcontroller peripherals for UART communication and LED control
     241                 :             :  * @note F1: USART1 TX on PA9 (AF push-pull), LED on PC13. F0: same PA9 UART
     242                 :             :  *       via MODER/AFR, LED on PA4 (no GPIOC on F030x6). G0: UART TX on
     243                 :             :  *       logical PA9 (PA11 pad after the SYSCFG remap, see ow_port_g0.h),
     244                 :             :  *       LED on PA4 (no PC13 bonded out on TSSOP20).
     245                 :             :  */
     246                 :             : __STATIC_FORCEINLINE void hardware_init(void) {
     247                 :             : #if defined(OW_PORT_FAMILY_F0) || defined(OW_PORT_FAMILY_G0)
     248                 :             :     // Enable clock for GPIOA and USART1 (G0: GPIO on IOPENR, USART1 on APBENR2)
     249                 :             : #if defined(OW_PORT_FAMILY_G0)
     250                 :             :     RCC->IOPENR |= RCC_IOPENR_GPIOAEN;
     251                 :             :     RCC->APBENR2 |= RCC_APBENR2_USART1EN;
     252                 :             : #else
     253                 :             :     RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
     254                 :             :     RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
     255                 :             : #endif
     256                 :             : 
     257                 :             :     // Configure PA9 as alternate function push-pull output (F0: AF1, G0: AF1
     258                 :             :     // = USART1_TX; on G0 the signal lands on the PA11 pad via SYSCFG remap)
     259                 :             : #if defined(OW_PORT_FAMILY_G0)
     260                 :             :     GPIOA->MODER = (GPIOA->MODER & ~GPIO_MODER_MODE9) | GPIO_MODER_MODE9_1;
     261                 :             : #else
     262                 :             :     GPIOA->MODER = (GPIOA->MODER & ~GPIO_MODER_MODER9) | GPIO_MODER_MODER9_1;
     263                 :             : #endif
     264                 :             :     GPIOA->AFR[1] = (GPIOA->AFR[1] & ~GPIO_AFRH_AFSEL9) | (1u << GPIO_AFRH_AFSEL9_Pos);
     265                 :             : 
     266                 :             :     // Configure PA4 as general purpose output for LED control
     267                 :             : #if defined(OW_PORT_FAMILY_G0)
     268                 :             :     GPIOA->MODER = (GPIOA->MODER & ~GPIO_MODER_MODE4) | GPIO_MODER_MODE4_0;
     269                 :             : #else
     270                 :             :     GPIOA->MODER = (GPIOA->MODER & ~GPIO_MODER_MODER4) | GPIO_MODER_MODER4_0;
     271                 :             : #endif
     272                 :             : 
     273                 :             :     // Configure USART1: 115200 baud, 8 data bits, no parity, 1 stop bit, TX only
     274                 :             :     USART1->BRR = USART_BRR_CALC((OW_PORT_SYSCLK_MHZ) * 1000000u, 115200); // PCLK = SYSCLK
     275                 :             :     USART1->CR1 = USART_CR1_TE | USART_CR1_UE; // Enable USART1; TX enable only
     276                 :             : #else
     277                 :             :     // Enable clock for GPIOA, USART1, and GPIOC peripherals
     278                 :             :     RCC->APB2ENR |= (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_IOPCEN);
     279                 :             : 
     280                 :             :     // Configure PA9 as alternate function push-pull output, 2MHz speed
     281                 :             :     // Clear existing configuration bits
     282                 :             :     GPIOA->CRH &= ~(GPIO_CRH_MODE9 | GPIO_CRH_CNF9);
     283                 :             :     // Set alternate function push-pull output mode, 2MHz speed
     284                 :             :     GPIOA->CRH |= (GPIO_CRH_MODE9_1 | GPIO_CRH_CNF9_1);
     285                 :             : 
     286                 :             :     // Configure PC13 as general purpose output, 2MHz speed for LED control
     287                 :             :     GPIOC->CRH &= ~(GPIO_CRH_MODE13 | GPIO_CRH_CNF13);
     288                 :             :     GPIOC->CRH |= GPIO_CRH_MODE13_1;
     289                 :             : 
     290                 :             :     // Configure USART1: 115200 baud, 8 data bits, no parity, 1 stop bit, TX only
     291                 :             :     USART1->BRR = USART_BRR_CALC((OW_PORT_SYSCLK_MHZ) * 1000000u, 115200); // PCLK2 = SYSCLK
     292                 :             :     USART1->CR1 = USART_CR1_TE | USART_CR1_UE; // Enable USART1; TX enable only
     293                 :             : #endif
     294                 :             : }
     295                 :             : 
     296                 :             : /**
     297                 :             :  * @brief Initialize system clock, USART1 TX and the busy LED GPIO
     298                 :             :  */
     299                 :             : void app_init(void) {
     300                 :             :     configure_system_clock();
     301                 :             :     hardware_init();
     302                 :             : }
     303                 :             : 
     304                 :             : /**
     305                 :             :  * @brief Busy indicator - toggles LED during measurement
     306                 :             :  * @param[in] action 0 = idle, non-zero = busy
     307                 :             :  * @note Non-blocking LED control using atomic BSRR register operations.
     308                 :             :  *       Strong definition overrides the weak one in the DS18B20 driver.
     309                 :             :  *       F1: LED on PC13 (active low). F0: LED on PA4 (active low assumed).
     310                 :             :  */
     311                 :             : void ds18b20_busy(unsigned action) {
     312                 :             : #if defined(OW_PORT_FAMILY_F0) || defined(OW_PORT_FAMILY_G0)
     313                 :             :     if (action) {
     314                 :             :         // Turn LED on (PA4 low)
     315                 :             : #if defined(OW_PORT_FAMILY_G0)
     316                 :             :         GPIOA->BSRR = GPIO_BSRR_BR4;
     317                 :             : #else
     318                 :             :         GPIOA->BSRR = GPIO_BSRR_BR_4;
     319                 :             : #endif
     320                 :             :     } else {
     321                 :             :         // Turn LED off (PA4 high)
     322                 :             : #if defined(OW_PORT_FAMILY_G0)
     323                 :             :         GPIOA->BSRR = GPIO_BSRR_BS4;
     324                 :             : #else
     325                 :             :         GPIOA->BSRR = GPIO_BSRR_BS_4;
     326                 :             : #endif
     327                 :             :     }
     328                 :             : #else
     329                 :             :     if (action) {
     330                 :             :         // Turn LED on (PC13 low due to pull-up LED configuration)
     331                 :             :         // BSRR BR register: atomic bit reset operation
     332                 :             :         GPIOC->BSRR = GPIO_BSRR_BR13;
     333                 :             :     } else {
     334                 :             :         // Turn LED off (PC13 high)
     335                 :             :         // BSRR BS register: atomic bit set operation
     336                 :             :         GPIOC->BSRR = GPIO_BSRR_BS13;
     337                 :             :     }
     338                 :             : #endif
     339                 :             : }
     340                 :             : #endif /* !DS18B20_TEST_HARNESS */
        

Generated by: LCOV version 2.0-1