单片机

工程师在开发一个电路系统,往往会需要用到中央处理器,比如单片机、FPGA、或者DSP等等;当然一些简单的纯硬件电路项目方案例外,如充电器、热水壶等等。

作为单片机研发设计的项目,它的最小电路工作系统包含电源电路、复位电路、时钟频率电路;其中电源电路与复位电路,相信工程师都非常容易理解与设计。然而时钟频率电路,由于不同的开发项目功能需求不一样,设计的方案选择也不尽相同,很难得到有效的统一设计。

比如:

  • A项目对研发成本要求较严格,功能较简单;

  • B项目电路系统需要与外界电路系统完成串口通信,通信数据要求不能出错;

  • C项目包含一个时钟万年历功能,时间要求不能间断而且精度要求高。

针对单片机的时钟频率电路,工程师依据不同的项目要求去设计与选择匹配的方案,具体的选择方案包含三类。

01、外部晶振方案

所谓外部晶振方案,是指在单片机的时钟引脚X1与X2外部连接一个晶振。

“单片机外部晶振图"
单片机外部晶振图

优点:时钟频率精度高,稳定性能好;对于一些数据处理能力要求较高的项目,尤其是多个电路系统彼此需要信息通讯,如包含USB通讯、CAN通讯的项目,选用外部晶振的方案较多。

缺点:由于增加了外部晶振,所以研发的BOM表元器件成本增加扩大了。

02、内部晶振方案

所谓内部晶振方案,是指单片机利用内部集成的RC振荡电路产生的时钟频率。

“单片机内部晶振图"
单片机内部晶振图

优点:省去外部晶振,工程师可以有效的节约研发BOM元器件成本。

缺点:RC振荡电路产生的时钟频率精度比较低,误差较大,容易引起一些高频率通信的数据交互错误。

03、时钟芯片方案

所谓时钟芯片方案,是指在单片机外部加入一个专门处理时钟的时钟芯片,用来给单片机提供精准的时钟信号。

“单片机与时钟芯片电路"
单片机与时钟芯片电路

优点:精度高,误差小;适用于一些要求较高的电路项目。

缺点:电路设计复杂,工程师开发难度较高,研发BOM元器件成本高。

关于时钟芯片的一些电路特性,以美信的DS1338型号为例说明:

“DS1338时钟芯片"
DS1338时钟芯片

(1)供电

时钟芯片的供电电源包含两个部分:

  • VCC供电,是指电路项目系统的电源,同时也是单片机的电源。

  • Vbat供电,是指电池供电的电源,由于某种原因在VCC供电突然失去的条件下,时钟芯片自动启用Vbat电池电源,用以保持时钟芯片内部的时钟信号处理,不必因为电路系统电源VCC断电而失去电路工作。

(2)功能

时钟芯片内部集成时间的“秒”“分”“时”“日”“周”“月”和“年”详细信息计时电路功能,通过IIC通信方式将时间的信息发送至单片机,单片机即可获得高精度的时钟信息。

(3)接口

时钟芯片与单片机的接口是IIC通信接口,此接口方式为串口通信,工程师开发设计较为简单,容易实现电路功能;

(4)精度

精度,是指时钟芯片在正常工作条件下产生的时钟误差;例如美信的DS1338时钟芯片精度控制在10PPM,换算成一天24小时误差精度在0.8秒左右。

(5)应用

时钟芯片,一般用来处理精确计算时间的电路项目,如时间万年历。

结语

当然这三个方案都是针对一些工业与民用领域,如果涉及到航空航天应用领域,比如卫星导航与遥感测量等,则需要选择更高精度的时钟频率电路,如原子钟方案。

本文转载自:今日头条(作者:芯片哥)
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:
cathy@eetrend.com)。

围观 106

我们通过IO和串口的软件开发,已经体验了嵌入式软件开发。不知道大家有没有疑惑,为什么软件能控制硬件?反正当年我学习51的时候,有这个疑惑。今天我们就暂停软件开发,分析单片机到底是如何软硬件结合的。并通过一个基本的程序,分析单片机程序的编译,运行。

软硬件结合

初学者,通常有一个困惑,就是为什么软件能控制硬件?就像当年的51,为什么只要写P1=0X55,就可以在IO口输出高低电平?要理清这个问题,先要认识一个概念:地址空间。

寻址空间

什么是地址空间呢?所谓的地址空间,就是PC指针的寻址范围,因此也叫寻址空间。

大家应该都知道,我们的电脑有32位系统和64位系统之分,为什么呢?因为32位系统,PC指针就是一个32位的二进制数,也就是0xffffffff,范围只有4G寻址空间。现在内存越来越大,4G根本不够,所以需要扩展,为了能访问超出4G范围的内存,就有了64位系统。STM32是多少位的?是32位的,因此PC指针也是32位,寻址空间也就是4G。

我们来看看STM32的寻址空间是怎么样的。在数据手册《STM32F407_数据手册.pdf》中有一个图,这个图,就是STM32的寻址空间分配。所有的芯片,都会有这个图,名字基本上都是叫Memory map,用一个新芯片,就先看这个图。

“”
  • 最左边,8个block,每个block 512M,总共就是4G,也就是芯片的寻址空间。
  • block 0 里面有一段叫做FLASH,也就是内部FLASH,我们的程序就是下载到这个地方,起始地址是0X800 0000,大家注意,这个只有1M空间。现在STM32已经有2M flash的芯片了,超出1M的FLASH放在哪里呢?请自行查看对应的芯片手册。
  • 3 在block 1 内,有两段SRAM,总共128K,这个空间,也就是我们前面说的内存,存放程序使用的变量。如果需要,也可以把程序放到SRAM中运行。407不是有196K吗?
  • 其实407有196K内存,但是有64k并不是普通的SRAM,而是放在block 0 内的CCM。这两段区域不连续,而且,CCM只能内核使用,外设不能使用,例如DMA就不能用CCM内存,否则就死机。
  • block 2,是Peripherals,也就是外设空间。我们看右边,主要就是APB1/APB2、AHB1/AHB2,什么东西呢?回头再说。
  • block 3、block4、block5,是FSMC的空间,FSMC可以外扩SRAM,NAND FALSH,LCD等外设。

好的,我们分析了寻址空间,我们回过头看看,软件是如何控制硬件的。在IO口输出的例程中,我们配置IO口是调用库函数,我们看看库函数是怎么做的。

例如:

GPIO_SetBits(GPIOG, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2| GPIO_Pin_3);

