# STM32

# 开发环境

# 使用 Keil 开发

Keil MDK 是开发 STM32 单片机最常用的工具链

可以跟着视频里的操作配置 Keil 的开发环境, 推荐视频:STM32入门教程-江协科技 (opens new window)

要使用 Keil 开发单片机,首先要在 keil 里安装对应微控制器型号的 DFP 包,可以在 Keil 中在线安装,也可以手动导入已经下载的 DFP 包。 DFP 包提供了微控制器的重要资源信息,有了它 keil 才能开发对应的微控制器,例如 Keil.STM32F1xx_DFP.2.2.0.pack 就是STM32F1系列的开发资源包

下载DFP包

  1. 打开 Keil,点击菜单栏中的 Pack Installer 图标。
  2. Pack Installer 窗口中,浏览或搜索 "STM32F1"。
  3. 找到 Keil.STM32F1xx_DFP ,点击 Install 即可自动下载和安装。

新建工程

  1. 建立工程文件夹,Keil中新建工程,选择型号
  2. 工程文件夹里建立Start、Library、User等文件夹,复制固件库里面的文件到工程文件夹
  3. 工程里对应建立Start、Library、User等同名称的分组,然后将文件夹内的文件添加到工程分组里
  4. Options for Target -> C/C++ -> Include Paths 内声明所有包含头文件的文件夹
  5. Options for Target -> C/C++ -> Define 内定义 USE_STDPERIPH_DRIVER
  6. Options for Target -> Debug,下拉列表选择对应调试器,Options for Target -> Debug -> Settings -> Flash Download 里勾选 Reset and Run

当我们要将程序在单片机中烧录或调试时就需要将电脑和单片机通过 ST-LINK 或 J-Link 连接,如果电脑没有安装过相关驱动则需要安装驱动后才能使用,

安装驱动

ST-Link 与 STM32 通过 4 根线连接,具体引脚对应关系如下:

ST-Link引脚 STM32引脚
3.3V 3.3V
GND GND
SWDIO PA13(JTMS/SWDIO)
SWCLK PA14(JTCK/SWCLK)

部分型号可能需要额外连接 NRST(复位引脚)以实现硬件复位功能。

STM32工程架构

STM32工程架构

项目文件结构
.
├─User                         // 用户文件
|  ├─main.c                    // 主函数文件
|  ├─stm32f10x_conf.h          // 库函数配置
|  ├─stm32f10x_it.c            // 中断处理函数
|  └stm32f10x_it.h
├─Start                        
|   ├─core_cm3.c               // 内核寄存器描述文件
|   ├─core_cm3.h
|   ├─startup_stm32f10x_md.s   // 启动文件
|   ├─stm32f10x.h              // 外设寄存器描述
|   ├─system_stm32f10x.c       // 定义Systemlnit
|   └system_stm32f10x.h
└─Library                      // 库函数
    ├─misc.c
    ├─misc.h
    ├─stm32f10x_adc.c
    ├─stm32f10x_adc.h
    ├─stm32f10x_bkp.c
    ├─stm32f10x_bkp.h
    ├─stm32f10x_can.c
    ├─stm32f10x_can.h
    ├─stm32f10x_cec.c
    ├─stm32f10x_cec.h
    ├─stm32f10x_crc.c
    ├─stm32f10x_crc.h
    ├─stm32f10x_dac.c
    ├─stm32f10x_dac.h
    ├─stm32f10x_dbgmcu.c
    ├─stm32f10x_dbgmcu.h
    ├─stm32f10x_dma.c
    ├─stm32f10x_dma.h
    ├─stm32f10x_exti.c
    ├─stm32f10x_exti.h
    ├─stm32f10x_flash.c
    ├─stm32f10x_flash.h
    ├─stm32f10x_fsmc.c
    ├─stm32f10x_fsmc.h
    ├─stm32f10x_gpio.c
    ├─stm32f10x_gpio.h
    ├─stm32f10x_i2c.c
    ├─stm32f10x_i2c.h
    ├─stm32f10x_iwdg.c
    ├─stm32f10x_iwdg.h
    ├─stm32f10x_pwr.c
    ├─stm32f10x_pwr.h
    ├─stm32f10x_rcc.c
    ├─stm32f10x_rcc.h
    ├─stm32f10x_rtc.c
    ├─stm32f10x_rtc.h
    ├─stm32f10x_sdio.c
    ├─stm32f10x_sdio.h
    ├─stm32f10x_spi.c
    ├─stm32f10x_spi.h
    ├─stm32f10x_tim.c
    ├─stm32f10x_tim.h
    ├─stm32f10x_usart.c
    ├─stm32f10x_usart.h
    ├─stm32f10x_wwdg.c
    └stm32f10x_wwdg.h

