您现在的位置是:网站首页> 硬件

单片机系列理论信息收集

  • 硬件
  • 2025-01-01
  • 450人已阅读
摘要

单片机系列理论信息收集


常用单片机型号

各CPU烧写程序

   STM32烧写

   AVR烧写

   C51烧写

   ARM芯片

单片机执行编制程序的全过程解析

MCU和CPU的区别,CPU、MCU、DSP三者之间的联系区别

cpu的多少位是什么意思

程序存储器与数据存储器区别总结

CPU寄存器

程序加载过程说明

HEX烧录到单片机的原理

单片机的存储空间

MCU寄存器地址内存地址

单片机执行程序过程

PC是非intel厂家对IP的称呼,也就是说PC起始跟CS:IP是一回事儿。

AVR存储器简介

信号强度(RSSI)知识整理

下载器与仿真器区别

单片机中为什么有了Flash还有EEPROM?



常用单片机型号

目前使用最多的单片机CPU有:

8051系列:这是Intel公司开发的一种8位单片机,广泛用于工业控制、医疗设备、汽车电子等领域。

AVR系列:这是Atmel公司开发的一种8位单片机,广泛用于嵌入式系统、机器人、汽车电子等领域。

PIC系列:这是Microchip公司开发的一种8位单片机,广泛用于工业控制、医疗设备、汽车电子等领域。

ARM Cortex-M系列:这是ARM公司开发的一种32位单片机,广泛用于嵌入式系统、物联网设备、汽车电子等领域。

STM32系列:这是STMicroelectronics公司开发的一种32位单片机,广泛用于嵌入式系统、物联网设备、汽车电子等领域。

MSP430系列:这是Texas Instruments公司开发的一


每种芯片都有其自己的优缺点和应用场景,因此很难说哪一个的性价比最高。然而,根据市场趋势和用户反馈,以下是一些大致的比较:

8051系列:性价比较高,价格便宜,但性能较低,适合简单的应用场景。

AVR系列:性价比较高,价格中等,性能中等,适合中等复杂度的应用场景。

PIC系列:性价比较低,价格较高,性能较高,适合高复杂度的应用场景。

ARM Cortex-M系列:性价比较高,价格中等,性能较高,适合高复杂度的应用场景。

STM32系列:性价比较高,价格中等,性能较高,适合高复杂度的应用场景。

MSP430系列:性价比较低,价格较高,性能较高,适合低功耗和高精度的应用场景。

综上所述,如果您需要一个性价比高的芯片,AVR系列和STM32系列可能是比较好的选择。如果您需要一个高性能的芯片,ARM Cortex-M系列和STM32系列可能是比较好的选择。如果您需要一个低功耗的芯片,MSP430系列可能是比较好的选择。



各CPU烧写程序

   STM32烧写   

STM32芯片的程序烧写通常有以下几种方法:

使用ST-Link编程器

连接ST-Link到STM32芯片的SWD或JTAG接口

使用STM32CubeProgrammer或其他兼容软件

选择要烧写的hex或bin文件

点击"Program"开始烧写


使用串口下载

将STM32设置为bootloader模式

使用USB转TTL模块连接到STM32的UART接口

使用STM32CubeProgrammer或其他串口下载工具

选择正确的COM口和波特率

选择要烧写的文件并开始下载


使用DFU(Device Firmware Upgrade)模式:

将STM32设置为DFU模式

通过USB连接STM32到电脑

使用DfuSe工具或STM32CubeProgrammer

选择DFU设备和要烧写的文件

开始烧写过程


使用集成开发环境(IDE):

在Keil、IAR或STM32CubeIDE等IDE中编写和编译程序

配置调试器(如ST-Link)

使用IDE的"Download"或"Flash"功能直接烧写程序


使用板载烧录器(如果开发板支持):

某些开发板内置了烧录器

直接通过USB连接开发板到电脑

使用相应的烧写软件进行程序下载


注意事项:

确保选择正确的芯片型号和烧写地址

检查连接是否正确,避免接线错误

某些方法可能需要额外的硬件支持

建议在烧写前备份重要数据

选择哪种方法取决于你的具体硬件设置、开发环境和个人偏好。对于初学者,使用ST-Link或集成开发环境通常是最简单和可靠的方法。



  AVR烧写

AVR芯片的程序烧写方法有几种常见的方式:

使用ISP(In-System Programming)编程器

常用的如AVR ISP MKII、USBasp等

连接ISP编程器到AVR芯片的MOSI、MISO、SCK和RESET引脚

使用AVRDUDE或Atmel Studio等软件进行烧写

选择正确的芯片型号和烧写文件

执行烧写操作


使用Arduino IDE(适用于Arduino开发板或bootloader烧写的AVR芯片):

将Arduino开发板或AVR芯片通过USB连接到电脑

在Arduino IDE中选择正确的板型和端口

点击"上传"按钮进行程序烧写


使用Atmel Studio集成开发环境:

在Atmel Studio中编写和编译程序

配置编程器(如AVR ISP MKII)

使用"Program"功能直接烧写程序


使用JTAG接口:

适用于支持JTAG的AVR芯片

使用JTAG编程器连接到AVR芯片

通过Atmel Studio或其他支持JTAG的软件进行烧写


使用串口引导程序(Bootloader):

需要预先烧写bootloader到AVR芯片

通过串口连接AVR芯片到电脑

使用特定的串口烧写工具上传程序


使用高压串行编程(HVSP)或高压并行编程(HVPP):

用于重置熔丝位或恢复被锁定的芯片

需要特殊的高压编程器

通常作为最后的恢复手段


注意事项:

确保选择正确的芯片型号和烧写设置

检查连接是否正确,避免接线错误

某些AVR芯片可能有特定的烧写要求

建议在烧写前备份重要数据

注意设置正确的熔丝位(fuse bits),避免锁死芯片

对于初学者,使用Arduino IDE(如果使用Arduino兼容板)或ISP编程器配合AVRDUDE是较为简单和常用的方法。随着经验的增加,你可能会更倾向于使用Atmel Studio等更专业的工具。



   C51烧写

对于C51系列的单片机芯片,常见的程序烧写方法如下:

使用串行编程器:

常见的编程器有STC-ISP、IAP-ISP等

将编程器的串行接口连接到C51芯片的串行接口(RXD、TXD)

使用编程器自带的软件进行程序烧写


使用JTAG编程器:

适用于支持JTAG接口的C51芯片

使用JTAG编程器连接到芯片的JTAG接口

通过专用软件进行程序烧写


使用USB编程器:

部分编程器具有USB接口,如STC-ISP USB

通过USB连接编程器到电脑,再连接到C51芯片

使用编程器软件进行程序烧写


使用片内ISP/IAP功能:

部分C51芯片具有片内ISP/IAP功能

通过软件控制片内ISP/IAP模块进行程序烧写

无需外部编程器


使用串行引导程序(Bootloader):

需要预先烧写Bootloader到C51芯片

通过串口连接C51芯片到电脑

使用特定的串口烧写软件上传程序

一般来说,使用专用的编程器(如STC-ISP)是最常见和简单的烧写方式。如果芯片支持片内ISP/IAP功能,也可以不需要编程器直接进行烧写。


使用时需要注意:

选择正确的芯片型号和烧写设置

检查接线是否正确,避免损坏芯片

部分编程器可能需要特定的驱动程序

备份重要数据,避免意外丢失

总的来说,C51芯片的程序烧写方法较为简单和成熟,只要掌握好基本步骤即可顺利完成。



  ARM芯片

ARM芯片的程序烧写方法较为多样,主要取决于具体的ARM芯片型号和开发板。以下是几种常见的烧写方法:

使用JTAG/SWD调试器:

常用的调试器如J-Link、ST-Link、CMSIS-DAP等

连接调试器到ARM芯片的JTAG或SWD接口

使用IDE(如Keil MDK、IAR Embedded Workbench)或专用软件进行烧写


使用板载调试器:

许多ARM开发板内置了调试器(如ST-Link)

直接通过USB连接开发板到电脑

使用IDE或专用软件进行烧写


使用引导加载程序(Bootloader)

预先烧写Bootloader到ARM芯片

通过串口、USB或其他接口连接到电脑

使用专用软件进行程序烧写


使用片上ROM引导加载程序:

一些ARM芯片内置ROM Bootloader

通过特定的引脚设置进入Bootloader模式

使用制造商提供的工具进行烧写


使用CMSIS-DAP:

开源的调试接口标准

支持多种ARM Cortex核心

可以通过USB直接连接到电脑进行烧写


使用专用烧写器:

一些厂商提供专用的烧写器

通常用于批量生产或特定芯片系列


通过集成开发环境(IDE):

使用如STM32CubeIDE、Keil MDK、IAR EWARM等IDE

在IDE中编写、编译程序

通过配置的调试器直接烧写程序


注意事项:

确保选择正确的芯片型号和烧写设置

检查连接是否正确,避免接线错误