这个函数其实就是对一个变量赋值,对GPIOx这个结构体的成员BSRRL赋值。

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
 /* Check the parameters */
 assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
 assert_param(IS_GPIO_PIN(GPIO_Pin));

 GPIOx->BSRRL = GPIO_Pin;
}

assert_param:这个是断言,用于判断输入参数是否符合要求GPIOx是一个输入参数,是一个GPIO_TypeDef结构体指针,所以,要用->获取其成员

GPIOx是我们传入的参数GPIOG,具体是啥?在stm32f4xx.h中有定义。

#define GPIOG               ((GPIO_TypeDef *) GPIOG_BASE)

GPIOG_BASE同样在文件中有定义,如下:

#define GPIOG_BASE           (AHB1PERIPH_BASE + 0x1800)

AHB1PERIPH_BASE,AHB1地址,有点眉目了吧?在进一步看看

/*!< Peripheral memory map */
#define APB1PERIPH_BASE       PERIPH_BASE
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x00010000)
#define AHB1PERIPH_BASE       (PERIPH_BASE + 0x00020000)
#define AHB2PERIPH_BASE       (PERIPH_BASE + 0x10000000)

再找找PERIPH_BASE的定义

#define PERIPH_BASE           ((uint32_t)0x40000000)    

到这里,我们可以看出,操作IO口G,其实就是操作0X40000000+0X1800这个地址上的一个结构体里面的成员。说白了,就是操作了这个地方的寄存器。实质跟我们操作普通变量一样,就像下面的两句代码,区别就是变量i是SRAM空间地址,0X40000000+0X1800是外设空间地址。

u32 i;
i = 0x55aa55aa;

这个外设空间地址的寄存器是IO口硬件的一部分。如下图,左边的输出数据寄存器,就是我们操作的寄存器(内存、变量),它的地址就是0X40000000+0X1800+0x14.

“”

控制其他外设也类似,就是将数据写到外设寄存器上,跟操作内存一样,就可控制外设了。

寄存器,其实应该是内存的统称,外设寄存器应该叫做特殊寄存器。慢慢的,所有人都把外设的叫做寄存器,其他的统称内存或RAM。寄存器为什么能控制硬件外设呢?因为,粗略的说,一个寄存器的一个BIT,就是一个开关,开就是1,关就是0。通过这个电子开关去控制电路,从而控制外设硬件。

纯软件-包罗万象的小程序

我们已经完成了串口和IO口的控制,但是我们仅仅知道了怎么用,对其他一无所知。程序怎么跑的?代码到底放在哪里?内存又是怎么保存的?下面,我们通过一个简单的程序,学习嵌入式软件的基本要素。

分析启动代码

函数从哪里开始运行?

每个芯片都有复位功能,复位后,芯片的PC指针(一个寄存器,指示程序运行位置,对于多级流水线的芯片,PC可能跟真正执行的指令位置不一致,这里暂且认为一致)会复位到固定值,一般是0x00000000,在STM32中,复位到0X08000004。因此复位后运行的第一条代码就是0X08000004。前面我们不是拷贝了一个启动代码文件到工程吗?startup_stm32f40_41xxx.s,这个汇编文件为什么叫启动代码?因为里面的汇编程序,就是复位之后执行的程序。在文件中,有一段数据表,称为中断向量,里面保存了各个中断的执行地址。复位,也是一个中断。

芯片复位时,芯片从中断表中将Reset_Handler这个值(函数指针)加载到PC指针,芯片就会执行Reset_Handler函数了。(一个函数入口就是一个指针)

; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler

Reset_Handler函数,先执行SystemInit函数,这个函数在标准库内,主要是初始芯片时钟。然后跳到__main执行,__main函数是什么函数?

是我们在main.c中定义的main函数吗?后面我们再说这个问题。

; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

芯片是怎么知道开始就执行启动代码的呢?或者说,我们如何把这个启动代码放到复位的位置?这就牵涉到一个一般情况下不关注的文件wujique.sct,这个文件在wujique\prj\Objects目录下,通常把这个文件叫做分散加载文件,编译工具在链接时,根据这个文件放置各个代码段和变量。

在MDK软件Options菜单Linker下有关于这个菜单的设置。

“”

把Use Memory Layout from Target Dialog前面的勾去掉,之前不可设置的框都可以设置了。点击Edit进行编辑。

“”

在代码编辑框出现了分散加载文件内容,当前文件只有基本的内容。

其实这个文件功能很强大,通过修改这个文件可以配置程序的很多功能,例如:1 指定FLASH跟RAM的大小于起始位置,当我们把程序分成BOOT、CORE、APP,甚至进行驱动分离的时候,就可以用上了。2 指定函数与变量的位置,例如把函数加载到RAM中运行。

“”

从这个基本的分散加载文件我们可以看出:

  • 第6行 ER_IROM1 0x08000000 0x00080000定义了ER_IROM1,也就是我们说的内部FLASH,从0x08000000开始,大小0x00080000。
  • 第7行 .o (RESET, +First)从0x08000000开始,先放置一个.o文件, 并且用(RESET, +First)指定RESET块优先放置,RESET块是什么?请查看启动代码,中断向量就是一个AREA,名字叫RESET,属于READONLY。这样编译后,RESET块将放在0x08000000位置,也就是说,中断向量就放在这个地方。DCD是分配空间,4字节,第一个就是__initial_sp,第二个就是Reset_Handler函数指针。也就是说,最后编译后的程序,将Reset_Handler这个函数的指针(地址),放在0x800000+4的地方。所以芯片在复位的时候,就能找到复位函数Reset_Handler。
  • 第8行 *(InRoot$$Sections)什么鬼?GOOGLE啊!回头再说。
  • 第9行 .ANY (+RO)意思就是其他的所有RO,顺序往后放。就是说,其他代码,跟着启动代码后面。
  • 第11行 RW_IRAM1 0x20000000 0x00020000定义了RAM大小。
  • 第12行 .ANY (+RW +ZI)所有的RW ZI,全部放到RAM里面。RW,ZI,也就是变量,这一行指定了变量保存到什么地址。

分析用户代码

到此,基本启动过程已经分析完。下一步开始分析用户代码,就从main函数开始。1 程序跳转到main函数后:RCC_GetClocksFreq获取RCC时钟频率;SysTick_Config配置SysTick,在这里打开了SysTick中断,10毫秒一次。

Delay(5);延时50毫秒。

