STM32

STM32是STMicroelectronics(意法半导体)推出的一系列基于ARM Cortex-M内核的32位微控制器(MCU)产品。这些微控制器提供了广泛的产品系列,覆盖了多种不同的性能和功能需求,适用于各种应用领域,包括工业控制、汽车电子、消费类电子、医疗设备等。

STM32系列微控制器以其高性能、低功耗、丰富的外设接口和灵活的开发工具而闻名。它们通常具有丰富的存储器、多种通信接口(如UART、SPI、I2C、CAN等)、模拟数字转换器(ADC)、定时器、PWM输出等功能,以满足不同应用场景下的需求。

STM32微控制器通常使用标准的ARM Cortex-M内核,包括Cortex-M0、M0+、M3、M4和M7等,这些内核具有不同的性能和功耗特性,可根据具体应用的需求进行选择。此外,STM32系列还提供了多种封装和引脚配置,以满足不同尺寸和集成度的要求。

STMicroelectronics为STM32系列提供了丰富的开发工具和支持资源,包括基于ARM开发环境的集成开发环境(IDE)、调试器、评估板和参考设计等。这些工具和资源有助于开发人员快速开发和部署他们的应用,并提供了全面的技术支持和文档资料,帮助用户充分发挥STM32微控制器的性能和功能优势。

本应用笔记中包含的一些指南用于检验下表列出的所选STM32微控制器(MCU)中嵌入的RNG外设生成的数字的随机性。

详阅请点击下载《使用NIST统计测试集验证STM32微控制器随机数生成》

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

围观 20

开发低功耗产品,我们会比较关注整个系统的功耗问题。那么,LPTIM低功耗定时器你有关注吗?

1、写在前面

在早些年,可能较少听见LPTIM这个名词。随着低功耗产品需求越来越严格,MCU厂商就推出了针对低功耗应用的LPTIM定时器。

定时器是我们常见的一种外设,之所以这么常见,原因在于定时器的用途非常广泛。

在STM32所有MCU中都配有定时器,那么你有关注、对比过各系列,各型号MCU中定时器的差异吗?

2、哪些STM32配有LPTIM定时器

在STM32中,相对较新的MCU部分型号配有LPTIM定时器。

比如:STM32F7、H7高性能MCU,STM32L0、 L4低功耗MCU,以及新推出的G0、G4系列中都配有这种LPTIM定时器。

具体哪些MCU配有LPTIM,大家可以下载对应的数据手册查看。

本文围绕STM32G0讲述其中的LPTIM定时器。

3、LPTIM功能

LPTIM:Low-power timer,即低功耗定时器。

LPTIM 是一个 16 位定时器,得益于其定时器的低功耗。

由于 LPTIM 的时钟源具有多样性,因此 LPTIM 能够在所有电源模式(待机模式除外)下保持运行状态。

即使没有内部时钟源, LPTIM 也能运行,鉴于这一点,可将其用作“脉冲计数器”,这种脉冲计数器在某些应用中十分有用。

此外, LPTIM 还能将系统从低功耗模式唤醒,因此非常适合实现“超时功能”,而且功耗极低。

LPTIM 引入了一个灵活的时钟方案,该方案能够提供所需的功能和性能,同时还能最大程度地降低功耗。

我仔细对比了一下STM32各系列的LPTIM低功耗定时器,发现很多功能基本一样。

1)框图

STM32G0低功耗定时器框图:

“STM32低功耗定时器(LPTIM)有哪些独特功能?"

STM32L0低功耗定时器框图:

“STM32低功耗定时器(LPTIM)有哪些独特功能?"

对比框图,可以发现这个LPTIM片上外设有相似之处。

当然,有些细节是不一样的,像在STM32H7中有多个LPTIM,这几个LPTIM之间是有一定差异的。

2)LPTIM 主要特性

  • 16 位递增计数器

  • 3 位预分频器,可采用 8 种分频系数(1、 2、 4、 8、 16、 32、 64 和 128)

  • 可选时钟

– 内部时钟源:LSE、 LSI、 HSI 或 APB 时钟

– LPTIM 输入的外部时钟源(在没有 LP 振荡器运行的情况下工作,可在使用脉冲计数器应用场景中使用)

  • 16 位 ARR 自动重载寄存器

  • 16 位比较寄存器

  • 连续/单触发模式

  • 可选软件/硬件输入触发

  • 可编程数字防抖动干扰滤波器

  • 可配置输出:脉冲和 PWM

  • 可配置 I/O 极性

  • 编码器模式