以上工程架构中的文件需要在官网下载:STM32F10x标准外设库 (opens new window), 如果需要其他更多型号芯片的可以访问STM32标准外设软件库 (opens new window)进行下载

启动文件选择对照表

启动文件为 .s 格式的汇编文件,是芯片上电或复位后执行的​​第一段代码​​,负责完成硬件初始化、软件环境准备等关键任务,为后续用户程序(如main函数)的正常运行奠定基础

缩写 说明 Flash容量 型号
LD_VL 小容量产品超值系列 16-32K STM32F100
MD_VL 中容量产品超值系列 64-128K STM32F100
HD_VL 大容量产品超值系列 256-512K STM32F100
LD 小容量产品 16-32K STM32F101/102/103
MD 中容量产品 64-128K STM32F101/102/103
HD 大容量产品 256-512K STM32F101/102/103
XL 加大容量产品 大于512K STM32F101/102/103

以下是 stm32f10x 所有的启动文件,根据芯片型号和对应的需求选择相应的启动文件,这里我选择了 startup_stm32f10x_md.s






 



startup_stm32f10x_cl.s
startup_stm32f10x_hd.s
startup_stm32f10x_hd_vl.s
startup_stm32f10x_ld.s
startup_stm32f10x_ld_vl.s
startup_stm32f10x_md.s
startup_stm32f10x_md_vl.s
startup_stm32f10x_xl.s

注意

ARM Compiler 5(AC5) 使用 ARMCC; 而 ARM Compiler 6(AC6) 使用 armclang(基于 LLVM/Clang) 作为工具链,不再支持 ARMCC,Keil MDK V5.36 之后版本默认集成 AC6。

用 AC6 开发 AC5 会出现兼容性问题,如果要使用 ARMCC 推荐安装 Keil MDK V5.36 之前的版本

# 使用 VScode 开发

在 VScode 中开发 STM32 需要安装插件 Embedded IDESTM32Cube for Visual Studio CodeEmbedded IDE 可以将 Keil 中创建的项目导入在 VScode 中开发;STM32Cube for Visual Studio Code 是官方推出的插件,通过将 STM32CubeMX 生成的 CMake 项目导入到 VSCode 进行开发。

# 在使用 Embedded IDE 时遇到的问题及解决办法

  • 问题1:编译出错显示 #error directive: "Please select first the target STM32F10x device used in your application (in stm32f10x.h file)" armcc(35)

解决办法:点击 芯片支持包(Chip Support Package) -> From Repo -> 安装对应型号的芯片支持包 (opens new window)并选择对应的芯片类型

  • 问题2:烧录程序时找不到烧录文件 Error: File does not exist: Project.hex

解决办法:打开 构建配置 -> 构建器选项 -> 链接器 -> 取消勾选 不生成 Hex/Bin/S19 等二进制文件 并保存。推荐文章:解决:VScode插件EmbeddedIDE插件烧录时找不到hex文件 (opens new window)

其他有关 Embedded IDE 的问题可以查阅EIDE插件官网 (opens new window)

# 使用 Arduino IDE 开发

# 在 Arduino 中开发 STM32F1XX 系列 MCU

文件 —> 首选项 —> 其他开发板管理器地址中填写链接

https://dan.drown.org/stm32duino/package_STM32duino_index.json

然后在 开发板管理器 中搜索 STM32F1XX/GD32F1XX boards 并安装

# 在 Arduino 中开发 STM32 全系列 MCU

可以尝试使用这个链接

https://raw.githubusercontent.com/stm32duino/BoardManagerFiles/main/package_stmicroelectronics_index.json

文件 —> 首选项 —> 其他开发板管理器地址 输入链接后可以在 开发板管理器 中搜索到 STM32 MCU based boardsSTM8 MCU based boards

该项目的文档可以在这里查看 (opens new window)

# STM32开发示例

开发STM32单片机主要有以下几种方式:

  • 寄存器开发:直接操作STM32的硬件寄存器,通过地址映射和指针访问外设寄存器。代码执行效率高,深入硬件底层工作原理。
  • 标准库函数开发:使用ST提供的标准外设库,通过封装好的API函数操作外设。开发效率较高,简化了寄存器配置。
  • HAL库开发:基于硬件抽象层(Hardware Abstraction Layer)库,提供更高层次的API,支持跨芯片移植。使用图形化工具 STM32CubeMX 自动生成初始化代码,同一代码可适配不同STM32型号。