int main(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;

 /*!< At this stage the microcontroller clock setting is already configured,
       this is done through SystemInit() function which is called from startup
       files before to branch to application main.
       To reconfigure the default setting of SystemInit() function,
       refer to system_stm32f4xx.c file */

  /* SysTick end of count event each 10ms */
  RCC_GetClocksFreq(&RCC_Clocks);
  SysTick_Config(RCC_Clocks.HCLK_Frequency / 100);

  /* Add your application code here */
  /* Insert 50 ms delay */
  Delay(5);

2 初始化IO就不说了,进入while(1),也就是一个死循环,嵌入式程序,都是一个死循环,否则就跑飞了。

/*初始化LED IO口*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2| GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;

GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOG, &GPIO_InitStructure);    

/* Infinite loop */
mcu_uart_open(3);
while (1)
{
  GPIO_ResetBits(GPIOG, GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);
  Delay(100);
  GPIO_SetBits(GPIOG, GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);
  Delay(100);
  mcu_uart_test();

  TestFun(TestTmp2);
}

3 在while(1)中调用TestFun函数,这个函数使用两个全局变量,两个局部变量。

/* Private functions ---------------------------------------------------------*/
u32 TestTmp1 = 5;//全局变量,初始化为5
u32 TestTmp2;//全局变量,未初始化

const u32 TestTmp3[10] = {6,7,8,9,10,11,12,13,12,13};

u8 TestFun(u32 x)//函数,带一个参数,并返回一个u8值
{
 u8 test_tmp1 = 4;//局部变量,初始化
 u8 test_tmp2;//局部变量,未初始化

 static u8 test_tmp3 = 0;//静态局部变量

 test_tmp3++;

 test_tmp2 = x;

 if(test_tmp2> TestTmp1)
  test_tmp1 = 10;
 else
  test_tmp1 = 5;

 TestTmp2 +=TestTmp3[test_tmp1];

 return test_tmp1;
}

然后程序就一直在main函数的while循环里面执行。中断呢?对,还有中断。中断中断,就是中断正常的程序执行流程。我们查看Delay函数,uwTimingDelay不等于0就死等?谁会将uwTimingDelay改为0?

/**
  * @brief  Inserts a delay time.
  * @param  nTime: specifies the delay time length, in milliseconds.
  * @retval None
  */
void Delay(__IO uint32_t nTime)
{
  uwTimingDelay = nTime;

  while(uwTimingDelay != 0);
}

搜索uwTimingDelay变量,函数TimingDelay_Decrement会将变量一直减到0。

/**
  * @brief  Decrements the TimingDelay variable.
  * @param  None
  * @retval None
  */
void TimingDelay_Decrement(void)
{
  if (uwTimingDelay != 0x00)
  {
    uwTimingDelay--;
  }
}

这个函数在哪里执行?经查找,在SysTick_Handler函数中运行。谁用这个函数?

/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
  */
void SysTick_Handler(void)
{
  TimingDelay_Decrement();
}

经查找,在中断向量表中有这个函数,也即是说这个函数指针保存在中断向量表内。当发生中断时,就会执行这个函数。当然,在进出中断会有保存和恢复现场的操作。这个主要涉及到汇编,暂时不进行分析了。有兴趣自己研究研究。通常,现在我们开发程序不用关心上下文切换了。

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

余下问题

1 __main函数是什么函数?是我们在main.c中定义的main函数吗?2 分散加载文件中*(InRoot$$Sections)是什么?3 ZI段,也就是初始化为0的数据段,什么时候初始化?谁初始化?

为什么这几个问题前面留着不说?因为这是同一个问题。顺藤摸瓜!

通过MAP文件了解代码构成

编译结果

程序编译后,在下方的Build Output窗口会输出信息:

*** Using Compiler 'V5.06 update 5 (build 528)', folder: 'C:\Keil_v5\ARM\ARMCC\Bin'
Build target 'wujique'
compiling stm32f4xx_it.c...
...
assembling startup_stm32f40_41xxx.s...
compiling misc.c...
...
compiling mcu_uart.c...
linking...
Program Size: Code=9038 RO-data=990 RW-data=40 ZI-data=6000  
FromELF: creating hex file...
".\Objects\wujique.axf" - 0 Error(s), 0 Warning(s).
Build Time Elapsed:  00:00:32
  • 编译目标是wujique
  • C文件compiling,汇编文件assembling,这个过程叫编译
  • 编译结束后,就进行link,链接。
  • 最后得到一个编译结果,9038字节code,RO 990,RW 40,ZI 6000。CODE,是代码,很好理解,那RO、RW、ZI都是什么?
  • FromELF,创建hex文件,FromELF是一个好工具,需要自己添加到option中才能用

map文件配置

更多编译具体信息在map文件中,在MDK Options中我们可以看到,所有信息都放在\Listings\wujique.map

默认很多编译信息可能没钩,钩上所有信息会增加编译时间。

“”

map文件

打开map文件,好乱?习惯就好。我们抓重点就行了。

“”

map 总信息

从最后看起,看到没?最后的这一段map内容,说明了整个程序的基本概况。

有多少RO?RO到底是什么?

有多少RW?RW又是什么?

ROM为什么不包括ZI Data?为什么包含RW Data?

“”

Image component sizes

往上,看看Image component sizes,这个就比刚刚的总体统计更细了。

这部分内容,说明了每个源文件的概况

首先,是我们自己的源码,这个程序我们的代码不多,只有main.o,wujique_log.o,和其他一些STM32的库文件。

“”

第2部分是库里面的文件,看到没?里面有一个main.o。main函数是不是我们写的main函数?明显不是,我们的main函数是放在main.o文件。这么小的一个工程,用了这么多库,你以前关注过吗?估计没有,除非你曾经将一个原本在1M flash上的程序压缩到能在512K上运行。

“”

第3部分也是库,暂时没去分析这两个是什么东西。

“”

库文件是什么?库文件就是别人已经别写好的代码库。在代码中,我们经常会包含一些头文件,例如:

#include <stdarg.h>
#include <stdlib.h>
#include <string.h>  

这些就是库的头文件。这些头文件保存在MDK开发工具的安装目录下。我们经常用的库函数有:memcpy、memcmp、strcmp等。只要代码中包含了这些函数,就会链接库文件。

文件map

再往上,就是文件MAP了,也就是每个文件中的代码段(函数)跟变量在ROM跟RAM中的位置。首先是ROM在0x08000000确实放的是startup_stm32f40_41xxx.o中的RESET

库文件是什么?

库文件就是别人已经别写好的代码库。

在代码中,我们经常会包含一些头文件,例如:

#include <stdarg.h>
#include <stdlib.h>
#include <string.h>   

这些就是库的头文件。这些头文件保存在MDK开发工具的安装目录下。

我们经常用的库函数有:

memcpy、memcmp、strcmp等。

只要代码中包含了这些函数,就会链接库文件。

文件map

再往上,就是文件MAP了,也就是每个文件中的代码段(函数)跟变量在ROM跟RAM中的位置。首先是ROM在0x08000000确实放的是startup_stm32f40_41xxx.o中的RESET

“”

每个文件有有多行,例如串口,4个函数。

“”

然后是RAM的,main.o中的变量,放在0x20000000,总共有0x0000000c,类型是Data、RW。串口有两种变量,data和bss,什么是bss?这两个名称,是section name,也就是段的意思。看前面type和Attr,RW Data,放在.data段;RW Zero放在.bss段,RW Zero,其实就是ZI。到底哪些变量是RW,哪些是ZI?

“”

Image Symbol Table

再往上就是Image Symbol Table,就更进一步到每个函数或者变量的信息了

“”

例如,全局变量TestTmp1,是Data,4字节,分配的位置是0x20000004。

“”

TestTmp3数组放在哪里?放在0X080024E0这个地方,这可是代码区额。因为我们用const修饰了这个全局变量数组,告诉编译器,这个数组是不可以改变的,编译器就将这个数组保存到代码中了。程序中我们经常会使用一些大数组数据,例如字符点阵,通常有几K几十K大,不可能也没必要放到RAM区,整个程序运行过程这些数据都不改变,因此通过const修饰,将其存放到代码区。

const的用处比较多,可以修饰变量,也可以修饰函数。更多用法自行学习

“”

那局部变量存放在哪里呢?我们找到了test_tmp3,

“”

没找到test_tmp1/test_tmp2,为什么呢?在定义时,test_tmp3增加了static定义,意思就是静态局部变量,功能上,相当于全局变量,定义在函数内,限制了这个全局变量只能在这个函数内使用。那test_tmp1、test_tmp2放在哪里呢? 局部变量,在编译链接时,并没有分配空间,只有在运行时,才从栈分配空间。

u8 TestFun(u32 x)//函数,带一个参数,并返回一个u8值
{
 u8 test_tmp1 = 4;//局部变量,初始化
 u8 test_tmp2;//局部变量,未初始化

 static u8 test_tmp3 = 0;//静态局部变量

上一部分,我们留了一个问题,哪些变量是RW,哪些是ZI?我们看看串口变量的情况,UartBuf3放在bss段,其他变量放在.data段。为什么数组就放在bss?bss是英文Block Started by Symbol的简称。

“”

到这里,我们可解释下面几个概念了:

Code就是代码,函数。

RO Data,就是只读变量,例如用const修饰的数组。

RW Data,就是读写变量,例如全局变量跟static修饰的局部变量。

ZI Data,就是系统自动初始化为0的读写变量,大部分是数组,放在bss段。

RO Size等于代码加只读变量。

RW Size等于读写变量(包括自动初始化为0的),这个也就是RAM的大小。

ROM Size,也就是我们编译之后的目标文件大小,也就是FLASH的大小。但是?为什么会包含RW Data呢?因为所有全局变量都需要一个初始化的值(就算没有真正初始化,系统也会分配一个初始化空间),例如我们定义一个变量u8 i = 8;这样的全局变量,8,这个值,就需要保存在FALSH区。

“”

我们看看函数的情况,前面我们不是有一个问题吗?__main和main是一个函数吗?查找main后发现,main是main,放在0x08000579

“”

main是main,放在0x08000189

“”

__main到main之间发生了什么?还记得分散加载文件中的这句吗?

*(InRoot$$Sections)

__main就在这个段内。下图是__main的地址,在0x08000189。__Vectors就是中断向量,放在最开始。

“”

在分散加载文件中,紧跟RESET的就是*(InRoot$$Sections)。

“”

而且,RESET段正好大小0x00000188。

“”

巧合?参考PPT文档《ARM嵌入式软件开发.ppt》,或自行GOOGLE。

“”

这一段代码都完成什么功能呢?主要完成ZI代码的初始化,也就是将一部分RAM初始化为0。其他环境初始化。通常,我们不用管这一部分。

其他再往上,就是其他信息了,例如优化了哪些东西,移除了哪些函数。

最后

到这里,一个程序,是怎么组成的,程序是如何运行的,基本有一个总体印象了。

本文转载自:FPGA之家
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:
cathy@eetrend.com)。

围观 103

欠压复位(BOR:Brownout Reset)是单片机可靠性的一项重要功能,通常用于解决电源问题。

1、概述

单片机的“电量不足”是电源电压不足或暂时降低,低于可靠运行所需的水平。许多单片机具有保护电路,该电路可检测电源电压何时低于此水平,并将设备置于复位状态,以确保在电源恢复时正确启动。

此操作也称为“欠压复位”,英文缩写为“BOR”。类似的功能称为低电压检测(LVD),它更复杂,增加了对多个电压电平的检测,可以在触发复位之前产生中断。

BOR通常由控制寄存器中的某个位使能,当BOR引起复位时,状态位会置1。该状态位在复位后仍然有效,并允许程序检测到问题并执行其他恢复或记录事件。

现在的STM、NXP、PIC等常见的单片机中,基本都有欠压复位的功能。

2、如果BOR被禁用会怎样?

如果BOR被禁用,一般情况下,其表现为电源电压稳定性下降。

至于原因,可能是电源老化或电池放电。

“”

如上图所示,V1是正常电源电压。V2是微控制器可能无法可靠运行的电压,V3显示为操作完全停止的点。

在V2和V3之间是一个“危险区域”,在该区域可能发生错误并且操作不可靠。当电源进出危险区域时,该设备可以正常工作数年,然后损坏,出现故障。

BOR级别设置为高于V2,并通过复位设备来代替危险区域。重置不太可靠,但总比不确定好。

3、BOR如何保护你的系统?

接下来,介绍一种情况,其中电源正常运行,但使用BOR解决了另一个问题。

“”

当电源关闭时,电压不会一直下降。相反,其他电源将电源电压保持在危险区域。这种电压的另外一种叫法是“虚假电量”。

目前,没有BOR可以检测到这种情况并引起复位。再次打开电源时,设备可能无法正常上电,因为可能不会触发上电复位电路。由于电源电压低于最小值并且没有复位,因此后续操作不确定。

比如某单片机电源范围为+4.0V至+5.5V。模块内部的工作电压(V1)是+5V。而单片机(V2)上的虚假电量电压为+1.5V。

还有其他两种情况,“RAM数据保持电压(VDR)”为+1.5V,“典型值”。“VDD启动电压”(VPOR)以确保内部上电复位为0V,“典型值”。将所有这些情况加在一起可以告诉我们,该设备处于危险区域之内。由于电压远高于此电压,因此无法预期上电复位(VPOR)。此外,由于虚假电量处于RAM保持电压下,因此也无法预期的欠压会使设备保持活动状态(VDR)。谁知道设备的其余部分在做什么?

为什么打开BOR可以解决此问题?如果欠压复位触发规范(VBOR)的范围是+3.7V至+4.35V,典型值为+4.0V。虚假电量电平远低于BOR的触发电压,就会触发欠压复位,解决不确定性问题。

总结,虚假电量可能有几种情况:外部信号,电路中的多个电源,电容器需要时间才能完全放电。

本文转载自:网络
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:
cathy@eetrend.com)。

