嵌入式系统入门篇-1

嵌入式系统入门篇笔记-1

芯片型号:STM32F407ZETx ,STM32F407ZETx采用内核是 ARM Cortex M4,哈佛架构

1.嵌入式系统的定义(来自百度百科)

嵌入式系统是以应用为中心,以现代计算机技术为基础,能够根据用户需求(功能、可靠性、成本、体积、功耗、环境等)灵活裁剪软硬件模块的专用计算机系统。

2.嵌入式系统的体系结构

冯诺依曼体系结构如图

在这里插入图片描述
哈佛架构如图
在这里插入图片描述

冯.诺伊曼把计算机系统分为如下几个部分:
CPU(中央处理单元) = 算术逻辑单元(运算器) + 控制电路
存储器 Memory:
·ROM: Read Only Memory 只读存储器
·RAM: Random Access Memory 随机访问存储器 “R/W”
输入输出设备

哈佛架构(Harvard Architecture)
是一种将程序存储和数据存储分开的存储器结构,数据存储器与程序存储器采用不同的总线。
C51,ARM9之后嵌入式系统芯片

3.嵌入式系统的组成

硬件:芯片(CPU+总线+外设控制器)、外围电路……
STM32F407ZETx
软件bootloader 引导程序(作用:引导其他系统软件运行)在上电的时候,大多数硬件是不能正常工作的,并且CPU等少数硬件,只能在比较低的频率下工作。
bootloader的作用:
(1) 初始化必要的硬件
(2) 把系统软件(如:OS)引导到内存中运行
OS: Operator System 操作系统 (OS是用来管理和分配(硬件)资源的系统软件。)
现代操作系统一个特征是:并发。 “多任务”所有任务的执行,都需要CPU,但是CPU的数量非常有限。
调度:决定哪一个任务获取CPU的执行权。
调度策略
(1)分时策略 -> 分时系统:分时间片执行
windows 塞班 ios linux unix android ubuntu RedHat
(2)实时策略 -> 实时系统 RTOS: Real Time OS 实时系统每次取优先级最高的那个任务执行,直到这个任务结束、或者是它主动放弃CPU、或者有更高优先级的任务抢占它。
ucos freeRTOS vxworks …
应用程序(包括文件系统)……

4.各组件间的通信

  • 通信方式: 通信双方(发送方,接收方)

    • 全双工:同一时刻一方即可以发送数据给对方,又可以从对方接收数据。
    • 半双工:同一时刻一方只能发送数据给对方,或者是从对方接收数据。下一个时刻,可以改变。
    • 单工:任意时刻 一方只能发送数据给对方,或者是从对方接收数据。
      总线bus:计算机系统内部各组件之间是通过“总线”的来通信的。
  • 总线两个特点:
    (1) 多个部件可以同时从总线上接收相同的信息 -> 广播式
    (2) 任意时刻只能有一个设备向总线发送信息 -> 系统瓶颈
    (总线协议规定发送规则)

  • 总线: 按功能分

    • 数据总线(DB): 双向,宽度差别8bits/16bits/32bits
    • 地址总线(AB): 单向,CPU发地址,宽度与寻址空间有关 24bit==>16MB
    • 控制总线(CB): 命令和状态

    按位置分

    • 片内总线
    • 系统总线
    • 通信总线(I/O总线)

5.存储器的逻辑结构

在这里插入图片描述

双地址译码存储结构
如:存储器(内存)64*64字节存储矩阵:字节编码,需要12 bits

6.ARM Cortex M4体系结构

1.Cortex M4总线接口:哈佛架构,三套总线
(1) Icode总线,用于访问代码空间的指令instruction, 32bits 访问的空间为: 0x0000 0000 ~ 0x1FFF FFFF (512M). 每次取4字节指令,只读.
(2) Dcode总线,用于访问代码空间的数据data, 32bits访问的空间为: 0x0000 0000 ~ 0x1FFF FFFF(512M)。非对齐的访问会被总线分割成几个对齐的访问。“4字节对齐”:地址必须为4倍数(后两位为0,会做其它用途)。
从最底层来讲: 4字节对齐,CPU写出的地址必须为4的倍数。
如果底层硬件(体系结构)规定是4字节对齐,相应的编译器也会考虑到对齐的问题.否则效率会变低!
(3) System 总线,用于访问其他系统空间。如: 各硬件控制器,GPIO……。访问空间为 0x2000 0000 ~ 0xDFFF FFFF 和 0XE010 0000 ~ 0xFFFF FFFF。非对齐的访问会被总线分割成几个对齐的访问。I/O总线,用于访问芯片上的各外设寄存器的地址空间的。