拿这些特性和其它基本定时器相对较,你会发现,这些特性中很多都是LPTIM独有的。

3)LPTIM RCC

LPTIM的RCC和其他定时器相比较,其RCC功能更加丰富。

通过上面框图可以发现,LPTIM 可通过多个时钟源提供时钟。

它可以由内部时钟信号提供时钟,内部时钟信号可通过复位和时钟控制器 (RCC) 在 APB、 LSI、 LSE 或 HSI 时钟源中进行选择。

4)干扰滤波器

这个功能也是LPTIM所特有的一个功能。

LPTIM 输入由数字滤波器保护,避免任何毛刺和噪声干扰在 LPTIM 内部传播,从而防止产生意外计数或触发。

滤波示意图:

“STM32低功耗定时器(LPTIM)有哪些独特功能?"

这个原理比较简单,如果不能理解请查看参考手册详解。

LPTIM定时器的功能比较多,可能初学者一看到那么多内容就吓到了。其实,把内容拆开来看并不难。

本文旨在让更多朋友知道这些功能,想要深入掌握其中知识,需结合手册和实践编程。

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

围观 716
  • 扩展后的STM32Cube 生态系统可支持 STM32WB 无线 MCU
  • 新的 STM32CubeWB 固件,升级的编程器和射频测试工具
  • 改进的无线功耗估算器准确计算电池续航时间

服务多重电子应用领域的全球半导体领导者意法半导体(STMicroelectronics,简称ST;纽约证券交易所代码:STM) 发布了新的STM32WB无线微控制器(MCU)开发工具和软件,为智能建筑、智能工业和智能基础设施的开发者降低设计经济、节能的无线设备的难度。

“意法半导体市场领先的

意法半导体的高集成度 STM32WB 单片集成一个 2.4GHz射频收发器和Arm® Cortex®-M4 和Cortex-M0+ 双核微控制器,从而消除了诸多射频电路设计挑战,因为射频电路设计会增加项目的开发时间,而且会给项目开发带来很多不确定性。而用STM32WB开发硬件设计只需要少量的外部组件,例如,选择天线。STM32WB MCU 配备许多外设,包括 12 位模数转换器 (ADC)、数字接口和无晶振 USB 2.0 全速接口,具体根据所选型号而定。芯片支持的协议包括 Bluetooth® LE 5.2、Zigbee®、OpenThread 和专有协议,包括这些协议组合的并发模式。

STM32家族是市场领先的Arm Cortex-M微控制器,作为该产品家族的成员,STM32WB 基于经过市场检验的、受到广泛支持的开发工具和软件资源丰富的STM32Cube 生态系统。

意法半导体 STM32 无线市场总监 Hakim Jaafar 表示:“STM32Cube 生态系统已经被广泛使用,并得到第三方开发者资源的广泛支持,这有助于加快项目开发。我们新推出的经过强化的无线产品扩展了 STM32 系列处理新需求和用例的能力,进一步增强了稳健的STM32 解决方案的市场领先地位。”

技术详情:生态系统新特性助力无线设计

STM32WB 生态系统加强了对无线设计的支持力度,提供了所有必要的嵌入式软件模块和工具,让用户可以轻松地开发应用。在STM32CubeWB MCU软件包里面有很多代码示例,并提供一整套外设驱动程序(HAL 和 LL)和所有的必要的射频协议栈,包括用于蓝牙 5.2、Zigbee 3.0、OpenThread v1.1 和专有协议的 802.15.4 MAC,以及多个实现这些协议栈并发模式(静态和/或动态)的例程。STM32CubeMX and STM32CubeIDE等软件工具的GUI界面直接支持射频协议栈,方便访问和配置这些协议栈。用户可以轻松地选择和配置Profiles和Clusters,以支持主流的标准,并受益于现成的代码示例。

STM32CubeMX 配置器为功耗估算工具增加了额外的控制功能,有助于计算射频子系统对整体功耗预算的影响。用户可以设置各种场景来准确评估电池续航时间。

此外还有更多新功能,例如,STM32Cube 编程器的强化功能可以优化对STM32WB 双核架构的编程功能,利用Cortex-M0+ 处理器与Cortex-M4 主内核一起控制射频子系统,确保实时应用性能。

借助 STM32CubeMonitor-RF评估工具,生态系统将开发过程拓展到在客户环境中高效安装射频并测试性能。STM32CubeMonitor-RF 支持Bluetooth® LE和通用 802.15.4 射频技术,可执行收发测试和射频性能测量,并协助编写测试脚本、测试协议和命令序列。最新版本为 802.15.4 协议引入了侦测器功能,降低网状网络产品的开发难度。

