目录
一、STM32F103系列芯片的地址映射和寄存器映射原理
1.地址映射和寄存器映射原理
存储器本身不具有地址信息,它的地址是由芯片厂商或用户分配,给存储器分配地址的过程就称为存储器映射,具体见下图。如果给存储器再分配一个地址就叫存储器重映射。
在存储器 Block2 这块区域,设计的是片上外设,它们以四个字节为一个单元,共 32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过 C 语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。
2.如何找到某个寄存器的地址
这里我们以“GPIO 端口置位/复位寄存器”为例
(1)寄存器说明中首先列出了该寄存器中的名称,“(GPIOx_BSRR)(x=A…E)”这段的意思是该寄存器名为“GPIOx_BSRR”其中的“x”可以为 A-E,也就是说这个寄存器说明适 用于 GPIOA、GPIOB 至 GPIOE,这些 GPIO 端口都有这样的一个寄存器。
(2)偏移地址是指本寄存器相对于这个外设的基地址的偏移。本寄存器的偏移地址是 0x18, 从参考手册中我们可以查到 GPIOA 外设的基地址为 0x4001 0800 ,我们就可以算出 GPIOA 的这个 GPIOA_BSRR 寄存器的地址为:0x4001 0800+0x18 ;同理,由于 GPIOB 的外设基 地址为 0x4001 0C00,可算出 GPIOB_BSRR 寄存器的地址为:0x4001 0C00+0x18 。其他 GPIO 端口以此类推即可。
另外,可参考以下博客:
二、GPIO端口的初始化设置步骤
1.基础知识
GPIO的工作模式主要有八种:4种输入方式,4种输出方式,分别为输入浮空,输入上拉,输入下拉,模拟输入;输出方式为开漏输出,开漏复用输出,推挽输出,推挽复用输出。
(1)GPIO_Mode_AIN 模拟输入 (应用ADC模拟输入,或者低功耗下省电)
(2)GPIO_Mode_IN_FLOATING 浮空输入 (浮空就是浮在半空,可以被其他物体拉上或者拉下,可以用于按键输入)
(3)GPIO_Mode_IPD 下拉输入 (IO内部下拉电阻输入)
(4)GPIO_Mode_IPU 上拉输入 (IO内部上拉电阻输入)
(5)GPIO_Mode_Out_OD 开漏输出(开漏输出:输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行)
(6)GPIO_Mode_Out_PP 推挽输出 (推挽就是有推有拉电平都是确定的,不需要上拉和下拉,IO输出0-接GND, IO输出1 -接VCC,读输入值是未知的 )
(7)GPIO_Mode_AF_OD 复用开漏输出(片内外设功能(I2C的SCL,SDA))
(8)GPIO_Mode_AF_PP 复用推挽输出 (片内外设功能(TX1,MOSI,MISO.SCK.SS))
2. GPIO初始化步骤
第一步:使能GPIOx口的时钟
第二步:指明GPIOx口的哪一位,这一位的速度大小以及模式。
第三步:调用GPIOx口初始化函数,进行初始化。
第四步:调用GPIO-SetBits函数,进行相应为的置位。
3.实例
对于单个GPIO口的初始化如下
GPIO_InitTypeDef GPIO_InitStructure;
第一步:使能GPIOA的时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);第二步:设置GPIOA参数:输出OR输入,工作模式,端口翻转速率
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_6| GPIO_Pin_7| GPIO_Pin_8; //设定要操作的管脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz第三步:调用GPIOA口初始化函数,进行初始化。
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA第四步:调用GPIO-SetBits函数,进行相应为的置位。
GPIO_SetBits(GPIOA,GPIO_Pin_0); //输出高
对于多个GPIO口的初始化如下
GPIO_InitTypeDef GPIO_InitStructure;
第一步:使能GPIOA,GPIOE的时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);第二步:设置GPIOA,GPIOE参数:输出OR输入,工作模式,端口翻转速率
第三步:调用GPIOA口初始化函数,进行初始化。
第四步:调用GPIO-SetBits函数,进行相应为的置位。把第二、三、四步合并分别设置GPIOA和GPIOE
先设置GPIOA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; // 第四个口,PA4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
GPIO_Init(GPIOA,&GPIO-InitST); //根据设定参数初始化GPIOA
GPIO_SetBits(GPIOA,GPIO_Pin_4); //输出高再设置GPIOE
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; // 第三个口,PE3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
GPIO_Init(GPIOE,&GPIO-InitST); //根据设定参数初始化GPIOE
GPIO_SetBits(GPIOE,GPIO_Pin_3); //输出高
三、 创建项目
1.寄存器起始地址
由上图可知RCC与GPIO各端口寄存器的起始地址。
2.外设时钟使能寄存器
如下图,外设时钟使能寄存器偏移地址为0x18,而在前表可以看到起始地址为0x4002 1000,偏移地址为0x18,所以该寄存器的地址为0x4002 1018。
此次点亮LED灯使用GPIOA,GPIOB,GPIOC三个端口,当其位置为1时,时钟开启,以GPIOA为例,代码如下:
#define RCC_AP2ENR *((unsigned volatile int*)0x40021018) //APB2使能时钟寄存器
RCC_AP2ENR|=1<<2; //APB2-GPIOA外设时钟使能
3.端口配置寄存器
端口配置寄存器分为为端口配置低寄存器(CRL)和端口配置高寄存器(CRH),低寄存器的偏移地址为0x00,高寄存器的偏移地址为0x04,如下图:
此次点亮LED灯使用PA5,PB8,PC14三个端口,以PA5为例,相应端口配置器GPIOA_CRL地址为GPIOA的起始地址址+偏移地址,为0x40010800。设置推挽输出并设置最大速度为2Mhz,即使23、22、21、20位的值为0010,十六进制为0x20000000,代码如下:
#define GPIOA_CRL *((unsigned volatile int*)0x40010800)
GPIOA_CRL|=0x00200000; //PA5推挽输出
PB8,PC14同理可得。
4.端口输出寄存器
如下图,偏移地址为0Ch,所以该寄存器的地址等于端口的起始地址+偏移地址,在相应的位赋值0为低电压,1为高电压。
以PA5为例,输出高电压,就需要在第5位赋1,代码如下:
#define GPIOA_ORD *((unsigned volatile int*)0x4001080C)
GPIOA_ORD|=1<<5; //设置初始灯为灭
5.创建项目
创建项目过程与之前一样,参考此文章:(1条消息) 基于MDK创建纯汇编语言的STM32工程_XTF201029的博客-CSDN博客https://blog.csdn.net/XTF201029/article/details/120563912其中选择芯片时,选择STM32F108C8芯片。
注:需点击魔法棒,要在output里勾选create hex file!
四、程序代码
//APB2使能时钟寄存器
#define RCC_AP2ENR *((unsigned volatile int*)0x40021018)
//GPIOA配置寄存器
#define GPIOA_CRL *((unsigned volatile int*)0x40010800)
#define GPIOA_ORD *((unsigned volatile int*)0x4001080C)
//GPIOB配置寄存器
#define GPIOB_CRH *((unsigned volatile int*)0x40010C04)
#define GPIOB_ORD *((unsigned volatile int*)0x40010C0C)
//GPIOC配置寄存器
#define GPIOC_CRH *((unsigned volatile int*)0x40011004)
#define GPIOC_ORD *((unsigned volatile int*)0x4001100C)
//延时函数
void Delay_ms( volatile unsigned int t)
{
unsigned int i;
while(t--)
for (i=0;i<800;i++);
}
void LIGHT_A(){
GPIOA_ORD&=~(1<<5); //PA5低电平
GPIOB_ORD|=1<<8; //PB8高电平
GPIOC_ORD|=1<<14; //PC14高电平
}
void LIGHT_B(){
GPIOA_ORD|=1<<5; //PA5高电平
GPIOB_ORD&=~(1<<8); //PB8低电平
GPIOC_ORD|=1<<14; //PC14高电平
}
void LIGHT_C(){
GPIOA_ORD|=1<<5; //PA5高电平
GPIOB_ORD|=1<<8; //PB8高电平
GPIOC_ORD&=~(1<<14); //PC14低电平
}
//主函数
int main()
{
RCC_AP2ENR|=1<<2; //APB2-GPIOA外设时钟使能
RCC_AP2ENR|=1<<3; //APB2-GPIOB外设时钟使能
RCC_AP2ENR|=1<<4; //APB2-GPIOC外设时钟使能
GPIOA_CRL&=0xFF0FFFFF; //设置位清零
GPIOA_CRL|=0x00200000; //PA5推挽输出
GPIOA_ORD|=1<<5; //设置初始灯为灭
GPIOB_CRH&=0xFFFFFFF0; //设置位清零
GPIOB_CRH|=0x00000002; //PB8推挽输出
GPIOB_ORD|=1<<8; //设置初始灯为灭
GPIOC_CRH&=0xF0FFFFFF; //设置位清零
GPIOC_CRH|=0x02000000; //PC14推挽输出
GPIOC_ORD|=1<<14; //设置初始灯为灭
while(1)
{
LIGHT_A();
Delay_ms(10000000);
LIGHT_B();
Delay_ms(10000000);
LIGHT_C();
Delay_ms(10000000);
}
}
五、电路连接
对于USB转TTL模块和stm32f103c8t6连接,参考下图:
电路连接如下图:
六、烧录
代码编译成功后,就会在object文件夹下产生hex文件,要将hex文件烧录到板子上。
打开FLYMCU,烧录步骤如下图:
注:有串口之后要把板子上的boot0置1,boot1置0,并且要按下reset键
七、运行结果
八、汇编语言
1.新建工程
此处不选择startup,如下图:
2.程序代码
RCC_APB2ENR EQU 0x40021018
GPIOC_CRH EQU 0x40011004
GPIOC_ORD EQU 0x4001100c
GPIOA_CRL EQU 0x40010800
GPIOA_ORD EQU 0x4001080C
GPIOB_CRH EQU 0x40010C04
GPIOB_ORD EQU 0x40010C0C
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
AREA RESET, DATA, READONLY
__Vectors DCD __initial_sp
DCD Reset_Handler
AREA |.text|, CODE, READONLY
THUMB
REQUIRE8
PRESERVE8
ENTRY
Reset_Handler
bl LED_Init
MainLoop BL LED_ON_C
BL Delay
BL LED_OFF_C
BL Delay
BL LED_ON_A
BL Delay
BL LED_OFF_A
BL Delay
BL LED_ON_B
BL Delay
BL LED_OFF_B
BL Delay
B MainLoop
LED_Init
PUSH {R0,R1, LR}
LDR R0,=RCC_APB2ENR
ORR R0,R0,#0x1c
LDR R1,=RCC_APB2ENR
STR R0,[R1]
LDR R0,=GPIOA_CRL
BIC R0,R0,#0xff0fffff
LDR R1,=GPIOA_CRL
STR R0,[R1]
LDR R0,=GPIOA_CRL
ORR R0,#0x00200000
LDR R1,=GPIOA_CRL
STR R0,[R1]
MOV R0,#0x20;
LDR R1,=GPIOA_ORD
STR R0,[R1]
LDR R0,=GPIOB_CRH
BIC R0,R0,#0xfffffff0
LDR R1,=GPIOB_CRH
STR R0,[R1]
LDR R0,=GPIOB_CRH
ORR R0,#0x00000002
LDR R1,=GPIOB_CRH
STR R0,[R1]
MOV R0,#0x100
LDR R1,=GPIOB_ORD
STR R0,[R1]
LDR R0,=GPIOC_CRH
BIC R0,R0,#0xf0ffffff
LDR R1,=GPIOC_CRH
STR R0,[R1]
LDR R0,=GPIOC_CRH
ORR R0,#0x02000000
LDR R1,=GPIOC_CRH
STR R0,[R1]
MOV R0,#0x4000
LDR R1,=GPIOC_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_ON_A
PUSH {R0,R1, LR}
MOV R0,#0x00
LDR R1,=GPIOA_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_OFF_A
PUSH {R0,R1, LR}
MOV R0,#0x20
LDR R1,=GPIOA_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_ON_B
PUSH {R0,R1, LR}
MOV R0,#0x000
LDR R1,=GPIOB_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_OFF_B
PUSH {R0,R1, LR}
MOV R0,#0x100
LDR R1,=GPIOB_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_ON_C
PUSH {R0,R1, LR}
MOV R0,#0x0000
LDR R1,=GPIOC_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_OFF_C
PUSH {R0,R1, LR}
MOV R0,#0x4000
LDR R1,=GPIOC_ORD
STR R0,[R1]
POP {R0,R1,PC}
Delay
PUSH {R0,R1, LR}
MOVS R0,#0
MOVS R1,#0
MOVS R2,#0
DelayLoop0
ADDS R0,R0,#1
CMP R0,#330
BCC DelayLoop0
MOVS R0,#0
ADDS R1,R1,#1
CMP R1,#330
BCC DelayLoop0
MOVS R0,#0
MOVS R1,#0
ADDS R2,R2,#1
CMP R2,#15
BCC DelayLoop0
POP {R0,R1,PC}
NOP
END
3.烧录
烧录与之前一致
4.运行结果
九、总结
此次用STM32F103点亮流水灯是的我学习了许多知识,也对STM32有了更多的理解。完成过程中,我遇到了许多困难,例如最开始对STM32地址映射与寄存器映射的原理不太理解,不懂如何对各个端口进行置位去控制流水灯,也不太熟悉电路该如何接线。但通过查看STM32参考手册,百度查阅资料以及参考大佬的文章都解决了这些问题。在实现流水灯亮灯后还是感觉成就满满的!
十、参考资料
(1条消息) 【嵌入式07】寄存器映射原理详解,GPIO端口的初始化设置步骤_噗噗的罐子博客-CSDN博客https://blog.csdn.net/qq_46467126/article/details/120737655?utm_source=app&app_version=4.16.0&code=app_1562916241&uLinkId=usr1mkqgl919ble(1条消息) STM32中文参考手册_V10_mshlc0728的博客-CSDN博客_stm32中文参考手册v10https://blog.csdn.net/mshlc0728/article/details/109476897?utm_source=app&app_version=4.16.0&code=app_1562916241&uLinkId=usr1mkqgl919blen(1条消息) STM32寄存器点亮流水灯的三种方法_gelad_w的博客-CSDN博客https://blog.csdn.net/gelad_w/article/details/115555631?utm_source=app&app_version=4.16.0&code=app_1562916241&uLinkId=usr1mkqgl919blen(1条消息) STM32F103C8T6实现流水灯(c语言和汇编两个版本)_junseven164的博客-CSDN博客https://blog.csdn.net/junseven164/article/details/120804940?utm_source=app&app_version=4.16.0&code=app_1562916241&uLinkId=usr1mkqgl919blen【STM系列】 流水灯 (代码 + Protuse 仿真)_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1qK4y1h7EM?p=1&share_medium=android&share_plat=android&share_session_id=b3cea61f-3379-449a-9916-b92caa0d0916&share_source=COPY&share_tag=s_i×tamp=1635067651&unique_k=lIuUPb