zl程序教程

您现在的位置是:首页 >  后端

当前栏目

STM32开发必备知识篇:STM32的运行机制

开发 知识 必备 STM32 运行机制
2023-09-14 09:16:19 时间

       时间飞逝,转眼间已经硕士毕业工作第四年了,嵌入式研发成长道路上曲折坎坷,所以这也是我今年撰写博客的初心,即分享技术积累和研发经验,目前主要分为STM32和FPGA两个专辑,都包括开发必备知识篇和实战项目例程篇,最后推出ARM和FPGA系统开发实战工程结合项目背景和技术难点,很多东西不是说很难学完全学不会,而是没有条件去系统性学习,所以学来学去得到更多的是知识碎片,然而当系统性掌握开发知识后,再多一些真实优秀的项目代码和调试经验,勤学勤练很快就可以从一名学生蜕变成一名嵌入式工程师。

        其中就拿STM32来说吧,在上学读书的时候笔者接触过,当时只跑过一些正点原子的例程demo,玩过那些demo后,脑子里只知道有这件事情。比如跑跑Emwin人机界面的demo,知道有某个API可以绘制某个控件;跑跑中断demo,知道某个中断需要初始化怎么配置,中断来了中断函数才会被触发;跑跑freertos例程,知道原来很多东西都可以分为任务集,任务集可以编成函数,再在任务集里处理某些事情等等,完全没有太多专研,毕业为了找工作跑跑demo也丰富简历,那时相信大部分同学都会认为FPGA和ARM LIUNX更吃香更有值钱,后来却不曾想STM32机缘巧合地陪伴了我毕业工作后的四年研发生活。

       和FPGA一样,在撰写10个STM32实战工程前(10个工程中包括IAP更新程序、Modbus rtu移植、Lwip库精讲精析、Fats32移植、Usbtmc二次开发、Freertos任务调度模式解析、Emwin+Freertos搭建整机显示控制面板、Dma+Freertos+消息队列等也都是MCU开发高薪必备的技术要点,同时也关系到整个嵌入式软件的稳定性和健壮性)还是多写一些关键性的基础知识博客,打好基础真的非常重要,比如在写FPGA 20个例程之前,笔者花费很多精力去写基础知识,因为只有当具备了这些基础后,才可能以后越来越清晰,设想如果连FPGA计数器和状态机怎么写都不清楚,那么后期遇到更复杂的设计和需求只会越学习越吃力,每个模块都编写很多Testbench测试,再不停地返工,状态机夹杂着计数器各种打拍子,根据仿真波形不停在凑时序,人为地在给自己研发工作做加法,而恰恰相反,当步入工作岗位以后,大家都需要寻找项目周期和家庭生活的平衡点,把研发工作做减法,把需求层层划分,细化模块和评估风险后把项目顺利地推进,这也需要更多地积累总结,和系统性地学习认识。类似地如果STM32连它的工作原理和运行方式都模模糊糊,那么后期写出来的代码,只会是delay嵌套和一堆if else else if等,随着需求的增加代码维护性越来越低,各种隐藏bug会接踵而至。而写这篇博客的目的,就是为了讲明白STM32的启动过程,把那些模糊的要点搞透彻。

       首先不管STM32还是ARM LIUNX,最初一段启动初始化CPU的代码都是用汇编写的,当然这里面有很多含义包括初始化一些寄存器,栈顶指针和中断向量表等等,在这里我们需要来看一个图,是笔者用Visio特意画的,结合图片更容易理解整个ARM运行的过程,对于ARM来说一条指令的执行分为三个阶段即1.取址:CPU将PC寄存器中的地址发送给内存,内存将其地址中对应的指令返回到CPU中的指令寄存器(IR);2.译码:译码器对IR中的指令进行识别,将指令(机器码)解析成具体的运算;3.执行:控制器控制运算器中对应的运算单元进行运算,运算结果写入寄存器,每执行一条指令后PC的值会自动增加指向下一条指令,所以正常运行的状态下ARM在不断地执行每一条指令。

 

 图1 ARM的工作原理

       然后大家需要知道如图2所示ARM得8个基本的工作模式,当然不同厂家生产的不同MCU或者ARM可能标准不一,因为写博客准确些所以这里选择最全面的说法吧,正常知道User、FIQ、 IRQ常用模式即可,对于ARM来说在正常执行指令的时候,这时有用户按下按键,定时器计数满,串口网口来了一包数据等,这些对于ARM是一种异常,即处理器在正常执行程序的过程中可能会遇到一些不正常的事件发生,这时处理器就要将当前的程序暂停下来转而去处理这个异常的事件,异常事件处理完成之后再返回到被异常打断的点继续执行程序,整个流程如图3所示。

  图2 ARM的8个基本的工作模式

图3 ARM中断异常处理示意图

       接着这时候就需要说下前面提到的中断向量表了,ARM在初始化一些配置后,当异常来到的时候它需要知道是什么异常来打断它,是用户按键、是串口报文还是编码器旋转等,对于STM32来说中断向量表就把CPU遇到的异常和中断函数绑定到一起,CPU遇到异常,如图3所示先压栈保护现场,再进入中断函数就是如图4所示的很多IRQHandler函数,在中断函数里大家可以做一些判断和赋值逻辑,处理完中断函数后,CPU就会重新通过栈指针恢复进入中断函数之前程序执行的位置,继续正常运行。

 图4 STM32F103中断向量表

        最后搞清楚这些原理以后,其实大家也就明白了为什么STM32编程需要前期INIT初始化各种配置,因为STM32很多引脚有重复用的功能,需要把引脚初始化等才能当异常来到的时候,正确地跳转到对应的中断函数里,为什么IRQHandler函数要尽量少做复杂操作呢,因为实时性问题STM32进入中断函数之前会压栈保护现场方便退出后重新执行异常来到前的程序,同时也会屏蔽其他IRQ中断信号,设想你在一个中断里做了一件可能需要10秒钟才能处理完的函数,那么这10秒钟内串口来了数据CPU无法及时响应会丢包,用户触摸屏幕也没有反应好像CPU假死一样,那么用户体验毫无疑问是非常糟糕的。

       这篇博客就写到这里,感谢大家的阅读,周末如果有空会更新FPGA 20个例程篇的第5篇,关于LM75温度传感器实时采集的例程,通过这个例程笔者会把FPGA对IIC的时序逻辑设计进行详细地说明,也适用于其他IIC开发的场景。