某些ARM芯片可能有特定的烧写要求或安全设置

建议在烧写前备份重要数据

注意设置正确的选项字节(Option Bytes)或安全位

对于初学者,使用开发板自带的调试器(如ST-Link)配合相应的IDE是最简单的方法。随着经验的增加,你可能会使用更专业的工具如J-Link或自定义的烧写方法。选择哪种方法主要取决于你的具体硬件、开发环境和项目需求。



单片机执行编制程序的全过程解析

单片机执行程序的过程,实际上就是执行我们所编制程序的过程。即逐条指令的过程。计算机每执行一条指令都可分为三个阶段进行。即取指令-----分析指令-----执行指令。

取指令的任务是:根据程序计数器PC中的值从程序存储器读出现行指令,送到指令寄存器

分析指令阶段的任务是:将指令寄存器中的指令操作码取出后进行译码,分析其指令性质。如指令要求操作数,则寻找操作数地址。

计算机执行程序的过程实际上就是逐条指令地重复上述操作过程,直至遇到停机指令可循环等待指令

一般计算机进行工作时,首先要通过外部设备把程序和数据通过输入接口电路和数据总线送入到存储器,然后逐条取出执行。但单片机中的程序一般事先我们都已通过写入器固化在片内或片外程序存储器中。因而一开机即可执行指令。

下面我们将举个实例来说明指令的执行过程。

开机时,程序计算器PC变为0000H。然后单片机在时序电路作用下自动进入执行程序过程。执行过程实际上就是取出指令(取出存储器中事先存放的指令阶段)和执行指令(分析和执行指令)的循环过程。

例如执行指令:MOV A,#0E0H,其机器码为“74H E0H”,该指令的功能是把操作数E0H送入累加器, 0000H单元中已存放74H,0001H单元中已存放E0H。当单片机开始运行时,首先是进入取指阶段,其次序是:

1程序计数器的内容(这时是0000H)送到地址寄存器;

2程序计数器的内容自动加1(变为0001H);

3地址寄存器的内容(0000H)通过内部地址总线送到存储器,以存储器中地址译码电跟,使地址为0000H的单元被选中;

CPU使读控制线有效;

5在读命令控制下被选中存储器单元的内容(此时应为74H)送到内部数据总线上,因为是取指阶段,所以该内容通过数据总线被送到指令寄存器。

至此,取指阶段完成,进入译码分析和执行指令阶段。

由于本次进入指令寄存器中的内容是74H(操作码),以译码器译码后单片机就会知道该指令是要将一个数送到A累加器,而该数是在这个代码的下一个存储单元。所以,执行该指令还必须把数据(E0H)从存储器中取出送到CPU,即还要在存储器中取第二个字节。其过程与取指阶段很相似,只是此时PC已为0001H。指令译码器结合时序部件,产生74H操作码的微操作系列,使数字E0H从0001H单元取出。因为指令是要求把取得的数送到A累加器,所以取出的数字经内部数据总线进入A累加器,而不是进入指令寄存器。至此,一条指令的执行完毕。单片机中PC=0002H,PC在CPU每次向存储器取指或取数时自动加1,单片机又进入下一取指阶段。这一过程一直重复下去,直至收到暂停指令或循环等待指令暂停。CPU就是这样一条一条地执行指令,完成所有规定的功能。


MCU和CPU的区别,CPU、MCU、DSP三者之间的联系区别

MCU(Micro Controller Unit),又称单片微型计算机(Single Chip Microcomputer),简称单片机,是指随着大规模集成电路的出现及其发展,将计算机的CPU、RAM、ROM、定时数器和多种I/O接口集成在一片芯片上,形成芯片级的计算机。 MCU按其存储器类型可分为无片内ROM型和带片内ROM型两种。

 对于无片内ROM型的芯片,必须外接EPROM才能应用(典型芯片为8031)。带片内ROM型的芯片又分为片内EPROM型(典型芯片为87C51)、MASK片内掩模ROM型(典型芯片为8051)、片内FLASH型(典型芯片为89C51)等类型 CPU是中央处理单元(Central Processing Unit)的缩写,它可以被简称做微处理器(Microprocessor),不过经常被人们直接称为处理器(processor)。

不要因为这些简称而忽视它的作用,CPU是计算机的核心,其重要性好比大脑对于人一样,因为它负责处理、运算计算机内部的所有数据,而主板芯片组则更像是心脏,它控制着数据的交换。CPU的种类决定了你使用的操作系统和相应的软件。CPU主要由运算器、控制器、寄存器组和内部总线等构成,是PC的核心,再配上储存器、输入/输出接口和系统总线组成为完整的PC(个人电脑)。 寄存器组用于在指令执行过后存放操作数和中间数据,由运算器完成指令所规定的运算及操作。

  CPU和MCU之间的界限并不是很清晰,不过CPU主要用在电脑上,也算是控制器,MCU主要是工业控制领域,DSP用途也比较广。比如说DSP可以实现MP3解码,电机控制等。它的特点是处理速度比较快。

  CPU(Central Processing Unit,中央处理器)发展出来三个分枝,一个是DSP(Digital Signal Processing/Processor,数字信号处理),另外两个是MCU(Micro Control Unit,微控制器单元)和MPU(Micro Processor Unit,微处理器单元)。

MCU集成了片上外围器件;MPU不带外围器件(例如存储器阵列),是高度集成的通用结构的处理器,是去除了集成外设的MCU;DSP运算能力强,擅长很多的重复数据运算,而MCU则适合不同信息源的多种数据的处理诊断和运算,侧重于控制,速度并不如DSP。

  MCU区别于DSP的最大特点在于它的通用性,反应在指令集和寻址模式中。DSP与MCU的结合是DSC,它终将取代这两种芯片。

  在20世纪最值得人们称道的成就中,就有集成电路和电子计算机的发展。20世纪70年代出现的微型计算机,在科学技术界引起了影响深远的变革。在70年代中期,微型计算机家族中又分裂出一个小小的派系--单片机。随着4位单片机出现之后,又推出了8位的单片机。MCS48系列,特别是MCS51系列单片机的出现,确立了单片机作为微控制器(MCU)的地位,引起了微型计算机领域新的变革。在当今世界上,微处理器(MPU)和微控制器(MCU)形成了各具特色的两个分支。它们互相区别,但又互相融合、互相促进。与微处理器(MPU)以运算性能和速度为特征的飞速发展不同,微控制器(MCU)则是以其控制功能的不断完善为发展标志的。

  mcu .cpu .dsp 不同点在于他们的运算处理能力,以及侧重的应用领域。

  mcu 专注于工业控制,功能简单,有一个alu(算数逻辑运算单元),成熟的经典的是8051.

  cpu 功能强大,但是不侧重于任何一点,优势均衡。适合于对于处理没有严格要求的复杂系统。成熟经典的是x86、arm体系。

  dsp 专注于数据处理,有多个alu,多用于多媒体应用等,需要大数据量的地方。

  mcu、cpu、dsp三者按处理能力来排列是正序。但是基本的原理是一致的,从相对简单的mcu入门是明智的选择。

MPU (Microprocessor Unit)微处理器

  微机中的中央处理器(CPU)称为微处理器(MPU),是构成微机的核心部件,也可以说是微机的心脏。它起到控制整个微型计算机工作的作用,产生控制信号对相应的部件进行控制,并执行相应的操作。

  在微机中,CPU被集成在一片超大规模集成电路芯片上,称为微处理器(MPU),微处理器插在主板的cpu插槽中。

  DSP(digital signal processor)是一种独特的微处理器,是以数字信号来处理大量信息的器件。其工作原理是接收模拟信号,转换为0或1的数字信号。再对数字信号进行修改、删除、强化,并在其他系统芯片中把数字数据解译回模拟数据或实际环境格式。它不仅具有可编程性,而且其实时运行速度可达每秒数以千万条复杂指令程序,远远超过通用微处理器,是数字化电子世界中日益重要的电脑芯片。它的强大数据处理能力和高运行速度,是最值得称道的两大特色。

  1、数字信号处理器的内核结构进一步改善,多通道结构和单指令多重数据(SIMD)、特大指令字组(VLIM)将在新的高性能处理器中将占主导地位,如Analog Devices的 ADSP-2116x。

  2、DSP 和微处理器的融合:

  微处理器是低成本的,主要执行智能定向控制任务的通用处理器能很好执行智能控制任务,但是数字信号处理功能很差。而DSP的功能正好与之相反。在许多应用中均需要同时具有智能控制和数字信号处理两种功能,如数字蜂窝电话就需要监测和声音处理功能。因此,把DSP和微处理器结合起来,用单一芯片的处理器实现这两种功能,将加速个人通信机、智能电话、无线网络产品的开发,同时简化设计,减小PCB体积,降低功耗和整个系统的成本。例如,有多个处理器的Motorola公司的DSP5665x,有协处理器功能的Massan公司FILU-200,把MCU功能扩展成DSP和MCU功能的TI公司的TMS320C27xx以及Hitachi公司的SH-DSP,都是DSP和MCU融合在一起的产品。互联网和多媒体的应用需要将进一步加速这一融合过程。

