打印
[经验分享]

单片机编程思维

[复制链接]
133|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
youtome|  楼主 | 2025-2-18 04:10 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
模块化编程
模块化编程是开发者首先会掌握的一种编程思想,就像前面我们多次提到的把一些特定功能的代码大打包成一个函数,这么一来以后在其他项目中就可以通过复制、粘贴轻松的移植了。这就是最简单的模块化思想了,当然如果要规范一点的话我们需要在函数前做一些必要的注释说明,方便自己日后重新使用这段代码时能很快就可以知道代码的功能。比如下面这段代码:
/*
***********************************************
* 函数名:led_display
* 描   述:****
* 输   入:****
* 输   出:****
* 返   回:****
* 调   用:****
* 备   注:****
************************************************
*/
void led_display(uint16_t duty ,uint8_t up_rate,uint8_t down_rate)
{
    ****
}
当然也可以不用写这么多注释,注释内容只要能保证可以看懂函数的意思就行,内容根据自己的喜好来确定,比如你也可以这样些:
/**
  * @brief   :****
  * @param : ****
  * @retval  : ****
**/
甚至你也可以直接就用一句注释说明,比如:
/*    ****   */
或者直接
// ****
只要能保证可以一眼看懂你的说明就行,当然最好是自形成一个统一的格式注释格式,让程序看起来更加美观大方,以后别人读你的程序心情也会好很多,毕竟项目开发不一定是你一个人完成特别是互联网项目,比如一个大型项目就涉及到前端开发者,后端开发者,移动端(或桌面端)开发者,甚至还需要测试人员配合,这种情况虽然不会每个人都需要看你的代码但至少会有人来跟你协同完成应用功能。所以很多公司都会规定注释规范或编程规范。
平时编程时除了善于打包函数将程序模块化,但项目中很多时候我们的程序不止是几个函数,我们项目可能使用了大量的外设,程序中需要大量的函数来完成功能,这时候如果只是打包函数,我们的代码也会变得非常庞大,如果一个文件里面有几千甚至上万行代码,你日后维护起来是不是会崩溃,可能找一个bug都能找半天,这时候我们需要怎么办呢?其实C语言库文件就已经告诉我们做法了,当然其他语言也是一样。这时候我们就可以进行分文件打包程序,就是将相同功能或控制同一个外设的代码放在同一个文件里进行模块化,这样做是不是比单纯使用的函数会更有优势,以后我们如果要重复使用这些代码都不用在代码中查找这些函数的了,直接将整个文件拷贝就可以了,现在我们一起来看看这是怎么实现的。
以上图片中展示的就是这种模块化的做法,在项目中将不同作用的代码放在不同的文件夹下,将不同模块的代码放在不同的***.c文件里面,这样后续修改,调试,移植代码的效率就会大大提高。那这种做法是怎么实现的呢,之前我们在介绍C语言文件时说过C语言文件主要有两种,即.c和.h文件,.c文件我们称它问源文件,.h文件我们称为头文件,在源文件中我们存放各种变量或功能函数的定义等内容,在头文件中声明对应的变量、函数或宏定义等等,所以一般情况下源文件和头文件都是成对出现的。若某个文件需要使用另一个文件中定义的函数时就在该文件头部添加另一个文件对应的头文件,这样就可以实现各部模块程序相互调用了。通过源文件和头文件分离,当你遇到有一些代码文件你不想给别人知道,但又要发布给别人,这种情况下也有对应的处理办法,具体的做法我们后面再介绍。
一般情况下在不同的源文件中我们也会像之前些函数一样对该文件做一些注释描述,比如:/**** (C) COPYRIGHT  ********* * 文件名   :**** * 描述      :**** * 操作系统 :**** * 软件平台 :**** * 硬件基础 :**** * 库版本    :**** * 作者       :**** * 版本编号 :**** * 修改时间 :**** * 修改说明 :**************************************/当然你也可以不用写这么多内容,以你能看懂为准。多任务编程一般情况下我们在使用 51、AVR、STM32等单片机编程的时候都是在main函数里面用while(1)做一个大循环来完成所有的处理,即应用程序是一个无限的循环,循环中调用相应的函数完成所需的处理。有时候我们也需要用到中断来完成一些功能操作。相对于多任务系统而言,这个就是单任务系统,也称作前后台系统,中断服务函数作为前台程序,大循环while(1)作为后台程序。
类似一下的做法:
void IRQHandler_fun()
{
    flag1 = 1;
}
int main (void)
{
    while(1)
    {
        if (1 = flag1)
        {
            func1();
            flag1 = 0;
        }
    }
}
这种做法在一般项目中是完全没有问题的,但是这种程序结构的一个缺陷是它的前后台系统的实时性不强,前后台系统各个任务(应用程序)都是排队等着轮流执行,不管你这个程序现在有多紧急,没轮到你就只能等着!相当于所有任务(应用程序)的优先级都是一样的。在小项目中前后台系统简单,资源消耗也少,单片机处理起来还得心应手!但在稍微大一点的嵌入式应用中前后台系统对实时性要求较高的应用中就明显力不从心了。这时候就需要进行多任务处理了,就像一下的做法:
void first_task()
{
    while (1)
    {
        if(has_data())
            put_data();
    }
}
void second_task()
{
    while (1)
    {
        if(get_data())
            do_something();
    }
}
int main(void)
{
    create_task(first_task);
    create_task(second_task);
    start_task();
}
相信细心的朋友一眼就能发现,这种做法很明显的不一样就是每个任务函数中都有一个while (1)死循环,是不是我们只要在程序中多使用一些while (1)死循环就OK了呢?简单说可以这么认为吧,当然实际情况肯定不会这么简单。
那多任务编程是怎么实现程序功能的呢?多任务系统把一个大问题“分而治之”,把大任务划分成很多个小问题,逐步的把小任务解决掉,大任务也就随之解决了,这些任务是并发处理的。注意,并不是说同一时刻一起执行很多个任务,毕竟我们单片机项目都是使用单核芯片,任意时刻它也只能存在一个任务占用其内核,它是由于每个任务执行的时间很短,导致看起来像是同一时刻执行了很多个任务一样。
看了以上内容是不是感觉多任务编程非常的难?其实难肯定是难,但也没有那么难,因为现在有非常多开源多任务系统供我们选择,比如freeRTOS,RT-Thread,UC/OS等等,所以就不用我们自己从零写一个多任务系统了,使用时选择一个合适的系统进行移植就能减少很多工作量了。


以上是STM32某系列芯片官方标准库函数中的部分片段代码。

上图中是freeRTOS中任务创建的函数,对于初学者来说这些咋看都是很深奥的内容,所以这里也不需要你现在就掌握它,以后你有机会接触它们,现在知道有这么回事就好了。现在总不至于哪天面试的时候被人问你一句你是否尝试过面向对象开发单片机程序时自己啥话都说不上来。
这是微软某个版本的某C源文件的某段源码,看起来很复杂吧,说实话这段代码我也没细看,细看也一时不可能看明白,只是刚好看到就这个就贴上来跟大家一起分享一下。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

39

主题

4010

帖子

2

粉丝