单片机——杂项

单片机专题之杂项

单片机

51单片机的延时计算

前言 正文 首先是时钟周期的算法:时钟周期(T)=1(秒)/晶振频率。 其次是机器周期:机器周期是由时钟周期组成的,机器周期是单片机完成一个基本操作所需要的时间。 1 传统的8051单片机: 2 STC单片机: 最后是指令周期:这个是单片机执行一条指令所需要的时间,它是由机器周期组成的。 总结 前言 我使用51,STC这一类的单片机做控制好几年,一直是使用现成的程序,在其上修修改改,以达到需求动作目的即可。从来都是不求甚解。想法既是如此,会用即可,了解那么多做什么。 此次又在做一项目,里面用到I2C通讯。本来是直接复制粘贴了事,却没想对里面的一个小小的延时函数起了兴趣,由于本人是基础功底只有5战斗力的渣渣,写写画画了一天才搞了个大体明白。 以前总是在看其他博主的文章,默默潜水。然而此次,突然就忍不住想写篇文章发表一下费尽心思的微不足道的心得体会。

正文 void Delay10us() //@12.000MHz { unsigned char i;

_nop_();
_nop_();
i = 27;
while (--i);

} 上面这段代码是用STC-ISP软件中的软件延时计算器给出的,选用的是8051指令集STC-Y5,延时10us。 以前都是直接这么拿来用的,今天却突然想搞个明白,为什么代码要这么写。

于是查了各方资料。 从单片机计时的源头找起,它由下面几部分依次组成。

首先是时钟周期的算法:时钟周期(T)=1(秒)/晶振频率。 (比如:上面代码的时钟周期为1/12M(秒))。 这是单片机的基本时间单位。是由晶振震荡出来的,也叫震荡周期。

其次是机器周期:机器周期是由时钟周期组成的,机器周期是单片机完成一个基本操作所需要的时间。 关于机器周期,每种单片机可能都不太一样,我也只用过传统51和STC这两款,就拿此来对比下

1 传统的8051单片机: 它的1个机器周期是由12个时钟周期组成的。 以12M晶振举例,它的一个机器周期就是:12(个时钟周期)*1(秒)/12MHz = 1(us)

2 STC单片机: 拿我常用的STC12C5A60S2这款单片机来讲,它可以有两个模式选择, 一个是1T模式,在这个模式下STC单片机1个时钟周期就是1个机器周期; 另一个是12T模式,这个模式下STC单片就和传统的8051单片机一样,12个时钟周期组成1个机器周期。 由此可见1T模式的速度就是12T模式的12倍。 以12M晶振为例,1T模式下就可以算得机器周期是: 1(个时钟周期)*1(秒)/12Mhz = 1/12(us)

最后是指令周期:这个是单片机执行一条指令所需要的时间,它是由机器周期组成的。 现在可以回到正文开头的代码中了。这个10us的函数是怎么得出来的呢? 这个我之前查过很多资料,比如执行while语句需要多少个机器周期。赋值需要多少个周期。也就是查这个占用了我很大一部分时间。直到最后将上面的延时函数直接调到main函数中debug调试,才明白,问题其实很简单啊。 无论是执行什么语句,最终都会回到汇编上来,debug里单步调试,所有的指令周期就会明明白白了。 我用main函数直接调用延时函数,如下:

void Delay10us() //@12.000MHz { unsigned char i;

_nop_();
_nop_();
i = 27;
while (--i);

} main { Delay10us(); }

我用的keil软件,将上述build之后,点击debug,开始调试

看图片上,开始debug,程序的起始就在C:0x0183 020171 LJMP Delay10us(C:0171), 这里有个长转移指令LJMP,它要转移到C:0171行去执行Delay10us这个函数。 那执行LJMP这个指令需要多长时间呢,查找STC数据手册,在1T模式下,此条指令在单片机上运行需要4个时钟周期。 接下来,按单步调试F11键,如下图:

程序成功转移到C:0171行,跳转到Delay10us函数中,此行程序执行NOP指令,空操作。查STC数据手册,NOP指令占用1个时钟周期。 接下来C:0172行,依然是NOP指令,1个时钟周期。 接下来C:0173行,此行执行 MOV R7,#0x1B,将立即数送入寄存器。是将27赋值给i。依然查手册,此条指令2个时钟周期。 继续:

此时执行到while语句了,这里执行的指令时 DJNZ R7,C:0175,寄存器减1非0转移。此条指令执行1次4个时钟周期。 上面已经将寄存器填入27了,因此这条指令将执行27次。 继续:

循环了27次,终于到0了,程序继续向下执行,此行指令RET,子程序返回。此条指令4个时钟周期。 继续:

程序又回到了起点。 好了,可以计算一下此次延时的时间了。1个LJMP,4时钟;2个NOP,2时钟;1个MOV,2时钟;27个DJNZ,108时钟;1个RET,4时钟。 4+2+2+108+4=120。 单片机的时钟周期是:1(S)/12MHz = 1/12(us) 此次延时的时间是:120 × 1/12(us)= 10(us)

1. 输入输出以及复用模式

1.1 输入

(1)输入上拉:默认输入高电平

(2)输入下拉:默认输入低电平

(3)浮空输入:不稳定

(4)模拟输入:如AD模拟信号

1.2输出

(1)推挽输出:P-MOS和N-MOS均有效,高低电平均有较强驱动能力

(2)开漏输出:N-MOS有效,低电平有较强的驱动能力

​ 补充:TTL 是斯密特触发器:防止信号不稳定

2. 初始化过程

(1)使能IO口时钟

(2)初始化IO口模式

(3)操作IO

3. 通信

形式 IO口 方式
UART TXD,RXD 全双工,异步
I2C SCL,SDA 半双工,同步
SPI SCLK,MOSI,MISO,CS 全双工,同步
1-wire DQ 半双工,异步

(1)全双工:同一时刻可互传数据

(2)半双工:同一时刻不可互传数据

(3)同步:双方靠一根时钟线来约定通信速率

(4)异步:双方各自约定通信速率

4. 单片机使用printf()

使用之前,一定要将配置里的【use microlLIB】选项卡勾上!不然用不了!!

1
2
3
4
5
6
7
8
#include "stdio.h"

int fputc(int ch, FILE *f)
{ 	
	while((USART1->SR & 0X40)==0);//循环发送,直到发送完毕   
	USART1->DR = (u8) ch;      
	return ch;
}

5. PWM和占空比

PWM,是脉冲宽度调制,是控制在一个周期内,控制高电平多长时间,低电平多长时间,在数字电路中IO口就只有两种状态,0和1(高低),对应就是0和5V或者0和3.3V)。也就是说,通过调节高低电平时间的变化来调节信号、能量等的变化

占空比是指 高电平持续 时间 一个 周期持续时间。 所以可以通过控制占空比,来控制输出的等效电压。所以对于方波的话,频率和占空比就确定了一个波

6. 输入捕获

输入捕获模式可以用来测量脉冲宽度或者测量频率,下图以测量脉宽为例来说明输入捕获的原理

img

假定定时器工作在向上计数模式,图中t1-t2的时间就是我们需要测量的低电平时间。测量方法为:首先设置定时器通道x为下降沿捕获,在t1时刻就会捕获到当前的CNT值,然后立即清零CNT,并设置通道x为上升沿捕获,到t2时刻又会发送捕获事件,得到此时的CNT值(记为CCRx2)。在t1-t2之间可能产生N次定时器溢出,因此需要对定时器溢出做处理,防止低电平太长导致数据不准确。

t1-t2之间计数的次数为:N * ARR + CCRx2,再乘以CNT计数周期即可得到低电平持续时间

7. 定时器时间计算

7.1 单片机的定时原理

通过每一个机器周期,就加一,通过数1的方式进行计时。

7.2 基本概念

1.晶振:又称晶体振荡器,是数字电路“心脏”,是电子元件中不可或缺的频率元件,对于数字电路系统,晶振的好还直接影响系统的稳定性。