3、DSP 和高档CPU的融合:

  大多数高档GPP如PenTIum 和PowerPC都是SIMD指令组的超标量结构,速度很快。LSI Logic 公司的LSI401Z采用高档CPU的分支预示和动态缓冲技术,结构规范,利于编程,不用担心指令排队,使得性能大幅度提高。Intel公司涉足数字信号处理器领域将会加速这种融合。

  4、DSP 和SOC的融合:

  SOC(System-On-Chip)是指把一个系统集成在一块芯片上。这个系统包括DSP 和系统接口软件等。比如Virata公司购买了LSI Logic公司的ZSP400处理器内核使用许可证,将其与系统软件如USB、10BASET、以太网、UART、GPIO、HDLC等一起集成在芯片上,应用在xDSL上,得到了很好的经济效益。因此,SOC芯片近几年销售很好,由1998年的1.6亿片猛增至1999年的3.45亿片。1999年,约39%的SOC产品应用于通讯系统。今后几年,SOC将以每年31%的平均速度增长,到2004年将达到13亿片。毋庸置疑,SOC将成为市场中越来越耀眼的明星。

  5、DSP 和FPGA的融合:

  FPGA是现场编程门阵列器件。它和DSP集成在一块芯片上,可实现宽带信号处理,大大提高信号处理速度。据报道,Xilinx 公司的Virtex-II FPGA对快速傅立叶变换(FFT)的处理可提高30倍以上。它的芯片中有自由的FPGA可供编程。Xilinx公司开发出一种称作Turbo卷积编译码器的高性能内核。设计者可以在FPGA中集成一个或多个Turbo内核,它支持多路大数据流,以满足第三代(3G)WCDMA无线基站和手机的需要,同时大大节省开发时间,使功能的增加或性能的改善非常容易。因此在无线通信、多媒体等领域将有广泛应用


cpu的多少位是什么意思

CPU的位数是指处理器运算位数,是指微处理器一次执行指令的数据带宽。处理器的寻址位宽增长很快,业界已使用过4、8、16位寻址再到目前主流的32位,而64位寻址浮点运算已经逐步成为CPU的主流产品。

1字节=8位,32位处理器可以一次性处理4个字节的数据量。依次类推,64位处理器可以一次性处理8个字节的数据量,比32位处理器的处理速率加快一倍。

计算机内部表示的数值是有范围的,决定性因素在于CPU的寄存器 。它是计算机处理数值的最小单元 ,如果寄存器是32位的,那么它所能表示的数的范围最大是2的32次方,即表示的范围是32个0到32个1,如果寄存器是64位的话 ,显而易见,表示的范围是64个0到64个1。数的表示范围大了,那么其计算的精确度就提高了。

要注意的是,CPU不只需要位宽够宽的寄存器,也需要足够数量的寄存器,以确保大量数据处理。因此为了容纳更多的数据,寄存器和内部数据通道也必须加倍,因此在64位CPU中的寄存器位数一般是32位CPU中的两倍。

位就是一个时钟周期可以处理的数据数量。
8位为一个字节,1个字节相当于一个半脚标点符号的数据量。两个字节就是一个中文字体所需的数据量。
32位就是一次可以处理4个字节。64位一次可以处理8个字节。现在最高的就是64位了。
当然,这些处理的数据量也不是直接影响机器的性能。首先,处理的数据并不是全部用到我们所应用的东西上。CPU里的数据还有许多是我们看不到的复杂运算,所以对整体的性能影响并不是特别明显,但影响肯定是有的。
另外还要看操作系统和应用软件。当初AMD首先推出64位处理器的时候,就是因为微软没有迅速推出相应的支持64位处理的操作系统,从而抵消了AMD对INTEL的攻势。同样,当64位诞生的时候还没有相应的支持64位运算的其他软件,所以性能的提升就无从谈起了。

当然,现在我们用的软件都支持了。



程序存储器与数据存储器区别总结

存储器概念:


1.    程序存储器:
一般程序存储器一般采用只读存储器,因为这种存储器在电源关闭后,仍能保存程序(此特性成为非遗失性),在系统上电后CPU可去除这些指令重新执行。
只读存储器简称ROM,ROM中的信息一旦写入,就不能随意更改,特别是不能在程序运行过程中写入新的内容,故称为只读存储器。
向ROM存储器中写入信息称为ROM编程,根据编程的方式不同,可分为以下几种:
(1)      掩模ROM:掩模ROM是在制造过程中编程。因为编程是以掩模工艺实现的,因此称为掩模ROM。这种芯片存储结构简单,集成度高,但由于掩模工艺成本高,因此只适用于大批量生产。
(2)      可编程ROM(PROM):PROM(可编程只读存储器)芯片出厂时并没有任何程序信息,由用户独立的编程写入。但PROM只能写入一次,吸入内容后,就不能再修改。
(3)      EPROM:EPROM使用电信号编程,用紫外线擦除的只读存储器芯片。在芯片外壳的中间位置有一个圆形窗口,通过该窗口照射紫外线就可擦除原有的信息,使用编程器可将调试完毕的程序写入。
(4)      E2PROM(EEPROM):E2PROM这是一种用电信号编程,也用电信号擦除的ROM芯片,对E2PROM的读写操作与RAM存储器几乎没有什么差别,只是写入的速度慢一些,但掉电后仍能保存信息。
(5)      Flash ROM:FlashROM又称为闪速存储器(简称闪存),FlashROM是在EPROM,E2PROM的基础上发展起来的一种电擦除型只读存储器。其特点是可快速在线修改其存储单元中的数据,改写次数可达1万多次,其读写速度很快,存取时间可达70ns,而成本却比E2PROM低得多,所以现如今大多数单片机上应有的这种程序存储器。


例:STC89C52 RC单片机芯片
工作频率范围:0~40MHz,相当于普通8051 的0~80MHz,实际工作 频率可达48MHz
1.      程序存储器又分为片内和片外两部分。STC89C52 RC单片机片内程序存储器为8KB的Flash存储器(Flash ROM),编程和擦除完全是电气实现,且速度快。
当片内8K的Flash的存储器不够用时,用户可在片外扩展程序存储器,最多64KB,但STC89C52RC单片机芯片只有片内的8K Flash并没有片外扩展。
2.      数据存储器也分为片内和片外,. 片上(片内)集成512 字节RAM
51单片机的C语言中有个需要关注的概念就是变量或数据的存储模式(PC机是否有类似的情况我不了解)。在C51中的存储模式是data、bdata、idata、pdata、xdata、code共6种:
data、bdata、idata:就是说变量或数据位于单片机的内部RAM中(ST89C52有256字节),访问速度最快。
pdata、xdata:就是说变量或数据位于扩展的外部RAM(其实是相对来说的,还是片内RAM中)中(ST89C52内集成了256字节),相对内部RAM访问速度要慢。
3.   当片内RAM中不够用时,又给用户提供了在片外可扩展至64KB RAM功能。
4.   E2PROM :2KB


2. 数据存储器:
RAM(Random Access Memory) 随机存储器。存储单元的内容可按需随意取出或存入,且存取的速度与存储单元的位置无关的存储器。这种存储器在断电时将丢失其存储内容,故主要用于存储短时间使用的程序。 按照存储信息的不同,随机存储器又分为静态随机存储器(Static RAM,SRAM)和动态随机存储器(Dynamic RAM,DRAM)。
SRAM(Static RAM)不需要刷新电路即能保存它内部存储的数据。
DRAM(Dynamic RAM)每隔一段时间,要刷新充电一次,否则内部的数据即会消失。
因此SRAM具有较高的性能,但是SRAM也有它的缺点,即它的集成度较低,相同容量的DRAM内存可以设计为较小的体积,但是SRAM却需要很大的体积,且功耗较大。所以在主板上SRAM存储器要占用一部分面积。
SRAM的速率高、性能好,它主要有如下应用:
1)CPU与主存之间的高速缓存。
2)CPU内部的L1/L2或外部的L2高速缓存。
SSRAM(Synchronous SRAM)即同步静态随机存取存储器。同步是指Memory工作需要同步时钟,内部的命令的发送与数据的传输都以它为基准;随机是指数据不是线性依次存储,而是由指定地址进行数据读写。
对于SSRAM的所有访问都在时钟的上升/下降沿启动。地址、数据输入和其它控制信号均于时钟信号相关。这一点与异步SRAM不同,异步SRAM的访问独立于时钟,数据输入和输出都由地址的变化控制。
SDRAM(Synchronous DRAM)即同步动态随机存取存储器。同步是指 Memory工作需要同步时钟,内部的命令的发送与数据的传输都以它为基准;动态是指存储阵列需要不断的刷新来保证数据不丢失;随机是指数据不是线性依次存储,而是自由指定地址进行数据读写。