STM32CubeWB 无线生态系统中的所有工具和射频协议栈都取得了相关认证并免费提供,以及随附蓝牙 5.2 和 802.15.4 认证项目的详细说明文档,使客户能够快速且经济地获得适用的射频产品许可证书。

STM32WB 无线微控制器生态系统还包含一套 STM32WB 无线微控制器评估板,帮助用户加快无线产品的开发速度。

含有Nucleo-64开发板和USB适配器、NUCLEO-WB55RG 开发板和 NUCLEO-WB15CC Nucleo-64 开发板的P- NUCLEO-WB55开发套件,以及 STM32WB5MM-DK探索套件 ,为用户提供立即开发应用的各种功能,适合各种无线应用。

使用同一系列芯片,各种应用设计都可以共用同一个基础设计,充分利用产品开发和认证投资。

STM32WB 产品的灵活性很高,从高端到成本敏感的不同类型产品均可使用,应用前景广阔。

详情访问www.st.com/stm32wb.

关于意法半导体

意法半导体拥有46,000名半导体技术的创造者和创新者,掌握半导体供应链和先进的制造设备。作为一家独立的半导体设备制造商,意法半导体与十万余客户、数千名合作伙伴一起研发产品和解决方案,共同构建生态系统,帮助他们更好地应对各种挑战和新机遇,满足世界对可持续发展的更高需求。意法半导体的技术让人们的出行更智能,电力和能源管理更高效,物联网和5G技术应用更广泛。详情请浏览意法半导体公司网站:www.st.com

围观 20

前言

UART是重要的片上资源,主流单片机基本上都有该功能,通过UART可以扩展出很多的通信接口,如RS232、RS485、LIN,甚至WIFI、蓝牙模组等,可以说只要搞通讯就会涉及到UART。下面和大家分享STM32的UART配置。

1、UART是什么

USART全称universal synchronous asynchronous receiver transmitter通用同步异步接收发送器;速率最高可达4.5Mbits/s,波特率460800;

数据按位顺序发送的串行通信接口简称串口,USART模块是采用串行通信接口最常见的模块,为了方便,就把USART简称为串口;

USART接口通过RX,TX,GND同其他设备相连;当TX引脚被禁止时,该引脚恢复GPIO的配置;当TX引脚使能且未发送数据时,该引脚处于高电平(空闲态);

USART接口的数据字长度可编程,停止位长度可编程;可配置为DMA多缓冲通信;

2、USART的帧格式

串口数据应该遵循USART帧的格式,才能被串口识别;

首先总线需要持续至少一个空闲帧,然后连续发送数据帧,数据帧与数据帧之间有时会有断开帧,断开帧后需要接1-2bit停止位,连接下个数据帧;

断开帧只能为10bit或11bit低电平的帧(CR1_SBK[0]);然后接1或2bit的高电平作为停止位,然后接下一个数据帧;

数据帧的数据字有两种格式,(1)8 bit 数据位;(2)8bit 数据位 + 1 bit 奇偶校验位;

“STM32单片机,UART的寄存器配置以及工作原理"

3、USART的寄存器使用

每个USART都有7个自己的寄存器;用来配置该USART的所有功能;

有许多功能诸如硬件流控制,LIN模式,智能卡模式等,由于没用过或是用不上,实在晦涩难懂费时费力,故在此全部跳过;

以下给出了USART作为常用串口收发数据的工作框图,以及相关的寄存器配置;

3.1 工作框图

“STM32单片机,UART的寄存器配置以及工作原理"

“STM32单片机,UART的寄存器配置以及工作原理"

3.2 相关寄存器配置

1)首先需要配置USART的6个参数:

波特率USART_BRR,字长M,停止位STOP,校验位PCE,PS,PEIE,USART的收发模式TE和RE和硬件流控制CTSIE,CTSE,RTSE;

2)USART提供了8个中断:TXEIE, TCIE, RXNEIE, PEIE, IDLEIE, CTSIE, LBDIE, EIE;8个中断使能均可以进入USART的中断函数,根据需要配置合适的中断使能位为1;通常为RXNEIE位;

3)然后使能接收器RE和发送器TE;

4)然后使能UE中断;

“STM32单片机,UART的寄存器配置以及工作原理"

4、USART的代码示例

4.1 标准库提供的常用USART接口

标准库为所有的外设都提供了封装寄存器的API接口函数,文件名为stm32f10x_peripheral.c;

以下为usart外设的常用函数;

