I am experimenting with STM32F303RE Nucleo board on SW4STM32 with FreeRTOS v10.1.1 and I was thinking of parsing NMEA sentences by adding the characters to a buffer using UART interrupt and then process them at my convenience in a task. Since most NMEA sentence parser libraries I know are written in C++, I thought of modifying my existing STM32 examples before throwing the C++ GPS parser library into the mix.
The following code (main.c
) works well in reading the NMEA sentences. It was initially a C project which I converted to C++ using the feature on SW4STM32 although I'm not sure if it actually got converted. It works by reading the characters from USART3, populates the buffer, and then prints '#' every time the buffer is full and resets the counter of the buffer. As sanity check, it also prints a "hello world" message with the tick count to USART2 (Virtual COM) every second.
When I change main.c
to main.cpp
, the project builds and can be uploaded to the board, but it freezes after the first printout on USART2 and USART3. I've traced back the code on the debugger and I can see that the DefaultHandler
executes and traps the board in an Infinite_Loop
as a result of WWDG_IRQHandler
misfiring. Here's a screenshot of the debug window.
I can confirm that the crash occurs due to USART3_EXTI28_IRQHandler
since disabling it renders the board to print the "hello world" messages normally. However, I can't seem to figure out where (or how) things go wrong after I rename main.c
to main.cpp
. Any help is appreciated.
main.c
#define true 1
#define false 0
#define AVAILABLE true
#define NOT_AVAILABLE false
#include "stddef.h"
#include "string.h"
#include "stdio.h"
#include "stm32f30x.h"
#include "FreeRTOS.h"
#include "task.h"
//#include "TinyGPS++.h"
TaskHandle_t task1_handle = NULL;
void vTask1(void *pvParameters);
void prvSetupUSART();
void prvHardwareSetup();
void printmsg(char * data);
// global variables
uint8_t uart_access_key = AVAILABLE;
char output_msg[255] = {0};
//extern void initialise_monitor_handles();
#define BUFFER_LEN 255
char uart_msg[BUFFER_LEN] = {0};
uint16_t uart_counter = 0;
int main(void)
{
// Enable Data Watchpoint and Trace cycle counter (DWT CYCCNT)
// Cycle counter is used to get time stamps for SEGGER SystemView
DWT->CTRL |= (1 << 0);
// 1. Reset system clock settings
// PLL = 0, HSE = 0, HSI = 1, system_clock = 8 MHz, cpu_clock = 8 MHz
RCC_DeInit();
// 2. Update SystemClockSpeed variable
SystemCoreClockUpdate();
// Setup hardware and make necessary initializations
prvHardwareSetup();
//initialise_monitor_handles();
// Make calls to SEGGER SystemView to configure and start recording
SEGGER_SYSVIEW_Conf();
SEGGER_SYSVIEW_Start();
xTaskCreate(vTask1, "Task-1", configMINIMAL_STACK_SIZE + 256, NULL, 2, &task1_handle);
vTaskStartScheduler();
for(;;);
}
void vTask1(void *pvParameters)
{
while (true)
{
if (uart_access_key == AVAILABLE)
{
uart_access_key = NOT_AVAILABLE;
memset(output_msg, 0, sizeof(output_msg));
sprintf(output_msg, "Hello world from Task-1: %lu\r\n", (uint32_t)xTaskGetTickCount());
printmsg(output_msg);
USART_SendData(USART3, '#');
uart_access_key = AVAILABLE;
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
vTaskDelete(NULL);
}
void prvSetupUSART()
{
// Setup USART2
// 1. Enable clock
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
// 2. Initialize GPIOA pins PA2 and PA3
GPIO_InitTypeDef gpio_init_pins;
memset(&gpio_init_pins, 0, sizeof(gpio_init_pins));
gpio_init_pins.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
gpio_init_pins.GPIO_Mode = GPIO_Mode_AF;
gpio_init_pins.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &gpio_init_pins);
// 3. AF modes settings for pins
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_7); // original lecture uses GPIO_AF_USART2
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_7); // original lecture uses GPIO_AF_USART2
// 4. Initialize UART2 and set parameters
USART_InitTypeDef usart2_init;
memset(&usart2_init, 0, sizeof(usart2_init));
usart2_init.USART_BaudRate = 9600;
usart2_init.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
usart2_init.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
usart2_init.USART_Parity = USART_Parity_No;
usart2_init.USART_StopBits = USART_StopBits_1;
usart2_init.USART_WordLength = USART_WordLength_8b;
// Initialize USART2 using defined parameters
USART_Init(USART2, &usart2_init);
/* Enable RXNE interrupt */
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
/* Enable USART1 global interrupt */
NVIC_EnableIRQ(USART2_IRQn);
// Enable USART
USART_Cmd(USART2, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
// 2. Initialize GPIOA pins PA2 and PA3
memset(&gpio_init_pins, 0, sizeof(gpio_init_pins));
gpio_init_pins.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
gpio_init_pins.GPIO_Mode = GPIO_Mode_AF;
gpio_init_pins.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB, &gpio_init_pins);
// 3. AF modes settings for pins
GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_7); // original lecture uses GPIO_AF_USART3
GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_7); // original lecture uses GPIO_AF_USART3
// 4. Initialize UART2 and set parameters
USART_InitTypeDef usart3_init;
memset(&usart3_init, 0, sizeof(usart3_init));
usart3_init.USART_BaudRate = 9600;
usart3_init.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
usart3_init.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
usart3_init.USART_Parity = USART_Parity_No;
usart3_init.USART_StopBits = USART_StopBits_1;
usart3_init.USART_WordLength = USART_WordLength_8b;
// Initialize USART2 using defined parameters
USART_Init(USART3, &usart3_init);
/* Enable RXNE interrupt */
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
/* Enable USART1 global interrupt */
NVIC_EnableIRQ(USART3_IRQn);
// Enable USART
USART_Cmd(USART3, ENABLE);
}
void prvHardwareSetup()
{
// Enable bus clock
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); // original lecture uses AHB1 - maybe difference in boards?
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); // original lecture uses AHB1 - maybe difference in boards?
// Enable USART2
prvSetupUSART();
}
void printmsg(char * data)
{
for (int i = 0; i < strlen(data); i++)
{
while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) != SET);
USART_SendData(USART2, data[i]);
}
}
void USART2_EXTI26_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
char c = (char)USART_ReceiveData(USART2);
if (c == 't')
{
USART_SendData(USART2, 'T');
}
++uart_counter;
}
}
void USART3_EXTI28_IRQHandler(void)
{
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
{
char c = (char)USART_ReceiveData(USART3);
uart_msg[uart_counter++] = c;
if (uart_counter >= BUFFER_LEN)
{
USART_SendData(USART3, '#');
uart_counter = 0;
}
}
}
LinkerScript.ld
/*
******************************************************************************
**
** File : LinkerScript.ld
**
** Author : Auto-generated by Ac6 System Workbench
**
** Abstract : Linker script for STM32F303RETx Device from STM32F30 series
** 64Kbytes RAM
** 512Kbytes ROM
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used.
**
** Target : STMicroelectronics STM32
**
** Distribution: The file is distributed “as is,” without any warranty
** of any kind.
**
*****************************************************************************
** @attention
**
** <h2><center>© COPYRIGHT(c) 2019 Ac6</center></h2>
**
** Redistribution and use in source and binary forms, with or without modification,
** are permitted provided that the following conditions are met:
** 1. Redistributions of source code must retain the above copyright notice,
** this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright notice,
** this list of conditions and the following disclaimer in the documentation
** and/or other materials provided with the distribution.
** 3. Neither the name of Ac6 nor the names of its contributors
** may be used to endorse or promote products derived from this software
** without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
*****************************************************************************
*/
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x20010000; /* end of RAM */
_Min_Heap_Size = 0; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* Memories definition */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
ROM (rx) : ORIGIN = 0x8000000, LENGTH = 512K
}
/* Sections */
SECTIONS
{
/* The startup code into ROM memory */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >ROM
/* The program code and other data into ROM memory */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >ROM
/* Constant data into ROM memory*/
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >ROM
.ARM.extab : {
. = ALIGN(4);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(4);
} >ROM
.ARM : {
. = ALIGN(4);
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
. = ALIGN(4);
} >ROM
.preinit_array :
{
. = ALIGN(4);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
} >ROM
.init_array :
{
. = ALIGN(4);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
} >ROM
.fini_array :
{
. = ALIGN(4);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(4);
} >ROM
/* Used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections into RAM memory */
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >RAM AT> ROM
/* Uninitialized data section into RAM memory */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
/* Remove information from the compiler libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}