2.时钟周期:将晶振的频率的倒数定义为时钟周期,在一个时钟周期内,CPU完成一个基础动作,对于更小的时钟周期,意味着CPU有更好的性能,更高的工作效率。

3.机器周期:将时钟周期的6倍或者12倍定义为一个机器周期,具体的设定需要通过人为进行控制

7.3 每一次“加1”经过的时间是多少

当晶振频率是11.0592MHz的时候,等于11059.2KHz = 11059200Hz

机器周期 = 12 x 时钟周期 =12 x (1/时钟频率) 秒 = 12 / 11059200 秒 = 12 000 000 / 11059200 微秒 = 1.085 微秒

也就是说对于11.0592Mhz的晶振,其机器周期就是1.085微秒

7.4 实际案例

案例:通过单片机设定一个10ms的定时器有如下的部分

符号 含义 TL0(Timer Low0) 定时器0的低8位寄存器 TH0(Timer HIgh0) 定时器0的高8位寄存器 根据上表,可以知道共16位的寄存器可以数2^16次,就是65536次,而每次计数一下,就会耗费1.085微秒,因此当计数超过65535时,定时器寄存器就会爆表,也就是经过之后就会爆表,通常可以理解为经过71ms之后就会爆表。

对于10ms而要,首先可以计算出10ms定时定时器需要数多少次,设需要数x次,则

设定时器的初始值为y,则

将56320转化成16进制数,通过计算可以知道16#DC00,因此TH = DC ;TL = 00

也就是说需要计数9612次,就可以认为经过了10ms,这时,我们可以用71ms减去10ms的次数,就可以知道定时开始计数的初始值,这个值就是y值

7.5 小结

单片机定时器的原理就是通过计算出所定时间需要计数的次数x,通过65536减去x,算出定时器所需的初始值,然后将初始值转化成16进制,填入TH与TL寄存器中,然后通过读取定时器溢出标志位,就可以知道定时器的定时状态。

8. stm32内存映射

摘要:要想把STM32单片机学好,芯片的内部结构就要必须搞清楚。所谓基础不牢,地动山摇。今天带大家来看看STM32F429的Memory map。

STM32F429芯片系统结构

img

STM32F429采用的是Cortex-M4内核,内核即CPU,由ARM公司设计。ARM公司并不生产芯片,而是出售其芯片技术授权。芯片生产厂商(SOC)如ST、TI、Freescale,负责在内核之外设计部件并生产整个芯片,这些内核之外的部件被称为核外外设或片上外设。如GPIO、USART(串口)、I2C、SPI等都叫做片上外设。

从上图我们可以清楚的看到芯片和外设之间通过各种总线连接,其中主控总线有 8条,被控总线有7条。主控总线通过一个总线矩阵来连接被控总线总线矩阵用于主控总线之间的访问仲裁管理,仲裁采用循环调度算法。比如数据从Cotex-M4到高速外设USB,数据交给在总线矩阵后,总线矩阵就会判给USB,然后通过USB所在的AHB1传输给USB。

img

三大总线

指令总线、数据总线、系统总线

ICode 着重传输指令,DCode 和 System 着重传输数据,至于更详细的区分,不用关心。实际上 ICode、DCode 和 System 内部都包含三个部分,即地址总线、控制总线、数据总线。

高速总线

直接挂接在总线矩阵上的有哪些呢?

ICode、DCode、System;FLASH连接总线;SRAM 连接总线;高速外设连接总线 AHB1/AHB2/AHB3;连接“桥”的总线。

这些高速总线直接与总线矩阵连接在一起,其实这些高速总线实际上就是总线矩阵的延伸,或者说就是总线矩阵的一部分。

内存映射

这张图太重要了,看懂这张图,你的STM32已经可以掌握50%了,下面就来着重讲解这一张图。这张图来自STM32F429参考手册第84页,由于原版是英文的,搞了一个翻译过来的版本。

img

img

1、STM32存储空间

**芯片能访问的存储空间有多大,是由谁定的?**这个是由芯片内CPU的地址总线的数量决来定的,STM32芯片内部的地址总线为32根。

