STM32F103寄存器方式点亮LED流水灯
一、地址映射和寄存器映射
1.1 总线基地址
总线名称 | 总线基地址 | 相对外设基地址的偏移 |
---|---|---|
APB1 | 0x4000 0000 | 0x0 |
APB2 | 0x4001 0000 | 0x0001 0000 |
AHB | 0x4001 8000 | 0x0001 8000 |
1.2 外设基地址
外设名称 | 外设基地址 | 相对 APB2 总线的地址偏移 |
---|---|---|
GPIOA | 0x4001 0800 | 0x0000 0800 |
GPIOB | 0x4001 0C00 | 0x0001 0000 |
GPIOC | 0x4001 1000 | 0x0001 8000 |
GPIOD | 0x4001 1400 | 0x0000 1400 |
GPIOE | 0x4001 1800 | 0x0000 1800 |
GPIOF | 0x4001 1C00 | 0x0000 1C00 |
GPIOG | 0x4001 2000 | 0x0000 2000 |
1.3 外设寄存器地址
stm32最小板仅有ABC三个引脚系列。0-7位为CRL,8-15位为CRH,相对基址的偏移即从基址开始多少十六进制后是此寄存器地址的开始,不同的寄存器有不同的地址,需要去访问该地址,就需要查表。
时钟寄存器在APB2线
因此后续代码都是0x00000002对应的是0010
高位同理。
二、搭建电路
2.1 接线
(1)将串口USB转TTL线与stm32核心板连接如图所示
(2)BOOT0 与 BOOT1 配置启动方式
(3) 程序下载
(4)实际连线图
2.2 用C语言寄存器实现流水灯
(1) main.c
#define RCC_AP2ENR *((unsigned volatile int*)0x40021018)
//----------------GPIOA配置寄存器 ------------------------
#define GPIOA_CRH *((unsigned volatile int*)0x40010804)
#define GPIOA_ORD *((unsigned volatile int*)0x4001080C)
//----------------GPIOB配置寄存器 ------------------------
#define GPIOB_CRL *((unsigned volatile int*)0x40010C00)
#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++);
}
//------------------------主函数--------------------------
int main()
{
int j=100;
RCC_AP2ENR|=1<<2; //APB2-GPIOA外设时钟使能
RCC_AP2ENR|=1<<3; //APB2-GPIOB外设时钟使能
RCC_AP2ENR|=1<<4; //APB2-GPIOC外设时钟使能
//这两行代码可以合为 RCC_APB2ENR|=1<<3|1<<4;
GPIOA_CRH&=0xFFF0FFFF; //设置位 清零
GPIOA_CRH|=0x00020000; //PA12推挽输出
GPIOA_ORD|=1<<12; //设置初始灯为亮
GPIOB_CRL&=0xFFFFFF0F; //设置位 清零
GPIOB_CRL|=0x00000020; //PB1推挽输出
GPIOB_ORD|=1<<1; //设置初始灯为灭
GPIOC_CRH&=0xF0FFFFFF; //设置位 清零
GPIOC_CRH|=0x02000000; //PC14推挽输出
GPIOC_ORD|=1<<14; //设置初始灯为灭
while(j)
{
GPIOA_ORD=0x1<<12; //PA12高电平
Delay_ms(3000000);
GPIOA_ORD=0x0<<12; //PA12低电平
Delay_ms(3000000);
GPIOB_ORD=0x1<<1; //PB1高电平
Delay_ms(3000000);
GPIOB_ORD=0x0<<1; //PB1低电平
Delay_ms(3000000);
GPIOC_ORD=0x1<<14; //PC14高电平
Delay_ms(3000000);
GPIOC_ORD=0x0<<14; //PC14低电平
Delay_ms(3000000);
}
}
使用高电平点亮低电平熄灭
(2)接入设备
(3)编译生成hex文件
(4)用mcuisp烧入stm32如·2.1(3)
2.3 汇编语言实现流水灯
(1)汇编代码
RCC_APB2ENR EQU 0x40021018
GPIOA_CRH EQU 0x40010804
GPIOA_ODR EQU 0x4001080C
GPIOB_CRL EQU 0x40010C00 ;寄存器映射
GPIOB_ODR EQU 0x40010C0C
GPIOC_CRH EQU 0x40011004
GPIOC_ODR EQU 0x4001100C
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
;NOINIT: = NO Init,不初始化。READWRITE : 可读,可写。ALIGN =3 : 2^3 对齐,即8字节对齐。
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
MainLoop BL LED2_Init
BL LED2_ON
BL Delay ;LED2灯闪烁
BL LED2_OFF
BL Delay
BL LED1_Init
BL LED1_ON
BL Delay ;LED1灯闪烁
BL LED1_OFF
BL Delay
BL LED3_Init
BL LED3_ON
BL Delay ;LED3灯闪烁
BL LED3_OFF
BL Delay
B MainLoop
LED1_Init
PUSH {R0,R1, LR} ;R0,R1,LR中的值放入堆栈
LDR R0,=RCC_APB2ENR ;LDR是把地址装载到寄存器中(比如R0)。
ORR R0,R0,#0x08 ;开启端口GPIOB的时钟,ORR 按位或操作,01000将R0的第二位置1,其他位不变
LDR R1,=RCC_APB2ENR
STR R0,[R1] ;STR是把值存储到寄存器所指的地址中,将r0里存储的值给rcc寄存器
;上面一部分汇编代码是控制时钟的
LDR R0,=GPIOB_CRL
ORR R0,R0,#0X00000020 ;GPIOB_Pin_1配置为通用推挽输出;开启的是pb1,所以是2,为0010,是推挽输出模式,最大速度为2mhz
LDR R1,=GPIOB_CRL
STR R0,[R1]
LDR R0,=GPIOB_ODR
BIC R0,R0,#0X00000002 ;BIC 先把立即数取反,再按位与
LDR R1,=GPIOB_ODR ;GPIO_Pin_1输出为0;由r1控制ord寄存器
STR R0,[R1] ;将ord寄存器的值变为r0的值
POP {R0,R1,PC} ;将栈中之前存的R0,R1,LR的值返还给R0,R1,PC
LED1_OFF
PUSH {R0,R1, LR}
LDR R0,=GPIOB_ODR
BIC R0,R0,#0X00000002 ;因为是pb1所以对应二进制0010;GPIO_Pin_1输出为0,LED1熄灭
LDR R1,=GPIOB_ODR
STR R0,[R1]
POP {R0,R1,PC}
LED1_ON
PUSH {R0,R1, LR}
LDR R0,=GPIOB_ODR
ORR R0,R0,#0X00000002 ;GPIO_Pin_1输出为1,LED1亮
LDR R1,=GPIOB_ODR
STR R0,[R1]
POP {R0,R1,PC}
LED2_Init
PUSH {R0,R1, LR};R0,R1,LR中的值放入堆栈
LDR R0,=RCC_APB2ENR
ORR R0,R0,#0x04 ;打开GPIOA的时钟
LDR R1,=RCC_APB2ENR
STR R0,[R1]
LDR R0,=GPIOA_CRH
ORR R0,R0,#0X00020000 ;GPIOA_Pin_12配置为通用推挽输出
LDR R1,=GPIOA_CRH
STR R0,[R1]
LDR R0,=GPIOA_ODR
BIC R0,R0,#0X00001000
LDR R1,=GPIOA_ODR ;GPIOA_Pin_12输出为0
STR R0,[R1]
POP {R0,R1,PC}
LED2_OFF
PUSH {R0,R1, LR}
LDR R0,=GPIOA_ODR
BIC R0,R0,#0X00001000 ;GPIOA_Pin_12输出为0,LED2熄灭
LDR R1,=GPIOA_ODR
STR R0,[R1]
POP {R0,R1,PC}
LED2_ON
PUSH {R0,R1, LR}
LDR R0,=GPIOA_ODR
ORR R0,R0,#0X00001000 ;GPIOA_Pin_12输出为1,LED2亮
LDR R1,=GPIOA_ODR
STR R0,[R1]
POP {R0,R1,PC}
LED3_Init
PUSH {R0,R1, LR}
LDR R0,=RCC_APB2ENR
ORR R0,R0,#0x10 ;打开GPIOC的时钟
LDR R1,=RCC_APB2ENR
STR R0,[R1]
LDR R0,=GPIOC_CRH
ORR R0,R0,#0X02000000 ;GPIOC_Pin_14配置为通用推挽输出
LDR R1,=GPIOC_CRH
STR R0,[R1]
LDR R0,=GPIOC_ODR
BIC R0,R0,#0X00004000 ;GPIOC_Pin_14输出为0
LDR R1,=GPIOC_ODR
STR R0,[R1]
POP {R0,R1,PC}
LED3_OFF
PUSH {R0,R1, LR}
LDR R0,=GPIOC_ODR
BIC R0,R0,#0X00004000 ;GPIOC_Pin_14输出为0,LED3熄灭
LDR R1,=GPIOC_ODR
STR R0,[R1]
POP {R0,R1,PC}
LED3_ON
PUSH {R0,R1, LR}
LDR R0,=GPIOC_ODR
ORR R0,R0,#0X00004000 ;GPIOC_Pin_14输出为1,LED3亮
LDR R1,=GPIOC_ODR
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,#300
BCC DelayLoop0
MOVS R0,#0
ADDS R1,R1,#1
CMP R1,#300
BCC DelayLoop0
MOVS R0,#0
MOVS R1,#0
ADDS R2,R2,#1
CMP R2,#15
BCC DelayLoop0
POP {R0,R1,PC}
END
(2)添加工程文件
(3)进行编译生成hex文件
(4)烧录程序
(5)结果
三、总结
通过查阅资料与请教,慢慢的理解了stm32的工作原理,以及串行口的转换关系,基本掌握了软硬件结合的实验,实验过程与原理清晰容易理解的。
参考文献
STM32寄存器的简介、地址查找,与直接操作寄存器
STM32从地址到寄存器
STM32F103寄存器方式点亮LED流水灯