# 使用寄存器开发

以 STM32F103C8T6 芯片为例,简单开发一个程序点亮开发板上的 LED 灯 (引脚为C13),将代码写入 main.c 文件中

#include "stm32f10x.h"

int main(void)
{
	RCC->APB2ENR = 0x00000010;  // 使能GPIOC的时钟,使GPIOC可以正常工作
	GPIOC->CRH =0x00300000;  // 配置PC13引脚为推挽输出模式
	GPIOC->ODR = 0x00002000; // 将PC13引脚设置为高电平
	while (1)  // 通过无限循环保持程序持续运行
	{
	
	}
}

提示

PC13是指GPIOC的13号引脚(P:表示Port(端口);C:表示GPIO端口)

STM32 是​32位​的微控制器,绝大多数外设的控制/状态寄存器​​,内核的通用寄存器都是32位的。

我们需要通过控制 GPIO 输出高低电平来实现 LED 灯的亮灭,而 GPIO 都是高速总线 APB2 的外设,所以我们要先打开 GPIO 的时钟,让 GPIO 端口可以正常工作。

以下是APB2外设时钟使能寄存器的结构:

外设时钟使能寄存器

APB2外设时钟使能寄存器(RCC_APB2ENR)说明
功能描述
31:15 保留, 始终读为0
14 USART1EN: USART1时钟使能。由软件置1或清0
0: USART1时钟关闭;
1: USART1时钟开启
13 保留, 始终读为0
12 SPI1EN: SPI1时钟使能。由软件置1或清0
0: SPI1时钟关闭;
1: SPI1时钟开启
11 TIM1EN: TIM1定时器时钟使能。由软件置1或清0
0: TIM1定时器时钟关闭;
1: TIM1定时器时钟开启
10 ADC2EN: ADC2接口时钟使能。由软件置1或清0
0: ADC2接口时钟关闭;
1: ADC2接口时钟开启
9 ADC1EN: ADC1接口时钟使能。由软件置1或清0
0: ADC1接口时钟关闭;
1: ADC1接口时钟开启
8:7 保留, 始终读为0
6 IOPEEN: IO端口E时钟使能。由软件置1或清0
0: IO端口E时钟关闭;
1: IO端口E时钟开启
5 IOPDEN: IO端口D时钟使能。由软件置1或清0
0: IO端口D时钟关闭;
1: IO端口D时钟开启
4 IOPCEN: IO端口C时钟使能。由软件置1或清0
0: IO端口C时钟关闭;
1: IO端口C时钟开启
3 IOPBEN: IO端口B时钟使能。由软件置1或清0
0: IO端口B时钟关闭;
1: IO端口B时钟开启
2 IOPAEN: IO端口A时钟使能。由软件置1或清0
0: IO端口A时钟关闭;
1: IO端口A时钟开启
1 保留, 始终读为0
0 AFIOEN: 辅助功能IO时钟使能。由软件置1或清0
0: 辅助功能IO时钟关闭;
1: 辅助功能IO时钟开启

要点亮的 LED 灯的引脚是 PC13,属于 GPIOC 端口,所以我们先要打开 GPIOC 的时钟,该时钟由第 4 位 IOPCEN 控制,将这该位设为 1 表示开启 GPIOC 时钟。 这时寄存器 APB2ENR 的值为:

0000 0000 0000 0000
0000 0000 0001 0000

每 4 位一组,转换成对应的十六进制为 0x00000010

使用代码打开 GPIOC 的时钟

RCC->APB2ENR = 0x00000010;

然后配置 PC13 端口的输入输出模式,以下是端口配置高寄存器的结构说明,要控制 GPIOC_Pin_13 端口需要 CNF13MODE13 两个位段(两个连续的二进制位(Bit)组成)

端口配置高寄存器(GPIOx_CRH)

端口配置低寄存器(GPIOx_CRL)

端口配置低寄存器(GPIOx_CRL)

GPIOC_Pin_13 端口设置为推挽输出模式,最大速度为 50 MHz,CNF13MODE13 的值分别为 0011

位段 编号 说明 配置选项
CNF13 23:22 端口13配置位
用于设置端口13的具体输入/输出配置
其具体含义取决于MODE13的设置
当 MODE13 设置为输入模式 (00) 时:
00: 模拟输入模式 (用于ADC等)
01: 浮空输入模式 (电平由外部决定)
10: 上拉/下拉输入模式
11: 保留