1根地址线:可以传输的地址为0和1的,那么理论上就可以访问2个字节。

2根地址线:可以传输地址为00、01、10、11,理论上可以访问4个字节。

3根地址线:可以传输的地址为000、001、010、011、100、101、110、111,理论上可以访问8个字节。

32根地址线:可以产生00000000 00000000 00000000 00000000 — 11111111 11111111 11111111 11111111的2^32个地址,范围刚好为4G,所以我们就说STM32的32根地址线,理论上可以访问4G字节的存储器空间。

在上图的最右边可以看到STM32地址是从0x00000000到0xFFFFFFFF,这就是4GB的存储空间。但是STM32真的有4GB的存储空间吗?

img

你在想啥呢?答案当然不是,我们的PC电脑也才4GB的内存。一个小小的单片机怎么可能有4GB的存储空间!这个4GB的是STM32理论分配的地址空间。也就是说实际上并不是有这么大的存储单元。上图中第二排可以看到有很多预留的地址,这些地址并没有给他分配存储单元。

所有的存储器都是与地址线连着的,但是实际上如果你只接了一个10M的存储器,而且是从0地址开始映射的,那么32根地址线所产生的0~10M的地址信号其实才是有意义的,因为这些地址信号才有对应真实的存储器,而所产生的10M以上地址信号其实并无意义,因为并不对应真实的存储器。

举个例子,政府给你化了10栋楼房的面积用来盖房子,但是实际上你没有那么多钱,只盖了3栋楼,其他的7栋房子预留的面积只能放在那里,这样说你应该明白了吧。

STM32中的32是32根地址线的意思吗?

答:不是,STM32的32不是32根地址线的意思,而是表示MCU芯片内部CPU在处理数据时,每次可以处理的数据位宽为32个bit。正是由于这个原因,STM32 内部的寄存器大小都是32位的,刚好等于位宽。

某个芯片是32位的,但是它的地址线完全可以只有16根、或者8根,对于 STM32 来说,刚好碰巧的是,CPU能够处理的数据位宽与地址线数量恰好都是 32,所以不少小伙伴往往被搞迷糊了,认为是一回事。

2、什么是存储器映射

映射其实就是对应的意思。事实上存储器本身并不具备地址,将芯片理论上的地址分配给存储器,这就是存储器映射

举例理解:比如前面举的10M存储器的例子,这个10M存储器原本并没有地址,我将10M存储器映射到理论32根地址线可以传输4G个地址信号,每个地址信号访问一个字节,4G地址信号则可以访问4G个字节,所以理论上的可访问范围为4G。

地址0往后的10M范围,这10M的存储器就有了0—10M的地址,地址线所产生的0~10M之间的地址信号,就可以访问10M的这个真实存储器。至于在生产芯片时,在工艺和技术上具体是怎么实现我们所描述的映射的,我们无需关心。

3、STM32F429的存储器映射

STM32的所有片内外设其实都是存储器,所以所有的这些存储器都需要被映射,只是理论上的4G范围远远大与实际的存储器空间,也就说实际的存储器空间并没有4G。

存储器是很贵的,一个STM32单片机如果有4G存储器的话,那就很贵了,而且单片机的产品根本不需要这么大的存储空间。

理论上地址起始就是门牌号,存储中的每个字节就是房间,存储器生产出来后,这些房间是没有地址的(门牌号),映射的过程其实就是将这些门牌号分配给这些房间,分配好后,每个门牌号只能访问自己的房间,没有被分配的地址就是保留地址,所谓保留地址的意思就是,没有对应实际存储空间。

可不可以保留一些地址不分配呢?

当然可以,因为理论上可以有4G的地址,但是实际上不可能给你4G存储空间,否则这个单片机芯片你可买不起,PC机的内存也才4G/8G,单片机怎么可能真的给你4G存储空间呢?

寄存器映射

存储器本身没有地址,给存储器分配地址的过程叫存储器映射,那什么叫寄存器映射?寄存器到底是什么?