CPU寄存器

寄存器地址和内存地址区别

寄存器地址相对于CPU,内存地址相对于内存

寄存器是有特殊定义的相对于CPU,对应于相应的硬件,比如串口,定时器什么的,状态位,累加器什么的,内存是属于通用的变量,数据存储区

地址寄存器

地址寄存器(Address Register,AR)用来保存当前CPU所访问的内存单元的地址。由于在内存和CPU之间存在着操作速度上的差别,所以必须使用地址寄存器来保持地址信息,直到内存的读/写操作完成为止。,当CPU和内存进行信息交换,即CPU向内存存/取数据时,或者CPU从内存中读出指令时,都要使用地址寄存器和数据缓冲寄存器。同样,如果我们把外围设备的设备地址作为像内存的地址单元那样来看待,那么,当CPU和外围设备交换信息时,我们同样使用 地址寄存器和数据缓冲寄存器。地址寄存器的结构和数据缓冲寄存器、指令寄存器一样,通常使用单纯的寄存器结构。信息的存入一般采用电位-脉冲方式,即电位输入端对应数据信息位,脉冲输入端对应控制信号,在控制信号作用下,瞬时地将信息打入寄存器。

程序计数器(pc)

用于存放指令的地址,为了保证程序(在操作系统中理解为进程)能够连续地执行下去,CPU必须具有某些手段来确定下一条指令的地址。当执行一条指令时,首先需要根据PC中存放的指令地址,将指令由内存取到指令寄存器中,此过程称,为“取指令”。与此同时,PC中的地址或自动加1或由转移指针给出下一条指令的地址。此后经过分析指令,执行指令。完成第一条指令的执行,而后根据PC取出第二条指令的地址,如此循环,执行每一条指令。

指令寄存器(IR)

用来保存当前正在执行的一条指令。是临时放置从内存里面取得的程序指令的寄存器,用于存放当前从主存储器读出的正在执行的一条指令。当执行一条指令时,先把它从内存取到数据寄存器(DR,Data Register)中,然后再传送至IR。指令划分为操作码和地址码字段,由二进制数字组成。为了执行任何给定的指令,必须对操作码进行测试,以便识别所要求的操作。指令译码器就是做这项工作的。指令寄存器中操作码字段的输出就是指令译码器的输入。操作码一经译码后,即可向操作控制器发出具体操作的特定信号。

通用寄存器(GR)

通用寄存器可用于传送和暂存数据,也可参与算术逻辑运算,并保存运算结果。除此之外,它们还各自具有一些特殊功能。通用寄存器的长度取决于机器字长,汇编语言程序员必须熟悉每个寄存器的一般用途和特殊用途,只有这样,才能在程序中做到正确、合理地使用它们。

16位cpu通用寄存器共有8个:AX(累加器(Accumulator Register)),BX(基地址寄存器(Base Register)),CX(计数寄存器(Count Register)),DX(数据寄存器(Data Register)),BP(基址指针寄存器(Base Pointer)),SP(堆栈指针寄存器(Stack Pointer)),SI(源变址寄存器 (Source Index)),DI(目的变址寄存器(Destination Index)).八个寄存器都可以作为普通的数据寄存器使用。

寄存器AX通常称为累加器(Accumulator);用累加器进行的操作可能需要更少时间。累加器可用于乘、除、输入/输出等操作,它们的使用频率很高; 


寄存器BX称为基地址寄存器(Base Register):它可作为存储器指针来使用; 


寄存器CX称为计数寄存器(Count Register):在循环和字符串操作时,要用它来控制循环次数;在位操作中,当移多位时,要用CL来指明移位的位数; 


寄存器DX称为数据寄存器(Data Register):在进行乘、除运算时,它可作为默认的操作数参与运算,也可用于存放I/O的端口地址。


指针寄存器  EBP,  ESP,(Pointer Register),指针寄存器主要用于存放堆栈内存储单元的偏移量,用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便。指针寄存器不可分割成8位寄存器。作为通用寄存器,也可存储算术逻辑运算的操作数和运算结果。寄存器BP称为基址指针寄存器(Base Pointer);寄存器SP称为堆栈指针寄存器(Stack Pointer)。


变址寄存器  ESI,   EDI , 变址寄存器主要用于存放存储单元在段内的偏移量,用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便。 变址寄存器不可分割成8位寄存器。作为通用寄存器,也可存储算术逻辑运算的操作数和运算结果。


寄存器SI称为源变址寄存器 (Source Index);


寄存器DI称为目的变址寄存器(Destination Index)。


16位cpu通用寄存器共有 8 个:  AX,     BX,   CX,  DX,  BP, SP,   SI,   DI.

32位cpu通用寄存器共有 8 个: EAX, EBX , ECX, EDX, EBP, ESP, ESI, EDI

但有的有特殊的用途:AX为累加器,CX为计数器,BX,BP为基址寄存器,SI,DI为变址寄存器,BP还可以是基指针,SP为堆栈指针。


状态寄存器(SR)

状态寄存器又名条件码寄存器(SR,Status register),它是计算机系统的核心部件——运算器的一部分,状态寄存器用来存放两类信息:一类是体现当前指令执行结果的各种状态信息(条件码),如有无进位(CF位)、有无溢出(OF位)、结果正负(SF位)、结果是否为零(ZF位)、奇偶标志位(P位)等;另一类是存放控制信息(PSW:程序状态字寄存器),如允许中断(IF位)、跟踪标志(TF位)等。有些机器中将PSW称为标志寄存器FR(Flag Register)。


程序状态字PSW

程序状态字PSW(PSW,Program Status Word)包括的状态位有进位标志位(CF)、结果为零标志位(ZF)、符号标志位(SF)、溢出标志位(OF)、陷阱标志位(TF)、中断使能(中断屏蔽)标志位(IF)、虚拟中断标志位(VIF)、虚拟中断待决标志位(VIP)、I0特权级别(IOPL)。



程序加载过程说明

程序经过编译、汇编、链接后,生成hex文件。用专用的烧录软件,通过烧录器将hex文件烧录到ROM中(究竟是怎样将hex文件传输到MCU内部的ROM中的呢?),因此,这个时候的ROM中,包含所有的程序内容:无论是一行一行的程序代码,函数中用到的局部变量,头文件中所声明的全局变量,const声明的只读常量,都被生成了二进制数据,包含在hex文件中,全部烧录到了ROM里面,此时的ROM,包含了程序的所有信息,正是由于这些信息,“指导”了CPU的所有动作。


可能有人会有疑问,既然所有的数据在ROM中,那RAM中的数据从哪里来?什么时候CPU将数据加载到RAM中?会不会是在烧录的时候,已经将需要放在RAM中数据烧录到了RAM中?


要回答这个问题,首先必须明确一条:ROM是只读存储器,CPU只能从里面读数据,而不能往里面写数据,掉电后数据依然保存在存储器中;RAM是随机存储器,CPU既可以从里面读出数据,又可以往里面写入数据,掉电后数据不保存,这是条永恒的真理,始终记挂在心。


清楚了上面的问题,那么就很容易想到,RAM中的数据不是在烧录的时候写入的,因为烧录完毕后,拔掉电源,当再给MCU上电后,CPU能正常执行动作,RAM中照样有数据,这就说明:RAM中的数据不是在烧录的时候写入的,同时也说明,在CPU运行时,RAM中已经写入了数据。关键就在这里:这个数据不是人为写入的,CPU写入的,那CPU又是什么时候写入的呢?听我娓娓道来。


上回说到,ROM中包含所有的程序内容,在MCU上电时,CPU开始从第1行代码处执行指令。这里所做的工作是为整个程序的顺利运行做好准备,或者说是对RAM的初始化(注:ROM是只读不写的),工作任务有几项:


1、为全局变量分配地址空间---à如果全局变量已赋初值,则将初始值从ROM中拷贝到RAM中,如果没有赋初值,则这个全局变量所对应的地址下的初值为0或者是不确定的。当然,如果已经指定了变量的地址空间,则直接定位到对应的地址就行,那么这里分配地址及定位地址的任务由“连接器”完成。


2、 设置堆栈段的长度及地址---à用C语言开发的单片机程序里面,普遍都没有涉及到堆栈段长度的设置,但这不意味着不用设置。堆栈段主要是用来在中断处理时起“保存现场”及“现场还原”的作用,其重要性不言而喻。而这么重要的内容,也包含在了编译器预设的内容里面,确实省事,可并不一定省心。平时怎么就没发现呢?奇怪。


3、 分配数据段data,常量段const,代码段code的起始地址。代码段与常量段的地址可以不管,它们都是固定在ROM里面的,无论它们怎么排列,都不会对程序产生影响。但是数据段的地址就必须得关心。数据段的数据时要从ROM拷贝到RAM中去的,而在RAM中,既有数据段data,也有堆栈段stack,还有通用的工作寄存器组。通常,工作寄存器组的地址是固定的,这就要求在绝对定址数据段时,不能使数据段覆盖所有的工作寄存器组的地址。必须引起严重关注。


