如何实现非阻塞的scanf函数?

1.简介

本文展示了如何以非阻塞的方式实现scanf函数并将其重定向到UART。因此,我们选择该功能在FreeRTOS上执行任务™ 因此主代码位于FreeRTOS™ 应用程序文件。本文的目标是STM32G071 Nucleo板,但它可以很容易地针对其他STM32板进行定制。
 

2.硬件:

  • STM32G071 Nucleo板

3.软件:

  • STM32CubeIDE v1.10.1或更高版本
  • Tera期限或类似期限

4.配置:

第一步是在STM32CubeIDE上为板创建一个新项目。在NUCLEO-G071RB中创建一个项目并命名。打开项目的*.ioc文件,转到引脚输出和配置选项卡>系统核心>系统,选中串行线和系统唤醒1框,并将Timebase Source设置为允许的更高计时器。

下一步是将用户按钮(PB13)配置为外部中断,因为它将在必要时触发调用scanf函数。配置后,转到System Core>GPIO,将带下降沿触发检测的外部中断模式置于GPIO模式,标记并选中NVIC框“EXTI line 4 to 5 interrupts”。



现在是时候配置扫描功能重定向到的UART了。在Connectivity>USART2上,选择异步模式,在Parameter Settings(参数设置)中设置Basic Parameter(基本参数),如下图所示,并选中NVIC框以查看USART2的全局中断。

最后,我们需要配置FreeRTOS参数。在“中间件”选项卡上>FreeRTOS™, 选择CMSIS_V1接口作为模式,转到配置参数并更改以下参数:
  • 在内核设置上:
  • MINIMAL_STACK_SIZE=255个单词
  • 最大任务名称长度=255
 
  • 关于内存管理设置:
  • TOTAL_HEAP_SIZE=12000字节
  • 内存管理方案=堆_4

该应用程序由两个任务组成:一个任务包含scanf函数,另一个任务控制其调用。控制器任务(MainTask)的优先级将高于受控任务(ScanfTask)。在“任务”和“队列”中,添加任务并将其配置为下图所示:

我们还将创建一个队列来接收和存储输入数据。在同一选项卡上,添加一个新队列,并按如下方式进行配置:

最后,将使用两个信号量:一个用于启用scanf函数任务的执行,另一个用于通过打印到目前为止接收到的输入数据来启用队列的返回值。在“计时器和信号量”选项卡上,添加两个信号量并命名它们。
现在,生成代码的最后一个挂起的配置是生成外围初始化,其中“.c/.h”文件是分开的。转到“项目管理器”部分,在“代码生成器”选项卡上,选中“生成的文件”区域的第一个框。

最后一步,完成了所有必要的配置。现在是时候编码了!

5.代码开发:

由于两个声明任务之间的优先级关系,scanf函数的所有功能都是可能的。下图显示了应用程序关系图和任务之间的关系。
基本上,主任务(“TASK1”)将使用按下用户按钮时释放的CallScanfTask信号量(“BUTTON_semaphore”)控制扫描任务(“TASK2”)的执行。按下按钮时,可以通过UART键入任何内容,这些内容将存储在创建的队列中。最后,当按下enter选项卡时,队列中包含的最后一个字符串将被释放并打印出来,从而释放位于ScanfTask(“TASK2”)内部的ReleaseQueue信号量(“SCANF_semaphore”)。
以下是最终的代码实现:
/*专用变量------------------------------------------------------*/*用户代码开始变量*/uint8_t ch[1];/*用户代码结束变量*/*用户代码开始Header_StartMainTask*/**@brief实现mainTask线程的函数。*@param argument:未使用*@retval None*/*用户代码END Header_StartMainTask*/void StartMainTask(void const*argument){/*用户代码BEGIN StartMainTask*/printf(“按用户按钮写入内容\r\n”);osSemaphoreWait(CallScanfTaskHandle,0xFFFF);/*(;)的无限循环*/{osSemaphreeWait(&huart2,ch,1);osDelay(1);}/*用户代码结束开始主任务*/}/*用户代码开始标题开始扫描任务*/**@实现扫描线程的简短函数。*@param argument:未使用*@retval None*/*用户代码END Header_StartScanfTask*/void StartScanfTask(void const*argument){/*用户代码BEGIN StartScanfTask*/uint32_t QueueSize=0;osEvent QueueData;osSemaphoreWait(ReleaseQueueHandle,0xFFFF);/*无限循环*/for(;){osSemaphreeWait(“\r\n您键入了:\r\n”);对于(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\n再按一次以获取新的扫描周期”);}/*用户代码结束开始扫描任务*/}/*专用应用程序代码--------------------------------------------------------*/*用户代码开始应用程序*/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);}/*用户代码结束应用程序*/
 

6.结果

以下是Tera Term控制台终端的结果:

希望你喜欢这篇文章!