发帖数

53

原创数

53

关注者

11

阅读数

9161

点赞数

1

黄忠

  • 异常和中断

    异常是能够引起程序流偏离正常流程的事件,当异常发生时,正在执行的程序就会被挂起,处理器转而执行一块与该事件相关的代码(异常处理)。事件可以是外部输入,也可以是内部产生的,外部产生的事件通常被称作中断或中断请求(IRQ)。几乎所有的现代处理器都支持异常和中断,微控制器的中断可以由片上外设或软件产生。由此可见,通常我们处理的中断是异常的一种。

    每种异常类型都有对应的优先级,有些异常的优先级是固定的,有些是可编程的。

    先说几个概念:

    1、不可屏蔽中断(NMI

    NMIIRQ类似,只是它不能被禁止,并且优先级仅次于复位,它对于工业控制和汽车之类的高可靠性系统非常有用。根据微控制器设计的不同,NMI可以用于掉电处理,也可以连接到看门狗单元,以便在系统停止响应时将系统复位。由于NMI不能被控制寄存器禁止,其响应的及时性就得到了保证。

    2、硬件错误

    硬件错误异常用于处理程序执行时产生的错误,这些错误可以是试图执行未知的操作码、总线接口或存储器系统的错误,也可以是试图切换至ARM状态之类的非法操作。

    3、SVC(请求管理调用)

    SVC指令执行时就会产生SVC异常,其通常用在具有操作系统的系统中,为应用程序提供了访问系统服务的入口。

    4、PendSV(可挂起的系统调用)

    PendSV是用于带OS(操作系统)的应用程序的另外一个异常,SVC异常在SVC指令执行后会马上开始,PendSV在这点上有所不同,它可以延迟执行,在OS上使用PendSV就要确保高优先级任务完成后才执行系统调度。

    5、系统节拍

    NVIC中的SysTick定时器为OS应用可以利用的另外一个特性。几乎所有操作系统的运行都需要上下文切换,而这一过程通常需要依靠定时器产生定时中断来完成。

    6、中断

    中断信号可以连接到片上外设,也可以通过IO端口连接到外部中断源上。外部中断只有在使能后才能使用,如果中断被禁止了,或者处理器正在运行另外一个相同或更高优先级的异常处理,则该中断请求会被存储在挂起状态寄存器中。当高优先级的中断处理完成或返回后,挂起的中断请求才可以执行。NVIC能够接受的中断请求信号可以是高逻辑电平,也可以是中断脉冲。应该注意的是,在微控制器的外部接口中,外部中断信号可以是高电平也可以是低电平,或者可以通过编程配置。

    异常的处理流程

    1、接受异常请求

    处理器要接受一个异常,需要满足的条件:

    ①对于中断和SysTick中断请求,中断必须使能

    ②处理器正在执行的异常处理的优先级不能相同或更大

    ③中断屏蔽寄存器没有屏蔽掉异常

    特别注意一点:对于SVC异常,如果用到SVC指令的异常处理的优先级与SVC异常本身相同或更大,这种情况就会引起硬件错误异常处理的执行。

    2、压栈和出栈

    为了使被中断的程序能正确继续执行,在程序切换至异常处理前,处理器当前状态的一部分应该被保存。不同架构处理器的处理方法不同,有的采用硬件自动处理的方法来备份和恢复处理器状态,看需求,有的是需要程序中增加软件处理过程。

    异常处理过程执行到最后时,将会利用执行特殊值来触发异常返回机制。处理器还会查看当前是否还有其他异常需要处理,如果没有,处理器就会恢复之前存储在栈空间的寄存器值,并继续执行中断前的程序。

    自动保存和恢复寄存器内容的操作被称为“压栈”和“出栈”,这种机制使得异常处理可以跟普通的C函数一样处理,同时也减小了软件开销以及回路大小,因此也降低了系统的功耗。

    3、异常返回指令

    根据处理器的不同中断处理返回有的需要特殊指令,一般都是普通的返回指令,加载到PC中的数值则会触发异常返回,这样就使得异常处理可以和普通的C函数一样使用。

    两个不同的指令可以用于异常返回:

    BX  <Reg>q     ;将寄存器中的值加载到PC

    POP {<Reg1>,<Reg1>,...,PC}  ;POP指令,PC也是更新的寄存器之一

    当其中一个指令执行,异常返回机制就会启动。

    4、末尾连锁

    如果当其他的异常处理完成后,还有异常处于挂起状态,这时处理器不会返回到中断前的程序,而是重新进入异常处理流程,这也被称作末尾连锁。当末尾连锁发生时,处理器不必马上恢复栈的值,因为如果这么做的话还得重新压栈。异常的末尾连锁降低了异常处理的开销,因此也提高了能耗效率。

    图片1.png

     


    收藏 0 回复 0 浏览 104
  • 如何学好嵌入式系统中的C语言编程

    大家好,我是张实战电子黄忠老师;今天我们来学习如何学好嵌入式系统中的C语言编程。

    1、真正深刻地认识存储器

    .诺伊曼说过“程序等于算法加数据结构”。首先,算法是什么?算法是通过存储在存储器中的程序代码实现的。其次,数据结构又是什么?数据结构是存放在存储器中的各种类型的数据。程序本质上就是处理器通过执行存放在存储器中的程序代码对存放在存储器中的数据进行操作和变换的过程。在这个过程中除了处理器本身外,最核心的环节就是存储器。因为不管是程序的可执行代码还是数据都是存放在存储器中的。撇开代码、变量、数组、指针、结构、堆栈等这些软件中的各个元素的表象,剩下的本质就是存储器!因此,理解C语言的关键是真正理解存储器。

    每一个存储单元都有两个属性:一是存储器里面存放的内容,二是存储器的地址。这个内容可以是代码,也可以是数据,甚至是另一个存储单元的地址(这个时候往往我们称这个存储单元放的是一个指针)。

    2、认识和理解嵌入式C编程环境

    嵌入式软件开发的 一个非常重要的特点就是交叉编译,也就是开发工具运行的环境和被调试的程序不是运行在同一个硬件平台(处理器)上的。一般而言编译器、汇编器、链接器等工具链软件以及调试工具都运行在通用的PC机平台上;调试工具通过一定的通信手段将链接器输出的可执行文件下载到嵌入式系统开发板(一般称为目标系统)的存储器中,并通过一定的机制控制和观测目标系统的寄存器、存储器等。这个开发过程往往需要使用多种不同的工具,对此初学者很容易感到困惑。只有真正理解开发过程中各个环节的作用,才能对嵌入式系统C编程有深入的认识。

    另一个问题是,虽然C语言是一门高级语言,但是想真正用好C语言,程序员必须对编程过程中所使用的工具非常了解,清楚地知道每个工具的作用以及这些工具与硬件平台的相互关系。比如:编译器是如何处理全局变量和全局数组的?对于全局变量的处理与局部变量有什么不同?编译器是如何利用堆栈进行传递参数的?又比如:C语言的编译器、链接器是如何处理一个项目中多个C文件之间的相互依赖关系的?链接器最终是如何生成可执行文件的?可执行文件的内存映像又是如何安排的?这些问题初看起来似乎与C编程本身没有什么关系,但因为在嵌入式软件的开发过程中程序员要经常直接和底层的设备与工具打交道,所以一个嵌入式软件的程序员应该对这些问题了如指掌。

    图片18.png 

    3、认识和掌握C语言中的常见陷阱

    C语言不是一门面向初学者的编程语言,C语言发明者的初衷是希望设计一种面向编译器和操作系统设计的高级语言,因此C语言中充满了各种各样对于初学者而言的陷阱。这些陷阱一方面来自于C语法本身的灵活性,另一方面来自于C对存储器边界的不检查,因此非常容易在代码中造成存储器越界访问的问题。在C语言中,最容易出错的地方是与存储器相关的内存访问越界以及内存泄漏的问题,C语言的使用者必须非常小心地规避这些陷阱。

    4、掌握C语言程序设计过程中的调试方法

    任何程序在编写的过程中都需要调试,尤其对于比较复杂的系统更是如此。面对程序编写过程中出现的问题,比较现实的问题应该是如何在最短的时间内发现程序错误的根源,修改这个错误,并且吸取教训争取在以后的程序中不再犯同样的错误。在这个环节中最重要也是最需要技巧的工作就是找到问题的根源。虽然很少有相关的参考书介绍这方面的内容,但事实上,程序的调试是有一定的方法和技巧的。

    20.png 


    收藏 0 回复 0 浏览 148
  • 单片机的几种复位电路

    大家好,我是张飞实战电子黄忠老师;在单片机的使用中,经常会接触到复位电路,它是单片机最小系统重要的一个构成部分。同样它也是非常重要的一部分。

    复位就是让单片机从初始化状态开始重新运行,即程序从头开始执行。复位电路设计的好坏,直接影响整个系统是否稳定可靠。复位电路与单片机的RESET/NRST引脚相连,拿STM32系列单片机举例,当系统正常工作时,如果RESET引脚电压低于某一阈值,则单片机进入复位状态。单片机的复位可分为低电平复位和高电平复位,这是由厂家决定的,区分的方式可以看数据手册,手册中的复位章节会写清楚是什么电平复位。单片机的复位可以分为:上电复位、掉电复位、软件复位、外部手动复位等。

    上电复位:单片机每次上电都会给RESET脚一个复位信号,让单片机从一个固定的相同状态重新开始工作;

    掉电复位:单片机复位引脚电压低于某一阈值电位时,单片机会进入复位状态。

    软件复位:程序员执行某一特定的复位指令,来使单片机进行复位,或者当程序在一定时间失去响应的情况下通过看门狗电路控制单片机进行复位。

    外部手动复位可以通过一个复位按键让死机或跑飞的程序重新运行。

        图片15.png图片15.png

    下面我们一起看一下常见的几种复位电路。

    图片16.png     图片17.png

                1                                                                                                                                                 2

    1是最常见的低电平上电复位原理图,我们来分析这个过程,上电前电容两端电压为0。上电后,电流从3.3V流经电阻、电容到地,由于电容两端电压不能突变,所以上电瞬间RESET脚上电压也为零,并保持一小段时间低电平,这段时间触发单片机复位;随着时间推移,电容两端电压太高超过某一阈值电位,复位完成。这个电阻、电容的取值大小影响到复位引脚电平的上升时间(电容的充电时间)。

    2 手动按键复位原理图,手动按钮复位在GND和RESET之间接一个按钮。当人为按下按钮时, RESET脚就会被GND拉为低电平,使单片机进入复位状态,如果手不松掉,那么会一直处于复位状态,直至手松掉之后,复位引脚电平恢复。下图位ST单片机内部的复位波形,上电的时候VDD大于Vpor并持续一定的时间,单片机脱离复位状态,掉电的时候Vdd电压低于Vpdr电压,单片机进行复位。

                 图片19.png

    软件复位也分两种,我们可以使用复位指令,直接使单片机进行复位,另外一种使用单片机内置看门狗,配置启动看门狗,在主循环程序中每隔一定地时间刷新看门狗,俗称喂狗。如果一定的时间没有喂狗,可以认为程序跑飞,则看门狗模块会复位单片机,内置看门狗又分窗口看门狗和独立看门狗,区别如下:

    时钟不同

    1、独立看门狗:独立看门狗使用的是内部专门的 40Khz低速时钟,不需要使能时钟操作。

    2、窗口看门狗:窗口看门狗使用的是 PCLK1的时钟,使用前需要先使能时钟。

    中断不同

    1、独立看门狗:独立看门狗没有中断,超时直接复位。

    2、窗口看门狗:窗口看门狗可以在中断中做复位前的函数操作,比如报错一些数据等。

    使用场景不同

    1、独立看门狗:独立看门狗一般用于避免程序跑飞或者死循环。

    2、窗口看门狗:窗口看门狗避免程序不安预定逻辑执行,比如先于理想环境完成,或者后于极限时间超时。

    当然复位衍生出来的电路形态可能不止上述的两种,但是基本上都是围绕上述的电路进行变换,最后强调一点,RESET的走线越短越好,复位电路一定要尽可能靠近MCU, 因为复位电路到单片机这一段的走线,可能会引入其他外界因素的干扰,是单片机处于不稳定的状态。关于复位的内容就跟大家分享到这里,大家有没有遇到过关于一些复位引起的问题呢?


    收藏 0 回复 0 浏览 593
  • 24C02存储细节规范

    大家好,我是张飞实战电子黄忠老师;前面文章分享了I2C的一个标准规范,只是知道这些标准规范,还不能和目标器件进行正常通信,我们以24C02(EEPROM)为例来做一个简单说明,想要和EEPROM通信,就得遵从EEPROM的通信规范因为EEPROM是一个不可编程器件,它的规范是固定的。
        那么它都有哪些规范需要我们去注意呢?比如数据传输的时候拉高拉低要保持多长时间才有效通信的时候要按照什么格式写,先发高位还是低位等等。在这里一共有4个时间需要注意,分别为数据建立时间,保持时间,时钟高电平时间,时钟低电平时间,手册上对这几个时间都是有要求的。
    图片9.png
        从图中我们看出,当我们在SCL为低电平期间,把SDA数据改变,这个时候SCL时钟还是要保持一段时间才能拉高发送,这个保持的时间就是数据建立时间
    图片10.png图片10.png

    1                                图2
        从图1箭头处可以看出,当我们把SCL拉低之后,SDA数据是不可以立马去改变的,SCL时钟拉低还是要保持一段时间才能去改变SDA数据线电平的,这个时间就是保持时间

    从图2箭头处可以看出,当我们把SCL拉高发送数据的时候,这个SCL时钟保持高电平,是要持续一定时间的,这个时间就是时钟高电平时间,时钟低电平时间也是同理

    图片12.png
      这个是24C02器件在400K高速通信4个时间的要求,那我们在进行单片机编程的时候,我们需要根据这几个C间设置相关寄存器24C02操作时,不光有这几个时间,它还有自己的通信协议规范我们以字节写为例,来进行说明。
      下图就是24C02通信协议我们先看对24C02写数据,是1个字节写指令根据这个写一个字节对应的定义我来一个一个进行解释
    图片13.png图片14.png
    1表示启动
    2表示设备地址也就是我们说的从机地址设备地址为7位),发地址时,先发地址的高位,再发低位(MSB为高位,MSB在前,表示先发),可以通过24C02器件某几个特定的引脚接不动的电平状态来设置地址。
    3表示是对从机如何操作,是读还是写(一个位)这里是写操作
    4表示要读写数据的地址
    5表示要读写的数据内容

    6表示停止
    7表示每进行一次传输从机的应答每次传送1个字节,也就是8个bit也就是每8个bit一个ACK信号

    以上就是一个字节的写,当然还有多字节写,单字节读,多字节读等操作,原理都是一样的,知道了这些之后,我们才可以对应的去写程序代码。赶快去试试吧!


    收藏 0 回复 0 浏览 154
  • 什么是Map文件,它有什么用?

         大家好!我是张飞实战电子黄忠老师;前文章《不要再找啦,关于Cortex-Mx芯片的启动没有比这里更清楚啦》STM32单片机的启动流程以及main函数的执行做了一个详细的分析,今天我们MDK编译、链接后生成的map文件简单分析一下,加深对链接器、嵌入式系统可执行映像特点的了解。

    什么是map文件?简单的说map文件是通过编译器编译之后,集众多信息为一身的一种映射文件。很多工程师在遇到内存越界溢出情况分析map文件。通过map文件可以知道函数大小,入口地址等一些重要信息。最直观的的在Keil编译之后,编译窗口会显示类似如下一段信息:Program Size这一段提示信息汇总了程序和数据的信息,这些信息是单个模块汇总而成,在map文件里有详细列表。

    图片1.png 

    1.Section Cross References,主要是各个源文件生成的模块之间相互引用的关系。

    图片2.png
    上面这句话,main.omain.c生成的目标文件模块,start.ostart.s生成的目标文件模块,上面这2个含义是:用户在start.o启动代码中调用了__main.o模块中的StartProgram函数,StartProgram又调用了同文件中Led_Init函数。
     
    2.Removing Unused input sections from the image. 就是将库中没有用到的函数从可执行映像中删除掉,减小程序的体积。
    3.Image Symbol Table

    图片3.png
     

    Local Symbols 是系统内部的局部标号以及用户的一些局部标号
    图片4.png
    Global Symbols此部分描述了系统以及外部标号的所属地址、所占空间大小、所属文件等信息。
    图片5.png     

    4. Memory Map of the image映像的内存分布图片6.png
    1>.Image Entry point : 0x08000009,这个指的是复位程序RESET_Handler的地址。
    2>.Load Region LR_1 (Base: 0x08000000, Size: 0x00000044, Max:0xFFFFFFFF, ABSOLUTE),是程序的加载映像地址和长度,0x00000044=Start.0文件的大小(0x0C) + main.oLed_Init函数的大小(0x3C) + main.oStartProgram函数的大小(0x08)
    3>.Execution Region ER_RO (Base: 0x08000000, Size: 0x00000044, Max:0xFFFFFFFF, ABSOLUTE,指的是程序中的代码段和常量。
    4>.Execution Region ER_RW(Base: 0x20000000, Size: 0x00000000, Max: 0xFFFFFFFF, ABSOLUTE) 指的是可读写的全局变量和静态变量区域,因为我们现在只是一个简单的测试程序,没有变量,所以这里Size大小为0

    5>.Execution Region ER_ZI(Base: 0x20000000, Size: 0x00000000, Max: 0xFFFFFFFF, ABSOLUTE) 指的是程序中被初始化为零的变量,这里Size大小为0的原因同上。

    6>6.Image component sizes 这是指各个模块的大小信息

    图片7.png
    Total RO  Size (Code + RO Data)                680.07kB)
    Total RW  Size (RW Data + ZI Data)              68 (   0.00kB)
    Total ROM Size (Code + RO Data + RW Data)      68 (  0.07kB)

    图片8.png
    由于我们测试程序总没有定义一些变量,只是简单的操作了3个寄存器,所以RW Data + Zi Data0,其余全是代码指令信息,共680x44)个字节,程序总大小为68/10241KB = 1024个字节), 约0.07Kb

    至此我们已经解开了Map文件的神秘面纱,文件包含的信息全部分析完成,从文件中我们可以得出一些有用的信息,便于我们在分析调试代码的时候使用,当然Map配合链接文件(.sct)可能会看到直接的效果,后期有机会我们聊一聊链接文件,谢谢大家。




    收藏 0 回复 0 浏览 1219
×
黄忠