围观 1003

今天分享扩展SDRAM的内容。

1、概述

在使用MCU的嵌入式系统设计中,当程序或者数据内存占用太大而无法放入片上闪存或SRAM时,开发者通常考虑使用SDRAM。

别问我为什么你的MCU不支持SDRAM图片

SDRAM是同步动态随机存取存储器的缩写,在微控制器应用中,微控制器通过使用外部存储控制器(EMC)操作访问SDRAM ,SDRAM时钟频率通常为100MHz或133MHz。

外部存储控制器通常不支持DDR SDRAM, 数据只是单边沿采样,即并行数据总线可以接受一个命令并在每个时钟周期传输一个数据字。

在SDRAM中执行程序是使用SDRAM的一种典型用法, 这里就介绍一下SDRAM中执行程序的方法和SDRAM执行程序的性能基准。

2、SDRAM初始化

SDRAM必须在使用前进行配置,SDRAM初始化分为6个步骤。

1、配置EMC寄存器的SDRAM时钟频率、字节顺序和时序参数。
SDRAM的时序比较复杂,用户需要通过查阅相关SDRAM芯片的手册获得时序参数(如刷新周期、预、充电命令周期、自刷新退出时间、写恢复时间等等)。
2、发送NOP命令
3、发送预充电命令
4、发送两次自动刷新命令
5、设置SDRAM模式
6、发送正常运行命令