//串口USARTx的参数配置初始化函数;
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
//使能串口,(主要是分频器和输出的设置)
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
//使能串口中断,(就是那8个中断,均可以进入中断函数)
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
//都是处理一个字节;
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
//读取SR寄存器的状态,SR的状态都是硬件设置的;
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
//读取SR寄存器和CRx控制寄存器的状态,和上面一个功能相同的;
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);

//修改SR寄存器的状态,单功能通讯用不上;
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);

4.2 USART1使用代码

#include "usartDemo.h"  
u8 USART1_RX_BUF[256];    //接收缓存
u8 USART1_RX_CNT = 0;    //接收字节计数
u8 USART1_REV_0D = 0;    //收到\r
u8 USART1_REV_0A = 0;    //收到\r和\n

//usart1初始化之后,便可以通过串口读写了;
void Usart1_Init(u32 bound)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);    //设置NVIC中断分组2:2位抢占优先级,2位响应优先级   0-3;

    //USART1外设中断配置
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;    //抢占优先级3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;            //子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);  

    //GPIO初始化 USART1_TX    PA9
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    //输出需要配置速率,输入不需要配置速率;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //复用推挽输出,<中文..手册>8.1.11外设的GPIO配置
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    //GPIO初始化    USART1_RX    PA10
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;        //浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);

   //USART1初始化
    USART_InitStructure.USART_BaudRate = bound;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;   //CR1中的TE,RE  
    USART_Init(USART1, &USART_InitStructure); 

    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//CR1中的RXNEIE中断
    USART_Cmd(USART1, ENABLE);                    //CR1中的UE
}

void USART1_Send_Data(u8 *buf,u16 len)
{
    u16 t;
    for(t=0;t<len;t++)        
    {
        USART_SendData(USART1,buf[t]);
        while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET); 
        //发送字节完成后,TC硬件置1;
    }    //先读SR,后写DR清除TC位;
    USART1_RX_CNT = 0;
    USART1_REV_0D = 0;
    USART1_REV_0A = 0;  
}

void USART1_IRQHandler(void)                    
{
    u8 Res;
    if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
     {
         Res =USART_ReceiveData(USART1);        //读DR,硬件清0 RXNE位;
        USART1_RX_BUF[USART1_RX_CNT]=Res;    //接收数据  
        USART1_RX_CNT++; 
        if(Res==0x0d)
            USART1_REV_0D = 1;
        if(USART1_REV_0D&&(Res==0x0a))
            USART1_REV_0A = 1;                      
     }
    //RXNE为1,读数据的同时又来了数据,那么新的数据丢失;产生溢出错误,读完数据后RXNE为0,但ORE标志还在;
    //RXNE为1,又来了数据,产生接收溢出错误,置位ORE;
    if(USART_GetFlagStatus(USART1,USART_FLAG_ORE) == SET)
    {
        USART_ReceiveData(USART1);
    //    USART_ClearFlag(USART1,USART_FLAG_ORE);//先读SR,后读DR,可以复位ORE位;应该不用软件清除了;
    }
    // USART_ClearFlag(USART1,USART_IT_RXNE); //读DR可以清除RXNE,应该不用软件清除了;
}

int main(void)
{
    Usart1_Init(460800);
    while(1)
    {
        if(USART1_REV_0A)
        {
            USART1_Send_Data(USART1_RX_BUF,USART1_RX_CNT);
        }
    }    
}

4.2.1 在前面代码的基础上不使用串口中断,直接通过SR状态位来判断数据的收发;

将上面代码的usart1初始化代码中CR1的RXNEIE配置行注释掉,然后修改main函数如下即可;

int main(void)
{
    Usart1_Init(460800); 
    while(1)
    {
        if ((USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET))
        {
            USART1_RX_BUF[USART1_RX_CNT] = USART_ReceiveData(USART1);
            if(USART1_RX_BUF[USART1_RX_CNT]==0x0a)
                USART1_REV_0A = 1;
            USART1_RX_CNT++;
        }
        if(USART1_REV_0A)
        {
            USART1_REV_0A = 0;
            for(int i=0;i<USART1_RX_CNT;i++)
            {
                USART_SendData(USART1, USART1_RX_BUF[i]);
                while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);
            }
            USART1_RX_CNT=0;
        }
    }
}

5、总结

USART的功能没想到还挺多的,寄存器看起来就有些费时了,很多概念都是新的,不好理解,直接拉低了效率;于是觉得这样不行,应该用什么看什么,用到再看,学海无涯,精力有限;

