基于LiteOS的多任务串行通信和LCD显示程序Demo
【摘要】 通常在基于系统的应用程序开发时,通信程序和显示程序会不同的线程中来运行,以避免由于通信程序的问题而导致界面程序也一同挂掉的现象。基于LiteOS这种轻量级的系统上的应用开发也会遇到类似的情况。本文使用小熊派开发板,参考IoT Link中的Demo程序,基于LiteOS系统之上,使用LiteOS的多任务管理功能,编写实现了两个任务之间串行通信的Demo程序,并在每个任务中将收发相关的信息使用L...
通常在基于系统的应用程序开发时,通信程序和显示程序会不同的线程中来运行,以避免由于通信程序的问题而导致界面程序也一同挂掉的现象。基于LiteOS这种轻量级的系统上的应用开发也会遇到类似的情况。本文使用小熊派开发板,参考IoT Link中的Demo程序,基于LiteOS系统之上,使用LiteOS的多任务管理功能,编写实现了两个任务之间串行通信的Demo程序,并在每个任务中将收发相关的信息使用LCD显示出来。
1. 硬件设计
本文使用了小熊派智慧农业的开发主板。在这个开发主板上,以STM32L431RTC6为主控制器。开发板上连接E53的接口中有UART3的连接端子,如下图中的18和19两个引脚。其中18引脚对应的是STM32L431RTC6的PC4引脚,即UART3_Tx; 19引脚对应的是STM32L431RTC6的PC5引脚,即UART3_Rx。
在其P5端子排中预留了UART2的连接端子,如下图中的5和6引脚。其中5引脚对应的是STM32L431RTC6的PA2引脚,即UART2_Tx;6引脚对应的是STM32L431RTC6的PA3引脚,即UART2_Rx。
Demo程序使用UART2发送数据,UART3接收数据。因此将5引脚和19引脚连接,6引脚和18引脚连接。注意这里的引脚编号是指小熊派开发主板上的相关编号。连接好的硬件如下图所示。
2. Demo程序设计
2.1 Demo程序的工程创建
基于IoT Link Studio进行设计开发。在VS Code中,使用IoT Link Studio,创建基于STM32L431_BearPi_OS_Func的硬件平台的示例工程osal_task_demo。如下图所示。
2.2 程序设计思路
2.2.1 两个任务的创建
在程序中创建两个任务,分别称为任务1和任务2.在任务1中使用UART2发送数据,并将发送的数据显示在LCD上。在任务2中使用UART3接收数据,并将接收的数据显示在LCD上。如下图是创建两个任务的函数。在这个函数中调用了初始化uart2_3_init()函数。
//demo_main函数
//初始化UART2和UART3,并创建了两个任务。这样两个任务就会被LiteOS系统自动调用。
int standard_app_demo_main()
{
//printf("user task 1 exit!\r\n");
uart2_3_init();
user_task1_id = osal_task_create("user_task1",user_task1_entry,NULL,0x400,NULL,USER_TASK1_PRI);
user_task2_id = osal_task_create("user_task2",user_task2_entry,NULL,0x400,NULL,USER_TASK2_PRI);
return 0;
}
2.2.2 UART2 和 UART3的初始化及相关中断的创建
为了使用UART2和UART3,自定义了初始化UART2和UART3的函数,即UART2_3_init(void)。如下图所示的代码。
//小熊派主板上的UART2和UART3的初始化以及相关中断的创建
//UART2是PA2_Tx,PA3_Rx; UART3是PC4_Tx,PC5_Rx
void uart2_3_init(void)
{
MX_USART2_UART_Init(); //初始化串口 PC4 PC5 波特率9600
MX_USART3_UART_Init(); //初始化串口 PA2 PA3 波特率为9600
LOS_HwiCreate(USART2_IRQn, 6,0,USART2_IRQHandler,NULL); //创建中断
LOS_HwiCreate(USART3_IRQn, 7,0,USART3_IRQHandler,NULL); //创建中断 LiteOS默认的最小的中断优先级数值是7
}
在这个函数中首先调用usart.c中的两个初始化函数,来进行UART2和UART3的初始化。
串行口在通信时两端的波特率、停止位、奇偶校验位等参数设置需要相同。在初始化两个UART时,要注意修改usart.c中的代码确保两个串行口的相关参数是相同的,这样可以避免一些问题产生。如下是usart.c中初始化UART2和UART3的函数代码。
/* USART2 init function */
void MX_USART2_UART_Init(void)
{
huart2.Instance = USART2;
huart2.Init.BaudRate = 9600;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/* USART3 init function */
void MX_USART3_UART_Init(void)
{
huart3.Instance = USART3;
huart3.Init.BaudRate = 9600; //115200
huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;
huart3.Init.Mode = UART_MODE_TX_RX;
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart3.Init.OverSampling = UART_OVERSAMPLING_16;
huart3.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart3.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
然后调用
LOS_HwiCreate(USART2_IRQn, 6,0,USART2_IRQHandler,NULL); //创建中断
LOS_HwiCreate(USART3_IRQn, 7,0,USART3_IRQHandler,NULL); //创建中断 LiteOS默认的最小的中断优先级数值是7
创建UART2的全局中断USART2_IRQn,并将全局中断处理函数USART2_IRQHandler与这个中断挂接。
创建UART3的全局中断USART3_IRQn,并将全局中断处理函数USART3_IRQHandler与这个中断挂接。
上述两个中断处理函数都在stm32l4xx_it.c文件中,代码如下。
**
* @brief This function handles USART2 global interrupt.
*/
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
/* USER CODE END USART2_IRQn 1 */
}
/**
* @brief This function handles USART3 global interrupt.
*/
void USART3_IRQHandler(void)
{
/* USER CODE BEGIN USART3_IRQn 0 */
/* USER CODE END USART3_IRQn 0 */
HAL_UART_IRQHandler(&huart3);
/* USER CODE BEGIN USART3_IRQn 1 */
/* USER CODE END USART3_IRQn 1 */
}
这两个中断服务主要调用了STM32L4xx_HAL_Driver(STM32L4系列的硬件驱动库)中的函数,在文件stm32l4xx_hal_uart.c中有它们的实现代码。这相当于最终使用了STM32L4库函数中的中断服务函数。在创建上述的中断时,要留意,中断优先级最好不要大于7,因为LiteOS中默认配置的中断优先级的最小数值是7。有了中断后,两个串口之间的通信就可以采用非阻塞的方式进行了。
2.2.3 采用非阻塞方式通信
主要定义了使用UART2发送数据的函数和使用UART3接收数据的函数。分别接收如下。
发送函数uart2_send,代码如下
static ssize_t uart2_send(const char *buf,size_t len, unsigned int timeout)
{
HAL_UART_Transmit(&huart2,(unsigned char *)buf,len,timeout);
//HAL_UART_Transmit(&huart3, "$CCMSG,GGA,1,0,*19\r\n", 20, 200);
HAL_Delay(200);
return len;
}
这个函数其实主要是调用HAL_UART_Transmit函数来通过UART2发送数据。这个HAL_UART_Transmit函数也属于STM32L4xx系列的硬件驱动库函数,也在文件stm32l4xx_hal_uart.c文件中。由于使用的是UART2发送数据,所以在调用这个函数的时候,第一个参数需要传递&huart2. huart2包含了UART2的很多配置信息,是在usart.c中定义的一个全局变量,可以在这里使用。
接收数据的函数uart3_receive,代码如下
//使用UART3接收数据的函数
void uart3_receive(void)
{
HAL_StatusTypeDef ret;
ret=HAL_UART_Receive_IT(&huart3,g_revBuf,UART_INFO_MAX_LEN);
}
这个函数其实主要是调用HAL_UART_Receive_IT函数来通过UART3接收数据。这个HAL_UART_Receive_IT函数也属于STM32L4xx系列的硬件驱动库函数,也在文件stm32l4xx_hal_uart.c文件中。由于使用的是UART3接收数据,所以在调用这个函数的时候,第一个参数需要传递&huart3. huart3包含了UART3的很多配置信息,是在usart.c中定义的一个全局变量,可以在这里使用。
上述两个函数的使用,可以在stm32l4xx_hal_uart.c文件中的说明部分能看到如下使用提示信息
2.2.4 两个任务的入口函数
LiteOS系统中,创建任务时,需要指定任务的入口函数。任务运行的时候,是从入口函数开始执行的。两个任务的入口函数分别说明如下。
任务1的入口函数user_task1_entry()
//任务1的入口函数
//任务1主要是通过UART2不断的发送数据,并将发送的数据在LCD上显示
static int user_task1_entry()
{
int i=1;
while(i>0)
{
LCD_Clear(WHITE);
POINT_COLOR = BLUE;
switch(i)
{
case 1:
uart2_send("abcdefg",UART_INFO_MAX_LEN,200);
LCD_ShowString(10, 10, 240, 24, 16, "UART2_Sent Info:");
LCD_ShowString(10, 30, 240, 24, 16, "abcdefg");
break;
case 2:
uart2_send("0123456789",UART_INFO_MAX_LEN,200);
LCD_ShowString(10, 10, 240, 24, 16, "UART2_Sent Info:");
LCD_ShowString(10, 30, 240, 24, 16, "0123456789");
break;
case 3:
uart2_send("ABCDEFG",UART_INFO_MAX_LEN,200);
LCD_ShowString(10, 10, 240, 24, 16, "UART2_Sent Info:");
LCD_ShowString(10, 30, 240, 24, 16, "ABCDEFG");
break;
case 4:
uart2_send("I love China.",UART_INFO_MAX_LEN,200);
LCD_ShowString(10, 10, 240, 24, 16, "UART2_Sent Info:");
LCD_ShowString(10, 30, 240, 24, 16, "I love China.");
break;
case 5:
uart2_send("I like IOT.",UART_INFO_MAX_LEN,200);
LCD_ShowString(10, 10, 240, 24, 16, "UART2_Sent Info:");
LCD_ShowString(10, 30, 240, 24, 16, "I like IOT.");
break;
}
osal_task_sleep(2*500);
i++;
if(i>5)
i=1;
printf("task1-%ld: Send task is working.\r\n", user_task1_id);
}
printf("user task 1 exit!\r\n");
return 0;
}
任务1入口函数主要是不断调用发送数据的函数发送一些示例的信息,同时将发送的信息显示在LCD上。逻辑比较简单,就不多做说明了。
任务2的入口函数user_task2_entry()
/任务2的入口函数
//任务2主要是通过UART3不断接收数据,并将接收到的数据显示在LCD上
static int user_task2_entry()
{
int i=5;
while (1)
{
LCD_Clear(WHITE);
POINT_COLOR = RED;
uart3_receive();
LCD_ShowString(10, 50, 240, 24, 16, "UART3_Received Info:");
if(g_revBuf[0] != 0 )
{
LCD_ShowString(10, 70, 240, 24, 16, g_revBuf);
}
printf("task 2-%ld: LCD show task is working.\r\n", user_task2_id);
osal_task_sleep(2*600);
}
printf("user task 2 exit!\r\n");
return 0;
}
任务2的入口函数主要是不断调用接收函数,接收数据,并将接收到的数据信息显示在LCD上。同样,逻辑比较简单,就不多做说明了。
2.2.5 头文件及一些全局变量
程序中使用的头文件及一些全局变量如下
#include <osal.h>
#include <usart.h>
#include <stm32l431xx.h>
#include "gpio.h"
#include "lcd.h"
#include <stm32l4xx_it.h>
#define USER_TASK1_PRI 12
#define USER_TASK2_PRI 11
#define UART_INFO_MAX_LEN 20
uint32_t user_task1_id = 0;
uint32_t user_task2_id = 0;
unsigned char g_revBuf[UART_INFO_MAX_LEN]={0};
注意在编译程序的时候,把相关的头文件包括在头文件路径中。
3. 编译运行验证
在IoT Link Studio中编译程序,编译成功后的结果如下
编译成功后,下载程序到开发板,运行验证,一些效果图片如下。
4. 总结
通过这个Demo程序,在LiteOS系统基础上,实现了多任务的串行数据收发及显示功能。总结Demo程序,可以看出,LiteOS提供了很好的任务管理功能。每个任务的具体功能的编写时,除了使用LiteOS的一些API函数外,还可以使用很多STM32的库函数。本文的Demo可以说是需要综合应用了LiteOS的一些API和STM32的库函数,实现了串行口的收发和信息显示功能。
个人的思路难免存在疏漏,有不正确或者不合适的地方,欢迎大家提出讨论,共同学习共同进步。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
作者其他文章
评论(0)