系统启动时,SDRAM尚未初始化,理论上,程序在系统启动后的任何时刻都可以进行SDRAM初始化。然而,由于SDRAM初始化过程比较复杂,使用的系统资源较多,SDRAM初始化必须在所需的系统资源初始化完成后再进行。

具体来讲,开发者在芯片刚刚启动时(如Reset_Handler中)初始化SDRAM需要留心以下细节:

1、由于SDRAM初始化函数使用系统堆栈或全局变量,开发者必须确保系统堆栈或全局变量所在的物理内存上电及时钟使能。

2、在程序跳转到主程序启动之前,全局变量未清零或初始化,如果在主函数之前执行SDRAM初始化,开发者必须手动初始化变量。

举个例子,在LPC5460x中,开发者需要在SystemInit函数中初始化SDRAM,该函数(SystemInit)由Reset_Handler调用。在调用系统初始化之前,要通过设置AHBCLKCTRLSET0寄存器将SRAM时钟使能。

Reset_Handler   PROC
EXPORT  Reset_Handler               [WEAK]
IMPORT  SystemInit
IMPORT  __main
; clock control SRAM1/SRAM2/SRAM3 for stack
LDR     r0, = 0x40000220 ; AHBCLKCTRLSET0
MOV     r1, #0x38
STR     r1, [r0]
LDR     r0, =SystemInit
BLX     r0
LDR     r0, =__main
BX      r0
ENDP

3、SDRAM存储器布局

当使用SDRAM时,外部存储控制器(EMC)分配SDRAM一定的地址空间。开发者可以使用链接描述文件将代码或数据分配到SDRAM中。值得注意的是,链接器脚本编程在不同IDE之间是不同的。

以LPC5460x系列微控制器为例,SDRAM支持4个片选区,每个片选区最大支持256MB空间。

“”

当SDRAM的硬件连接使用SDRAM片选0的情况下,在KEIL平台下,将加载在SPI FLASH的Coremark基准测试程序拷贝到SDRAM中执行需要以下几步。(coremark基准测试程序包括core_list_join.c,core_matrix.c,core_state.c及core_util.c)。

1、定义SDRAM区域,从0xA0000000开始,大小为0x80000。定义SPI FLASH区域,大小为0x80000(SPI FLASH存储器的起始地址为0x10000000)。

2、在C源码中使用“SDRAM_Data” 和 “SDRAM_Function”属性,标记放在SDRAM区域中的数据或程序。(SDRAM_Data和SDRAM_Function只是文本名字)。

3、也可以将整个目标文件的数据和程序段配置到SDRAM

#define m_spifi_start     0x10000000
#define m_spifi_size       0x800000
#define m_sdram_start     0xA0000000
#define m_sdram_size       0x80000
LR_m_text2 m_spifi_start m_spifi_size { ; load to SPIFI
LR_m_sdram_text   m_sdram_start    m_sdram_size {
    *(SDRAM_Data)    *(SDRAM_Function)    core_list_join.o    core_matrix.o    core_state.o
    core_util.o
}

4、配置MPU

在SDRAM中运行程序,开发者可能需要配置ARM内核内存保护单元(MPU)。

内存保护单元(MPU)是一个可编程单元,用于定义内存访问权限。当MPU没有使能时,内存地址空间具有默认的访问权限。

如ARM Cortex™-M4器件通用用户指南中所述,当程序执行SDRAM中的代码且SDRAM内存影射地址的默认属性为禁止执行时, 内核就会产生HARDFAULT异常,且指令访问冲突标志SCB->CFSR为 1,该异常表示处理器尝试从不允许执行的位置获取指令。

“”

因此,当SDRAM被影射到默认不可执行的地址空间时(如在LPC5460x中,SDRAM影射到0xA0000000起始的地址),开发者必须配置并使能MPU才能在SDRAM中执行代码。

如下例中,代码配置并使能MPU,允许从0xA0000000到0xA0100000的内存区域是可执行的。

MPU->RNR = 0;                    //Region number 0
MPU->RBAR = 0xA0000000; //Region base address
/* Full Access | TEX: 000 | S: 0 | C: 0 | B:0 (No cacheable, no shareable)| 1M SIZE | ENABLE */
MPU->RASR = (0 << 28) | (0x3 << 24) | (0x0 << 19) | (0 << 18) | (0 << 17) | (0 << 16) | (0xFF < 8) | (0x13 << 1) | (1 << 0); //Region size and enable
MPU->CTRL = MPU_CTRL_ENABLE_Msk | MPU_CTRL_PRIVDEFENA_Msk;

5、SDRAM性能基准

最后,作者在LPC5460x经过程序运行CoreMark性能基准测试,总结了一点点经验,分享给大家

1、SDRAM(16位带宽)中的代码执行效率仅为在内部SRAM中执行效率性能40%,大约是内部FLASH中运行代码性能的50%;

2、代码在SDRAM中运行时,较高的CPU频率(CPU没有Cache)不能改善执行效率,这时SDRAM带宽成为系统性能的瓶颈。

基于这样的测试结果,建议大家在要求较高性能时,把程序代码放在内部SRAM执行,而用片外大容量的SDRAM存放海量的数据。

本文转载自:strongerHuang
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:
cathy@eetrend.com)。