另外人家费心费力写好标准库不就是为了帮开发人员省时间吗?了解一下即可,以后没必要深入;

本文代码github:https://github.com/caesura-k/stm32f1_usart

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

围观 706

1、概述

说明

每一款芯片的启动文件都值得去研究,因为它可是你的程序跑的最初一段路,不可以不知道。通过了解启动文件,我们可以体会到处理器的架构、指令集、中断向量安排等内容,是非常值得玩味的。

STM32作为一款高端 Cortex-M3系列单片机,有必要了解它的启动文件。打好基础,为以后优化程序,写出高质量的代码最准备。

本文以一个实际测试代码--START_TEST为例进行阐述。

整体过程

STM32整个启动过程是指从上电开始,一直到运行到 main函数之间的这段过程,步骤为(以使用微库为例):

①上电后硬件设置SP、PC

②设置系统时钟

③软件设置SP

④加载.data、.bss,并初始化栈区

⑤跳转到C文件的main函数

代码

启动过程涉及的文件不仅包含 startup_stm32f10x_hd.s,还涉及到了MDK自带的连接库文件 entry.o、entry2.o、entry5.o、entry7.o等(从生成的 map文件可以看出来)。

2、程序在Flash上的存储结构

在真正讲解启动过程之前,先要讲解程序下载到 Flash上的结构和程序运行时(执行到main函数)时的SRAM数据结构。程序在用户Flash上的结构如下图所示。下图是通过阅读hex文件和在MDK下调试综合提炼出来的。

“了解STM32启动过程,好优化程序"

上图中:

  • MSP初始值由编译器生成,是主堆栈的初始值。

  • 初始化数据段是.data

  • 未初始化数据段是.bss

.data和.bss是在__main里进行初始化的,对于ARM Compiler,__main主要执行以下函数:

“了解STM32启动过程,好优化程序"

其中__scatterload会对.data和.bss进行初始化。

加载数据段和初始化栈的参数

加载数据段和初始化栈的参数分别有4个,这里只讲解加载数据段的参数,至于初始化栈的参数类似。

0x0800033c  Flash上的数据段(初始化数据段和未初始化数据段)起始地址
0x20000000  加载到SRAM上的目的地址
0x0000000c  数据段的总大小
0x080002f4  调用函数_scatterload_copy

需要说明的是初始化栈的函数-- 0x08000304与加载数据段的函数不一样,为 _scatterload_zeroinit,它的目的就是将栈空间清零。

“了解STM32启动过程,好优化程序"

3、数据在SRAM上的结构

程序运行时(执行到main函数)时的SRAM数据结构

“了解STM32启动过程,好优化程序"

4、详细过程分析

有了以上的基础,现在详细分析启动过程。

上电后硬件设置SP、PC

刚上电复位后,硬件会自动根据向量表偏移地址找到向量表,向量表偏移地址的定义如下:

“了解STM32启动过程,好优化程序"

调试现象如下:

“了解STM32启动过程,好优化程序"

看看我们的向量表内容(通过J-Flash打开hex文件)

“了解STM32启动过程,好优化程序"

硬件这时自动从0x0800 0000位置处读取数据赋给栈指针SP,然后自动从0x0800 0004位置处读取数据赋给PC,完成复位,结果为:

SP = 0x02000810
PC = 0x08000145

设置系统时钟

上一步中令 PC=0x08000145的地址没有对齐,硬件自动对齐到 0x08000144,执行 SystemInit函数初始化系统时钟。

软件设置SP

LDR   R0,=__main
  BX   R0

执行上两条之类,跳转到 __main程序段运行,注意不是main函数, ___main的地址是0x0800 0130。

