单片机程序编写常使用的程序架构

  • 0、前言
  • 1、顺序执行法
  • 2、时间片轮询法
  • 3、操作系统

0、前言

  对于做嵌入式开发,或者单片机开发的工程师来说,编写程序肯定是家常便饭,对于程序,大家肯定都不陌生。但是无论你是刚毕业的应届生,还是久经职场的工程师,在编写程序时,考虑程序架构是非常有必要的事情。但是往往实际开发中,真正考虑好的程序架构的恐怕并不多。随着程序开发的难度以及复杂度的增加,没有一个好的程序架构,往往会使开发者在编写程序的过程中产生逻辑混乱。
  当然,我也是一个普通的软件工程师,前人已经给我们总结出了优秀的程序架构,我们只要理解它们,并运用到自己的开发项目中,用着用着你就知道程序架构带来的好处了。目前,单片机程序的架构大致分为三种:

1、顺序执行法:这类写法是大多数人使用的方法,根据产品的工作逻辑,从上往下的执行顺序来编写应用程序即可,不需要考虑程序的具体架构。

2、时间片轮询法:这种方法是介于顺序执行法和操作系统之间的一种方法。本文也是主要对该方法的总结与讨论。

3、操作系统:这种方法是程序编写的最高境界,即单片机中嵌入实时操作系统,然后再操作系统的基础上进行编程。但是,上操作系统的话对单片机的硬件要求比较高,一般普通的或者8位的单片机还是不足以上操作系统的。

1、顺序执行法

  在应用程序比较简单,实时性,并行性要求不太高的情况下,使用顺序执行法进行编程是不错的方法,程序设计简单,思路也比较清晰。但是,当应用程序比较复杂的时候,如果没有一个完整的流程图,编写应用程序的工程师在写逻辑的时候会混乱。不利于在程序功能增加是进行代码维护和优化。特别是当多个任务需要用到定时器且定时时间不一样是,就需要增加标记变量和定时变量来加以区分,最终虽然能够实现功能,但是可能逻辑思维并不是很清晰,一直处于混乱状态。
  大多数人采用这种方法的原因是,一开始接触学习单片机的时候就一直以这种方式在写程序。而且在开发项目的过程中,从来没有去考虑过程序结构的问题,对于我们这边没有学过数据结构,程序架构的人单片机工程师来说,在程序设计上基本为零。

顺序结构如下面的程序模型:

/************************************************
*函数名称:main
*函数功能:主函数
*输入参数:void
*输出返回:void
*************************************************/
void main(void)
{ uint8 keyValue;InitSys();                  // 初始化while (1){TaskDisplayClock();keyValue = TaskKeySan();switch (keyValue){case x: TaskDispStatus(); break;...default: break;}}
}

2、时间片轮询法

  时间片轮询法,很多时候都是与操作系统一起出现的,因为很多操作系统中就使用了这种方法。许多刚入门的工程师当然还没有机会学习和掌握多任务处理的操作系统开发模式。但是,在使用和学习多任务操作系统开发之前,我们依然需要一种开发模式,它能够在某种程度上和多任务与的操作系统类型,能够实现某种意义上的时间片轮询执行的模式。
  当然,这种程序设计模式很多工程师也许都已经在使用了,使用起来比我还要好,还要高明。但是,很多搞那个出来的学生或者还没有出来的学习就没有这个机会看到这种处理模式了。其实,时间片轮询法,就是对定时器的应用,一个定时器多出复用,定时器产生一个1ms,或者10ms,或者100ms的定时周期,给每个需要执行的任务设置好执行任务的周期,直白点就是多久执行一次任务1,多久执行一次任务2,然后以定时器的周期进行计时,一旦时间到了就执行相应的任务。
**注意:**定时器初始化时的定时中断时间不可以过短也不可以过长,时间太短,中断过于频繁效率低,中断太长,实时性就差。