当 MODE13 设置为输出模式 (01,10,11) 时:
00: 通用输出推挽模式
01: 通用输出开漏模式
10: 复用功能推挽输出 (由外设控制)
11: 复用功能开漏输出 (由外设控制)
MODE13 21:20 端口13模式位
用于设置端口13的基本工作模式和输出速度
00: 输入模式 (复位后的状态)
01: 输出模式,最大速度 10 MHz
10: 输出模式,最大速度 2 MHz
11: 输出模式,最大速度 50 MHz

寄存器 GPIOx_CRH 的值为

0000 0000 0011 0000
0000 0000 0000 0000

转换为十六进制为 0x00300000,用代码设置 GPIOC 端口的输出模式

GPIOC->CRH =0x00300000;

现在就可以通过 GPIOC 端口输出数据了,输出数据需要使用端口输出数据寄存器(GPIOx_ODR),点亮LED灯需要将 13 引脚设置为高电平,ODR13 控制该引脚,值为 1 是高电平 0 是低电平

端口输出数据寄存器(GPIOx_ODR)

端口输入寄存器

端口输入数据寄存器(GPIOx_IDR)

我们将 13 引脚设置为高电平,寄存器 GPIOx_ODR 的值为

0000 0000 0000 0000
0010 0000 0000 0000

转换成十六进制为 0x00002000,用代码设置 GPIOC 端口的输出,13 引脚为高电平,其他 GPIOC 端口的引脚为低电平

GPIOC->ODR = 0x00002000;

注意

GPIOC->ODR = 0x00002000; 这种方法会改变寄存器中的所有位,会影响该端口其他引脚的原有配置;如果只设置 13 引脚,不影响其他引脚,可以使用位运算操作引脚

GPIOC->ODR |= (1 << 13);

1 是二进制的 0000000000000001<< 13 表示向左移动 13 位结果是二进制 10000000000000 就是在保持其他位不变的同时将 1 向左移动 13 位,这样ODR13 变成了 1

# 使用标准库函数开发

以 STM32F103C8T6 芯片为例,简单开发一个程序点亮开发板上的 LED 灯 (引脚为C13),将代码写入 main.c 文件中

#include "stm32f10x.h"  // 导入STM32F10x型号的头文件,该文件定义了所有外设寄存器和相关函数

int main(void)  // 该函数为程序的入口
{
  // 启用GPIOC端口的时钟,使GPIOC可以正常工作
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);

  // 定义一个GPIO结构体变量 GPIO_InitStructure
	GPIO_InitTypeDef GPIO_InitStructure;

  // 配置GPIO端口
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  // 将该变量的GPIO模式设置为推挽输出模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;  // 将该变量的引脚为13
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 将该变量的GPIO输出速度设置为50MHz

  // 调用GPIO初始化函数,将上述配置应用到GPIOC端口
	GPIO_Init(GPIOC,&GPIO_InitStructure);

  // 控制GPIO端口输入输出
	GPIO_SetBits(GPIOC,GPIO_Pin_13);  //设置GPIOC的第13引脚为高电平(3.3V)
	GPIO_ResetBits(GPIOC,GPIO_Pin_13);  //设置GPIOC的第13引脚为低电平(0V),用于点亮LED

  // 无限循环,确保程序持续运行
	while (1)
	{
	
	}
}
  • RCC_APB2PeriphClockCmd 是 RCC 时钟控制函数

    • RCC_APB2Periph_GPIOC 表示GPIOC端口
    • ENABLE 表示使能时钟
  • GPIO_InitTypeDef 是一个结构体类型名,它是STM32标准外设库中预定义的数据类型,专门用于GPIO外设的初始化配置,该结构体有以下成员

    • GPIO_Mode: 配置GPIO的工作模式(输入/输出模式)
    • GPIO_Speed: 配置GPIO的输出速度
    • GPIO_Pin: 指定要配置的GPIO引脚
  • GPIO_Init 用于初始化用于初始化GPIO,使GPIO端口根据配置的方式工作,该函数有两个参数

    • GPIOx: 指定要配置的GPIO端口,例如GPIOA、GPIOB、GPIOC等
    • GPIO_InitStruct: 指向包含GPIO配置信息的结构体的指针,例如 &GPIO_InitStructure
  • GPIO_SetBits: 将指定的GPIO引脚设置为高电平,该函数有两个参数

    • GPIOx: GPIO端口(如GPIOA、GPIOB、GPIOC等)
    • GPIO_Pin: 要设置为高电平的引脚(可以是单个或多个引脚)
  • GPIO_ResetBits: 将指定的GPIO引脚设置为低电平,该函数有两个参数

    • GPIOx: GPIO端口(如GPIOA、GPIOB、GPIOC等)
    • GPIO_Pin: 要设置为高电平的引脚(可以是单个或多个引脚)