7.Cortex M4工作状态(处理器状态)

  • ARM公司设计的CPU,可以支持两种指令集:
    • ARM指令集:32bits,功能比较强大,通用
    • Thumb指令集:
      • thumb 16bits, 功能也强大(Cortem M4只支持Thumb指令)
      • thumb-2 32bits,功能强大,增加了不少专用的DSP指令

8.Cortex M4寄存器

  • 通用寄存器: 没有特殊用途
    • R0~R7: 所有thumb thumb-2都可以访问
    • R8~R12: 只有少量的thumb指令可以访问,thumb-2都可以访问
  • 专用寄存器: 有专门的用途(R13 R14 R15 xPSR)
    • R13(SP): 栈顶指针寄存器(Stack Pointer)保存堆栈的栈顶地址。
      堆栈(Stack): 是用“栈的思想”来管理的一段内存。
      “栈的思想”:先进后出
      堆栈的作用:为了支持过程调用(函数)。1)“现场保护”;2)函数的具体功能的代码;3)“现场恢复”。
      过程调用(跳转):一个过程还没有结束,就去调用另外一个过程。所有指令的操作数和结果都需要在寄存器中,CPU才能访问一个过程的一些中间结果(保存在寄存器中的),如果一个过程,去调用另外一段代码(过程调用),另外的那段代码势必也要修改寄存器,如果另外那段代码不事先保存寄存器里面的内容,在代码结束,返回到前面的那个过程时,结果就会不对。
      过程调用(函数):
      “现场保护”:把寄存器里面的内容保存到内存中去 。 下一条指令地址保存到 R14 —入栈
      “现场恢复”:把原先保存的内容还原到相应的寄存器中去。 R14—出栈 R14----R15
      这个过程,正好是 “先进后出”

      小贴士: 堆栈有四种类型:空递增、空递减、满递增和满递减。(在该门课程中的堆栈是空递减)

      思考:(1)在C语言完成一个功能,我们可以定义一个宏,也可以定义 成一个函数,请问宏与函数有什么区别?
      答:函数有额外的开销(现场保护…现场恢复),但是宏没有。
      (2)C/C++中,为什么需要把一个函数声明成inline?
      答:inline内联函数:函数定义前加inline关键字,不会有额外开销,把被调用的部分复制到调用它的地方。

      小贴士: 所有支持过程调用的语言,都需要用到栈。
      Cortex M4有两个堆栈,双堆栈(MSP 主堆栈指针、PSP 进程堆栈指针)
      双堆栈的用处:为了支持操作系统。把操作系统用的堆栈和用户进程用的堆栈分开。

    • R14(LR): Linked Register 链接寄存器
      在执行过程调用的指令的时候,我们需要保存该指令的下一条指针的地址,因为这个地址,就是需要返回的地址。LR寄存器,就是用来保存过程调用调用的返回地址。

    • R15(PC): Program Counter 程序计数器,保存下一条要执行的指令的地址。PC会在取指后,会自动增加指令所占的bits位数。
      在ARM Cortex M4, PC + 4 在有“指令流水线”情况下,PC的值会有所不同。
      程序实现的跳转,就是往PC中赋一个跳转的地址(跳转就是不按顺序执行)。

    • xPSR: Program Status Register程序状态寄存器。
      程序状态寄存器:保存程序运行过程中的一些状态,这些要保存的状态,分为三类:

      • 应用状态寄存器 APSR: 计算结果的标志 N Z C V Q
      • 中断状态寄存器 IPSR: 中断的编号 Exception Number 8bits
      • 执行状态寄存器 EPSR: 执行状态,如: Thumb/ARM
        组合一个32bits的xPSR,如图:
        在这里插入图片描述
        N Z C V Q 我们每一条指令的执行都可以影响这些状态标志位。
        N: 负数标志。 Negative is set o bit 31 of the result(标志根据运算结果来设置)
        xPSR.N < Ret[31] 最高位是符号位 1 -> 负数, 0 -> 非负数
        如果 xPSR.N==1,表示上一条指令的操作结果为负数。

      N什么什么情况下会置位呢?
      答:如果一条指令,影响状态标志位(通常是+S的指令,如MOVS),指令可以不影响状态标志位,也可以设置为影响标志位。
      如:MOV R0, #3 ; ->不影响状态标志位,不管这条指令的执行结果如何,xPSR不变。
      MOVS R0, #3; ->影响状态标志位,xPSR.N <- 结果寄存器[31]
      MOVS R0, #3 R0[31] -> xSPR.N == 0

      Z : Zero 。零标志。结果所有bit位都为0,则xPSR.Z== 1否则xPSR.Z== 0,前提也是指令结果影响状态标志。
      如:MOVS R0, #0; -> xPSR.Z== 1
      MOVS R1, #4 ; -> xPSR.Z== 0
      MOV R2, #0 ;没加S,表示指令的执行,不影响状态寄存器 -> xPSR.Z值不变。
      C: Carry 借位或进位标志。
      进位: 在做加法运算时,产生了进位。则C== 1,否则 C== 0
      借位: 在做减法运算时,没产生借位。则C== 1,否则 C== 0
      ADC, ADD, CMN 加法。如果产生了进位,则C== 1,否则 C== 0
      SBC, SUB, CMP 减法。如果生生了借位,则C== 0,否则 C== 1
      如:MOV R0, #2
      MOV R1, #3
      SUBS R2, R0, R1; R0 - R1 -> R2
      R0 - R1 产生借位。 C== 0
      xPSR.C== 0=> R0 < R1
      --------
      SUBS R3, R0, R1
      R0 - R1 -> R3
      R0 - R1没产生借位,C== 1
      xPSR.C== 1,R0 >= R1
      -------
      CMP R0, R1
      xPSR.C== 0 => R0 < R1
      CMP R1, R0
      xPSR.C== 1 => R0 >= R1
      小贴士1: CMP是比较操作码,它会把两个操作数做减法看标志位从而看哪个数更小。
      小贴士2: 在计算中要注意数在机器中是用补码表示的,正数的补码是它自己,负数的补码=原码取反+1。如-9,9的原码为00001001,取反为11110110,加一为11110111,故-9的补码为11110111.
      小贴士3: 上述关于标志位结论都在机器上验证。

      以下指令执行后对应的标志位数值
      MOVS R0,#-2 //N=1,Z=0,C=0(R0=0xffff fffe)
      MOVS R1,#2 //N=0,Z=0,C=0(R1=0x0000 0002)
      ADDS R2,R0,R1 //N=0,Z=1,C=1
      SUBS R3,R0,R1 //N=1,Z=0,C=1(0xffff fffe-0x2000 0000)
      SUBS R4,R1,R0 //N=0,Z=0,C=0(0x0000 0002-0xffff fffe)
      CMP R0,R1 //N=1,Z=0,C=1
      CMP R1,R0 //N=0,Z=0,C=0

      V: oVerflow 溢出标志,反映有符号数做加减运算所得结果是否溢出,如果运算结果超过当前运算位数所能表示的范围,则溢出 xPSR.V = 1, 否则为0. 在有符号的运算中,进位(借位,C)与溢出是两个完全不同的概念。

      CASE 1: 有进位,结果溢出
      1111 1111 1111 1111 1111 1111 1111 1111 0xffff ffff(-1)
      +1000 0000 0000 0000 0000 0000 0000 0000 0x8000 0000(-2^31)
      -------------------------------------------------------------------------
      1 0111 1111 1111 1111 1111 1111 1111 1111 +2^31-1
      溢出:负数 + 负数 = 正数(结果溢出)
      C30 = 0
      C31 = 1
      代码测试
      MOVS R0,#0xffffffff
      MOVS R1,#0x80000000
      ADDS R2,R0,R1
      结果N=0,Z=0,C=1,V=1
      CASE 2: 有进位,结果未溢出
      0111 1111 1111 1111 1111 1111 1111 1111 (2^31-1)
      +1111 1111 1111 1111 1111 1111 1111 1111 (-1)
      ------------------------------------------------------------------
      1 0111 1111 1111 1111 1111 1111 1111 1110 (2^31-2)
      正数+ 负数 = 正数(结果未溢出)
      C30 = 1
      C31 = 1
      代码测试:
      MOVS R0,#0x7fffffff
      MOVS R1,#-1
      ADDS R2,R0,R1
      结果N=0,Z=0,C=1,V=0
      CASE 3: 无进位,结果溢出
      0111 1111 1111 1111 1111 1111 1111 1111 (2^31-1)
      +0000 0000 0000 0000 0000 0000 0000 0001 (1)
      -------------------------------------------------------------------
      1000 0000 0000 0000 0000 0000 0000 0000 (-2^31)
      正数+ 正数 = 负数(结果溢出)
      C30 = 1
      C31 = 0
      代码测试:
      MOVS R0,#0x7fffffff
      MOVS R1,#1
      ADDS R2,R0,R1
      结果N=1,Z=0,C=0,V=1
      CASE 4: 无进位,结果未溢出
      1111 1111 1111 1111 1111 1111 1111 1110 (-2)
      +0000 0000 0000 0000 0000 0000 0000 0001 (1)
      -------------------------------------------------------------------
      1111 1111 1111 1111 1111 1111 1111 1111 (-1)
      负数+ 正数 = 负数(结果未溢出)
      C30 = 0
      C31 = 0
      代码测试:
      MOVS R0,#0xfffffffe
      MOVS R1,#1
      ADDS R2,R0,R1
      结果N=1,Z=0,C=0,V=0

      Cn表示最高位的进位(或借位)标志
      Cn-1表示次高位的进位(或借位)标志
      综上所述:V = Cn 异或 Cn-1

      Q: 饱和标志
      饱和计算: 通过将数据强制置为最大(或最小)允许值,减小了数据畸变,当然畸变仍然存在,不过若数据没有超过最大允许范围太多,就不会有太大的问题。

      8bits无符号的加法:
      普通计算: 11111111 + 1 = 0
      11111111
      +00000001
      -----------------
      100000000
      饱和计算:11111111 + 1 = 11111111
      例子: 0111111111…11 => 0X7FFF FFFF
      1000000000…00 => - (2^31)
      ADD 普通加法
      QADD 将两上有符号的32bits相加后,进行饱和操作
      MOV R0, #0x7FFFFFFF
      MOV R1, #1
      ADD R2, R0, R1
      QADD R3, R0,R1

      T : xPSR[24]表示处理器当前执行的状态。(工作状态/处理器状态)
      ARM状态 xPSR.T== 0
      Thumb状态 xPST.T== 1

      IT:IF-THEN instruction status bit for conditional execution IF-THEN 位,它们是if-then指令的执行状态位(最多4条)
      例如:ITTT 三条then

      if (R0 > R1) // CMP R0,R1 GT:大于
      {
      r2 = 2 MOVGT R2, #2
      r3 = 3 MOVGT R3, #3
      r4 = 4 MOVGT R4, #4
      }
      ITE (下面汇编指令就相当于if-then-else语句)
      CMP R0,R1(比较R0,R1)
      MOVGT R2,#2(如果R0>R1,将2放进寄存器R2)
      MOVLE R3,#3(如果R0<R1,将3放进寄存器R3)
      上面指令执行了第24行指令,贴个图~~在这里插入图片描述

      ICI : Interruptible-Continuable Instrument 可中断-可继续指令位
      与IT共用位置,如果执行LDM/STM操作(对内存进行批量拷贝)时,数据没有操作完时,产生一个中断,中断的优先级很高,必须要打断这条指令的执行,这种情况,就需要记录被打断的寄存器的编号,在中断响应之后,处理器返回由该位指向的寄存器,并恢复操作,继续执行拷贝。

    • 中断屏蔽寄存器:中断 异常

      • PRIMASK[0] 片上外设的总中断开关
        1 屏蔽所有的片上外设中断
        0 响应所有外设的中断

      • FAULTMASK[0] 系统错误异常的中断总开关
        1 屏蔽所有异常
        0 响应所有异常

      • BASEPRI 为使中断屏蔽更加灵活,该寄存器根据中断优先级来屏蔽中断或异常。当被设置为一个非0的数值时,它就会屏蔽所有相同或更低优先级的异常(包括中断),则更高优先级的中断或异常还会被响应。每一个异常或中断,都会有一个优先级,中断数值越低优先级越高(一般从2开始往上设值)。

    • CONTROL寄存器(初始值0x00):用来控制选择哪个堆栈(主堆栈/进程堆栈),选择线程模式的访问等级(特权等级/非特权等级),特权等级可以访问所有;非特权等级只能有限访问。
      CONTROL[0] :线程模式的访问等级
      1 非特权等级
      0 特权等级
      CONTROL[1] :堆栈的选择
      1 进程堆栈 PSP
      0 主堆栈 MSP(目前只用到这个)

