[stm32][ucos] 1、基于ucos操作系统的LED闪烁、串口通信简单例程
* 内容简述:
本例程操作系统采用ucos2.86a版本, 建立了5个任务
任务名 优先级
APP_TASK_START_PRIO 2 主任务
Task_Com1_PRIO 4 COM1通信任务
Task_Led1_PRIO 7 LED1 闪烁任务
Task_Led2_PRIO 8 LED2 闪烁任务
Task_Led3_PRIO 9 LED3 闪烁任务
当然还包含了系统任务:
OS_TaskIdle 空闲任务-----------------优先级最低
OS_TaskStat 统计运行时间的任务-------优先级次低
1、主任务建立:
//建立主任务, 优先级最高 建立这个任务另外一个用途是为了以后使用统计任务
os_err = OSTaskCreate((void (*) (void *)) App_TaskStart, //指向任务代码的指针
(void *) , //任务开始执行时,传递给任务的参数的指针
(OS_STK *) &App_TaskStartStk[APP_TASK_START_STK_SIZE - ], //分配给任务的堆栈的栈顶指针 从顶向下递减
(INT8U) APP_TASK_START_PRIO); //分配给任务的优先级
- 这个采用老版本的任务建立函数,第一个参数通俗的说法就是该任务对应的函数,如下:
static void App_TaskStart(void* p_arg)
{
(void) p_arg;
//初始化ucos时钟节拍
OS_CPU_SysTickInit(); /* Initialize the SysTick. */ //使能ucos 的统计任务
#if (OS_TASK_STAT_EN > 0)
//----统计任务初始化函数
OSStatInit(); /* Determine CPU capacity. */
#endif
//建立其他的任务
App_TaskCreate(); while ()
{
//1秒一次循环
OSTimeDlyHMSM(, ,, );
}
}
- 当主任务建立之后,程序就转到该函数处,调用 App_TaskCreate();建立其他任务,然后进入死循环,我们会发现:这里的主任务在建立其他任务后就没啥作用的,这时可以调用相应的函数将主任务给杀死,这里没有这样做,只是让主任务进入循环。
2、其他任务建立:
static void App_TaskCreate(void)
{
//CPU_INT08U os_err; //Com1_SEM=OSSemCreate(1); //建立串口1中断的信号量
Com1_MBOX=OSMboxCreate((void *) ); //建立串口1中断的消息邮箱 //串口1接收及发送任务---------------------------------------------------------
OSTaskCreateExt(Task_Com1, //指向任务代码的指针
(void *), //任务开始执行时,传递给任务的参数的指针
(OS_STK *)&Task_Com1Stk[Task_Com1_STK_SIZE-],//分配给任务的堆栈的栈顶指针 从顶向下递减
Task_Com1_PRIO, //分配给任务的优先级
Task_Com1_PRIO, //预备给以后版本的特殊标识符,在现行版本同任务优先级
(OS_STK *)&Task_Com1Stk[], //指向任务堆栈栈底的指针,用于堆栈的检验
Task_Com1_STK_SIZE, //指定堆栈的容量,用于堆栈的检验
(void *), //指向用户附加的数据域的指针,用来扩展任务的任务控制块
OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //选项,指定是否允许堆栈检验,是否将堆栈清0,任务是否要进行浮点运算等等。
//LED1 闪烁任务------------------------------------------------------
OSTaskCreateExt(Task_Led1,(void *),(OS_STK *)&Task_Led1Stk[Task_Led1_STK_SIZE-],Task_Led1_PRIO,Task_Led1_PRIO,(OS_STK *)&Task_Led1Stk[],
Task_Led1_STK_SIZE,
(void *),
OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //LED2 闪烁任务------------------------------------------------------
OSTaskCreateExt(Task_Led2,(void *),(OS_STK *)&Task_Led2Stk[Task_Led2_STK_SIZE-],Task_Led2_PRIO,Task_Led2_PRIO,(OS_STK *)&Task_Led2Stk[],
Task_Led2_STK_SIZE,
(void *),
OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //LED3 闪烁任务------------------------------------------------------
OSTaskCreateExt(Task_Led3,(void *),(OS_STK *)&Task_Led3Stk[Task_Led3_STK_SIZE-],Task_Led3_PRIO,Task_Led3_PRIO,(OS_STK *)&Task_Led3Stk[],
Task_Led3_STK_SIZE,
(void *),
OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR);
}
- 这里是建立四个子任务第一个是串口通信的任务,一会再说,下面三个是LED闪烁任务,这里举Task_Led1说明:
//LED1闪烁任务----------------------------------------
static void Task_Led1(void* p_arg)
{
(void) p_arg;
while ()
{
LED_LED1_ON();
OSTimeDlyHMSM(, , , milsec1); LED_LED1_OFF();
OSTimeDlyHMSM(, , , milsec1);
}
}
- 可见LED闪烁任务其实就是一个无限循环,让灯的电平每隔一定时间高、每隔一定时间低来呈现闪烁的效果。那么,他是怎样实现任务切换的呢?这就是操作系统的功能了,操作系统根据每个任务的优先级,在每个子任务执行到一定时期查询当前挂起任务的优先级来选择优先级最高的进行执行。下面是在app_cfg.h中对这些任务优先级的声明:
#define APP_TASK_START_PRIO 2
#define APP_TASK_USER_IF_PRIO 13
#define APP_TASK_KBD_PRIO 12
#define Task_Com1_PRIO 4
#define Task_Led1_PRIO 7
#define Task_Led2_PRIO 8
#define Task_Led3_PRIO 9
- 可见主任务的优先级最高,串口通信的优先级其次,LED闪烁的优先级趋于中等,依次为7、8、9,
static void Task_Com1(void *p_arg){
INT8U err;
unsigned char * msg;
(void)p_arg;
while(){ //OSSemPend(Com1_SEM,0,&err); //等待串口接收指令成功的信号量
msg=(unsigned char *)OSMboxPend(Com1_MBOX,,&err); //等待串口接收指令成功的邮箱信息
//USART_OUT(USART1,&TxBuffer1[0]);
if(msg[]=='L'&&msg[]==0x31){
milsec1=atoi(&msg[]); //LED1 的延时毫秒 (mini and V3)
USART_OUT(USART1,"\r\n");
USART_OUT(USART1,"LED1: %d ms 间隔闪烁",milsec1);
}
else if(msg[]=='L'&&msg[]==0x32){
milsec2=atoi(&msg[]); //LED2 的延时毫秒 (only V3)
USART_OUT(USART1,"\r\n");
USART_OUT(USART1,"LED2: %d ms 间隔闪烁",milsec2);
}
else if(msg[]=='L'&&msg[]==0x33){
milsec3=atoi(&msg[]); //LED3 的延时毫秒 (only V3)
USART_OUT(USART1,"\r\n");
USART_OUT(USART1,"LED3: %d ms 间隔闪烁",milsec3);
}
}
}
- 这里重点讲一下串口通信的任务:这里采用消息邮箱进行消息传递,在建立其他任务App_TaskCreate(void)的开始就首先建立串口的消息邮箱:Com1_MBOX=OSMboxCreate((void *) 0);然后在串口通信的任务中进入循环后就一直等待消息邮箱的信息(第8行),如果没有消息过来就一直等待,在此期间其他任务可以进行,一旦有消息发送过来,由于串口通信的优先级较高,就能很快响应,根据收到的消息msg来重新设置led闪烁的频率。这里因为串口接收要用到中断,所以下面就说说串口通信的接收中断部分。
void USART1_IRQHandler(void)
{
unsigned int i;
unsigned char msg[];
OS_CPU_SR cpu_sr; OS_ENTER_CRITICAL(); //保存全局中断标志,关总中断// Tell uC/OS-II that we are starting an ISR
OSIntNesting++; OS_EXIT_CRITICAL(); //恢复全局中断标志 //OSTimeTick(); // Call uC/OS-II's OSTimeTick(),在os_core.c文件里定义,主要判断延时的任务是否计时到 if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //判断读寄存器是否非空
{
// Read one byte from the receive data register msg[RxCounter1++]= USART_ReceiveData(USART1); //将读寄存器的数据缓存到接收缓冲区里 if(msg[RxCounter1-]=='L'){msg[]='L'; RxCounter1=;} //判断起始标志
if(msg[RxCounter1-]=='F') //判断结束标志是否是"F" {
for(i=; i< RxCounter1; i++){
TxBuffer1[i] =msg[i]; //将接收缓冲器的数据转到发送缓冲区,准备转发 } TxBuffer1[RxCounter1]=; //接收缓冲区终止符
RxCounter1=;
//OSSemPost(Com1_SEM);
OSMboxPost(Com1_MBOX,(void *)&msg);
}
}
if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET) //
{
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
}
OSIntExit(); //在os_core.c文件里定义,如果有更高优先级的任务就绪了,则执行一次任务切换
}
- 这里接收串口数据,当发现是完整的帧时,就调用OSMboxPost(Com1_MBOX,(void *)&msg);发送一个邮箱消息,进而那边的串口任务从挂起到唤醒,执行相应的过程。
3、硬件初始化部分
说了这么多,竟然忘了说硬件初始化的部分啦!这里包括系统时钟设置、引脚使能、中断使能...放在bsp.c文件里,在main函数开始直接调用BSP_Init();就可以。
void BSP_Init(void)
{
/* System Clocks Configuration --72M*/
RCC_Configuration();
GPIO_Configuration();
/* NVIC configuration */
/*嵌套向量中断控制器
说明了USART1抢占优先级级别0(最多1位) ,和子优先级级别0(最多7位) */
NVIC_Configuration();
USART_Config(USART1,); //串口1初始化
}
void RCC_Configuration(void)
{
SystemInit();
}
void RCC_Configuration(void)
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure; /*对控制LED指示灯的IO口进行了初始化,将端口配置为推挽上拉输出,口线速度为50Mhz。PA9,PA10端口复用为串口1的TX,RX。
在配置某个口线时,首先应对它所在的端口的时钟进行使能。否则无法配置成功,由于用到了端口B,D,E, 因此要对这几个端口的时钟
进行使能,同时由于用到复用IO口功能用于配置串口。因此还要使能AFIO(复用功能IO)时钟。*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOB , ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_3; //LED2, LED3
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
void GPIO_Configuration(void)
void NVIC_Configuration(void)
{ //EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure; /* Configure one bit for preemption priority */
#if defined (VECT_TAB_RAM)
/* Set the Vector Table base location at 0x20000000 */
NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
#elif defined(VECT_TAB_FLASH_IAP)
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x2000);
#else /* VECT_TAB_FLASH */
/* Set the Vector Table base location at 0x08000000 */
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
#endif /* Configure the NVIC Preemption Priority Bits */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //设置串口1中断
NVIC_InitStructure.NVIC_IRQChannelSubPriority = ;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure); }
void NVIC_Configuration(void)
void USART_Config(USART_TypeDef* USARTx,u32 baud){
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure; //PA9,PA10 复用IO口功能用于配置串口。因此要使能AFIO(复用功能IO)时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //usart_init----------------------------------------------------
/* Configure USART1 Rx (PA.10) as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入模式
GPIO_Init(GPIOA, &GPIO_InitStructure); /* Configure USART1 Tx (PA.09) as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate =baud; //速率115200bps
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //数据位8位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位1位
USART_InitStructure.USART_Parity = USART_Parity_No; //无校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 /* Configure USART1 */
USART_Init(USARTx, &USART_InitStructure); //配置串口参数函数 /* Enable USART1 Receive and Transmit interrupts */
USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE); //使能接收中断
//USART_ITConfig(USARTx, USART_IT_TXE, ENABLE); //使能发送缓冲空中断 /* Enable the USART1 */
USART_Cmd(USARTx, ENABLE); //USART_ClearFlag(USARTx, USART_FLAG_TXE); /* 清发送完成标志,Transmission Complete flag */
}
void USART_Config(USART_TypeDef* USARTx,u32 baud)
PS:相关链接
LZ blog:http://www.cnblogs.com/zjutlitao/
工程代码:http://pan.baidu.com/s/1jG850X4
[stm32][ucos] 1、基于ucos操作系统的LED闪烁、串口通信简单例程的更多相关文章
- STM32学习笔记:【004】USART串口通信
版本:STM32F429 Hal库v1.10 串口通信能够实现两块电路之间不同的通信,在开发中作为打印调试也是一门利器(printf重定向). 补充一点小知识: 1. weak修饰符修饰的函数,说明这 ...
- 基于Arduino和python的串口通信和上位机控制
引言 经常的时候我们要实现两个代码之间的通信,比如说两个不同不同人写的代码要对接,例如将python指令控制Arduino控件的开关,此处使用串口通信是非常方便的,下面笔者将结合自己踩过的坑来讲述下自 ...
- 基于FPGA的电压表与串口通信(上)
实验原理 该实验主要为利用TLC549采集模拟信号,然后将模拟信号的数字量通过串口发送到PC上上位机进行显示,使用到的TLC549驱动模块在进阶实验已经使用到了,串口模块在基础实验也已经使用到了,本实 ...
- 基于FPGA的电压表与串口通信(下)
实验操作 上电 接入5V电源,用配套的线,USB那端接电脑即可: 电源开关 按下电源开关 接串口线 接下载线 现在电脑装串口线驱动 R340qd.zip 双击进行安装 设置串口调试助手 Com1要根据 ...
- [stm32][ucos][ucgui] 2、LED闪烁、串口、滑块、文本编辑框简单例程
上一篇:[stm32][ucos] 1.基于ucos操作系统的LED闪烁.串口通信简单例程 * 内容简述: 本例程操作系统采用ucos2.86a版本, 建立了7个任务 任务名 ...
- 【STM32项目笔记】STM32CubeMX+Keil+Proteus联合实现LED闪烁
摘要 利用STM32CubeMx配置STM32芯片的功能,然后将配置后的内容生成代码,并导出成可以使用Keil打开编辑的文件,在Keil中添加控制代码后,下载到Proteus仿真中,使用仿真观察代码执 ...
- [ZigBee] 16、Zigbee协议栈应用(二)——基于OSAL的无线控制LED闪烁分析(下)
说在前面:上一篇介绍了无线LED闪烁实现的OSAL部分,本篇介绍如何实现无线数据收发及数据处理: 上一篇是用SI跟着流程查看源码,我个人认为以架构的思维去了解代码能让人更清晰 ::ZMain.c程序入 ...
- uC/OS-III学习2::uC/OS-III LED闪烁实验
1 前言: 看完了uC/OS-III的基本介绍之后,大致对这个操作系统有了点了解,但真正的理解还是要通过不断的去使用,在使用中体验uC/OS-III的乐趣和更深的理解其工作原理是非常重要的.因此,我在 ...
- Keil MDK STM32系列(三) 基于标准外设库SPL的STM32F407开发
Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...
随机推荐
- Ubuntu 利用 xinetd 限制 SSH 连接数
安装 xinted sudo apt-get install xinetd 修改配置文件 sudo vim /etc/xinetd.conf 在配置文件中加入下列配置 defaults { insta ...
- jQuery 屏幕遮罩
1.先做一个可以覆盖整个屏幕的div,颜色为黑色,然后再设置透明度,作为遮罩#zhezhao { position: absolute; top: 0px; left: 0px; width: 100 ...
- [转载] Android逃逸技术汇编
本文转载自: http://blogs.360.cn/360mobile/2016/10/24/android_escape/ 摘 要 传统逃逸技术涉及网络攻防和病毒分析两大领域,网络攻防领域涉 ...
- to_char 详解
对TO_CHAR的讨论可以分为从两种类型的数据到字符的转换:DATE和NUMBER. TO_CHAR函数返回VARCHAR2数据类型的值. 1. NUMBER TO CHAR 语法: TO_CHAR( ...
- 如何在其他电脑上运行VS2005编译的DEBUG版应用程序
做项目的过程中,遇到这样的问题:在自己的电脑上用VS2005编译好的DEBUG版程序在其它的没有安装VS2005的电脑上没有办法运行,郁闷至极啊. 直 接拷贝文件后,错误信息如下:"This ...
- ssh搭建后的简化
关于ssh如何搭建还有不懂得朋友可以参考以下网址:http://www.cnblogs.com/LarryBlogger/p/5841446.html 在这里我就不重复再讲了! ssh搭建后的简化 简 ...
- Selenium2+python自动化5-操作浏览器基本方法
前言 前面已经把环境搭建好了,这从这篇开始,正式学习selenium的webdriver框架.我们平常说的 selenium自动化,其实它并不是类似于QTP之类的有GUI界面的可视化工具,我们要学的是 ...
- ios8以后,使用UIAlertViw时pop/push页面后,键盘闪一下的问题
代码为 UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"" message:@"感谢你对我们提出的意见或 ...
- XML文件操作(C#)
XML应用于Web开发的许多方面,常用于简化数据的存储和共享,它的设计宗旨是传输数据,而非显示数据.下面简单介绍一下C#是如何对XML文件进行读取的. 1.首先,建立XML对象并关联XML文件 str ...
- js截取字符串显示引号两种方法
//截取字符串多余显示引号 var cutStrForNum = function (str, num) { var len = 0; for (var i = 0; i < str.lengt ...