Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发

Keil MDK STM32系列

概述

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板都带一个COMLED, 一般是红绿两色组合, 也有些是红蓝两色, 两色分别有常亮, 常灭, 闪烁等状态. 不同状态代表不同的含义:

  • 闪烁红色: 正在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官网下载st-link utility 或 st-link驱动, 不安装无法使用 st-link

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

安装

  1. 运行mdk537.exe, 安装
  2. 安装完成后Pack Installer会自动运行, 可以让其自己更新
  3. 如果使用stlink下载器的, 还需要安装驱动

使用社区版, 可以自行申请license

安装开发包

在Pack Installer中

  1. 在左侧找到STMicroelectronics->STM32F1->STM32F103, 点击
  2. 在右侧会显示对应的Packs, 在Device Specific中找到Keil::STM32F1xx_DFP, 点击Install安装
  3. 同理安装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这两个目录

  1. 复制开发包下的Libraries整个目录到当前项目目录下
    • 这个目录中包含了CMSIS核心外设访问层代码
    • stm32的外设库文件
  2. mdk-arm
    用于放置Keil MDK项目文件, 以及项目开发过程中生成的临时文件
  3. hardware
    用于放置自定义的外设, 例如rfid, esp8266等
  4. 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中创建项目

  1. Project -> New uVision Project, 选择前面的MDK-ARM目录, 使用名称test001, 保存
  2. 选择对应的芯片型号

配置项目

点击Manage Project Items

  1. 修改project targets名称
  2. 添加groups
    1. CMSIS
    2. StdPeriph_Driver
    3. Hardware
    4. Startup
    5. 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++

  1. Define: 写入 USE_STDPERIPH_DRIVER
    • 不需要填STM32F10X_MD, STM32F10X_HD 这些配置, 这个变量在前面选择芯片型号的时候就自动定义了, 不确定的话可以观察产生的Compiler Control String
  2. 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
  1. 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接口, 连接方式为:

  1. 从PCB板上方俯视, 将JTAG接口座缺口朝上
  2. PIN1-PIN20为从上至下, 从右至左
  3. 最右侧为PIN1(上), PIN2(下), 都可以接stlink的3.3V
  4. 从右往左数第4个为PIN7, 接SWDIO, 第5个为PIN9, 接SWCLK
  5. 最左下角为PIN20, 接GND

在Keil uvision5中

  1. 点击configure target options , 定位到Debug, Use选择ST-Link Debuger, 点击Settings
  2. 如果Debug Adapter里时空白没有显示ST-LINK/V2, 去windows设备管理器看下设备是否正常
  3. 切换到Flash Download标签, 勾选Reset and Run
  4. 点击Download按钮, 或者按F8, 进行烧录

Debug调试说明

在F7编译完代码, F8将程序通过ST-Link烧录至开发板后, 就可以对程序进行调试

快捷键汇总

  • 进入/跳出Debug模式: Ctrl + F5
  • 继续运行 F5
  • 逐步运行 F10
  • 单步执行(会跳入) F11
  • 从函数内跳出运行 Ctrl + F11
  • 运行到光标所在处 Ctrl + F10
  • 插入/移除断点 F9
  • 移除所有断点Ctrl + Shift + F9

调试要点

  1. 点击Start/Stop Debug SessionCtrl+F5进入Debug模式, 在进入Debug模式后, 会重启程序, 并暂停程序运行
  2. 这时候可以按F5往下执行, 或者按F10F11一步一步执行, 或者Ctrl + F10跳到光标指定的行
  3. 通过Reset按钮, 可以将程序恢复到初始的暂停状态
  4. 常用的调试小窗口
    1. Watch窗口: 选中变量 -> 右键 -> Add xxx to ->Watch1
    2. 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等.
代码的套路一般是这样的

  1. 初始化需要的外设, 包括内建的定时器, GPIO, UART, SPI, I2C, 中断以及自定义的外设, 例如RFID, ESP8266等
    1. 对于自定义的外设, 需要自己编写外设驱动, 处理与STM32内建外设之间的通信
  2. 做一个while(1)循环, 在循环中完成业务逻辑
  3. 主体单进程, 在进程中通过各种中断, 触发和处理外设之间的通信

需要注意的地方

  1. 每个外设, 都有对应的总线, 对应的编号, 以及对应的PIN脚, 这些会直接体现到代码中
  2. 要根据总线频率, 自己控制时间, 定时和延迟都是自己控制的
  3. 传输是以byte为单位的, 一个中断可能只产生1个byte的数据
  4. 外设的初始化, 中断位的判断和清除, 都是固定用法
  5. 内存是有限的, 对每个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
  • 程序执行卡住, 有可能是变量申请的内存过大超出了内存限制

参考代码

posted on 2021-04-25 12:52  Milton  阅读(2998)  评论(0编辑  收藏  举报

导航