这里所说的“第一行代码处”,并不一定是你自己写的程序代码,绝大部分都是编译器代劳的,或者是编译器自带的demo程序文件。因为,你自己写的程序(C语言程序)里面,并不包含这些内容。高级一点的单片机,这些内容,都是在startup的文件里面。仔细阅读,有好处的。


通常的做法是:普通的flashMCU是在上电时或复位时,PC指针里面的存放的是“0000”,表示CPU从ROM的0000地址开始执行指令,在该地址处放一条跳转指令,使程序跳转到_main函数中,然后根据不同的指令,一条一条的执行,当中断发生时(中断数量也很有限,2~5个中断),按照系统分配的中断向量表地址,在中断向量里面,放置一条跳转到中断服务程序的指令,如此如此,整个程序就跑起来了。决定CPU这样做,是这种ROM结构所造成的。


其实,这里面,C语言编译器作了很多的工作,只是,你不知道而已。如果你仔细阅读编译器自带的help文件就会知道很多的事情,这是对编译器了解最好的途径。


I/O口寄存器:


也是可以被改变的量,它被安排在一个特别的RAM地址,为系统所访问,而不能将其他变量定义在这些位置。


中断向量表:


中断向量表是被固定在MCU内部的ROM地址中,不同的地址对应不同的中断。每次中断产生时,直接调用对应的中断服务子程序,将程序的入口地址放在中断向量表中。


ROM的大小问题:


对于flash类型的MCU,ROM空间的大小通常都是整字节的,即为ak*8bits。这很好理解,一眼就知道,ROM的空间为aK。但是,对于某些OTP类型的单片机,比如holtek或者sonix公司的单片机,经常看到数据手册上写的是“OTP progarming ROM 2k*15bit。。。。。”,可能会产生疑惑,这个“15bit”认为是1个字节有余,2个字节又不足,那这个ROM空间究竟是2k,多于2k,还是4k但是少了一点点呢?


这里要明确两个概念:一个是指令的位宽,另一个是指令的长度。指令的位宽是指一条指令所占的数据位的宽度;有些是8位位宽,有些是15位位宽。指令长度是指每条指令所占的存储空间,有1个字节,有2个字节的,也有3个字节甚至4个字节的指令。这个可以打个形象的比方:我们做广播体操时,有很多动作要做,但是每个复杂的动作都可以分解为几个简单的动作。例如,当做伸展运动时,我们只听到广播里面喊“2、2、3、4、5、6、7、8”,而这里每一个数字都代表一个指令,听到“3”这个指令后,我们的头、手、腰、腿、脚分别作出不同的动作:两眼目视前方,左手叉腰,右手往上抬起,五指伸直自然并拢打开,右腿伸直,左腿成弓步······等等一系列的分解动作,而要做完这些动作的指令只有一个“3”,要执行的动作却又很多,于是将多个分解动作合并成一个指令,而每个分解动作的“位宽”为15bits。实事上也确实如此,当在反汇编或者汇编时,可以看到,复合指令的确是有简单的指令组合起来的。


到此,回答前面那个问题,这个OTP的ROM空间应该是2K,指令位宽为15位。一般的,当指令位宽不是8的倍数时,则说明该MCU的大部分指令长度是一个字节(注:该字节宽度为15位,不是8位),极少数为2个或多个字节,虽然其总的空间少,但是其能容下的空间数据并不少。



HEX烧录到单片机的原理

HEX是一个格式,烧录器会将没作用的剔出,保留剩下的

Hex文件是可以烧录到MCU中,被MCU执行的一种文件格式。如果用记事本打开可发现,整个文件以行为单位,每行以冒号开头,内容全部为16进制码(以ASCII码形式显示)。Hex文件可以按照如下的方式进行拆分来分析其中的内容:
例如 “:1000080080318B1E0828092820280B1D0C280D2854”可以被看作“0x10 0x00 0x08 0x00 0x80 0x31 0x8B 0x1E 0x08 0x28 0x09 0x28 0x20 0x28 0x0B 0x1D 0x0C 0x28 0x0D 0x28 0x54”
第一个字节 0x10表示本行数据的长度;
第二、三字节 0x00 0x08表示本行数据的起始地址;
第四字节 0x00表示数据类型,数据类型有:0x00、0x01、0x02、0x03、0x04、0x05。
'00' Data Rrecord:用来记录数据,HEX文件的大部分记录都是数据记录
'01' End of File Record: 用来标识文件结束,放在文件的最后,标识HEX文件的结尾
'02' Extended Segment Address Record: 用来标识扩展段地址的记录
'03' Start Segment Address Record:开始段地址记录
'04' Extended Linear Address Record: 用来标识扩展线性地址的记录
'05' Start Linear Address Record:开始线性地址记录
然后是数据,最后一个字节 0x54为校验和。
校验和的算法为:计算0x54前所有16进制码的累加和(不计进位),检验和 = 0x100 - 累加和
在上面的后2种记录,都是用来提供地址信息的。每次碰到这2个记录的时候,都可以根据记录计算出一个“基”地址。对于后面的数据记录,计算地址的时候,都是以这些“基”地址为基础的。



单片机的存储空间

单片机从硬件上来说,只有两种空间:


ROM和RAM!


再说一遍,只有ROM和RAM!


再说一遍,只有ROM和RAM!


int main( )

{

    short b;//b放置在栈上,占用2个字节

    char a[100];//需要在栈上开辟100个字节,a的值是其首地址

    char s[]=”abcde”;

//s在栈上,占用4个字节,“abcde”本身放置在只读数据存储区,占6字节。s是一个地址

//常量,不能改变其地址数值,即s++是错误的。

    char *p1;  //p1在栈上,占用4个字节

    char *p2 ="123456";  //"123456"放置在只读数据存储区,占7个字节。p2在栈上,p2指向的内容不能更//改,但是p2的地址值可以改变,即p2++是对的。

    static char bss_2[100]; //局部未初始化数据段

    static int c=0 ; //局部(静态)初始化区

    p1 = (char *)malloc(10*sizeof(char)); //分配的内存区域在堆区

    strcpy(p1,”xxx”); //”xxx”放置在只读数据存储区,占5个字节

    free(p1); //使用free释放p1所指向的内存

    return 0;

}

寄存器一般是8位,或8的整数倍,在CPU内部或IO接口中。
如果寄存器在CPU内部,一般每个寄存器都有个名称,如R1,R2,AX,PC等等,汇编语言可以直接使用寄存器名称来访问,C语言不可直接访问。
如果寄存器在IO接口中,每个寄存器只有端口地址,没有名称,汇编语言和C语言可以通过端口地址来访问。

存储器是指放程序和数据的地方。

存储器有很多种类型,有RAM和ROM类型,还可以细分的。
RAM是可以读也可以写的,掉电数据丢失。ROM写入数据后,使用中不可再写,掉电再上电数据不丢失。


也就是说,你程序里的所有东西,不是放在RAM里,就是放在ROM里。


ROM的属性是read only(只读),只能用来读取,无法修改,其实也可以修改,只是需要特殊方法,这里暂时不谈!


RAM的属性是read+write(可读可写)


ROM和RAM区域有可能是间断的,分段的。其中RAM主要有数据段+堆+栈+通用寄存器+特殊功能寄存器等


所以编译器(你可以认为是keil)根据只读或者读写的属性将程序划分为


1、ROM区(也就是单片机的FLASH区)


我们都了解,rom是只读的,所以里面的内容也是只读的,这里面包括


1.1、软件的代码段(code)


void main(void)

