0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

LEDs状态灯任务(线程)设计 (基于RTOS)

黄工的嵌入式技术圈 来源:网站整理 2020-03-12 11:30 次阅读

我们学习MCU开发,大部分都是面向过程的开发,但实际项目一般要求我们有面向对象(模块化)的方式来开发。

刚学习C语言开发的朋友,应该常常听说面向对象,但实际对于面向对象开发可能还是不太了解。

为了初学者进一步理解,本文结合实际项目(LEDs状态灯)给大家带来比较基础的模块化设计。

Ⅰ关于C语言的模块化

对于MCU的开发,大部分人都还是习惯性用的C语言,原因之一在于C语言具有高效的特点。

可以了解一下,许多操作系统的内核使用的编程语言,其实都用到了C语言,这就是C语言的优点,也是C语言这么多年不衰败的原因。

说回来,对于MCU的开发,除了C语言,当然还可以其它语言,像C++有许多人就用上了。

C++语言本身就是面向对象的开发语言,定义一个类,可以包含许多成员。站在C语言的角度,可以理解成定义一个结构体,里面包含许多数据类型。如下面要说的LEDs数据结构体:

typedef struct { uint8_t Mode; //模式(常灭 常亮 闪烁) uint8_t Status; //当前状态(灭 亮) uint16_t OffTimes; //灭时间(xLED_COUNT_PERIOD毫秒) uint16_t OnTimes; //亮时间 uint16_t Counter; //计数(计时) void (*OffFun)(void); //灭函数接口 void (*OnFun)(void); //亮函数接口 }LED_TypeDef;

可以看到,结构体里面包含整型变量,函数指针。

补充,指针函数与函数指针的区别:

1、指针函数:本质是一个函数,函数返回类型是某一类型的指针。

格式: 类型标识符 *函数名(参数表)

如:int *f(x,y);

2、函数指针:本质是一个指针,指向函数的指针变量。

格式:类型说明符 (*函数名)(参数)

如:int (*f) (int x);

Ⅱ为什么要模块化设计

假如一个系统中做的事情非常多,比如:采集两个增量式编码器、两个绝对值编码器、控制4个电机、控制多个LED状态灯、通信收发数据,采集温度、湿度、超声波雷达等···许多模块,那么问题来了,这么多模块,你的软件该如何设计?

答案就是需要模块化设计。

模块化设计,包含底层驱动,中间接口函数,应用程序等。对于MCU级别的开发,为了规范,建议大家从底层设计到应用层设计都按照模块化的方式来设计。

简单的来说,模块化就是源文件、数据结构、变量、函数命名等需要按照模块的方式来设计。比如LEDs状态灯:IO口的定义用LED(模块),文件名用led,变量、函数名抬头用LED,定义一个LED数据结构(模块的数据结构)等。

模块化的设计优点在于:便于源代码管理、移植、理解等等。(相信有许多自己写的代码,放一段时间之后,重新再次阅读,可能看了半天都不明白源代码的意思。)

ⅢLEDs实例讲述

为方便大家理解,拿一个简单的LEDs状态灯的实例来分析。里面使用到了RTOS简单系统延时(本文不讲述关于RTOS的知识)。文末提供例程下载地址。

1.描述

绿、黄、红三个(可以自己添加许多个)LED状态灯,可独自实现常灭、常亮、闪烁三个模式。

闪烁:灭、亮时间可设置(提供函数接口修改)。

在一个线程(任务)里面执行。

3个LED不同亮灭时间效果:

2.数据结构

typedef struct { uint8_t Mode; //模式(常灭 常亮 闪烁) uint8_t Status; //当前状态(灭 亮) uint16_t OffTimes; //灭时间(xLED_COUNT_PERIOD毫秒) uint16_t OnTimes; //亮时间 uint16_t Counter; //计数(计时) void (*OffFun)(void); //灭函数接口 void (*OnFun)(void); //亮函数接口 }LED_TypeDef;

为了方便理解,只使用一个数据结构(实际大的项目可能有多个包含,类似C++继承关系)。

3.底层LED函数接口

void LEDGreen_Off(void);

void LEDGreen_On(void);

void LEDYellow_Off(void);

void LEDYellow_On(void);

void LEDRed_Off(void);

void LEDRed_On(void);

主要就是亮灭函数接口,这里提供三组LED(根据需求可添加)。

4.定义局部变量

static LED_TypeDef sLEDG_Structure; //绿灯 static LED_TypeDef sLEDY_Structure; //黄灯 static LED_TypeDef sLEDR_Structure; //红灯

5.初始化变量

/************************************************函数名称 : LED_Data_Init功 能 : 数据初始化参 数 : 无返 回 值 : 无作 者 : strongerHuang*************************************************/ static void LED_Data_Init(void){ /* 绿灯 */ sLEDG_Structure.Mode = LED_MODE_FLICKER; //初始化为闪烁 sLEDG_Structure.OffTimes = 50; //灭亮时间 sLEDG_Structure.OnTimes = 50; sLEDG_Structure.Counter = 0; //计数归零 sLEDG_Structure.OffFun = LEDGreen_Off; //灭函数接口 sLEDG_Structure.OnFun = LEDGreen_On; //亮函数接口 /* 黄灯 */ sLEDY_Structure.Mode = LED_MODE_ON; //初始化为常亮 sLEDY_Structure.OffTimes = 0; //灭亮时间 sLEDY_Structure.OnTimes = 0; sLEDY_Structure.Counter = 0; //计数归零 sLEDY_Structure.OffFun = LEDYellow_Off; //灭函数接口 sLEDY_Structure.OnFun = LEDYellow_On; //亮函数接口 /* 红灯 */ sLEDR_Structure.Mode = LED_MODE_ON; //初始化为常亮 sLEDR_Structure.OffTimes = 0; //灭亮时间 sLEDR_Structure.OnTimes = 0; sLEDR_Structure.Counter = 0; //计数归零 sLEDR_Structure.OffFun = LEDRed_Off; //灭函数接口 sLEDR_Structure.OnFun = LEDRed_On; //亮函数接口 /* 对外调用接口(例子) */ LEDG_Set(LED_MODE_FLICKER, 50, 50); LEDY_Set(LED_MODE_FLICKER, 50, 10); LEDR_Set(LED_MODE_FLICKER, 20, 30);}

这里重要的就是要初始化灭亮函数接口。

6.LEDs任务(线程)

/************************************************函数名称 : LED_Task_Proc功 能 : 状态灯任务程序参 数 : pvParameters --- 可选参数返 回 值 : 无作 者 : strongerHuang*************************************************/ static void LED_Task_Proc(void *pvParameters){ static TickType_t xLastWakeTime; xLastWakeTime = xTaskGetTickCount(); for(;;) { //间隔固定计数周期(采样时间) vTaskDelayUntil(&xLastWakeTime, LED_COUNT_PERIOD); /* 浏览LEDs */ LED_Scan(&sLEDG_Structure); LED_Scan(&sLEDY_Structure); LED_Scan(&sLEDR_Structure); }}

流程图:

7.LED浏览(或者说处理)

/************************************************函数名称 : LED_Scan功 能 : 状态灯扫描(修改状态)参 数 : LED_Struct --- 状态灯数据结构返 回 值 : 无作 者 : strongerHuang*************************************************/ static void LED_Scan(LED_TypeDef *LED_Struct){ /* 1.常灭模式 */ if(LED_MODE_OFF == LED_Struct->Mode) { LED_Struct->Status = LED_STATUS_OFF; //状态置为"灭" LED_Struct->OffFun(); //灭灯 } /* 2.常亮模式 */ else if(LED_MODE_ON == LED_Struct->Mode) { LED_Struct->Status = LED_STATUS_ON; //状态置为"亮" LED_Struct->OnFun(); //亮灯 } /* 3.闪烁模式 */ else if(LED_MODE_FLICKER == LED_Struct->Mode) { /* 在灭的状态 */ if(LED_STATUS_OFF == LED_Struct->Status) { LED_Struct->Counter++; if(LED_Struct->Counter >= LED_Struct->OffTimes) { LED_Struct->Counter = 0; LED_Struct->OnFun(); //亮灯 LED_Struct->Status = LED_STATUS_ON; //状态置为"亮" } } /* 在亮的状态 */ else if(LED_STATUS_ON == LED_Struct->Status) { LED_Struct->Counter++; if(LED_Struct->Counter >= LED_Struct->OnTimes) { LED_Struct->Counter = 0; LED_Struct->OffFun(); //灭灯 LED_Struct->Status = LED_STATUS_OFF; //状态置为"灭" } } else { LED_Struct->Status = LED_STATUS_OFF; //状态置为"灭" } } /* 4.未知模式 */ else { LED_Struct->Status = LED_STATUS_OFF; //状态置为"灭" LED_Struct->OffFun(); //灭灯 }}

源代码工程下载地址:

链接:https://pan.baidu.com/s/1cNtwJDdCOfyYwsvKCclFyw

密码:kk74

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • mcu
    mcu
    +关注

    关注

    146

    文章

    17148

    浏览量

    351212
  • LEDs
    +关注

    关注

    1

    文章

    39

    浏览量

    25533
  • RTOS
    +关注

    关注

    22

    文章

    813

    浏览量

    119643
  • 线程
    +关注

    关注

    0

    文章

    504

    浏览量

    19684
收藏 人收藏

    评论

    相关推荐

    使用任务通知提高RTOS应用的效率

    在实时嵌入式系统中,性能和资源效率是决定设计成败的关键因素。传统的实时操作系统(RTOS)提供了如队列、信号量和事件组机制,实现任务之间的同步和通信。FreeRTOS/SAFERTOS还提供一种方法可以使这些过程更快、更轻量化,即任务
    的头像 发表于 12-27 14:54 133次阅读

    rtthread是抢占式的rtos,那么线程的timeout参数具体的作用是什么呢?

    查阅相关说明,rtthread是抢占式的rtos,那么线程的timeout参数具体的作用是什么呢, 假如线程A,B的优先级分别是1和2,timeout是10ms。当线程B 在运行中,还
    发表于 09-27 08:39

    驱动指示leds

    电子发烧友网站提供《驱动指示leds.pdf》资料免费下载
    发表于 09-18 11:50 0次下载
    驱动指示<b class='flag-5'>灯</b><b class='flag-5'>leds</b>

    freertos和rtos区别是什么

    (Real-Time Operating System,实时操作系统)是一种特殊的操作系统,它能够为实时任务提供确定性的响应时间。RTOS 通常用于嵌入式系统,如工业自动化、汽车电子、医疗设备等领域
    的头像 发表于 09-02 14:18 1259次阅读

    鸿蒙开发:【线程模型】

    管理其他线程的ArkTS引擎实例,例如使用TaskPool(任务池)创建任务或取消任务、启动和终止Worker线程
    的头像 发表于 06-13 16:38 411次阅读
    鸿蒙开发:【<b class='flag-5'>线程</b>模型】

    移植exmapl,使用时cy_rtos被阻塞或死线程,为什么?

    我尝试移植 exmaple(ble_hello_sensor, mfg_test)。 但是,所有项目都存在一些问题。 当我使用时,可能 cy_rtos 被阻塞或死线程。 因此,我无法通过 bt_firmware_downlaod()。 您能检查我的代码吗?
    发表于 05-22 07:53

    请问CMSIS-RTOS RTX的任务调度锁在哪里?

    请问一下,CMSIS-RTOS RTX的任务调度锁在哪里?谢谢!
    发表于 05-13 08:28

    FreeRTOS系统使用xTaskCreate产生的任务与osThreadDef 产生的线程有什么不同?

    请教下是要 FreeRTOS系统, 使用 xTaskCreate 产生的任务 与 osThreadDef产生的线程有什么不同?
    发表于 04-29 07:20

    鸿蒙OS开发实例:【ArkTS类库多线程CPU密集型任务TaskPool】

    CPU密集型任务是指需要占用系统资源处理大量计算能力的任务,需要长时间运行,这段时间会阻塞线程其它事件的处理,不适宜放在主线程进行。例如图像处理、视频编码、数据分析等。 基于多
    的头像 发表于 04-01 22:25 844次阅读
    鸿蒙OS开发实例:【ArkTS类库多<b class='flag-5'>线程</b>CPU密集型<b class='flag-5'>任务</b>TaskPool】

    鸿蒙OS开发实例:【ArkTS类库多线程I/O密集型任务开发】

    使用异步并发可以解决单次I/O任务阻塞的问题,但是如果遇到I/O密集型任务,同样会阻塞线程中其它任务的执行,这时需要使用多线程并发能力来进行
    的头像 发表于 04-01 16:32 521次阅读
    鸿蒙OS开发实例:【ArkTS类库多<b class='flag-5'>线程</b>I/O密集型<b class='flag-5'>任务</b>开发】

    鸿蒙原生应用开发-ArkTS语言基础类库多线程I/O密集型任务开发

    使用异步并发可以解决单次I/O任务阻塞的问题,但是如果遇到I/O密集型任务,同样会阻塞线程中其它任务的执行,这时需要使用多线程并发能力来进行
    发表于 03-21 14:57

    基于RTOS的应用进程中的典型线程

    RTOS中的关键因素是最小的中断延迟和最小的线程切换延迟。RTOS的价值在于它的响应速度或可预测性,而不是它在给定时间段内可以执行的工作量。
    发表于 03-05 09:32 608次阅读
    基于<b class='flag-5'>RTOS</b>的应用进程中的典型<b class='flag-5'>线程</b>

    嵌入式系统中的线程、进程与任务概念与区别

    每个线程与主程序共用地址空间,受限于2GB地址空间; 2)线程之间的同步和加锁控制比较麻烦;一个线程的崩溃可能影响到整个程序的稳定性
    发表于 03-04 15:03 1361次阅读
    嵌入式系统中的<b class='flag-5'>线程</b>、进程与<b class='flag-5'>任务</b>概念与区别

    鸿蒙OS 线程管理开发指导

    场景介绍 如果应用的业务逻辑比较复杂,可能需要创建多个线程来执行多个任务。这种情况下,代码复杂难以维护,任务线程的交互也会更加繁杂。要解决此问题,开发者可以使用“TaskDispat
    的头像 发表于 01-29 16:22 843次阅读

    如何对状态指示进行编程

    以下步骤将介绍如何对状态指示进行编程。状态指示指示加热室的工作模式。当加热室处于工作状态时,变量 LED 的信号
    的头像 发表于 01-02 14:22 1004次阅读
    如何对<b class='flag-5'>状态</b>指示<b class='flag-5'>灯</b>进行编程