9.Cortex M4工作模式

“模式”: 不同环境,不同的角色

ARM cortex M4有两种工作模式:
Thread Mode: 线程模式(用户程序该干嘛干嘛)
Handler Mode: 处理模式(异常中断模式)

异常/中断: 打断CPU指令执行顺序的事件,称为中断

思考:为什么要支持两种模式呢? 为什么不只用一种模式呢? Thread Mode
如果只用一种模式,thread mode,为了响应一些外部事件(比如说,用户是否按下某个按键?):
轮询:轮流询问。 通过轮询,CPU也可能 响应外部事件,但是轮询天生就有缺陷:
(1) 浪费CPU
(2) 占用总线, Bus is always busy.
(3) 轮询有一个时间差,轮询的时间间隔。不及时!!!

在CPU内部设计一个 “中断模式”:为了提高效率和响应速度。

Handler Mode :中断模式,当一些比较重要的事件,产生时,CPU中止正在做的事情,切换到Handler Mode下去执行,此时 “特权等级”中断处理完成后,再返回到断点处,继续Thread Mode运行。
Thread Mode:线程模式。
特权等级 : 可以跑一些如OS的代码
非特权等级: 可以跑一些如 "用户态"的代码 在这里插入图片描述
小贴士: 特权等级 -> 非特权等级,但是:非特权等级不可以切换到特权事件,除非产生“中断”。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值