▼点击下方名片,关注公众号,获取更多精彩内容▼
欢迎关注【玩转单片机与嵌入式】公众号,回复关键字获取更多免费视频和资料
回复【加群】,【单片机】、【STM32】、【硬件知识】、【硬件设计】、【经典电路】、【论文】、【毕业设计】、【3D封装库】、【PCB】、【电容】、【TVS】、【阻抗匹配】、【资料】、【终端电阻】、【Keil】、【485】、【CAN】、【振荡器】、[USBCAN]、【PCB】、【智能手环】、【智能家居】、【智能小车】、【555】、【I2C】、【华为】、【中兴】,等……
我们平时在学习开发板、示例程序或者demo的时候,会发现这种程序设计都是单一的外设或者功能,功能实现就算演示成功。但是在实际的项目设计过程中,往往需要设计的程序是非常庞大且复杂的,项目的功能和项目需求可能会超级多。此时在程序设计中,既要使MCU同时兼顾各个外设而不发生冲突,又要合理的设计程序构架,使所有的需求都能逻辑清晰的实现相关功能。
所以程序设计一定要有思路。当我们拿到一份项目需求的时候,不能一上来就敲代码,最后随跳跃的思维写出来的程序逻辑把自己都绕晕了还没能实现所有功能。就像盖高楼前一定要有图纸一样,在进行程序设计前也要有程序构架。
程序框架与底层无关,它重在整体需求的实现与逻辑关系。例如我们把一个具体的项目需求细化为10个具体的任务,每个任务都完成一些特定的功能。先忽略每个任务里的内容,这10个任务和它们之间的切换逻辑就组成了此项目的程序构架。
有了程序设计构架,接下来就是实现每一个任务具体的功能,不同的部分分模块编写,例如外设单独写一个文件,传感器驱动单独列一个文件,所有文件之间相互独立,这样不仅思路清晰,且在排查问题的时候,能直接屏蔽某一个文件,更快速的定位问题所在。
首先把需求拆分,分为一个个任务,每一个任务去实现相应的部分功能,设计好任务之间的切换条件。此时暂且不考虑底层,只是列出所有任务和任务之间的逻辑切换关系,这样就有了程序构架,然后再规划每个任务里面里面需要多少具体功能,列出来一个表,或者写成空函数先放着。列完每个任务的具体功能之后,我们再回头看需求书,是否所有功能都实现,且它们之间的切换关系逻辑都很清晰。
第一步完成之后,整体的系统构架和逻辑关系就一清二楚了,接下来我们按照第一步中列的每个任务的功能表,去完成具体功能实现。此时,还不急着敲代码,继续规划。我们知道,对于MCU来讲,通常所有的功能最终是要通过调用底层实现。所以接下来我们要规划需要多少底层的资源,根据第一条中任务具体的功能列表,我们列一个底层驱动表。例如功能需要使用UART,SPI等这些外设资源,所有外设资源列成一个外设表,表中是所有需要使用的外设资源。如果还需要其他驱动,例如传感器、LCD、FLASH等,所有器件列一个器件驱动表。
有了第一步和第二步从上向下的规划,此时我们应该已经很清楚这个项目需求书如何变成代码实现。此时,开始编写程序,从下向上,逐层编写。
首先要根据外设表编写每个外设对应的驱动,每个外设驱动单独列一个文件,编写好之后验证功能是否正常,这个步骤跟本文开头提到的开发板示例程序类似,很基础。如果同一个外设,需要同时实现几路输出或输入,例如需要三个不同的IIC输出,那就同时启动三路IIC验证功能是否都正常,每一路之间会不会有干扰。
每编写一个驱动,一定要对驱动中的每一个函数进行功能验证。只有底层没问题,才能往上封装。
因为器件的驱动很多都是以外设驱动为基础的,所以写完外设驱动之后,利用外设驱动的函数,去实现器件的驱动,例如FLASH,可能是SPI通信的,那么我们首先要写好SPI驱动文件,然后调用SPI驱动文件,编写FLASH驱动文件,完成具体的功能函数,例如FLASH的擦除,读写等。
同样,每一个编写的器件驱动都要对每一个函数进行测试。
可能在项目中,还涉及到一些无关底层的东西,例如一些加密算法,复杂的数据处理算法,各类通信协议等。可以把每一类分为一个模块,编写或者移植完之后一定要验证每一个函数的功能。
驱动文件都准备好了之后,就利用驱动文件,加上必要的数据处理,去完成任务里的每一个具体的功能函数。
同样,每一个编写的功能函数都要进行测试。保证功能正常实现。
完成步骤6之后,要对每一个任务进行测试,看是否能实现规划中要求的功能。
完成步骤7之后,项目基本完工,因为每一层都是经过测试的,所以整个系统也不会有太大问题,但是也可能存在一些例如逻辑或者资源冲突的错误。此时按照需求书要求的功能对整个系统进行反复测试,查找可能存在的问题,进一步完善程序。
在整个设计过程中,还有一些其他的注意事项,例如:
● 如果开了很多中断,一定要对每个中断设置优先级。明确哪个优先级应该优先处理。
● 相同优先级下的两个中断,禁止在第一个中断中触发第二中断,程序会卡死。
● 少用全局变量,系统运行时必要的变量和状态可以都放在结构体中。然后只定义一个结构体变量。
● 当结构体的成员为不同数据类型时,成员排序就显得很重要,优化排序会节约很多内存。
● 结构体做形参的时候,为减少栈空间的开支,最好设置结构体指针类型的形参。
● 不可以返回指向栈空间的指针。
● 如果通信接收或者发送没有硬件FIFO,可以编写软件FIFO,避免丢包。
● 所有变量要初始化具体数值,尤其是局部变量。
● 定义指针类型变量一定要初始化。
● 只在本文件里使用的函数或者全局变量,一定要加static。
● 头文件中只能声明,不可以定义。
● 一定要添加合适的注释,大部分时候代码不知自己看。
● 使用数据结构存储或者处理数据,例如结构体、枚举、链表、队列等。
看完这些步骤,是不是觉得复杂项目的程序设计原来也挺简单的,那就愉快的开始程序设计之路吧。
声明:本文内容来源于网络,若有侵权请联系删除,谢谢。
END
扫描上方二维码加群,回复【加群】或扫码加我好友,限时免费进入技术交流群。
【专辑】器件选型
【专辑】单片机
【专辑】经验分享
【专辑】STM32
【专辑】硬件设计
【专辑】软件设计
【专辑】开源项目
【专辑】职业发展
感谢大家阅读,如果喜欢
请点赞和“在看”吧,或者分享到朋友圈。
点击跳转到原文,限时优惠加入我们的知识星球(加好友获取免费券)