How to implement a non-blocking scanf function
B.Montanari
ST Employee
Options
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Email to a Friend
- Printer Friendly Page
- Report Inappropriate Content
on 2022-11-08 8:57 AM
How to implement a nonblocking scanf function?
1. Introduction
This article shows how to implement and redirect the scanf function to the UART, in a nonblocking way. Therefore, was chosen that this function is working through tasks on FreeRTOS™ and so the main code is located at the FreeRTOS™ application file. The target for this article is the STM32G071 Nucleo board but it can be easily tailored to other STM32’s boards.
2. Hardware:
- STM32G071 Nucleo board
3. Software:
- STM32CubeIDE v1.10.1 or later
- Tera Term or similar
4. Configuration:
The first step is to create a new project on STM32CubeIDE for the board. Create a project to the NUCLEO-G071RB and name it. Opening the *.ioc file of the project, go to the Pinout & Configuration tab > System Core > SYS and check the Serial Wire and System Wake-Up 1 box, also put the Timebase Source as the higher timer allowed.The next step is to configure the User Button (PB13) as an External Interruption since it will be the trigger to call the scanf function when it is necessary. After configuring that, go to System Core > GPIO and put External Interrupt Mode with Falling edge trigger detection on GPIO mode, label it and check the NVIC box “EXTI line 4 to 5 interrupts”.
Now it is time to configure the UART where the scanf function will be redirect to. On Connectivity > USART2, select the asynchronous mode, set the Basic Parameter in the Parameter Settings as follows on the image below and check the NVIC box for the global interrupt of USART2.
Finally, we need to configure the FreeRTOS parameters. On the Middleware tab > FreeRTOS™, choose the CMSIS_V1 interface as mode, go to Config parameters and change the following parameters:
- On Kernel settings:
- MINIMAL_STACK_SIZE = 255 Words
- MAX_TASK_NAME_LEN = 255
- On Memory management settings:
- TOTAL_HEAP_SIZE = 12000 Bytes
- Memory Management scheme = heap_4
This application consists of two tasks: one that contains the scanf function and the other one controls its call. The controller one (MainTask) will have higher priority than the controlled one (ScanfTask). On Tasks and Queues, add the tasks and configure them as the figure below:
We will also create a queue to receive and store the input data. On the same tab, add a new queue and configure it as follows:
At last, there will be used two semaphores: one to enable the execution of the scanf function task and the other to enable the return value of the queue by printing the input data received so far. On Timers and Semaphores tab, add two semaphores and name them.
With this last step, all the necessary configuration is done. Now it is time to code!
5. Code Development:
All the functionality of the scanf function is possible thanks to the priority relation between the two declared tasks. The image below shows the application diagram and relationship between tasks.Here is the final code implementation:
/* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN Variables */ uint8_t ch[1]; /* USER CODE END Variables */ /* USER CODE BEGIN Header_StartMainTask */ /** * @brief Function implementing the mainTask thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_StartMainTask */ void StartMainTask(void const * argument) { /* USER CODE BEGIN StartMainTask */ printf("Press the user button to write something\r\n"); osSemaphoreWait(CallScanfTaskHandle, 0xFFFF); /* Infinite loop */ for(;;) { osSemaphoreWait(CallScanfTaskHandle, 0xFFFF); printf("\r\nWrite something:\r\n"); HAL_UART_Receive_IT(&huart2, ch, 1); osDelay(1); } /* USER CODE END StartMainTask */ } /* USER CODE BEGIN Header_StartScanfTask */ /** * @brief Function implementing the scanfT thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_StartScanfTask */ void StartScanfTask(void const * argument) { /* USER CODE BEGIN StartScanfTask */ uint32_t QueueSize = 0; osEvent QueueData; osSemaphoreWait(ReleaseQueueHandle, 0xFFFF); /* Infinite loop */ for(;;) { osSemaphoreWait(ReleaseQueueHandle, 0xFFFF); QueueSize = osMessageAvailableSpace(QueueHandle); printf ("\r\nYou typed:\r\n"); for(uint8_t i = 0; i<=(255-QueueSize); i++) { QueueData = osMessageGet(QueueHandle, 100); HAL_UART_Transmit(&huart2, (uint8_t*)&QueueData.value.v, 1, 4000); } printf ("\r\nPress again for a new scanf cycle\r\n"); } /* USER CODE END StartScanfTask */ } /* Private application code --------------------------------------------------*/ /* USER CODE BEGIN Application */ void __io_putchar(int ch) { HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF); } void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin) { osSemaphoreRelease(CallScanfTaskHandle); } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (ch[0] == '\r' ) { osSemaphoreRelease(ReleaseQueueHandle); } else { osMessagePut(QueueHandle, ch[0], 200); HAL_UART_Receive_IT(&huart2, ch, 1); } } /* USER CODE END Application */
6. Results
Here is the result at Tera Term console terminal:Hope you enjoyed the article!
Labels: