我们学习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
华为物联网:参与标准制定和增强海外布局
极米4K激光电视A3的详细介绍
NI公司大增软体功能 LabVIEW 2013扩张嵌入式版图
一文知道弹簧拉压试验机的选购技巧
总线伺服是什么设备?
LEDs状态灯任务(线程)设计 (基于RTOS)
基于台达20PM的点胶机数控系统
苹果大手笔采购OLED 投入超40亿美元
中国联通推动全光底座智能化演进,五大关键能力夯实全光底
智能传感器技术在呼吸监视仪中的应用
基于一种多功能设计的Coosno智能咖啡桌介绍
51单片机音乐播放程序设计
如何看懂继电器控制电路图
LDO线性稳压器的并联-什么是LDO线性稳压器的并联
连接技术的发展对工业连接器提出了新要求
ACM官网公布了2018 ACM博士论文奖
白雪电器推出新型冷柜 助力高端食材保鲜
在芯片上怎么分正极?
刘伟:补齐人工智能发展短板,推动轨道上的城市群
更大更快更亮---福禄克万用表升级版本荣耀上市