可以看到指令LDR.W sp,[pc,#12],结果SP=0x2000 0810。

加载.data、.bss,并初始化栈区

BL.W     __scatterload_rt2 

进入 __scatterload_rt2代码段。

__scatterload_rt2:
0x080001684C06      LDR      r4,[pc,#24]  ; @0x08000184
0x0800016A4D07      LDR      r5,[pc,#28]  ; @0x08000188
0x0800016C E006      B        0x0800017C
0x0800016E68E0      LDR      r0,[r4,#0x0C]
0x08000170 F0400301  ORR      r3,r0,#0x01
0x08000174 E8940007  LDM      r4,{r0-r2}
0x080001784798      BLX      r3
0x0800017A3410      ADDS     r4,r4,#0x10
0x0800017C42AC      CMP      r4,r5
0x0800017E D3F6      BCC      0x0800016E
0x08000180 F7FFFFDA  BL.W     _main_init (0x08000138)

这段代码是个循环 (BCC0x0800016e),实际运行时候循环了两次。第一次运行的时候,读取“加载数据段的函数 (_scatterload_copy)”的地址并跳转到该函数处运行(注意加载已初始化数据段和未初始化数据段用的是同一个函数);第二次运行的时候,读取“初始化栈的函数 (_scatterload_zeroinit)”的地址并跳转到该函数处运行。相应的代码如下:

0x0800016E68E0      LDR      r0,[r4,#0x0C]
0x08000170 F0400301  ORR      r3,r0,#0x01
0x08000174
0x080001784798      BLX      r3

当然执行这两个函数的时候,还需要传入参数。至于参数,我们在“加载数据段和初始化栈的参数”环节已经阐述过了。当这两个函数都执行完后,结果就是“数据在SRAM上的结构”所展示的图。最后,也把事实加载和初始化的两个函数代码奉上如下:

                 __scatterload_copy:
0x080002F4 E002      B        0x080002FC
0x080002F6 C808      LDM      r0!,{r3}
0x080002F81F12      SUBS     r2,r2,#4
0x080002FA C108      STM      r1!,{r3}
0x080002FC2A00      CMP      r2,#0x00
0x080002FE D1FA      BNE      0x080002F6
0x080003004770      BX       lr
                 __scatterload_null:
0x080003024770      BX       lr
                 __scatterload_zeroinit:
0x080003042000      MOVS     r0,#0x00
0x08000306 E001      B        0x0800030C
0x08000308 C101      STM      r1!,{r0}
0x0800030A1F12      SUBS     r2,r2,#4
0x0800030C2A00      CMP      r2,#0x00
0x0800030E D1FB      BNE      0x08000308
0x080003104770      BX       lr

跳转到C文件的main函数

                 _main_init:
0x080001384800      LDR      r0,[pc,#0]  ; @0x0800013C
0x0800013A4700      BX       r0

5、异常向量与中断向量表

; VectorTableMapped to Address0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size


__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler; ResetHandler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler; HardFaultHandler
                DCD     MemManage_Handler; MPU FaultHandler
                DCD     BusFault_Handler; BusFaultHandler
                DCD     UsageFault_Handler; UsageFaultHandler
                DCD     0; Reserved
                DCD     0; Reserved
                DCD     0; Reserved
                DCD     0; Reserved
                DCD     SVC_Handler                ; SVCallHandler
                DCD     DebugMon_Handler; DebugMonitorHandler
                DCD     0; Reserved
                DCD     PendSV_Handler; PendSVHandler
                DCD     SysTick_Handler; SysTickHandler


; ExternalInterrupts
                DCD     WWDG_IRQHandler            ; WindowWatchdog
                DCD     PVD_IRQHandler             ; PVD through EXTI Line detect
                DCD     TAMPER_IRQHandler          ; Tamper
                DCD     RTC_IRQHandler             ; RTC
                DCD     FLASH_IRQHandler           ; Flash
                DCD     RCC_IRQHandler             ; RCC
                DCD     EXTI0_IRQHandler           ; EXTI Line0
                DCD     EXTI1_IRQHandler           ; EXTI Line1
                DCD     EXTI2_IRQHandler           ; EXTI Line2
                DCD     EXTI3_IRQHandler           ; EXTI Line3
                DCD     EXTI4_IRQHandler           ; EXTI Line4
                DCD     DMA1_Channel1_IRQHandler   ; DMA1 Channel1
                DCD     DMA1_Channel2_IRQHandler   ; DMA1 Channel2
                DCD     DMA1_Channel3_IRQHandler   ; DMA1 Channel3
                DCD     DMA1_Channel4_IRQHandler   ; DMA1 Channel4
                DCD     DMA1_Channel5_IRQHandler   ; DMA1 Channel5
                DCD     DMA1_Channel6_IRQHandler   ; DMA1 Channel6
                DCD     DMA1_Channel7_IRQHandler   ; DMA1 Channel7
                DCD     ADC1_2_IRQHandler          ; ADC1 & ADC2
                DCD     USB_HP_CAN1_TX_IRQHandler  ; USB HighPriority or CAN1 TX
                DCD     USB_LP_CAN1_RX0_IRQHandler ; USB LowPriority or CAN1 RX0
                DCD     CAN1_RX1_IRQHandler        ; CAN1 RX1
                DCD     CAN1_SCE_IRQHandler        ; CAN1 SCE
                DCD     EXTI9_5_IRQHandler         ; EXTI Line9..5
                DCD     TIM1_BRK_IRQHandler        ; TIM1 Break
                DCD     TIM1_UP_IRQHandler         ; TIM1 Update
                DCD     TIM1_TRG_COM_IRQHandler    ; TIM1 Trigger and Commutation
                DCD     TIM1_CC_IRQHandler         ; TIM1 CaptureCompare
                DCD     TIM2_IRQHandler            ; TIM2
                DCD     TIM3_IRQHandler            ; TIM3
                DCD     TIM4_IRQHandler            ; TIM4
                DCD     I2C1_EV_IRQHandler         ; I2C1 Event
                DCD     I2C1_ER_IRQHandler         ; I2C1 Error
                DCD     I2C2_EV_IRQHandler         ; I2C2 Event
                DCD     I2C2_ER_IRQHandler         ; I2C2 Error
                DCD     SPI1_IRQHandler            ; SPI1
                DCD     SPI2_IRQHandler            ; SPI2
                DCD     USART1_IRQHandler          ; USART1
                DCD     USART2_IRQHandler          ; USART2
                DCD     USART3_IRQHandler          ; USART3
                DCD     EXTI15_10_IRQHandler       ; EXTI Line15..10
                DCD     RTCAlarm_IRQHandler; RTC Alarm through EXTI Line
                DCD     USBWakeUp_IRQHandler; USB Wakeup from suspend
                DCD     TIM8_BRK_IRQHandler        ; TIM8 Break
                DCD     TIM8_UP_IRQHandler         ; TIM8 Update
                DCD     TIM8_TRG_COM_IRQHandler    ; TIM8 Trigger and Commutation
                DCD     TIM8_CC_IRQHandler         ; TIM8 CaptureCompare
                DCD     ADC3_IRQHandler            ; ADC3
                DCD     FSMC_IRQHandler            ; FSMC
                DCD     SDIO_IRQHandler            ; SDIO
                DCD     TIM5_IRQHandler            ; TIM5
                DCD     SPI3_IRQHandler            ; SPI3
                DCD     UART4_IRQHandler           ; UART4
                DCD     UART5_IRQHandler           ; UART5
                DCD     TIM6_IRQHandler            ; TIM6
                DCD     TIM7_IRQHandler            ; TIM7
                DCD     DMA2_Channel1_IRQHandler   ; DMA2 Channel1
                DCD     DMA2_Channel2_IRQHandler   ; DMA2 Channel2
                DCD     DMA2_Channel3_IRQHandler   ; DMA2 Channel3
                DCD     DMA2_Channel4_5_IRQHandler ; DMA2 Channel4& Channel5
__Vectors_End

这段代码就是定义异常向量表,在之前有一个“J-Flash打开hex文件”的图片跟这个表格是一一对应的。编译器根据我们定义的函数 Reset_Handler、NMI_Handler等,在连接程序阶段将这个向量表填入这些函数的地址。

startup_stm32f10x_hd.s内容:


NMI_Handler     PROC
                EXPORT  NMI_Handler                [WEAK]
                B       .
                ENDP




stm32f10x_it.c中内容:
void NMI_Handler(void)
{
}

在启动汇编文件中已经定义了函数 NMI_Handler,但是使用了“弱”,它允许我们再重新定义一个 NMI_Handler函数,程序在编译的时候会将汇编文件中的弱函数“覆盖掉”--两个函数的代码在连接后都存在,只是在中断向量表中的地址填入的是我们重新定义函数的地址。

6、使用微库与不使用微库的区别

“了解STM32启动过程,好优化程序"

使用微库就意味着我们不想使用MDK提供的库函数,而想用自己定义的库函数,比如说printf函数。那么这一点是怎样实现的呢?我们以printf函数为例进行说明。

不使用微库而使用系统库

在连接程序时,肯定会把系统中包含printf函数的库拿来调用参与连接,即代码段有系统库的参与。

在启动过程中,不使用微库而使用系统库在初始化栈的时候,还需要初始化堆(猜测系统库需要用到堆),而使用微库则是不需要的。

    IF      :DEF:__MICROLIB


                 EXPORT  __initial_sp
                 EXPORT  __heap_base
                 EXPORT  __heap_limit


                 ELSE


                 IMPORT  __use_two_region_memory
                 EXPORT  __user_initial_stackheap


__user_initial_stackheap


                 LDR     R0, =  Heap_Mem
                 LDR     R1, =(Stack_Mem+ Stack_Size)
                 LDR     R2, = (Heap_Mem+  Heap_Size)
                 LDR     R3, = Stack_Mem
                 BX      LR


                 ALIGN


                 ENDIF

另外,在执行 __main函数的过程中,不仅需要完成“使用微库”情况下的所有工作,额外的工作还需要进行库的初始化,才能使用系统库(这一部分我还没有深入探讨)。附上 __main函数的内容:

                  __main:
0x08000130 F000F802  BL.W     __scatterload_rt2_thumb_only (0x08000138)
0x08000134 F000F83C  BL.W     __rt_entry_sh (0x080001B0)
                 __scatterload_rt2_thumb_only:
0x08000138 A00A      ADR      r0,{pc}+4; @0x08000164
0x0800013A E8900C00  LDM      r0,{r10-r11}
0x0800013E4482      ADD      r10,r10,r0
0x080001404483      ADD      r11,r11,r0
0x08000142 F1AA0701  SUB      r7,r10,#0x01
                 __scatterload_null:
0x0800014645DA      CMP      r10,r11
0x08000148 D101      BNE      0x0800014E
0x0800014A F000F831  BL.W     __rt_entry_sh (0x080001B0)
0x0800014E F2AF0E09  ADR.W    lr,{pc}-0x07; @0x08000147
0x08000152 E8BA000F  LDM      r10!,{r0-r3}
0x08000156 F0130F01  TST      r3,#0x01
0x0800015A BF18      IT       NE
0x0800015C1AFB      SUBNE    r3,r7,r3
0x0800015E F0430301  ORR      r3,r3,#0x01
0x080001624718      BX       r3
0x080001640298      LSLS     r0,r3,#10
0x080001660000      MOVS     r0,r0
0x0800016802B8      LSLS     r0,r7,#10
0x0800016A0000      MOVS     r0,r0
                 __scatterload_copy:
0x0800016C3A10      SUBS     r2,r2,#0x10
0x0800016E BF24      ITT      CS
0x08000170 C878      LDMCS    r0!,{r3-r6}
0x08000172 C178      STMCS    r1!,{r3-r6}
0x08000174 D8FA      BHI      __scatterload_copy (0x0800016C)
0x080001760752      LSLS     r2,r2,#29
0x08000178 BF24      ITT      CS
0x0800017A C830      LDMCS    r0!,{r4-r5}
0x0800017C C130      STMCS    r1!,{r4-r5}
0x0800017E BF44      ITT      MI
0x080001806804      LDRMI    r4,[r0,#0x00]
0x08000182600C      STRMI    r4,[r1,#0x00]
0x080001844770      BX       lr
0x080001860000      MOVS     r0,r0
                 __scatterload_zeroinit:
0x080001882300      MOVS     r3,#0x00
0x0800018A2400      MOVS     r4,#0x00
0x0800018C2500      MOVS     r5,#0x00
0x0800018E2600      MOVS     r6,#0x00
0x080001903A10      SUBS     r2,r2,#0x10
0x08000192 BF28      IT       CS
0x08000194 C178      STMCS    r1!,{r3-r6}
0x08000196 D8FB      BHI      0x08000190
0x080001980752      LSLS     r2,r2,#29
0x0800019A BF28      IT       CS
0x0800019C C130      STMCS    r1!,{r4-r5}
0x0800019E BF48      IT       MI
0x080001A0600B      STRMI    r3,[r1,#0x00]
0x080001A24770      BX       lr
                 __rt_lib_init:
0x080001A4 B51F      PUSH     {r0-r4,lr}
0x080001A6 F3AF8000  NOP.W
                 __rt_lib_init_user_alloc_1:
0x080001AA BD1F      POP      {r0-r4,pc}
                 __rt_lib_shutdown:
0x080001AC B510      PUSH     {r4,lr}
                 __rt_lib_shutdown_user_alloc_1:
0x080001AE BD10      POP      {r4,pc}
                 __rt_entry_sh:
0x080001B0 F000F82F  BL.W     __user_setup_stackheap (0x08000212)
0x080001B44611      MOV      r1,r2
                 __rt_entry_postsh_1:
0x080001B6 F7FFFFF5  BL.W     __rt_lib_init (0x080001A4)
                 __rt_entry_postli_1:
0x080001BA F000F919  BL.W     main (0x080003F0)

使用微库而不使用系统库

在程序连接时,不会把包含printf函数的库连接到终极目标文件中,而使用我们定义的库。

启动时需要完成的工作就是之前论述的步骤1、2、3、4、5,相比使用系统库,启动过程步骤更少。

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

围观 77

页面

订阅 RSS - STM32