在存储器Block2这块区域,也就是地址从0x4000000—0x5FFFFFF这块区域,设计的是片上外设,它们以四个字节为一个单元,共32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。可以找到每个单元的起始地址,然后通过C语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,聪明的工程师就根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。

img

这一张图够清晰吧!

img

因此如果我想往0x40020410这个地址写入数值0xFFFF,应该怎么操作呢?

是不是只需要下面这一句话就可以了?

1
*(unsigned int*)(0x4002 0410) = 0xFFFF;

一直以为这句话很清楚,但是却发现有人看不懂这句话,那我来解释一下:

首先编译器不知道0x40020410是一个啥东西,它可能表示小猫,也可能表示小狗。但是我们知道这个16进制数是一个地址对吧?那么怎么把它变成一个地址呢?

是不是在它的前面加上(unsigned int*)变成(unsigned int*)(0x40020410)就把这个数变成一个指针了也就是一个地址了?但是我们操作的是这个地址里面的内容,是不是再在前面加上一个星号变成*(unsigned int*)(0x40020410)就可以了,然后就可以给它赋值了:*(unsigned int*)(0x4002 0410) = 0xFFFF;

地址重映射

自举(bootstrap)计算机设备使用硬件加载的程序,用于初始化足够的软件来查找并加载功能完整的操作系统。也用来描述加载自举程序的过程。什么是单片机的自举,单片机的自举就是单片机的启动。

我们说,单片机程序基本都是从0地址出开始运行的,F429的0x00000000-0x001FFFFF地址映射了到什么存储器上,那么就从该存储器上读取指令,开始运行。

至于说0x00000000-0x001FFFFF到底映射在了什么存储器上,这个要看F429 芯片 BOOT1、BOOT0这两个引脚的电平值,说白了就是,通过BOOT1和BOOT0 引脚的电平值,可以选择将0x00000000-0x001FFFFF映射到不同的存储器上。

STM32片内的FLASH分成两部分:主存储块、信息块。主存储块(主Flash)用于存储程序,我们写的程序一般存储在这里。信息块又分成两部分:系统存储器(系统FLASH)、选项字节。系统存储器存储用于存放在系统存储器自举模式下的启动程序(BootLoader),当使用ISP方式加载程序时,就是由这个程序执行。这个区域由芯片厂写入BootLoader,然后锁死,用户是无法改变这个区域的。选项字节存储芯片的配置信息及对主存储块的保护信息。

img

主FLASH的地址

主FLASH地址为0x0800 0000-0x081FFFFF,Jlink下载时的FLASH设置是不是通过Jlink下载到了地址为0x08000000的地方,大小是0x00100000,也就是1MB。

img

疑问:明明代码是下载到0x80000000往后的存储空间中,为什么说运行又是从0x00000000地址运行的呢?为什么不是从0x80000000开始运行的呢?

有关这个问题,就是我们说的单片机的自举

img

正常情况下都是映射到主FLASH上,所以都是从主FLASH上启动的,为了从FLASH启动,我们需要将代码下载到主FLASH上。

什么是地址重映射

如果0x00000000-0x001FFFFF之前是映射在系统存储器或者嵌入式 SRAM上的,现在改变BOOT0、BOOT1 的电平为 0、x。0x00000000 -0x001FFFFF就被重新映射在了主FLASH上,这就是单片机的地址重映射

重映射就是本来是和张三进行映射的的,现在改为了和李四映射。换句话说重映射就是0x00000000 -0x001FFFFF(1MB)本来映射在系统存储器 0x1FFF 0000-0x1FFF7A0F(30KB)上面,现在映射到了主FLASH 0x08000000 -0x081FFFFF(1MB)上面。

img

选择从主FLASH启动时,显然FLASH会被映射在了两片地址上。

  • 原本映射的地址(1MB):0x08000000-0x081F FFFF,进行Jlink下载时使用这个地址
  • 重映射的地址(1MB):0x00000000-0x001F FFFF,启动时CPU就是从重映射的地址读取指令

这两片地址都是有效的,重映射到FLASH上后,CPU从0地址开始运行时,就从FLASH上读取指令,当然前提是我们需要将代码下载FLASH中。

