Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发
Keil MDK STM32系列
- Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发
- Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401开发
- Keil MDK STM32系列(三) 基于标准外设库SPL的STM32F407开发
- Keil MDK STM32系列(四) 基于抽象外设库HAL的STM32F401开发
- Keil MDK STM32系列(五) 使用STM32CubeMX创建项目基础结构
- Keil MDK STM32系列(六) 基于HAL的ADC模数转换
- Keil MDK STM32系列(七) 基于HAL的PWM和定时器
- Keil MDK STM32系列(八) 基于HAL的PWM和定时器输出音频
- Keil MDK STM32系列(九) 基于HAL和FatFs的FAT格式SD卡TF卡读写
- Keil MDK STM32系列(十) Ubuntu下的PlatformIO开发环境
概述
Windows下使用Keil MDK5进行stm32f103c8t6的开发和编译, 配合ST-LINK工具进行烧录, 使用标准外设库SPL.
所需硬件
stm32f103系列开发板
stm32f103c8t6核心板
参数
- ARM 32-bit Cortex-M3
- 72 MHz maximum frequency
- 20k ram, 64k flash 这是一个规格比较低的stm32芯片
- LQFP封装48pin
stm32f103vct6开发板
参数
- ARM 32-bit Cortex-M3
- 72 MHz maximum frequency
- 48k ram, 256k flash
- LQFP封装100pin
st-link烧录器
在烧录程序到目标芯片以及Debug时需要使用, 自ST-Link V2以来的所有ST-LINK板都带一个COM
LED, 一般是红绿两色组合, 也有些是红蓝两色, 两色分别有常亮, 常灭, 闪烁等状态. 不同状态代表不同的含义:
- 闪烁红色: 正在PC上创建USB模拟器, 如果持续闪烁有可能驱动未安装成功
- 红色: 在PC与ST-Link之间已经建立连接(USB模拟已完成)
- 闪烁交替绿色和红色: 目标芯片和PC之间正在交换数据
- 绿色: ST-Link与目标芯片通信成功
- 橙色(红绿同时亮): ST-Link与目标芯片通信失败。
USB2TTL转接卡
在观察串口输出时需要使用.
安装说明
文件准备
Keil5 MDK 社区版
2022年初, Arm 发布了 MDK-Community edition, 这个版本没有代码大小限制, 编译和debug功能齐全, 与正式版区别仅仅在于一些扩展. 一般开发可以直接用社区版.
- 各版本区别和下载: https://www2.keil.com/mdk5/selector 各个版本的安装文件是一样的, 只是授权不同体现的功能不同. MDK安装包比C51大很多, 接近1GB.
- 申请社区版授权, 需要提供邮箱和公司信息: https://keil.arm.com/mdk-community 走完流程后, 社区版的授权码会通过email发送给你, 授权的有效期是一年, 所以第二年还需要再申请一遍
- MDK V5.37 安装和默认使用的是新的编译器 Arm Compiler 6.18, 与旧的 AC5 不太一样
ST-Link 驱动
从ST官网下载st-link utility 或 st-link驱动, 不安装无法使用 st-link
- 只下载驱动部分: https://www.st.com/zh/development-tools/stsw-link009.html
- 或者下载包含驱动的 st-link utility https://www.st.com/zh/development-tools/stsw-link004.html
- 如果使用 st-link utility, 直接运行会报"mfc140.dll"错误, 还需要下载安装 Microsoft Visual C++ 2015 https://www.microsoft.com/en-us/download/details.aspx?id=52685, 文件不大十几兆.
ST官方库
前往https://www.st.com/, 点击Tools&Software > Embedded Software > MCU & MPU Embedded Software > STM32 Embedded Software > STM32 Standard Peripheral Libraries
(链接), 下载F1解压备用.
Update 2022-06-24: 之前STM32F10x Standard Peripherals Library已经升级到了3.6.0
安装
- 运行mdk537.exe, 安装
- 安装完成后Pack Installer会自动运行, 可以让其自己更新
- 如果使用stlink下载器的, 还需要安装驱动
使用社区版, 可以自行申请license
安装开发包
在Pack Installer中
- 在左侧找到STMicroelectronics->STM32F1->STM32F103, 点击
- 在右侧会显示对应的Packs, 在Device Specific中找到Keil::STM32F1xx_DFP, 点击Install安装
- 同理安装STM32F401
名词解释
CMSIS
微控制器软件接口标准(CMSIS:Cortex Microcontroller Software Interface Standard)是 Cortex-M 处理器系列的与供应商无关的硬件抽象层(A vendor-independent hardware abstraction layer for the Cortex-M processor series and defines generic tool interfaces). 使用CMSIS可以为处理器和外设实现一致且简单的软件接口, 从而简化软件的重用、缩短微控制器新开发人员的学习过程,并缩短新设备的上市时间
ld, md, hd, xl; cl, vl
- ld(low density), md(medium density), hd(high density), xl(XL-Density) 对应同一个型号线上, 不同flash和ram大小的芯片.
- vl(value line), cl(connectivity line) 指的是同一型号下, 不同的细分型号线
对于stm32f1x, 各名词的对应关系为
- Low-density devices are STM32F101xx, STM32F102xx and STM32F103xx microcontrollers where the Flash memory density ranges between 16 and 32 Kbytes.
- Medium-density devices are STM32F101xx, STM32F102xx and STM32F103xx microcontrollers where the Flash memory density ranges between 64 and 128 Kbytes.
- High-density devices are STM32F101xx and STM32F103xx microcontrollers where the Flash memory density ranges between 256 and 512 Kbytes.
- XL-density devices are STM32F101xx and STM32F103xx microcontrollers where the Flash memory density ranges between 768 Kbytes and 1 Mbyte.
- Connectivity line devices are STM32F105xx and STM32F107xx microcontrollers.
ST官方库结构说明
STM32F10x_StdPeriph_Lib_V3.5.0
目录结构及说明
├─Libraries
│ ├─CMSIS
│ │ ├─CM3
│ │ │ ├─CoreSupport # CMSIS核心外设访问层代码
│ │ │ └─DeviceSupport
│ │ │ └─ST
│ │ │ └─STM32F10x # stm32f10x相关代码
│ │ │ └─startup
│ │ │ ├─arm # startup_stm32f10x_ld/md/hd/xl.s文件,
# stm32f103c8t66规格为64k flash, 对应md
│ │ │ ├─gcc_ride7
│ │ │ ├─iar
│ │ │ └─TrueSTUDIO
│ │ └─Documentation
│ └─STM32F10x_StdPeriph_Driver # 对应stm32f10x的外设代码
│ ├─inc
│ └─src
├─Project
│ ├─STM32F10x_StdPeriph_Examples # 代码示例
│ └─STM32F10x_StdPeriph_Template # 代码模板, 需要用到stm32f10x_conf.h, stm32f10x_it.c, stm32f10x_it.h这三个文件(直接复制到新项目的USER目录)
│ ├─EWARM
│ ├─HiTOP
│ ├─MDK-ARM
│ ├─RIDE
│ └─TrueSTUDIO
├─Utilities
└─_htmresc
开发说明
创建项目
创建目录并填充文件
以stm32f103为例, 对于项目test001, 创建工作目录test001, 在工作目录下创建MDK-ARM, USER这两个目录
- 复制开发包下的Libraries整个目录到当前项目目录下
- 这个目录中包含了CMSIS核心外设访问层代码
- stm32的外设库文件
- mdk-arm
用于放置Keil MDK项目文件, 以及项目开发过程中生成的临时文件 - hardware
用于放置自定义的外设, 例如rfid, esp8266等 - user
- 复制 STM32F10x_StdPeriph_Template下面的stm32f10x_conf.h, stm32f10x_it.c, stm32f10x_it.h三个文件到这个目录
- 用于放置用户编写的代码
ld,md,hd的选择
md hd ld 根据芯片FLASH容量决定
- 16K < FLASH < 32K ld
- 64K < FLASH < 128K md
- 256K < FLASH < 512K hd
在Keil uVision5中创建项目
Project -> New uVision Project
, 选择前面的MDK-ARM目录, 使用名称test001, 保存- 选择对应的芯片型号
配置项目
点击Manage Project Items
- 修改project targets名称
- 添加groups
- CMSIS
- StdPeriph_Driver
- Hardware
- Startup
- User
对每个group, 添加的文件为
- CMSIS
- core_cm3.c <--- 如果是AC6, 不要包含这个文件, 不然编译不通过
- system_stm32f10x.c
- StdPeriph_Driver
- 添加 libraries/STM32F10x_StdPeriph_Driver/src 下面的所有c文件
- Hardware
- 添加 hardware目录下的c文件
- Startup
- 添加 libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/arm 下面对应的s文件
- User
- 添加 user 目录下的所有c文件
点击configure target options , 定位到c/c++
- Define: 写入 USE_STDPERIPH_DRIVER
- 不需要填STM32F10X_MD, STM32F10X_HD 这些配置, 这个变量在前面选择芯片型号的时候就自动定义了, 不确定的话可以观察产生的Compiler Control String
- Include Paths: 如果是按上面的目录结构组织的项目, 可以直接复制下面的配置
..\Libraries\CMSIS\CM3\CoreSupport;..\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x;..\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm;..\Libraries\STM32F10x_StdPeriph_Driver\inc;..\hardware;..\user
在c/c++下能看到完整的编译Compile control string
-c --cpu Cortex-M3 -D__MICROLIB -g -O0 --apcs=interwork --split_sections -I ../Libraries/CMSIS/CM3/CoreSupport -I ../Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x -I ../Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/arm -I ../Libraries/STM32F10x_StdPeriph_Driver/inc -I ../Libraries/ STM32F10x_StdPeriph_Driver/src -I ../hardware -I ../user
-I./RTE/_test001
-IC:/Keil_v5/ARM/PACK/Keil/STM32F1xx_DFP/2.3.0/Device/Include
-IC:/Keil_v5/ARM/CMSIS/Include
-D__UVISION_VERSION="525" -DSTM32F10X_MD -DUSE_STDPERIPH_DRIVER
-o .\Obj\*.o --omf_browse .\Obj\*.crf --depend .\Obj\*.d
- Language / Code Generation
- Language C: 选择C99, 默认的C90, 在AC6下编译会报错
- Language C++: 选择C++03
- Warnings: AC5-like Warnings
烧录
stlink与stm32f103c8t6核心板
连接需要4根线, 连接关系为
G -- GND
CLK -- SWCLK
IO -- SWDIO
V3 -- 3.3V
stlink与stm32f103vct6开发板
有些开发板没有单独的SWC/SWD接口, 只能连接到JTAG接口, 连接方式为:
- 从PCB板上方俯视, 将JTAG接口座缺口朝上
- PIN1-PIN20为从上至下, 从右至左
- 最右侧为PIN1(上), PIN2(下), 都可以接stlink的3.3V
- 从右往左数第4个为PIN7, 接SWDIO, 第5个为PIN9, 接SWCLK
- 最左下角为PIN20, 接GND
在Keil uvision5中
- 点击configure target options , 定位到Debug, Use选择ST-Link Debuger, 点击Settings
- 如果Debug Adapter里时空白没有显示ST-LINK/V2, 去windows设备管理器看下设备是否正常
- 切换到Flash Download标签, 勾选Reset and Run
- 点击Download按钮, 或者按F8, 进行烧录
Debug调试说明
在F7编译完代码, F8将程序通过ST-Link烧录至开发板后, 就可以对程序进行调试
快捷键汇总
- 进入/跳出Debug模式:
Ctrl + F5
- 继续运行
F5
- 逐步运行
F10
- 单步执行(会跳入)
F11
- 从函数内跳出运行
Ctrl + F11
- 运行到光标所在处
Ctrl + F10
- 插入/移除断点
F9
- 移除所有断点
Ctrl + Shift + F9
调试要点
- 点击
Start/Stop Debug Session
或Ctrl+F5
进入Debug模式, 在进入Debug模式后, 会重启程序, 并暂停程序运行 - 这时候可以按
F5
往下执行, 或者按F10
或F11
一步一步执行, 或者Ctrl + F10
跳到光标指定的行 - 通过Reset按钮, 可以将程序恢复到初始的暂停状态
- 常用的调试小窗口
- Watch窗口: 选中变量 -> 右键 -> Add xxx to ->Watch1
- System Viewer窗口: 可以实时查看外设寄存器值, 通过View->System View -> 指定外设 打开.
stm32开发说明
代码结构
项目的目录结构比较自由, 只要包含的C文件正确, 包含的头文件路径正确, 再加上正确的编译变量如USE_STDPERIPH_DRIVER, 编译就没问题. 但是在项目中还是需要注意目录结构, 建议将平时不需改动的代码, 需要频繁修改的代码, 以及IDE相关的文件, 编译中间产生的文件, 都按目录分隔开.
在项目中最终采的代码结构
├─Libraries
│ ├─CMSIS
│ ├─Hardware
│ └─STM32F10x_StdPeriph_Driver
├─MDK-ARM
└─User
- 其中Libraries直接用开发库的原始文件结构, CMSIS和STM32F10x_StdPeriph_Driver是开发中不需要修改的
- 将自定义的外设代码都放在单独的Hardware目录中, 这个目录可以放在Libraries下, 也可以放在最外层与Libraries平级
- 外设代码的头文件中, 不应当包含全局头文件
include.h
, 而是包含stm32f10x.h
, 以及其他需要用到的Hardware头文件.
- 外设代码的头文件中, 不应当包含全局头文件
- 项目相关的代码, 例如main.c都放在User目录下
- 创建一个
include.h
, 用于包含整体的项目头文件,stm32f10x.h
以及用到的Hardware头文件, 在main.c中引用include.h
- 创建一个
config.h
, 用于放置环境相关的变量, 在main.c以及需要用到的hardware中引用, 这个文件要加到.gitignore
- 创建一个
- IDE相关以及编译中间产生的文件, 都放在MDK-ARM目录下, 这个目录需要添加到.gitignore
代码逻辑
STM32F10x是基于ARM Cortex-M3 的32位内核, F4是基于M4的内核, M3跟M4比就是不带FPU和DSP指令集. 在STM32上的开发思路, 和一般的软件项目开发思路不太一样. STM32上跑的程序, 基本上就是围绕着它的外设进行的, 主要的外设包括 DMA, ETH, GPIO, TIM, U(S)ART, SPI, I2C, ADC等.
代码的套路一般是这样的
- 初始化需要的外设, 包括内建的定时器, GPIO, UART, SPI, I2C, 中断以及自定义的外设, 例如RFID, ESP8266等
- 对于自定义的外设, 需要自己编写外设驱动, 处理与STM32内建外设之间的通信
- 做一个while(1)循环, 在循环中完成业务逻辑
- 主体单进程, 在进程中通过各种中断, 触发和处理外设之间的通信
需要注意的地方
- 每个外设, 都有对应的总线, 对应的编号, 以及对应的PIN脚, 这些会直接体现到代码中
- 要根据总线频率, 自己控制时间, 定时和延迟都是自己控制的
- 传输是以byte为单位的, 一个中断可能只产生1个byte的数据
- 外设的初始化, 中断位的判断和清除, 都是固定用法
- 内存是有限的, 对每个byte都要计算精确, 不能浪费
内存管理
stm32f103c8t6的flash为64k, ram为20k, 容量都很小. 编译结果中的信息
Program Size: Code=8788 RO-data=424 RW-data=72 ZI-data=2328
与这两部分相关联的关系为
- FLASH占用 = Code + RO Data + RW Data
- RAM占用 = RW Data + ZI Data + Stack_Size + Heap_Size
实际测试发现, 不论RW Data + ZI Data多大, 运行中可以通过malloc+memset申请的内存只有500个字节, 只要申请的内存超过这个数量, 运行就会卡住.
在代码中, 尽量不要用malloc, 而应该直接声明为固定长度的数组, 这样声明的内存, 可以观察到会增长到ZI Data中去, 这种方式能使用的空间比500字节大得多.
问题排查
当遇到问题时, 例如引脚不工作, 不输出, 输出不正确, 需要通过各种手段检查和排查
- 引脚不工作, 要检查对应的模块是否正确初始化, 是否使能. STM32中GPIO, UART, NVIC这些模块的启动基本上都是固定套路
- 对于串口等外设语句上需要检查, AHB1, APB1, APB2 与外设对应关系是否正确, 是否错误使用RCC_APB1去启用RCC_APB2了
- 是否在main中调用初始化方法了
- 串口输出和输入的问题排查
- 首先是在debug界面, 查看串口外设的寄存器值, 看看有没有初始化成功
- 通过USB2TTL转接卡, 将信号输入输出转移到putty/xshell中进行检查
- 排除连线断路的问题
- 字符串输入输出. 因为有
\r
,\n
等格式符号的存在, 会导致输出字符显示较乱, 当需要严格对照时, 需要将每个字符打印成双位16进制输出%02X
- 程序执行卡住, 有可能是变量申请的内存过大超出了内存限制