/********************************************************************************* @文件名 : Task.h* @作  者 : JayYang* @版  本 : V1.0.0* @日  期 : 2021年04月22日* @摘  要 : 任务执行头文件******************************************************************************/
#ifndef __TASK_H__
#define __TASK_H__/*------------------------------- 包含的头文件 -------------------------------*//*---------------------------------- 宏定义 ----------------------------------*/
#define TASK_MAX    3       //定义会使用定时器定时的任务数方式一#define NotRunning  0       //初始运行状态标志为未运行//每个任务运行的间隔时间,目前定时器是10ms中断一次
#define TaskTimer1 10
#define TaskTimer2 20
#define TaskTimer3 30/*--------------------------------- 类型定义 ---------------------------------*/
/*
typedef enum{TASK_1,TASK_2,TASK_3,// 这里添加你的任务.....TASK_MAX       // 总的可供分配的定时任务数目,定义会使用定时器定时的任务数方式二
}Task_List_e;      //这里定义这个任务列表的目的其实就是参数TASK_MAX的值,其他值是没有具体的意义的,只是为了清晰的表面任务的关系而已
*/typedef struct{unsigned char RunningStatus;    //运行状态标志位:0-不运行;1-运行unsigned char TimerCountDown;   //定时器计数变量,进行--计数unsigned char TimerInitialVal;  //定时器定时初值,也就是每个任务运行的间隔时间void (*TaskHandler)(void);      //任务处理程序函数指针
}TaskManage_t;                      //定义任务管理结构体,它包含了一个任务所需要的所有信息typedef struct{void (*TaskMarks)(void);        //任务定时标志位处理函数,[定时器中断函数中调用]void (*TaskProcess)(void);      //任务处理函数,[主循环中调用]
}Task_t;                            //定义任务结构体/*--------------------------------- 变量定义 ---------------------------------*/
extern Task_t Task;     /*--------------------------------- 函数声明 ---------------------------------*/#endif/********************************************************************************* @文件名 : Task.c* @作  者 : JayYang* @版  本 : V1.0.0* @日  期 : 2021年04月22日* @摘  要 : 任务执行源文件******************************************************************************//*------------------------------- 包含的头文件 -------------------------------*/
#include "bsp.h"/*---------------------------------- 宏定义 ----------------------------------*//*--------------------------------- 变量定义 ---------------------------------*//*--------------------------------- 函数声明 ---------------------------------*/
static void TaskMarks(void);        //任务定时标志位处理函数
static void TaskProcess(void); //任务处理函数static void Task1(void);            //任务1
static void Task2(void);            //任务2
static void Task3(void);            //任务3TaskManage_t TaskManage[] = {{NotRunning,TaskTimer1,TaskTimer1,Task1},{NotRunning,TaskTimer2,TaskTimer2,Task2},{NotRunning,TaskTimer3,TaskTimer3,Task3}
};Task_t Task = {TaskMarks,TaskProcess};/*--------------------------------- 函数定义 ---------------------------------*/
/************************************************
*函数名称:TaskMarks
*函数功能:任务定时标志位处理函数
*          这个函数是用来更新每个任务的计数器以及它们的运行标志位的,
*          它是写在单片机的定时器中断服务函数中的
*输入参数:void
*输出返回:void
*************************************************/
static void TaskMarks(void)
{unsigned char i;//逐个任务进行时间计算for (i = 0; i < TASK_MAX; i++){//如果计数器不为0,即要执行任务的时间还没到if (TaskManage[i].TimerCountDown){TaskManage[i].TimerCountDown--; //任务执行时间--//如果时间到了if (TaskManage[i].TimerCountDown==0){TaskManage[i].TimerCountDown = TaskManage[i].TimerInitialVal;   //恢复计时器值,重新下一次计时TaskManage[i].RunningStatus = 1;    //标识任务可以运行}}}
}/************************************************
*函数名称:TaskProcess
*函数功能:任务处理函数
*          这个函数用来判断任务是否到了执行时间,它会逐个检查任务列表中的任务
*          如果到了就执行,同时清空执行标志位,没到就不执行
*输入参数:void
*输出返回:void
*************************************************/
static void TaskProcess(void)
{unsigned char i;for (i = 0; i < TASK_MAX; i++){if (TaskManage[i].RunningStatus)        //如果任务需要运行{TaskManage[i].TaskHandler();        //运行任务TaskManage[i].RunningStatus = 0;    //运行标志清0}}
}/************************************************
*函数名称:Task1
*函数功能:任务1函数
*输入参数:void
*输出返回:void
*************************************************/
static void Task1(void)
{}/************************************************
*函数名称:Task2
*函数功能:任务2函数
*输入参数:void
*输出返回:void
*************************************************/
static void Task2(void)
{}/************************************************
*函数名称:Task3
*函数功能:任务3函数
*输入参数:void
*输出返回:void
*************************************************/
static void Task3(void)
{}

3、操作系统

  操作系统是一个比较复杂的东西,任务管理,执行本身并不需要我们去了解。但是光是移植就比较麻烦,虽然有人说过 “你如果使用过系统,将不会再去使用前后台程序”。但是真正能使用操作系统的人并不多,不仅是因为系统的使用本身很复杂,而且还需要购买许可证(ucos也不例外,如果商用的话)。
  这里本人并不想过多的介绍操作系统本身,因为不是一两句话能过说明白的,下面列出UCOS下编写应该程序的模型。大家可以对比一下,这三种方式下的各自的优缺点。

/**************************************************************************************
* FunctionName   : main()
* Description    : 主函数
* EntryParameter : None
* ReturnValue    : None
**************************************************************************************/
int main(void)
{ OSInit();                // 初始化uCOS-IIOSTaskCreate((void (*) (void *)) TaskStart,        // 任务指针(void   *) 0,            // 参数(OS_STK *) &TaskStartStk[TASK_START_STK_SIZE - 1], // 堆栈指针(INT8U   ) TASK_START_PRIO);        // 任务优先级OSStart();                                       // 启动多任务环境return (0);
}/**************************************************************************************
* FunctionName   : TaskStart()
* Description    : 任务创建,只创建任务,不完成其他工作
* EntryParameter : None
* ReturnValue    : None
**************************************************************************************/
void TaskStart(void* p_arg)
{OS_CPU_SysTickInit();                                       // Initialize the SysTick.#if (OS_TASK_STAT_EN > 0)OSStatInit();                                               // 这东西可以测量CPU使用量
#endifOSTaskCreate((void (*) (void *)) TaskLed,     // 任务1(void   *) 0,               // 不带参数(OS_STK *) &TaskLedStk[TASK_LED_STK_SIZE - 1],  // 堆栈指针(INT8U   ) TASK_LED_PRIO);         // 优先级// Here the task of creating yourwhile (1){OSTimeDlyHMSM(0, 0, 0, 100);}
}

不难看出,时间片轮询法优势还是比较大的,即由顺序执行法的优点,也有操作系统的优点。结构清晰,简单,非常容易理解。

单片机程序编写常使用的程序架构相关推荐

  1. c语言T1中断程序编写步骤,用51单片机中断编写的4x4键盘程序

    当前位置:单片机教程网 >> MCU设计实例 >> 浏览文章 用51单片机中断编写的4x4键盘程序 作者:未知   来源:山涧一溪流   点击数:-  更新时间:2014年06 ...

  2. c语言最简单的程序编写,C语言简单程序编写.doc

    C语言简单程序编写 项目一 C语言简单程序编写 1.1学习目标 本项目达到的目标为: 掌握程序设计的基本思路 掌握C语言的基本结构 了解流程图的符号含义 掌握C语言的关键字 会设计和编写简单的C应用程 ...

  3. 敏感词过滤程序编写敏感词过滤程序

    敏感词过滤程序编写敏感词过滤程序 前言 Java程序设计语言课程让我们2-3人一个小组,找一个能用集合解决的问题进行介绍.说明,于是我们就找到了这道题目. 一.题目要求 编写敏感词过滤程序编写敏感词过 ...

  4. c语言键盘中断扫描码,用51单片机中断编写的4x4键盘程序

    应用查询扫描编写键盘程序,由于要给按键去抖动,程序变得比较复杂和冗长(详见2013年9月29日博文<MSP430和AT89C51单片机4x4键盘C程序>),如果用中断编写,设置中断响应在下 ...

  5. c语言延时100us程序,编写100MS软件延时程序 汇编语言编写延时程序

    怎么编写单片机晶振为6MHz,编写100ms延时程序? 设主频12MHz的51单片机,编一个100ms二重循环的延时程序如下,供参考. 其它汇编可以类似编程. SSSS:MOV R0,#99:本指令是 ...

  6. php如何编写应用程序,编写安全 PHP 应用程序的七个习惯

    在提及安全性问题时,需要注意,除了实际的平台和操作系统安全性问题之外,您还需要确保编写安全的应用程序.在编写 PHP 应用程序时,请应用下面的七个习惯以确保应用程序具有最好的安全性: 验证输入 保护文 ...

  7. 为什么“程序猿”常有而“程序媛”不常有?

    点击阅读原文 许多人对计算机科学界极客们有一种普遍的印象.这没什么--但假如这个领域内女生比男生多呢?你一定会想:什么?这一定是疯了! 喜欢<星际迷航>.不善社交与计算机编程能力的关系,其 ...

  8. SPI Flash芯片W25Q32英文版数据手册解读(三)---------程序编写,电路应用

    一.序言 序言对这篇文章进行一个总体的说明: 1.这部分是根据手册写程序,因此采用手册截图+程序截图的形式,对图片不进行标号,而且对重点部分进行颜色标注. 2.考虑到很多读者(包括我),使用手机看文章 ...

  9. 用计算机控制单片机的程序编写,单片机程序编写步骤

    单片机又称单片微控制器,它不是完成某一个逻辑功能的芯片,而是把一个计算机系统集成到一个芯片上.相当于一个微型的计算机,和计算机相比,单片机只缺少了I/O设备.概括的讲:一块芯片就成了一台计算机.它的体 ...

  10. 单片机c语言程序编写步骤,用c语言编写单片机流水灯程序详解

    用C语言编写的流水灯程序 一.硬件电路 因为电路用单片机控制,所以电路非常简洁.其电路原理图见下图,印制板图如下图所示. 电路的核心部分是AT89C2051单片机,前面提到它有Pl和P3两组I/O口, ...

最新文章

  1. 2022-2028年中国老年旅游市场深度调研及开发战略研究报告
  2. Spring(IOC+AOP)
  3. python学习笔记(六)——函数的作用域和装饰器
  4. js window.open()实现打印,如何在关闭打印窗口时刷新父窗口
  5. vim配置及插件安装管理
  6. JDK的bin目录下各种工具的使用说明_对不起自己,这么久没写博,抱歉
  7. linux命令详解——iostat
  8. REVERSE-PRACTICE-BUUCTF-5
  9. 微电子科学与工程要学计算机吗,微电子科学与工程专业就业前景如何 有前途吗...
  10. Oracle中ascii为0的陷阱
  11. WORD页眉如何引用章节名?
  12. 发展之道:简单与专注
  13. Detectron2和MMDetection的学习笔记
  14. 归并排序的java语言_归并排序 java语言
  15. 数据结构上机实践第三周项目3- 求集合并集
  16. centos7 设备 mariadb-10
  17. 红宝书第四版的一个错误?
  18. 5个简单的游戏设计技巧,让你的游戏更有趣
  19. Android 集成Tinker的gradle脚本
  20. 【魏先生搞定Python系列】一文搞定Cufflinks画图

热门文章

  1. Blender学习笔记:齿轮模型建立
  2. JVM,DVM,JIT,AOT,ART理解
  3. java报告模板_JAVA报告模板.doc
  4. VMware虚拟机XP系统安装
  5. 解决VMware虚拟机桥接模式无法上网的解决方法 ubuntu
  6. linux卸载bzip2,bzip2命令_Linux bzip2命令:压缩和解压文件(.bz2文件)
  7. 图纸管理协同办公软件推荐
  8. 【c语言】打印出100以内奇数
  9. 数学图形之双曲抛物面
  10. 2022道路运输企业安全生产管理人员考试练习题及在线模拟考试