img

这就解释了为什么我们在keil中设置好程序的下载地址为0x8000000,但是单片机上电是确实从0开始执行。是因为我们在硬件上设置了BOOT0=1,BOOT1=X,从而导致了主FLASH区(也叫主闪存,大小1MB)被映射到了0x00000000-0x001FFFFF(1MB),故而代码是下载到 0x80000000 往后的存储空间中,却说运行又是从0x00000000地址运行的。

疑问:下载时,能不能使用0x00000000地址来下载?

答:这个不行,因为下载时,0x00000000-0x001FFFFF还没有被重映射到FLASH上,只能使用0x08000000来下载。

上面说的是我们用JLink下载器下载代码,但是有时候我们还听说可以用串口来下载程序,这又是怎么回事?

用串口下载程序,也就是我们说的ISP在系统中编程。从系统存储器启动,即STM32的ISP了。此时硬件电路B00T0=1,B00T1=0。由于串口不能直接把程序下载到主FLASH里面,所以需要使用到ST公司内嵌于系统存储区的Bootloader来引导把程序下载到主FLASH里面。JLink能直接把程序下载到内置的FLASH里面,是因为JLink下载器内部有Bootloader来引导把程序下载到FLASH里面。程序下载完成后还需要配置BOOT引脚为BOOT0=0,BOOT1=X(即从主闪存存储器启动),复位后才能正常启动程序。如果你不修改BOOT引脚的话也就是B00T0=1,B00T1=0,那么0x0000 0000 - 0x001FFFFF是不是被重映射到系统存储器上面,而程序代码在主FLASH里面。你复位后程序肯定不能正常运行,只有在使用串口下载程序后配置BOOT引脚为BOOT0=0,BOOT1=X,复位后才能正常执行代码。你明白了吗?

总结:使用JLink下载代码,JLink下载器内部的Bootloader将程序引导下载到主FLASH里面。使用串口下载代码,由于串口没有Bootloader,就要使用ST官方内置在芯片系统存储区的Bootloader代码,将程序引导下载止主FLASH。又因为程序是从0开始执行的,所以我们复位后运行程序时一定要让BOOT0=0,BOOT1=X,将0x00000000-0x001FFFFF是重映射到主FLASH我们代码存在的地方,从0开始执行代码。

下图是使用FlyMcu串口下载程序,这个串口是USB-TTL,下载程序时让BOOT0=0,BOOT1=X即可。**不是说在系统中编程需要将B00T0=1,B00T1=0吗?**这是因为我们使用的是这个软件,这个软件可以通过DTR和RTS改变BOOT的引脚电平,达到不用修改BOOT引脚就可以下载运行代码,实际上是软件替我们做了改变BOOT引脚的操作,具体介绍可以看上面的说明。

img

关于ISP与IAP

关于这两者的区别已经在上面做过详细的解释了,下面是一个总结。

ISP在系统编程,是指直接在目标电路板上对芯片进行编程,一般需要一个自举程序(BootLoader)来执行。ISP也有叫ICP在电路编程、在线编程。

IAP在应用中编程,是指最终产品出厂后,由最终用户在使用中对用户程序部分进行编程,实现在线升级。IAP要求将程序分成两部分:引导程序、用户程序。引导程序总是不变的。IAP也有叫在程序中编程。ISP与IAP的区别在于,ISP一般是对芯片整片重新编程,用的是芯片厂的自举程序。而IAP只是更新程序的一部分,用的是电器厂开发的IAP引导程序。综合来看,ISP受到的限制更多,而IAP由于是自己开发的程序,更换程序的时候更容易操作。

9. 51单片机内存映射

思维结构很重要:

img

由上图可知,51单片机的存储器分为俩大部分:程序存储器ROM和数据存储器RAM,这一点有别与计算机

程序存储器ROM: 1、 程序存储器ROM顾名思义,存放程序的地方,程序指令指导单片机完成设定的功能 2、 51单片机专门设置一个16位的PC,用于指示下一时刻CPU将要执行的程序指令在ROM中的位置,由于PC指针长度位16位,所以单片机的程序存储器ROM空间大小为2^16 = 64Kb,从0000~FFFFH 3、 ROM还可以细分为片内ROM和片外ROM