{

    ;

1.2、数据常量(被const修饰的数据常量)就是放在这里的。


const unsigned int a[100] = 

{

    1,2....

};

1.3、数据段,定义的数据(这个理解起来有点困难,因为涉及运行视图和加载视图)


2、RAM区


RAM区域读写的,掉电易失的,再说一遍,掉电易失!


那么问题来了,刚上电复位时,ram区肯定是空的,那么访问全局变量时,数是怎么找到的?


如果我们仔细看一下ARM启动过程(从上电到main),就是main之前东西,我们就能知道了。这里我们首先要明白两个概念。程序的运行视图和加载视图。


其中RAM区域主要包含以下内容:


2.1、数据段(data)


BSS(未初始化的全局变量+未初始化的静态变量)


Data初始化的全局变量+初始化的静态变量


2.2、STATCK(栈)


(1)、函数传递参数较多时,参数会入栈;


(2)、中断发生时,用于保存和恢复中断前的现场。


2.3、HEAP(堆)


Malloc函数申请的空间(需要了解操作系统内核和链表)


2.4、SFR(通用寄存器,R1,R2。。。。。R15等)


(1)传递函数参数(个数较少时,一般为3个)使用


(2)中间变量使用


(3)寻址等等(查看汇编的寻址方式等)


2.5、SFR特殊功能寄存器(AD,UART,PWM等)


(1)设置芯片工作状态,操作外设。


MCU寄存器地址内存地址

寄存器在CPU内部;数据存储器不在CPU内部,相应的,访问寄存器的时间也比访问存储器的时间短

工作寄存器和数据存储器统一编址

堆栈指针SP 程序计数器PC


RAM:数据存储器 和 CPU寄存器统一编址

ROM:程序存储器

一个进程运行时,所占用的内存,可以分为如下几个部分:

1、栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量的值等。

2、堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS释放。

3、全局变量、静态变量:初始化的全局变量和静态变量放在一块区域,未初始化的全局变量和和未初始化的静态变量在相邻的的另一块区域。程序结束后由系统自动释放。

4、文字常量:常量字符串就是放在这里的。这些数据是只读的,分配在RO-data(只读数据存储区),则被包含在flash中,程序结束后由系统自动释放


5、程序代码(code):存放函数体的二进制代码。

单片机的程序存储分为code(代码存储区)RO-data(只读数据存储区)RW-data(读写数据存储区) 和 ZI-data(零初始化数据区)

  • Flash 存储 code和RO-data
  • Sram 存储 RW-data 和ZI-data

在这里插入图片描述

Code为程序代码部分 = 程序代码区(code)

RO-data 表示 程序定义的常量 = 文字常量区

RW-data 表示 已初始化的全局变量 = 栈区(stack)堆区(heap)全局区(静态区)(static)

ZI-data 表示 未初始化的全局变量


单片机执行程序过程

单片机执行程序的过程,实际上就是执行我们所编制程序的过程。即逐条指令的过程。计算机每执行一条指令都可分为三个阶段进行。即取指令-----分析指令-----执行指令。

取指令的任务是:根据程序计数器PC中的值从程序存储器读出现行指令,送到指令寄存器

分析指令阶段的任务是:将指令寄存器中的指令操作码取出后进行译码,分析其指令性质。如指令要求操作数,则寻找操作数地址。

计算机执行程序的过程实际上就是逐条指令地重复上述操作过程,直至遇到停机指令可循环等待指令。

一般计算机进行工作时,首先要通过外部设备把程序和数据通过输入接口电路和数据总线送入到存储器,然后逐条取出执行。但单片机中的程序一般事先我们都已通过写入器固化在片内或片外程序存储器中。因而一开机即可执行指令。

下面我们将举个实例来说明指令的执行过程。

开机时,程序计算器PC变为0000H。然后单片机在时序电路作用下自动进入执行程序过程。执行过程实际上就是取出指令(取出存储器中事先存放的指令阶段)和执行指令(分析和执行指令)的循环过程。

例如执行指令:MOV A,#0E0H,其机器码为“74H E0H”,该指令的功能是把操作数E0H送入累加器, 0000H单元中已存放74H,0001H单元中已存放E0H。当单片机开始运行时,首先是进入取指阶段,其次序是:

1程序计数器的内容(这时是0000H)送到地址寄存器;

2程序计数器的内容自动加1(变为0001H);

3地址寄存器的内容(0000H)通过内部地址总线送到存储器,以存储器中地址译码电跟,使地址为0000H的单元被选中;

CPU使读控制线有效;

5在读命令控制下被选中存储器单元的内容(此时应为74H)送到内部数据总线上,因为是取指阶段,所以该内容通过数据总线被送到指令寄存器。

至此,取指阶段完成,进入译码分析和执行指令阶段。

由于本次进入指令寄存器中的内容是74H(操作码),以译码器译码后单片机就会知道该指令是要将一个数送到A累加器,而该数是在这个代码的下一个存储单元。所以,执行该指令还必须把数据(E0H)从存储器中取出送到CPU,即还要在存储器中取第二个字节。其过程与取指阶段很相似,只是此时PC已为0001H。指令译码器结合时序部件,产生74H操作码的微操作系列,使数字E0H从0001H单元取出。因为指令是要求把取得的数送到A累加器,所以取出的数字经内部数据总线进入A累加器,而不是进入指令寄存器。至此,一条指令的执行完毕。单片机中PC=0002H,PC在CPU每次向存储器取指或取数时自动加1,单片机又进入下一取指阶段。这一过程一直重复下去,直至收到暂停指令或循环等待指令暂停。CPU就是这样一条一条地执行指令,完成所有规定的功能。


PC是非intel厂家对IP的称呼,也就是说PC起始跟CS:IP是一回事儿

PC是非intel厂家对IP的称呼,也就是说PC起始跟CS:IP是一回事儿。

在很多微机原理和计算机组成原理书中说每当完成取指令操作后,PC = PC + 1,感觉这个说法不太正确,我的理解是 PC=PC + “取出的指令长度”,比如上一条指令长度为 4 字节,PC=PC+4,这个理解正确吗?又或者比如 MIPS 指令集的每条指令长均为 4 字节,所以指令地址一定为4的倍数,所以指令地址后两位一定为 0,所以这里的 +1 指代的就是 +4 字节呢?

+1 是指增加一个——概念中的单位。为了方便教学往往说 +1,实际上是增加(一条指令的长度 ÷ 寻址粒度),在 MIPS 中就是 4,因为 MIPS 一条指令长度 4 字节,寻址粒度是 1 字节。而 x86 体系的指令长度不定,所以每次增加的量会变化。

还有一个问题就是因为虽说经常听到 PC 这个词,但是我却没有见到其实体,所以我的理解是 PC 实际上就是 CS:IP 组合的逻辑表示。PC 不是一个实体,真正用来表示 PC 值的是 CS:IP,所谓的 PC 自动增加是指令指针寄存器 IP 在自增?这个理解对吗?

在 x86 体系里是这样。x86 系统中自增的是 IP,用 CS:IP 组合表示正在执行的指令地址,此时 PC 只是一个概念上的说法。在 ARM 体系中 R15 就是 PC,当然 ARM 和 IA-32、x64 都支持高级内存管理,所以「PC」的内容未必是当前指令在内存中的绝对位置。



AVR存储器简介

AVR 系列单片机内部有三种类型的被独立编址的存储器,它们分别为:

1、Flash 程序存储器(即:程序存储空间、闪存)

2、SRAM 数据存储器(即:动态内存)

3、EEPROM 数据存储器


单片机采用哈弗结构,将程序存储器和数据存储器分开,而数据存储器RAM通常比较小,而程序存储器Flash空间比较大,因此就需要将占用空间较大的不需要改变的数据放在Flash中。


比如需要单片机支持LCD显示文字,就需要一个庞大的字体库,可达到几kb,这么大的数据量放在RAM中是不合适的,只能放在Flash中。


pgmspace.h就提供了与之相关的读写操作。


问题导引:

编译Arduino程序时,会提示:


项目使用了 656 字节,占用了 (0%) 程序存储空间。最大为 253952 字节。

全局变量使用了9字节,(0%)的动态内存,余留8183字节局部变量。最大为8192字节。

而我们编译时经常遇到的问题是:


项目使用了 4756 字节,占用了 (15%) 程序存储空间。最大为 30720 字节。

全局变量使用了2246字节,(109%)的动态内存,余留-198字节局部变量。最大为2048字节。

没有足够的内存; 编译时出错。

【程序存储空间】剩余很多,而【动态内存】不足,导致无法成功写入。这个问题往往出现在声明了数据“较大”的常量特别是数组的情况下。


解决方案:

为解决这个问题,我们可以将本来应该写到【动态内存】的常量,写入【程序存储空间】,以达到节约【动态内存】空间的目的。


定义全局常量时,使用 PROGMEM 关键字,或使用 PROGMEM 数据类型,告诉编译器 “ 把这个信息存到程序存储空间 ”,而不是存到“ 动态内存 ”。


PROGMEM 关键字(或数据类型)使用到的库:pgmspace.h


#include <avr/pgmspace.h>

数据定义:

const dataType variableName [] PROGMEM = {data0,data1,data3 ...};

// dataType- 任何变量类型

// variableName- 数据数组的名称

程序存储空间FLASH是不可改变的,因此定义时加关键字const 是个好的习惯。


1、作为【全局】常量时,直接使用 PROGMEM 关键字即可, PROGMEM 关键字的位置比较随意,但为了Arduino早期版本的兼容性,推荐放到后面。如:


const char str1[] PROGMEM = "Hi, I would like to tell you a bit about myself.\n"

const PROGMEM char str2[] = "Hi, I would like to tell you a bit about myself.\n"

PROGMEM const char str3[] = "Hi, I would like to tell you a bit about myself.\n"

2、作为【局部】常量时,需要配合 static 关键字使用,如:


const static char flash_str[] PROGMEM = "Hi, I would like to tell you a bit about myself.\n"

3、另外一种定义形式,不用 PROGMEM 关键字,而是直接用 PROGMEM 数据类型,如:


const prog_char flash_str[] = "Hi, I would like to tell you a bit about myself.\n"

4、字符串常量的定义


// 全局定义形式:

const char flash_str[] PROGMEM = “Hello, world!”;

 

// 函数内定义形式:

/* pgmspace.h提供了一个宏 PSTR 用来申明Flash中的字符串:

# define PSTR(s) ((const PROGMEM char *)(s))

所以,函数内可以采用下面的定义形式:*/

const char *flash_str = PSTR(“Hello, world!”);

 

// 以下为应用示例:

const char flash_str1[] PROGMEM = “全局定义字符串”;

int main(void)

{

char *flash_str2=PSTR(“函数内定义字符串”);

printf_P(flash_str1);

printf_P(flash_str2);

}

数据读取:

到这里,我们的程序还不能正常工作。

因为当你向一个函数传递指向Flash的指针时,它会认为这是指向RAM的指针,从而在RAM中寻找数据,使得程序出错。

所以还需要专门的函数来处理指向Flash的指针。

数据保存到程序存储空间后,需要特殊的方法(函数)来读取:


1、非数组常量的读取方法


char ram_val; //存到 ram 内的变量

const PROGMEM flash_val = 1; // 存到 flash 内的常量

// 读取

ram_val = pgm_read_byte( &flash_val ); // 读 flash 常量值到 RAM 变量,参数使用【地址&】传递。

2、数组常量的读取方法


char displayInt;

const char charSet [] RROGMEM = {0,1,2,3,4,5,6,7,8,9};

// 读取

for (int i = 0; i < 10; i++)

  {

    displayInt = pgm_read_byte(charSet + i);   // 第一种方法

    displayInt = pgm_read_byte(&charSet[i]);   // 第二种方法

  }

3、字符串复制方法


strcpy_P 函数负责从【程序存储空间】复制一个字符串到【动态内存】缓冲区"buffer"。

注意:复制时要确保缓冲区足够大。


char buffer[30]; 

// 方式一

strcpy_P(buffer,PSTR("dGltQuY29t\r\n"));  

// 方式二

const char string_0[] PROGMEM = "This is a String"; 

strcpy_P(buffer, (char *)pgm_read_word(&string_0)); 

相关的处理函数:

// 数据读取函数

pgm_read_byte(addr)

pgm_read_word(addr)

pgm_read_dword(addr)

pgm_read_float(addr)

pgm_read_ptr(addr)

 

// 字符串处理函数

void *memcpy_P(void *, const void *, size_t);

char *strcat_P(char *, const char *);

int strcmp_P(const char *, const char *);

char *strcpy_P(char *, const char *);

 

可以看到,字符串处理函数与标准处理函数一样,只是以_P结尾,它们的功能也是一样的。 

看这一句代码:

 

strcmp_P("ram item", PSTR("flash item"));

 

这句代码用来比较两个字符串,第一个字符串”ram item”是申明在RAM中的,第二个字符串”flash item”通过宏PSTR申明在Flash中。

关于使用F()宏:

通常我们都使用如下语句,进行串口输出:


Serial.print("Write something on  the Serial Monitor");

但这样使用,每次调用时,都会先将数据保存在【动态内存】中。

当我们要输出长的字符串时,就会占用很多的【动态内存】空间。

使用 F() 就可以很好的解决这个问题,F() 可以将字符串轻松的保存在FLASH中。


Serial.print(F("Write something on the Serial Monitor that is stored in FLASH"));

关于数据读取

1、对于Flash中数组的处理,pgmspace.h也提供了几个宏:


pgm_read_byte(addr)

pgm_read_word(addr)

pgm_read_dword(addr)

pgm_read_float(addr)

pgm_read_ptr(add)

分别用来读取地址addr处的1、2、4个字节和读取浮点数。

考虑这样一个问题,Flash中保存着若干个字符串,每个字符串的地址又以数组形式保存在Flash中,即:


const char *s1 PROGMEM = "s1";const char *s2 PROGMEM = "s2";const char* strPointer[] PROGMEM = {s1, s2};

要如何比较s1和s2呢?

首先需要读取两个字符串的地址,然后通过strcmp_P函数来比较字符串。

要注意,这两个指针都是16位的,而不是8位!

所以,代码应该是:


strcmp_P(pgm_read_word(&strPointer[0]),pgm_read_word(&strPointer[1]));

编译器会对这样的代码给出一个警告,因为pgm_read_byte()得到的是16位整形,而从函数原型中可以看到,函数需要的是指针,可以用两种方法消除这个警告:


(1)强制类型转换


strcmp_P(pgm_read_word((char*)&strPointer[0]), (char*)pgm_read_word(&strPointer[1]));

(2)使用另一个宏


strcmp_P(pgm_read_ptr(&strPointer[0]), pgm_read_ptr(&strPointer[1]));

2、读取数据的问题


之前说到,指针是16位的,能寻址64kB的地址空间。

而在AVR的有些芯片上比如mega2560,Flash空间为256kB,超过64kB的空间将如何寻址呢?

pgmspace.h提供了两种Flash寻址的宏:


一种是采用16位地址的短地址寻址,最多寻址64kB:


pgm_read_byte_near(address_short)

pgm_read_word_near(address_short)

pgm_read_dword_near(address_short)

pgm_read_float_near(address_short)

pgm_read_ptr_near(address_short)

另一种是采用32位地址的长地址寻址,最多寻址4GB空间:


pgm_read_byte_far(address_short)

pgm_read_word_far(address_short)

pgm_read_dword_far(address_short)

pgm_read_float_far(address_short)

pgm_read_ptr_faraddress_short)