注意

  1. 在STM32单片机开发中,​​主函数 main() 中通常需要包含一个无限循环,如 while(1)​​,否则会导致程序异常或功能失效。循环内可以是空的,但不能没有这个循环。
  2. 代码中的结构体变量是可以任意命名的, 可以把 GPIO_InitStructure 换成其他名字
  3. STM32具有多个GPIO端口,例如GPIOA、GPIOB、GPIOC等,这是由芯片架构设计决定的
  4. 什么是推挽输出模式?

推挽输出模式(Push-Pull Output)是GPIO的一种能够主动输出高电平和低电平输出模式

    VCC
     |
   [开关1]
     |
     |---- 输出引脚
     |
   [开关2]
     |
    GND
  • 输出高电平时,开关1 导通,开关2 截止,输出引脚连接到VCC
  • 输出低电平时,开关2 导通,开关1 截止,输出引脚连接到GND

# GPIO 的输入输出

GPIO 是通用输入输出接口,常用于与外部设备通信、控制外部设备、读取外设信号。

  • 引脚电平一般为 0 ~ 3.3V 部分引脚支持 5V
  • 可以控制端口输出高低电平控制外部设备、模拟通信协议输出时序信号,或者读取端口高低电平输入、模拟通信协议接收数据

所有的 GPIO 端口都与 ABP2 总线连接,有 GPIOA、GPIOB、GPIOC 等多个端口(不同型号的芯片端口数量不同),每个 GPIO 端口都有 16 个引脚,从 0 开始排序,例如 GPIOA 的第 0 号引脚为 PA0,GPIOC 的第 13 号引脚为 PC3

# GPIO基本结构

GPIO基本结构

每个 GPIO 端口模块中都有寄存器、驱动器等,内核可以通过APB2总线对寄存器进行读取,每一个引脚都对应着寄存器的一个为,一个端口有 16 个引脚,而 STM32 是一个 32 位的单片机,寄存器的低16位连接对应的引脚,而高16位没有被用到;驱动器用来增加信号的驱动能力

# STM32系统结构

这张图以 STM32F103C8T6 微控制器的系统结构为例,看图中的 APB1、APB2 可以很直观看到两条总线所连接的引脚,它们都是用来连接外设的

系统结构

# GPIO端口位的基本结构

这就是 GPIO 中位的基本结构,整个结构可以分为两部分:上面控制输出;下面控制输入。 其中左边部分是寄存器,中间是驱动器,右边是保护电路。

GPIO端口位的基本结构

# 输入模式

图中 TTL Schmitt trigger 的右边有两个电阻,上拉电阻至 VDD,下拉电阻至 VSS

    VDD
     |
   [开关1]
     |
     |---- 输出引脚
     |
   [开关2]
     |
    VSS
  • 上拉输入模式:上拉电阻,导通【开关1】 断开【开关2】
  • 下拉输入模式:下拉电阻,导通【开关2】 断开【开关1】
  • 浮空输入模式:两个开关都断开(电平状态不确定,容易受干扰)
  • 模拟输入模式:GPIO的输入输出都断开,引脚直接连接到内部的模数转换器(ADC)

当外部没有信号输入时,上拉输入模式默认为高电平,下拉输入模式默认为低电平

# 施密特触发器

施密特触发器(TTL Schmitt trigger)用于将输入的电压进行整形(整理波形),当电压大于某一阈值输出为高电平,当电压小于某一阈值时电压为为低电平。在传递信号时不可避免的会出现信号中的噪声和毛刺,该触发器可以将不规则的输入信号转换为标准的方波信号

经过施密特触发器整形后数据会存储在输入数据寄存器(GPIOx_IDR)中,使用程序读取该寄存器输入的每一位数据就可以知道端口的输入电平了