围观 452

5V来自于TTL电平。5为True,0为False,之后用了压降更低的PN节,衍生出了3.3这个电平。

12V和24V来自于汽车电瓶,早年乘用车又12V和24V两个系统,现在一般小型车12V,商用车24V,再究其由来应该是铅酸电池。

所以3v3和5v一般出现在信号电路或者单片机等vcc供电,而12v/24v一般出现在低压动力电,例如主板、显卡、轴流风机、监控器。硬件决定系统基础,如果锂电池早点应用的话估计还会有3.7/7.4这个系统。

为什么很多单片机的工作电压是5v?

因为大多数芯片都是5V的TTL电平,要做到电平兼容,电平匹配,避免要电平转换操作,所有很多单片机的工作电压都是5V。早期(196x)的晶体管电路(TTL)单管的压降是0.7v。一个电路里经常有多个晶体管串联。比如4管串联,电源至少保证0.7x4=2.8v才能保证电路正常工作。所以最早有3v 5v等标准。后来LM7805(197x)电源IC出来以后,5V成了事实标准。

TTL指的是TTL电平,0~5V之间,小于0.2V输出低电平,高于3.4V输出高电平。全称Transistor-Transistor Logic,即BJT-BJT逻辑门电路,是数字电子技术中常用的一种逻辑门电路,应用较早,技术已比较成熟。TTL主要有BJT(Bipolar Junction Transistor 即双极结型晶体管,晶体三极管)和电阻构成,具有速度快的特点。最早的TTL门电路是74系列,后来出现了74H系列,74L系列,74LS,74AS,74ALS等系列。但是由于TTL功耗大等缺点,正逐渐被CMOS电路取代。TTL输出高电平>2.4V,输出低电平<0.4V。在室温下,一般输出高电平是3.5V,输出低电平是0.2V。最小输入高电平和低电平:输入高电平>=2.0V,输入低电平<=0.8V,噪声容限是0.4V。

为什么很多都是5V,而且有大量电源芯片支持的也是5V

电压浮动为5%,而电压标准,在A/D当中使用,标准应该是5.12V。

“”

因为512 是2的N次方,这样A/D 的每一个字都是一个整数,当作为无符号计算的时候,更简单,但是没见到哪个成品用这个电压的,大部分都是5V,为什么不用呢?

因为做5.12的标准电压成本会成倍增长。5V与5.12V精度差别在百倍,小数点后0.12V,基本很难做到高精度标准电压,市场通用电压为5V,上浮一定百分比。

2008年11月发布的STC12系列单片机数据手册中,STC12C系列的单片机电压范围是3.3~5.5V;STC12L系列的单片机电压范围是2.2~3.6V。如果选择STC12C系列的单片机,只要单片机的工作频率不是太高,使用3.7V供电是没有任何顾虑的,官方声称单片机的抗干扰能力可以达到4000V,但实际应用说法不一。

“”

1、大多数单片机都是 TTL 电平,各自的高低电平定义不一样;
2、当电源电压为5V时:51,avr单片机是5V;
3、当电源电压为3.3V时:51,avr单片机高电平是3.3v;
4、arm 如lpc2138,电源电压只能为3.3v,io输出高电平为3.3V;但io口可承受5V电压

现在单片机工作电压主要有两种:一种工作在3.3V,一种工作在5V。

本文转载自:网络
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:
cathy@eetrend.com)。

围观 142

中断作用

1、实现实时处理。

2、实现分时操作。

3、进行故障处理。

4、待机状态的唤醒。单片机嵌入式系统的应用中,为了减少电源的功耗,当系统不处理任何事物,处于待机状态时,可以让单片机工作在休眠的低功耗方式。通常,恢复到正常工作方式往往也是利用中断信号来唤醒。

中断处理过程

中断系统中,MCU正常情况下运行的程序称为主程序,把产生申请中断信号的单元和事件称为中断源,由中断源向MCU所发出的申请中断信号称为中断请求,MCU接受中断申请并停止现行程序的运行而转向为中断服务称为中断响应,为中断服务的程序称为中断服务程序,现行程序打断的地方称为断点,执行完中断处理程序后返回断点处继续执行主程序称为中断返回。

“”

其中K就是断点,由于中断服务程序执行完后仍要返回主程序,因此,在执行中断处理程序之前,要将主程序中断点处的地址保存,即中断返回后要执行的命令地址,这个地址就是程序计数器PC的值。这个过程称为保护断点。又由于MCU在执行中断处理程序时,可能会使用和改变主程序使用过的寄存器、标志位、甚至内存单元,因此,在执行中断服务程序前,还要把有关的数据保护起来,称为中断现场保护。在MCU执行完中断处理程序后,又要恢复原来的数据,并返回主程序的断点处继续执行,称为恢复现场。

中断过程中,断点的保护和恢复操作是由单片机内部硬件自动实现,即保存和恢复计数器PC。

中断现场的保护和恢复,需要自己设计中断处理程序时编程实现。在使用中断时,要认真和仔细考虑中断现场的保护和恢复。

中断的三个概念:中断源、中断信号、中断向量(中断入口地址);系统有若干个中断源,每个中断源对应一个中断向量,中断向量只是中断服务程序的一个入口地址,所有中断向量连续存放在固定区域,构成了中断向量区。

中断优先级和嵌套

中断优先级的概念是针对有多个中断源同时申请中断时,MCU如何响应中断,以及响应哪个中断而提出来的。

中断优先级的确定,某中断对应的中断向量地址越小,其中断优先级越高(硬件确定方式)通过软件对中断控制器的设定,改变中断的优先级(用户可设置方式,但是AVR不支持)。