两种寻址在性能上有所差别,短地址寻址速度要快很多,而且64kB也足够使用了,因此默认使用短地址寻址:


#define pgm_read_byte(address_short) pgm_read_byte_near(address_short)

#define pgm_read_word(address_short) pgm_read_word_near(address_short)

#define pgm_read_dword(address_short) pgm_read_dword_near(address_short)

#define pgm_read_float(address_short) pgm_read_float_near(address_short)

#define pgm_read_ptr(address_short) pgm_read_ptr_near(address_short)

当必须寻址超过64kB空间时,可以手动的使用长地址寻址。


信号强度(RSSI)知识整理

为什么无线信号(RSSI)是负值

答:其实归根到底为什么接收的无线信号是负值,这样子是不是容易理解多了。因为无线信号多为mW级别,所以对它进行了极化,转化为dBm而已,不表示信号是负的。1mW就是0dBm,小于1mW就是负数的dBm数。


弄清信号强度的定义就行了: 

RSSI(接收信号强度)Received Signal Strength Indicator 

Rss=10logP, 

只需将接受到的信号功率P代入就是接收信号强度(灵敏度)。 

[例1] 如果发射功率P为1mw,折算为dBm后为0dBm。 

[例2] 对于40W的功率,按dBm单位进行折算后的值应为: 

10lg(40W/1mw)=10lg(40000)=10lg4+10lg10+10lg1000=46dBm。


为什么测量出来的dbm值都是负数?

答:首先我们需要知道的是无线信号dbm都是负数,最大是0。因此测量出来的dbm值肯定都是负数。因为dbm值只在一种情况下为0,那就是在理想状态下经过实验测量的结果,一般我们认为dbm为0是其最大值,意味着接收方把发射方发射的所有无线信号都接收到了,即无线路由器发射多少功率,接收的无线网卡就获得多少功率。当然这是在理想状态下测量的,在实际中即使将无线网卡挨着无线路由器的发射天线也不会达到dbm为0的效果。所以说测量出来的dbm值都是负数,不要盲目的认为负数就是信号不好。


功率单位

dBm

dBm是一个考征功率绝对值的值,计算公式为:10lgP(功率值/1mw)。 

[例1] 如果发射功率P为1mw,折算为dBm后为0dBm。 

[例2] 对于40W的功率,按dBm单位进行折算后的值应为: 

10lg(40W/1mw)=10lg(40000)=10lg4+10lg10+10lg1000=46dBm。


dB

dB是一个表征相对值的值,当考虑甲的功率相比于乙功率大或小多少个dB时,按下面计算公式:10lg(甲功率/乙功率) 

[例6] 甲功率比乙功率大一倍,那么10lg(甲功率/乙功率)=10lg2=3dB。也就是说,甲的功率比乙的功率大3 dB。 

[例7] 7/8 英寸GSM900馈线的100米传输损耗约为3.9dB。 

[例8] 如果甲的功率为46dBm,乙的功率为40dBm,则可以说,甲比乙大6 dB。 

[例9] 如果甲天线为12dBd,乙天线为14dBd,可以说甲比乙小2 dB。


如何从dbm值看出接收功率

dbm是一个表示功率绝对值的单位,他的计算公式为10lg功率值/1mw。例如如果接收到的功率为1mw,按照dbm单位进行折算后的值应该为10lg 1mw/1mw=0dbm。当然在实际传输过程中接收方是很难达到接收功率1mw的。因此我们通过这个公式就可以从dbm值反向推出接收方接收到的功率值了。


