1 搭建STM32的开发环境
1.1 MDK是什么?
我们开发STM32使用HAL或者是3.5标准库函数都是使用keil MDK。后面可以使用VSCODE开发。MDK 即RealView MDK 或MDK-ARM(Microcontroller Development kit),是 ARM 公司收购Keil公司以后,基于uVision界面推出的针对ARM7、ARM9、Cortex-M0、Cortex-M1、Cortex-M2、Cortex-M3、Cortex-R4等ARM处理器的嵌入式软件开发工具。MDK-ARM 集成了业内最领先的技术,包括 uVision4 集成开发环境与 RealView 编译器RVCT。支持 ARM7、ARM9 和最新的Cortex-M3/M1/M0 核处理器,自动配置启动代码,集成 Flash 烧写模块,强大的 Simulation 设备模拟,性能分析等功能,与 ARM 之前的工具包 ADS 等相比,RealView 编译器的最新版本可将性能改善超过 20%。
1.2 MDK安装—Keil
1.2.1 安装前先了解注意
- 1 安装路径为英文路径(不要是中文路径)。
- 2 系统用户名不能为中文 。
- 3 多个版本MDK(Keil)不要安装在同一目录。
- 4 MDK5需要加载芯片对应的支持包。
1.2.2 找到keiI软件包安装
鼠标右键选择以管理员权限运行。点击next,直到安装结束。
安装完成后在桌面会出现keil5软件图标:
然后再安装相应的芯片支持包:我们用的是stm32f103所以安装1xx系列的支持包。 聖 Keil.STM32F1 xx_DFP.2.0.0.pack
安装注意事项:
安装完MDK之后,MDK会自动从网站下载相关包描述,如果电脑没有连网,或者网速较慢,就会报错。这是没有问题的,直接关闭即可,手动来加载包即可。
关于怎么破J使用,请各位自己百度或者查看提供的文件教程。
1.3 安装芯片加载的包
安装注意事项
基于不同类型的芯片要加载的包可能不一样。
这些包可以直接点击.pack格式的包(如果有)也可以再mdk里面加载,让MDK从keil官网 http://www.keil.com/dd2/pack下载。
2 keil工程搭建
2.1 目录结构管理
2.2 工程结构管理
2.2.1 打开keil5软件,点击project,
创建新的keil5工程
- 创建新的Project
2.2.2 2 选择芯片型号(stm32f103c8)
2.2.3 管理keil工程内部的目录结构
2.2.4 文件添加
2.3 配置
2.4 最终项目
3 系统架构(中等容量芯片stm32f103c8)
3.1 主系统架构
-
四 个 驱 动 单 元
: -
- Cortex-M3内核
-
Dcode总线
-
Icode总线
- DMA1&DMA2
-
四 个 被 动 单 元
: -
- Internel SRAM
- Internel Flash
- FSMC
- AHB到APB桥
3.2 总线框图
STM32 的系统架构图:
3.3 总线介绍
ICode总线(Instruction Code)
:
该总线将Cortex™-M3内核的指令总线与闪存指令接口相连接。指令预取在此总线上完成
DCode总线 (Data Code)
:
该总线将Cortex™-M3内核的DCode总线与闪存存储器的数据接口相连接(常量加载和调试访问)
系统总线
此总线连接Cortex™-M3内核的系统总线(外设总线)到总线矩阵,总线矩阵协调着内核和DMA间的访问
DMA总线
此总线将DMA的AHB主控接口与总线矩阵相联,总线矩阵协调着CPU的DCode和DMA到SRAM、闪存和外设的访问
总线矩阵
总线矩阵协调内核系统总线和DMA主控总线之间的访问仲裁,仲裁利用轮换算法。在互联型产品中,总线矩阵包含5个驱动部件(CPU的DCode、系统总线、以太网DMA、DMA1总线和DMA2总线)和3个从部件(闪存存储器接口(FLITF)、SRAM和AHB2APB桥)。在其它产品中总线矩阵包含4个驱动部件(CPU的DCode、系统总线、DMA1总线和DMA2总线)和4个被动部件(闪存存储器接口(FLITF)、SRAM、FSMC和AHB2APB桥)。AHB外设通过总线矩阵与系统总线相连,允许DMA访问
AHB/APB桥(APB)
两个AHB/APB桥在AHB和2个APB总线间提供同步连接。APB1操作速度限于36MHz, APB2操作于全速(最高72MHz)。有关连接到每个桥的不同外设的地址映射请参考表1。在每一次复位以后,所有除SRAM 和FLITF以外的外设都被关闭,在使用一个外设之前,必须设置寄存器RCC_AHBENR来打开该外设的时钟
4 存储器结构
程序存储器
、数据存储器
、寄存器
和输入输出端口
,被组织在同一个4G的线性地址空间中。可以通过地址的方法访问对应的存储器或寄存器。
5 启动模式
我们使用的stm32f103c8核心板:
boot0 | 0 |
---|---|
boot1 | 0 |
启动方式
:从内部的Flash中启动存储器映射:
0x0000 0000
—— 0x0800 0000
映射的内部Flash
6 启动文件
cl
:互联型产品,stm32f105/107系列vl
:超值型产品,stm32f100系列xl
:超高密度产品,stm32f101/103系列
flash容量大小:
ld
:小容量产品, 小于64KBmd
:中等容量度产品,64KB和128KB hd:大容量产品,大于128KB
stm32f103C8 :选择的启动文件->`startup_stm32f10x_md.s
7 启动文件分析
上电或按下复位按键,从Reset_Handler 开始执行。
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, = main
BX R0
ENDP
执行的第一个函数:SystemInit
初始化flash接口
初始化设置PLL
初始化设置系统时钟
执行的第二个函数:,main 属于C库函数作用:
完成全局/静态变量的初始化初始化堆栈
库函数的初始化
-
程序的跳转,进入用户的main函数入口
启动代码
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
;AREA 伪指令,用于定义一个代码段、数据段、栈...
;ALIGN=3 ---> 2^3=8 8字节对齐
;STACK 段名
;NOINIT:指定此数据段仅仅保留了内存单元
;READWRITE属性:指定本段为可读可写,数据段的默认属性为READWRITE
====================================================堆、栈=================================================================
Stack_Mem SPACE Stack_Size
;SPACE 用来分配一片连续的存储区域并初始化为0
;Stack_Mem 表示分配0x400个连续字节,并初始化为0
__initial_sp ;表示栈顶地址,汇编代码地址标号
; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Heap_Size EQU 0x00000200
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit ;表示堆空间结束地址
PRESERVE8
THUMB
=================================================中断向量表===============================================================
; 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
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
; External Interrupts
DCD WWDG_IRQHandler ; Window Watchdog
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 Line 0
DCD EXTI1_IRQHandler ; EXTI Line 1
DCD EXTI2_IRQHandler ; EXTI Line 2
DCD EXTI3_IRQHandler ; EXTI Line 3
DCD EXTI4_IRQHandler ; EXTI Line 4
DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1
DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2
DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3
DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4
DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5
DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6
DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7
DCD ADC1_2_IRQHandler ; ADC1 & ADC2
DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX
DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0
DCD CAN1_RX1_IRQHandler ; CAN1 RX1
DCD CAN1_SCE_IRQHandler ; CAN1 SCE
DCD EXTI9_5_IRQHandler ; EXTI Line 9..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 Capture Compare
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 Line 15..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 Capture Compare
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
===================================================Reset_Handler=================================================================
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
8 STM32 开发基础知识
8.1 学习 位
操作
学过 C 语言的人都不陌生了,就是对基本类型变量可以在位级别进行操作。C 语言支持如下 6 中位操作:
运算符 | 含义 | 运算符 | 含义 | |
---|---|---|---|---|
& | 按位与 | ~ | 取反 | |
\ | 按位或 | << | 左移 | |
^ | 按位异或 | >> | 右移 |
下面我们想着重讲解位操作在单片机开发中的一些实用技巧。
8.1.1 不改变其他位的值的状况下,对某几个位进行设值。
位 操作在单片机开发中经常使用,方法就是先对需要设置的位用&操作符进行清零操作,然后用|
操作符设值。比如我要改变 GPIOA 的状态,可以先对寄存器的值进行&清零操作。
GPIOA->CRL&=0XFFFFFF0F; //将第 4-7 位清 0
然后再与需要设置的值进行|或运算
GPIOA->CRL|=0X00000040; //设置相应位的值,不改变其他位的值
8.2.2 移位操作提高代码的可读性。
移位操作在单片机开发中也非常重要,下面让我们看看固件库的 GPIO 初始化的函数里面的一行代码
GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
8.2.3 ~
取反操作使用技巧
SR 寄存器的每一位都代表一个状态,某个时刻我们希望去设置某一位的值为 0,同时其他位都保留为 1,简单的作法是直接给寄存器设置一个值:
TIMx->SR=0xFFF7;
这样的作法设置第 3 位为 0,但是这样的作法同样不好看,并且可读性很差。看看库函数代码中怎样使用的:
TIMx->SR = (uint16_t)~TIM_FLAG;
而 TIM_FLAG 是通过宏定义定义的值:
#define TIM_FLAG_Update ((uint16_t)0x0001)
#define TIM_FLAG_CC1 ((uint16_t)0x0002)
看这个应该很容易明白,可以直接从宏定义中看出 TIM_FLAG_Update 就是设置的第 0 位了,可读性非常强。
8.3 define宏定义
define 是 C 语言中的预处理命令,它用于宏定义,可以提高源代码的可读性,为编程提供方便。常见的格式:
#define 标识符 字符串
“标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。例如:
#define SYSCLK_FREQ_72MHz 72000000
8.4 ifdef 条件编译
单片机程序开发过程中,经常会遇到一种情况,当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。条件编译命令最常见的形式为:
#ifdef 标识符
程序段 1
#else
程序段 2
#endif
它的作用是:当标识符已经被定义过(一般是用#define 命令定义),则对程序段 1 进行编译,否则编译程序段 2。 其中#else 部分也可以没有,即:
#ifdef
程序段 1
#endif
这个条件编译在 MDK 里面是用得很多的,在 stm32f10x.h 这个头文件中经常会看到这样的语句:
#ifdef STM32F10X_HD
大容量芯片需要的一些变量定义
#end
8.5 extern 变量申明
C 语言中 extern 可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。这里面要注意,对于 extern 申明变量可以多次,但定义只有一次。在我们的代码中你会看到看到这样的语句:
extern u16 USART_RX_STA;
这个语句是申明 USART_RX_STA 变量在其他文件中已经定义了,在这里要使用到。所以,你肯定可以找到在某个地方有变量定义的语句:
u16 USART_RX_STA;
8.6 typedef 类型别名
typedef 用于为现有类型创建一个新的名字,或称为类型别名,用来简化变量的定义。typedef 在 MDK 用得最多的就是定义结构体的类型别名和枚举类型了。
struct _GPIO
{
__IO uint32_t CRL;
__IO uint32_t CRH;
…
}
定义了一个结构体 GPIO,这样我们定义变量的方式为:
struct _GPIO GPIOA;//定义结构体变量 GPIOA
但是这样很繁琐,MDK 中有很多这样的结构体变量需要定义。这里我们可以为结体定义一个别名 GPIO_TypeDef,这样我们就可以在其他地方通过别名 GPIO_TypeDef 来定义结构体变量了。方法如下:
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
…
} GPIO_TypeDef;
Typedef 为结构体定义一个别名 GPIO_TypeDef,这样我们可以通过 GPIO_TypeDef 来定义结构体变量:
GPIO_TypeDef _GPIOA,_GPIOB;
8.7 结构体
经常很多用户提到,他们对结构体使用不是很熟悉,但是 MDK 中太多地方使用结构体以及结构体指针,这让他们一下子摸不着头脑,学习 STM32 的积极性大大降低,其实结构体并不是那么复杂,这里我们稍微提一下结构体的一些知识,还有一些知识我们会在下一节的“寄存器地址名称映射分析”中讲到一些。声明结构体类型:
Struct 结构体名{
成员列表;
}变量名列表;
在结构体申明的时候可以定义变量,也可以申明之后定义,方法是:
Struct 结构体名字 结构体变量列表 ;
通过使用结构体组合参数,可以提高代码的可读性。
9 代码下载
STM32F103C8T6烧录下载方法大体上有三种,分别为JTAG下载,SWD下载,以及串口下载。
JTAG下载,JTAG全名为Joint Test Action Group,即为联合测试行动小组,是一种国际标准测试协议。JTAG下载是需要依靠烧录器的,标准的JTAG接口是4线的,包括JTMS, JTCK, JTDI, JTDO,它们的功能分别是模式选择,时钟输入,数据输入和数据输出,烧录器上的引脚对应连接到STM32F103C8T6的I/O口分别为PA13,PA14,PA15,PB3,加上接VCC和GND,所以JTAG接口最少需要6个引脚。同时单片机的BOOT0,BOOT1引脚要接地,具体原因在本节后面详述。
SWD下载,SWD全名为Serial Wire Debug,即为串行调试接口。SWD下载是需要依靠烧录器的,SWD接口是2线的,包括SWDIO,SWCLK,它们的功能分别是数据输入输出和时钟输入,烧录器上的引脚对应对应连接到STM32F103C8T6的I/O口分别为PA13,PA14,加上接VCC和GND,所以SWD接口最少需要4个引脚。同时单片机的BOOT0,BOOT1引脚要接地,具体原因在本节后面详述。
串口下载,通过USART进行烧录下载。串口下载是不需要依靠烧录器的,它直接通过安卓线连接电脑和单片机,其中STM32F103C8T6与电脑相连接的串口为USART1,对应的I/O口为PA9(TX),PA10(RX),一般在开发板上会使用串口下载,简单方便。同时单片机的BOOT0引脚要接高电平,BOOT1引脚要接低电平,具体原因在本节后面详述。
9.1 CH340 串口下载
安装CH340驱动
-
1.先在电脑安装好CH340的驱动
-
2.打开STM ISP 下载器MCUISP,并选择好要下载的hex文件
-
3.将USB 转 TTL下载器(CH340)的TXD、RXD、GND与开发板的RXD、TXD、GND连接好,下载器另一头插电脑
-
- 开发板的BOOT0要用跳冒跳到1端
开发板上有两个跳帽来调整BOOT0和BOOT1的状态。跳帽跳到上边是选择1也就是高电平,跳到下边是选择0也就是低电平BOOT0 和 BOOT1 是用于设置 STM32 的启动方式的
9.2 ST-link 下载
ST-Link v2是STM8、STM32系列单片机的在线仿真器和下载器。STM8采用SWIM接口模式。STM32采用的是SWD接口模式,因此ST-Link出生就带有两种接口模式。
ST-Link V2是支持STM32家族所有芯片型号的存在。因为它的独特功能,使得它比jlink ob要全一点,比如H7系列的STM32,ob款就无法支持。
前端(电脑端) USB接口
后端(mcu端)排针接口10根;有5V、3.3V供电
SWD接线 ( for STM32 ):3.3V、GND、SWDIO、SWCLK
- 1.先安装好ST-Link的驱动,64位的电脑选择amd64.exe的安装程序,一直下一步
- 2.驱动安装成功
-
3.将ST-Link V2插入电脑,在设备管理里可以看到ST-Link的串行口,表示驱动没问题
-
4.将ST-Link V2的SWDIO、GND、SWCLK、3.3V接到开发板的DIO、GND、CLK、3.3引脚上,有些板的丝印标法不同,但都能对应的看出来,然后将ST-Link插电脑上
-
5.打开keil软件,并打开一个工程,然后打开魔术棒,选择Debug,选择ST-Link Debugger,再点击Settings
- 6.Port默认是JTAG的,改为SW
-
7.在Flash Download界面里,首先下载功能的三个勾要勾上,Reset and Run默认是没有勾的,这个是下载后重启单片机并运行程序,所以有时候下载成功了没看到现象,可能是这里没有勾上;点击Add,看手上的开发板是什么型号,手上的是STM32F103C8T6,就找到STM32F10x系列的,选择中容量Med-density,128k的,最后点击确定
-
8.回到打开魔术棒的界面,点击Utilities,选择ST-Link Debugger,点击OK
- 9.回到编程界面,先编译一下程序,确保编译通过,然后点击下载选项
*10.看到Verify OK,则表示程序下载成功,开发板LED灯闪烁
评论(0)
您还未登录,请登录后发表或查看评论