一些单片机(如8051)的硬件能够自动实现中断嵌套的处理,即单片机内部的硬件电路能够识别中断的优先级,并根据优先级的高低,自动完成对高优先级中断的优先响应,实现中断的嵌套处理。AVR单片机,硬件系统不支持自动实现中断嵌套的处理。如果在系统设计中,必须使用中断嵌套处理,则需要由用户编写相应的程序,通过软件设置来实现中断嵌套的功能。

中断响应条件和控制

1)中断的屏蔽:通常存在一些特殊的标志位用于控制开放或关闭(屏蔽)MCU对中断响应处理,这些标志称为中断屏蔽标志位或中断允许控制位。注意,屏蔽中断信号,不是取消。

中断源分为:非屏蔽中断、可屏蔽中断、软件中断。软件中断是指CPU具有相应的软件中断指令,当MCU执行这条指令时能进入软件中断服务,以完成特定的功能(通常用于调试),但是一般的单片机不具备软件中断的指令。

2)中断控制与终端响应条件:单片机中,对应每一个中断源都有一个相应的中断标志位,该中断标志位将占据中断控制器中的一位。当单片机检测到某一中断源产生符合条件的中断信号时,其硬件会自动将该中断源对应的中断标志位置“1”,这就意味着有中断信号产生了,向MCU申请中断。

中断标志位置“1”,并不代表MCU一定响应该中断。为了合理控制中断响应,在单片机内部还有相关的用于中断控制的中断允许标志位。最重要的一个中断允许标志位是全局中断允许标志位。当该标志位为“0”,表示禁止MCU响应所有的可屏蔽中断的响应。此时不管是否有中断产生,MCU不会响应任何中断请求。只有全局中断允许标志位为“1”,MCU才响应。

全局中断标志位为“1”,MCU响应可屏蔽中断,每个中断源对应一个各自独立的中断允许标志位。当某个中断允许标志位为“0”时,表示MCU不响应该中断的中断申请。

MCU响应一个可屏蔽中断源的中断请求的条件是:响应A中断 = 全局中断允许标志位 AND 中断A允许标志 AND 中断A标志;这些标志位全部要置“1”。

所以,只有当全局中断允许标志位为“1”(由用户软件设置),中断A允许标志位为“1”(由用户软件设置),中断A标志位为“1”(符合中断条件时由硬件自动设置或由用户软件设置)时,MCU才会响应中断A的请求信号(如果有多个中断请求信号同时存在的情况下,还要根据中断A的优先级来确定)。

本文转载自:airX嵌入式
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:
cathy@eetrend.com)。

围观 72

在电子电路设计中,干扰的存在让设计者们苦不堪言,干扰会导致电路发生异常,甚至会导致最终的产品无法正常使用。如何巧妙地减少甚至避免干扰始终是设计者们关心的重点,其中单片机的抗干扰设计就是较为重要的一环,本文将为大家介绍与上拉电阻有关的单片机抗干扰。

单片机的输入阻抗解析

想要实现单片机抗干扰,首先要综合考虑各I/O口的输入阻抗,采集速率等因素设计I/O口的外围电路。

一般决定一个I/O口的输入阻抗有3种情况。

第一种情况:

I/O口有上拉电阻,上拉电阻值就是I/O口的输入阻抗。人们大多用4K-20K电阻做上拉。

由于干扰信号也遵循欧姆定律,所以在越存在干扰的场合,选择上拉电阻就要越小,因为干扰信号在电阻上产生的电压就越小。

由于上拉电阻越小就越耗电,所以在设计上,上拉电阻一般都是10-20K,而在强干扰场合上拉电阻甚至可以低到1K。如果在强干扰场合要抛弃B口上拉功能,一定要用外部上拉。

第二种情况:

I/O口与其它数字电路输出脚相连,此时I/O口输入阻抗就是数字电路输出口的阻抗,一般是几十到几百欧。

可以看出用数字电路做中介可以把阻抗减低到最理想,在许多工业控制板上可以看见大量的数字电路就是为了保证性能和保护MCU。

第三种情况:

I/O口并联了小电容。

由于电容是通交流阻直流的,并且干扰信号是瞬间产生,瞬间熄灭的,所以电容可以把干扰信号滤除。但代价是造成I/O口收集信号的速率下降,比如在串口上并电容是绝不可取的,因为电容会把数字信号当干扰信号滤掉。

对于一些特殊器件,如检测开关、霍尔元件等,是能够进行并电容设计的,这主要是因为其开关量的变化较为迟缓,并不能形成很高的速率,所以即便电路中并联电容,对信号的采集也是不会有任何影响的。

本文转载自:STM32嵌入式开发
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:
cathy@eetrend.com)。

围观 230

每项新应用设计都需要一个单片机或微处理器。当在两者之间选择其一时,需要考虑一些因素。以下是微处理器、单片机的概述和对比。

考虑选择微处理器(MPU)或者单片机(MCU)时,应用类型通常是关键因素。另一方面,最终选择取决于诸如操作系统和内存之类的因素。不过,有时可以将微处理器和单片机内核结合使用,这称作异构架构。

操作系统

对于一些基于Linux或安卓等操作系统的计算机密集型工业和消费类应用,需要大量高速连接或功能范围广泛的用户接口,微处理器就是最佳选择。这是因为大多数单片机都没有操作系统,而只有裸机程序,借助于顺序处理循环和状态机,几乎无需任何人工干预即可运行程序。然而,许多高性能单片机可以支持诸如FreeRTOS之类的实时操作系统(RTOS),从而以确定性方式实时响应需要硬实时行为的应用程序。

作为具有许多免费软件、广泛硬件支持和不断发展的生态系统的通用操作系统,嵌入式Linux取得了巨大的成功。它的另一个优点就是没有用户或授权许可费用。不过,与嵌入式Linux一起运行的应用程序至少需要300至400 DMIPS(ARM-Dhrystone MIPS)性能,因此较适合使用微处理器。单片机没有足够的计算能力和内存来应付此类应用。

如果是用于复杂或对实时性要求高的控制系统, RTOS则很有用,但至少要配合50 DMIPS的高性能单片机。这比嵌入式Linux所需的性能要求要少得多。传统的RTOS设计精简,因此可以在单片机上运行。针对实时计算硬件时,这是合理的,例如用于车辆的防抱死系统,若响应时间过长会带来致命的后果。即使必须支持大量的功能、中断源和标准通信接口,也建议使用带有RTOS的单片机。

内 存

微处理器与单片机之间的另一个主要区别是,微处理器依赖外部存储器来保存和执行程序,而单片机则依赖嵌入式闪存。在微处理器中,程序通常存储在非易失性存储器中,例如eMMC或串行闪存。在启动过程中,将其加载到外部DRAM中并在此执行启动程序。DRAM和非易失性存储器都可以具有几百兆甚至几千兆字节容量,这意味着微处理器几乎从来不受存储容量限制。但有一个潜在缺点:外部存储器或许会使得PCB布局的设计变得更加复杂。