误区一:dbm值越小越好

既然前面提到了dbm值都是负数,所以很多人都认为dbm值越小越好。其实这个认知是错误的。正如前面所说dbm值最大是0,而且是理想状态。那么越接近理想状态下的dbm值,越说明无线路由器发射的功率都被无线网卡接收到了。因此dbm值应该越大越好,-50dbm说明接收到的无线信号要好于-70dbm。


误区二:dbm值越大越好

又有很多朋友认为既然dbm值是0说明接收发送信号的效果最好,那么我们就应该让企业无线网络各个地方的dbm值尽可能的大。实际上这个观点也是错误的,虽然dbm值越大发送接收信号效果越好,但是与此同时也需要我们为企业内部无线网络安装足够多的无线信号中继设备,这比费用也是不小的。经过实验表明在XP系统无线信号扫描组件中显示为“非常好”状态时是可以满足网络传输要求的,速度和稳定性都没有任何问题,而这个“非常好”状态对应的dbm值为0到-50dbm。因此我们只需要保证企业内部无线网各个地方的dbm值不大于-50dbm即可,这样建立的无线网就是一个高速稳定的网络。我们对于无线网络投入的性价比才会最高。


小提示 

当然有的时候出于经费的考虑我们无法保证企业内部网络每个地方的dbm值都是0到-50dbm之间,那么在测量时也要保证在0到-70dbm之间。因为当XP系统接收到的无线信号小于-70dbm则会出现传输不稳定,速度缓慢的现象,那样我们的无线网络就无法正常使用了。


误区三:接收功率小是因为传输受干扰

众所周知无线路由器发射功率一般都是100mw,还有更高的。那么为什么我们接收到的功率却如此之小呢?是因为传输过程中受到干扰比较大呢?下面我们拿接收到的信号为-50dbm即0.01μW为例进行介绍,如果无线路由器发射功率为100mw,而接收到的仅仅为0.01μW,两者差别为10000000倍。 

实际上这个是正常的传输,就好象太阳发出的能量只有一亿分之一被地球接收到一样。接收功率肯定要远远小于发射功率。所以网络管理员在测量时比需担心,只要你的信号强度大于-50dbm就可以没有任何问题的无线传输数据,再退一步即使到了-70dbm也可以保证无线速度为54M进行传输。


dB,dBi, dBd, dBc,dBm,dBw

1、dB

  dB是一个表征相对值的值,纯粹的比值,只表示两个量的相对大小关系,没有单位,当考虑甲的功率相比于乙功率大或小多少个dB时,按下面计算公式:10log(甲功率/乙功率),如果采用两者的电压比计算,要用20log(甲电压/乙电压)。


  [例] 甲功率比乙功率大一倍,那么10lg(甲功率/乙功率)=10lg2=3dB。也就是说,甲的功率比乙的功率大3 dB。反之,如果甲的功率是乙的功率的一半,则甲的功率比乙的功率小3 dB。


2、dBi 和dBd

  dBi和dBd是表示天线功率增益的量,两者都是一个相对值,但参考基准不一样。dBi的参考基准为全方向性天线,dBd的参考基准为偶极子,所以两者略有不同。一般认为,表示同一个增益,用dBi表示出来比用dBd表示出来要大2.15。


  [例] 对于一面增益为16dBd的天线,其增益折算成单位为dBi时,则为18.15dBi(一般忽略小数位,为18dBi)。


  [例] 0dBd=2.15dBi。


3、dBc

  dBc也是一个表示功率相对值的单位,与dB的计算方法完全一样。一般来说,dBc 是相对于载波(Carrier)功率而言,在许多情况下,用来度量与载波功率的相对值,如用来度量干扰(同频干扰、互调干扰、交调干扰、带外干扰等)以及耦合、杂散等的相对量值。 在采用dBc的地方,原则上也可以使用dB替代。


4、dBm

  dBm是一个表示功率绝对值的值(也可以认为是以1mW功率为基准的一个比值),计算公式为:10log(功率值/1mw)。


  [例] 如果功率P为1mw,折算为dBm后为0dBm。


  [例] 对于40W的功率,按dBm单位进行折算后的值应为: 

  10log(40W/1mw)=10log(40000)=10log4+10log10000=46dBm。


5、dBw

  与dBm一样,dBw是一个表示功率绝对值的单位(也可以认为是以1W功率为基准的一个比值),计算公式为:10log(功率值/1w)。dBw与dBm之间的换算关系为:0 dBw = 10log1 W = 10log1000 mw = 30 dBm。


  [例] 如果功率P为1w,折算为dBw后为0dBw。 

  总之,dB,dBi, dBd, dBc是两个量之间的比值,表示两个量间的相对大小,而dBm、dBw则是表示功率绝对大小的值。在dB,dBm,dBw计算中,要注意基本概念,用一个dBm(或dBw)减另外一个dBm(dBw)时,得到的结果是dB,如:30dBm - 0dBm = 30dB。 

  一般来讲,在工程中,dBm(或dBw)和dBm(或dBw)之间只有加减,没有乘除。而用得最多的是减法:dBm 减 dBm 实际上是两个功率相除,信号功率和噪声功率相除就是信噪比(SNR)。dBm 加 dBm 实际上是两个功率相乘


为什么天线增益的单位有的时候用dBi表示,而有时又用dBd,二者有何 区别? 

答:dBi和dBd都是天线增益的单位,我们习惯上以理想电源辐射器作为参照 

来规定全向天线的增益;有以理想的半波对称振子作为参照来考量定向天线 

的增益。dBi和dBd能够互相换算,G(dBd)=G(dBi)-2.15


下载器与仿真器区别

下载器只是将程序写入单片机

仿真器可以控制程序单步运行便于调试单片机程序,单片机开发人员可以通过单片机仿真器输入和修改程序,观察程序运行结果与中间值,同时对与单片机配套的硬件进行检测与观察,可以大大提高单片机的编程效率和效果



单片机中为什么有了Flash还有EEPROM?

狭义的EEPROM:

这种rom的特点是可以随机访问和修改任何一个字节,可以往每个bit中写入0或者1。这是最传统的一种EEPROM,掉电后数据不丢失,可以保存100年,可以擦写100w次。具有较高的可靠性,但是电路复杂/成本也高。因此目前的EEPROM都是几十千字节到几百千字节的,绝少有超过512K的。

Flash:

Flash属于广义的EEPROM,因为它也是电擦除的ROM。但是为了区别于一般的按字节为单位的擦写的EEPROM,我们都叫它Flash。

既然两者差不多,为什么单片机中还要既有Flash又有EEPROM呢?

通常,单片机里的Flash都用于存放运行代码,在运行过程中不能改;EEPROM是用来保存用户数据,运行过程中可以改变,比如一个时钟的闹铃时间初始化设定为12:00,后来在运行中改为6:00,这是保存在EEPROM里,不怕掉电,就算重新上电也不需要重新调整到6:00。

但最大区别是其实是:FLASH按扇区操作,EEPROM则按字节操作,二者寻址方法不同,存储单元的结构也不同,FLASH的电路结构较简单,同样容量占芯片面积较小,成本自然比EEPROM低,因而适合用作程序存储器,EEPROM则更多的用作非易失的数据存储器。当然用FLASH做数据存储器也行,但操作比EEPROM麻烦的多,所以更“人性化”的MCU设计会集成FLASH和EEPROM两种非易失性存储器,而廉价型设计往往只有 FLASH,早期可电擦写型MCU则都是EEPRM结构,现在已基本上停产了。

在芯片的内电路中,FLASH和EEPROM不仅电路不同,地址空间也不同,操作方法和指令自然也不同,不论冯诺伊曼结构还是哈佛结构都是这样。技术上,程序存储器和非易失数据存储器都可以只用FALSH结构或EEPROM结构,甚至可以用“变通”的技术手段在程序存储区模拟“数据存储区”,但就算如此,概念上二者依然不同,这是基本常识问题。

EEPROM:电可擦除可编程只读存储器,Flash的操作特性完全符合EEPROM的定义,属EEPROM无疑,首款Flash推出时其数据手册上也清楚的标明是EEPROM,现在的多数Flash手册上也是这么标明的,二者的关系是“白马”和“马”。至于为什么业界要区分二者,主要的原因是 Flash EEPROM的操作方法和传统EEPROM截然不同,次要的原因是为了语言的简练,非正式文件和口语中Flash EEPROM就简称为Flash,这里要强调的是白马的“白”属性而非其“马”属性以区别Flash和传统EEPROM。

Flash的特点是结构简单,同样工艺和同样晶元面积下可以得到更高容量且大数据量下的操作速度更快,但缺点是操作过程麻烦,特别是在小数据量反复重写时,所以在MCU中Flash结构适于不需频繁改写的程序存储器


















Top