片内ROM: 8051片内有4KB ROM,地址范围从0000H~0FFFH

片外ROM: 片外可扩展,一般从1000H~FFFFH ,64KB

PS:那么问题来了,51单片机是如何区分是片内ROM还是片外ROM呢?

片内ROM和片外ROM的区分: 在单片机既有片内ROM又有片外ROM时,会产生一部分重复的地址范围, 为了解决区分片内和片外ROM的问题,51单片机设置了一根控制线EA(低电平有效),所以 EA = 0 => 有效 => 访问片内存储器 EA = 1 => 无效 => 片内存储器被忽略 本质的原理: 就是当PC的值大于了某个值时将访问外部存储器,PC的值小于了某个值时将访问内部存储器。这个值由存储器容量的大小绝定,在8051单片机中,片内ROM = 4KB,所以当PC小于0FFFH时,访问片内ROM。 具体的执行过程: 当EA = 0,毫不犹豫直接从0000H开始访问片外ROM 当EA = 1,PC从0000H开始访问片内ROM,当PC大于0FFFH时,转向访问 片外ROM,如图:

在这里插入图片描述

程序存储器ROM中的7个特殊地址: 即7个外部中断的入口地址,每个地址相隔8个地址单元,存放终端服务程序显然不够,所以一般在这7个特殊地址中存放跳转指令,跳转到相应的中断服务程序。

在这里插入图片描述

数据存储器RAM: 1、 RAM存储器一般存放单片机运行期间所需要的的数据和临时生成的数据,需要能够快速的读写。掉电丢失。 2、 同ROM相同,RAM也分为片内RAM和片外RAM

片内RAM:

在这里插入图片描述

1、 00H~1FH:工作寄存器区,又称通用寄存器,32字节,8字节为一组共4组。 在特殊功能寄存器篇说到标志寄存器PSW的RS0、RS1位来选择工作的4组寄存器中的一组,即选择的就是这个位置中的某一组 2、 20H~2FH:位寻址区,16字节,128位,这个地址空间中的128位可以按位访问,每一位都有一个地址,寻址空间为00H ~ 7FH ,如图:

在这里插入图片描述

在20H~2FH空间中,重新为每一位地址分配一个地址00H ~ 7FH,从而实现按位访问。

3、 一般RAM区:又称用户RAM区,80字节,对于52系列,一般从30H~FFH 供用户使用,对于前俩个区中未使用的地址单元也可以作为用户单元使用 4、 堆栈区与堆栈指针 一般设置在2FH单元以后,避开工作寄存器区和位寻址区,典型应用就是子函数调用:

在这里插入图片描述

片外RAM: 同ROM相似,通过外部总线扩展RAM从而获得更大的存储空间,由于外部总线宽度为16位,所以片外扩展最多64KB,地址范围0000H~FFFFH

10. cc2530内存映射

本文以CC2530的F256为例,即有256kb的flash存储器和8K的sram存储器

一、CC2530里的四种存储空间(结构上划分的存储空间,并不是实际的存储器,是一种理论上的概念) \1. CODE 程序存储器 用处存放程序代码和一些常量 有16根地址总线,所以CODE的寻址范围是 0000H~FFFFH 共64KB

\2. DATA 数据存储器 用于存放程序运行过程中的数据 有8根地址总线,所以DATA的寻址空间为 00H~FFH 共256 byte.低128位可以直接寻址,高128位只能间接寻址。

​ \3. XDATA 外部数据存储器(只能间接寻址,访问速度比较慢) DMA是再XDATA上寻址的,这一点很重要 ​ 有16根地址总线,所以 XDATA 的寻址空间为 0000H ~ FFFFH 共64K

​ \4. SFR 特殊功能寄存器 就是那些T1CTL, EA, P0 等配置寄存器存储的地方 共128K。因为CC2530的配置寄存器比较多,所以一些多余的寄存器就放到了XREG 里面。XREG的大小为1K XREG的访问速度比 SFR慢。