即使是当前的高性能单片机,例如由意法半导体(STMicroelectronics)生产的STM32H7,最多也仅提供2 MB程序内存,对于许多需要操作系统的应用而言可能不足。由于程序位于片上内存中,因此其优点是执行启动和重置过程的速度明显更快。

计算能力

计算能力是典型的选择因素。不过,在这方面,微处理机与单片机之间的界线变得模糊了。例如,如果你将ARM体系结构视为单片机和微处理器市场中分布最广泛的体系结构之一,这就变得显而易见了。ARM提供了不同的处理器体系结构以满足各种要求:

  • Cortex-A提供了最高性能,并且已经针对综合操作系统进行了优化。它们主要部署在功能强大的设备中,比如智能手机或服务器。
  • Cortex-M较小,具有更多的片上外设,但是能耗较低,并且针对嵌入式应用进行了优化。

Dhrystone是比较不同处理器性能的测试基准。根据该基准,普通平价单片机具有30 DMIPS,而当前性能最高的单片机(包括嵌入式程序闪存)与这些平价单片机的差距高达1027 DMIPS。相比之下,微处理器的起步点约为1000 DMIPS。

能 耗

单片机在能耗方面表现出色,要比微处理器低很多。尽管微处理器具有节能模式,但其能耗仍然比典型的单片机高得多。而且,微处理器使用外部存储器,因此较难切换到节能模式。对于需要较长的电池运行时间,并且很少使用或没有用户接口的超低功耗应用,单片机是更好的选择,尤其是对于消费类电子产品或智能电表来说。

连接性

大多数单片机和微处理器都配备了所有常规外围设备接口。但是,如果用户需要的是超高速外围设备,在单片机里是找不到例如千兆以太网这种相关接口的。尽管这实际上已成为微处理器中的标准功能单片机。这是十分合理的,因为单片机几乎无法处理这些高速接口所产生的数据量。一个关键问题是:是否有足够的带宽和通道来处理爆发的数据量?

实时表现

当实时性能是最重要的考虑因素时,单片机绝对是首选。凭借处理器内核、嵌入式闪存和软件(RTOS或裸机OS),单片机可以出色地完成实时任务。因为Cortex-A微处理器使用高性能的流水线,用户可以看到在跳转和中断期间,随着流水线的深度不断增加,延迟时间也随之升高。由于OS与微处理器一起执行多任务,因此很难实现硬实时操作。

系统基础IC

由于电源已经集成在单片机中,因此它们仅需要一个单电平电源。另一方面,微处理器需要许多不同电压的电源来为内核和其它组件供电,所以通常需要一个特殊配置的电源管理IC(即所谓的系统基础芯片)来进行供电管理。

结 语

很难说微处理器或单片机哪个才是更好的选择,但经验法则是,你应该始终权衡各种利弊条件。以下几点可以用作大致指导:

  • 单片机非常适合以能耗为主要关注点,且价格较低的移动应用以及具有实时需求的应用。
  • 微处理器则非常适合与操作系统一起运行并需要高速接口的密集计算应用。游戏和其他图形密集型应用使用特殊的微处理器进行联网处理。

本文转载自:21ic
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:
cathy@eetrend.com)。

围观 42

单片机执行指令过程详解

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

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

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

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

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

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

“”

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

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

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

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

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

4、CPU使读控制线有效;

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

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

由于本次进入指令寄存器中的内容是74H(操作码),以译码器译码后单片机就会知道该指令是要将一个数送到A累加器,而该数是在这个代码的下一个存储单元。所以,执行该指令还必须把数据(E0H)从存储器中取出送到CPU,即还要在存储器中取第二个字节。其过程与取指阶段很相似,只是此时PC已为0001H。指令译码器结合时序部件,产生74H操作码的微操作系列,使数字E0H从0001H单元取出。

“”

因为指令是要求把取得的数送到A累加器,所以取出的数字经内部数据总线进入A累加器,而不是进入指令寄存器。至此,一条指令的执行完毕。单片机中PC=0002H,PC在CPU每次向存储器取指或取数时自动加1,单片机又进入下一取指阶段。这一过程一直重复下去,直至收到暂停指令或循环等待指令暂停。CPU就是这样一条一条地执行指令,完成所有规定的功能。

对于一款mcu来说,在性能描述的时候都会告诉sram,flash的容量大小,对于初学者来说,也不会去考虑和理会这些东西,拿到东西就只用。其实不然,这些量都是十分重要的,仔细想想,代码为什么可以运行,代码量是多少,定义的int、short等等类型的变量究竟是怎么分配和存储的,这些问题都和内寸有关系。

首先单片机的内存可以大小分为ram和rom,这里就不再解释ram和rom的区别了,我们可以将其等效为flash和sram,其中根据flash和sram的定义可得,flash里面的数据掉电可保存,sram中的并不可以,但是sram的执行速度要快于flash,可以将单片机的程序分为code(代码存储区)、RO-data(只读数据存储区)、RW-data(读写数据存储区)和ZI-data(零初始化数据区)。在MDK编译器下可以观察到在代码中这4个量的值,如下图1所示:

“”

其中code和RO-data存储在flash中,所以两者之和为单片机中flash需要分配给它们的空间大小(并且等于代码所生成的.bin文件大小),另外RW-data和ZI-data存储在sram中,同样两者之和为单片机中sram需要分配给它们的空间大小。

另外,我们必然会想到栈区(stack)、堆区(heap)、全局区(静态区)(staTIc)、文字常量区和程序代码区和上面所介绍的code、RO-data等的关系。

1、栈区(stack):由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。这些值是可读写的,那么stack应该被包含在RW-data(读写数据存储区),也就是单片机的sram中。

2、堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。可以理解,这些也是被包含在单片机的sram中的。

3、全局区(静态区)(staTIc):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后由系统释放。这些数据也是可读可写的,和stack、heap一样,被包含在sram中。

4、文字常量区:常量字符串就是放在这里的。这些数据是只读的,分配在RO-data(只读数据存储区),则被包含在flash中。

5、程序代码区:存放函数体的二进制代码,可以想象也是被包含在flash,因为对于MCU来说,当其重新上电,代码还会继续运行,并不会消失,所以存储在flash中。

本文转载自:网络
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:
cathy@eetrend.com)。

围观 26

页面

订阅 RSS - 单片机