# 输出模式

    VDD
     |
   [P-MOS]
     |
     |---- 输出引脚
     |
   [N-MOS]
     |
    VSS
  • 推挽输出模式:是GPIO的一种能够主动输出高电平和低电平输出模式
    • 输出高电平时,开关1 导通,开关2 截止,输出引脚连接到 VDD
    • 输出低电平时,开关2 导通,开关1 截止,输出引脚连接到 VSS
  • 开漏输出模式: P-MOS 不生效,只使用 N-MOS
    • 输出低电平时,N-MOS管导通,输出引脚直接连接到 GND
    • 输出高电平时,N-MOS管截止(断开),输出引脚呈高阻态(悬空)

在输入模式时,N-MOSP-MOS 都是断开的,输出模式为关闭状态;在输出模式时,GPIO 输出可以正常使用。输出模式时可以输入,但输入模式时不能输出。

复用功能输出模式:这个模式下输出不与输出数据寄存器连接,引脚的控制权转移到了片上外设

  • 复用功能推挽输出:由外设控制的推挽输出模式
  • 复用功能开漏输出:由外设控制的开漏输出模式

# 位设置/清除寄存器

该寄存器可以操作输出数据寄存器的某一位,而不影响其他位。输出数据寄存器控制同时控制16个端口,寄存器只能整体读写,如果要单独控制某一个端口时有两种办法:

  1. 先读出寄存器数据,然后通过位运算更改其中的某一位,最后再将更改后的方式写回去
// 只将PC13设置为高电平
GPIOC->ODR |= (1 << 13); 

// 只将PC13设置为低电平
GPIOC->ODR &= ~(1 << 13); 
  1. 通过位设置寄存器(GPIOx_BSRR)和位清除寄存器(GPIOx_BRR)。如果要将输出数据寄存器(GPIOx_IDR)的某一位置为 1 时,在位设置寄存器(GPIOx_BSRR)对应的那一位置为 1 ,其他位为 0,内部电路会按照位设置寄存器(GPIOx_BSRR)中的值自动将输出数据寄存器(GPIOx_IDR)对应的位置为 1,而剩下写 0 的位置保持不变
// 使用BSRR寄存器设置PC13为高电平
GPIOC->BSRR = (1 << 13);

如果要将输出数据寄存器(GPIOx_IDR)的某一位置为 0 时,在位清除寄存器(GPIOx_BSRR)对应的那一位置为 1 ,其他位为 0,内部电路就会自动将输出数据寄存器(GPIOx_IDR)中对应的那一位清零

//使用BSRR寄存器设置PC13为低电平
GPIOC->BSRR = (1 << (13 + 16));

// 使用BRR寄存器设置PC13为低电平
GPIOC->BRR = (1 << 13); 

端口位设置/清除寄存器(GPIOx_BSRR) 既可以设置也可以清除,高 16 位用于位清除,低 16 位用于位设置

端口位设置/清除寄存器

端口位清除寄存器(GPIOx_BRR) 只能用于清除,高 16 位用于位保留,低 16 位用于位清除

端口位清除寄存器

# 问题解决

这是由于 ST-link 固件版本过旧所导致的,可以通过升级固件解决此问题。

推荐两篇文章,文章中详细说明了解决的具体步骤:

# 使用Keil5编译程序,代码正确,编译报错

报错代码

CORE/core_cm3.c(445): error: non-ASM statement in naked function is not supported
  uint32_t result=0;
  ^
CORE/core_cm3.c(442): note: attribute is here
uint32_t __get_PSP(void) __attribute__( ( naked ) );
                                          ^
CORE/core_cm3.c(465): error: parameter references not allowed in naked functions
                  "BX  lr     \n\t" : : "r" (topOfProcStack) );
                                             ^
CORE/core_cm3.c(461): note: attribute is here
void __set_PSP(uint32_t topOfProcStack) __attribute__( ( naked ) );
                                                         ^
CORE/core_cm3.c(479): error: non-ASM statement in naked function is not supported
  uint32_t result=0;
  ^
CORE/core_cm3.c(476): note: attribute is here
uint32_t __get_MSP(void) __attribute__( ( naked ) );
                                          ^
CORE/core_cm3.c(499): error: parameter references not allowed in naked functions
                  "BX  lr     \n\t" : : "r" (topOfMainStack) );
                                             ^
CORE/core_cm3.c(495): note: attribute is here
void __set_MSP(uint32_t topOfMainStack) __attribute__( ( naked ) );
                                                         ^

问题分析: 这是由于编译器版本过高导致的,AC5 的项目在使用 AC6 编译时会出现兼容性问题

解决办法:在 Options for Target 中修改编译器为 Use default compiler version5

推荐帖子:STM32使用Keil5编译程序出错 (opens new window)