以上4中存储空间只是4种不同寻址方式的概念,并不代表物理上具体的存储设备。例如 FLASH 或者 EEPROM都可以作为物理的存储媒介映射到CODE上,DRAM或者SRAM都可以作为存储媒介映射到DATA中。CODE和DATA是存储空间的概念,FLASH、SRAM、EEPROM等是具体的物理存储设备,这两个概念不要混淆。这好比,电脑需要RAM和ROM,这个ROM可以是西部数据的硬盘,也可以使三星的硬盘,也可以是不同材料的固态硬盘。一个是存储空间,另一个是具体的物理存储设备。

二、关于CODE存储器的映射 大家肯定会有疑问:既然CODE的寻址范围只有64KB,那CC2530F256怎么有256KB的flash呢? 正是为了解决寻址空间不足的问题,CC2530才提出了映射的概念。(当然,映射的另一重要目的是为了DMA) CC2530把FLASH存储器分成了几个bank,每个bank的大小是32KB,即对于F256来讲,它有8个bank分别是bank0~bank7(不同芯片,bank数目不同)。通过FMAP.MAP[2:0] 控制,把不同的编号的bank映射到CODE上,解决了寻址空间受限制的问题。上图.. CODE_1.png Banked code memory layout 从图中可以看出,bank0是rootbank,就是程序开始执行的地方,这个common area始终都是对应FLASH存储器的0000H~7FFFH, 上面的另一半可以映射bank0~7. 我以前一直疑惑,为什么common area已经有bank0了,上面怎么还可以有bank0,从user guide里看,这样是可以的,就比如CC2530F32,只有一个bank0,它也只能这么映射了吧。

三、关于XDATA存储器的映射 关于XDATA的映射,可以这么说:一切都是为了DMA!! 为了让DMA能访问所有的存储区域,所以把所有的存储器都映射到了XDATA上。上图: XDATA.png 从图中可以看出,XDATA中包含了所有存储器的映射,包括256kb的FLASH存储器,8K 的SRAM存储器,还有 SFR , XREG, INFORMATION FAGE。这里看出来,其实CC2530的DATA,和 XDATA,都是用SRAM作为物理存储媒介的,但是它们的寻址方式不一样,所以访问DATA,比访问XDATA要快。至于SFR,XREG,INFORMATION PAGE,我不知道它们用了什么物理存储媒介,但是它们都被映射到XDATA上,可以被DMA访问。 这里要搞清楚一个概念,映射到XDATA上,不代表就只能用XDATA的寻址方式访问。比如SFR,它虽然被映射到了XDATA上,只能说明,DMA可以通过访问XDATA来操作SFR,但是CPU还是可以通过单周期访问SRF.打个比方,我们平时坐的公交车上都有一把逃生应急锤,在紧急情况下可以敲破窗子逃生。我们平时不会使用锤子敲碎窗子进出车厢,我们平时有车门可以走。但是在特殊情况下(比如DMA要操作某个存储器中的数据时),我们可以用特殊的方法(从XDATA上的映射来得到我们想要的数据)。

四、关于从SRAM启动代码 上图

CODE_2.png 这种情况下,CC2530把SRAM存储器整个都映射到了CODE的bank area,可以从SRAM中执行代码。不要理解错了,这里只是说可以从SRAM中运行代码,不代表程序从SRAM中启动。程序还是会从 CODE的rootbank的0000H开始执行,只不过我们可以通过程序控制,让程序跳到 8000H之后,执行我们SRAM中想要的代码。

11. BCD码(Binary Coded Decimal‎)

用4位二进制数来表示1位十进制数

例:0001 0011表示13,1000 0101表示85,0001 1010不合法

在十六进制中的体现:0x13表示13,0x85表示85,0x1A不合法

BCD码转十进制:DEC=BCD/16*10+BCD%16; (2位BCD)

十进制转BCD码:BCD=DEC/10*16+DEC%10; (2位BCD)

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy