目录
1.4.1 将串口USB转TTL线与stm32核心板连接如图所示
1.4.2 并且要设.BOOT0 与 BOOT1 配置启动方式。
一、初始化
1.地址映射和寄存器映射
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 | 0x0000 0C00 |
GPIOC | 0x4001 1000 | 0x0000 1000 |
GPIOD | 0x4001 1400 | 0x0000 1400 |
GPIOE | 0x4001 1800 | 0x0000 1800 |
GPIOF | 0x4001 1C00 | 0x0000 1C00 |
GPIOG | 0x4001 2000 | 0x0000 2000 |
1.3 外设寄存器地址
本文要用到GPIOA,GPIOB,GPIOC。
下列表格是通过查表得出如果需要了解寄存器地址原理请访问STM32寄存器的简介、地址查找,与直接操作寄存器_geekYatao-CSDN博客_stm32寄存器
STM32从地址到寄存器_geekYatao-CSDN博客_stm32寄存器地址
寄存器名称 | 寄存器地址 | 相对 GPIOA 基址的偏移 |
---|---|---|
GPIOA_CRL | 0x4001 0800 | 0x00 |
GPIOA_CRH | 0x4001 0804 | 0x04 |
GPIOA_IDR | 0x4001 0808 | 0x08 |
GPIOA_ODR | 0x4001 080C | 0x0C |
GPIOA_BSRR | 0x4001 0810 | 0x10 |
GPIOA_BRR | 0x4001 0814 | 0x14 |
GPIOA_LCKR | 0x4001 0818 | 0x18 |
寄存器名称 | 寄存器地址 | 相对 GPIOB 基址的偏移 |
---|---|---|
GPIOB_CRL | 0x4001 0C00 | 0x00 |
GPIOB_CRH | 0x4001 0C04 | 0x04 |
GPIOB_IDR | 0x4001 0C08 | 0x08 |
GPIOB_ODR | 0x4001 0C0C | 0x0C |
GPIOB_BSRR | 0x4001 0C10 | 0x10 |
GPIOB_BRR | 0x4001 0C14 | 0x14 |
GPIOB_LCKR | 0x4001 0C18 | 0x18 |
寄存器名称 | 寄存器地址 | 相对 GPIOC 基址的偏移 |
---|---|---|
GPIOC_CRL | 0x4001 1000 | 0x00 |
GPIOC_CRH | 0x4001 1004 | 0x04 |
GPIOC_IDR | 0x4001 1008 | 0x08 |
GPIOC_ODR | 0x4001 100C | 0x0C |
GPIOC_BSRR | 0x4001 1010 | 0x10 |
GPIOC_BRR | 0x4001 1014 | 0x14 |
GPIOC_LCKR | 0x4001 1018 | 0x18 |
寄存器名称 | 寄存器地址 | 相对 GPIOD 基址的偏移 |
---|---|---|
GPIOD_CRL | 0x4001 1400 | 0x00 |
GPIOD_CRH | 0x4001 1404 | 0x04 |
GPIOD_IDR | 0x4001 1408 | 0x08 |
GPIOD_ODR | 0x4001 140C | 0x0C |
GPIOD_BSRR | 0x4001 1410 | 0x10 |
GPIOD_BRR | 0x4001 0C14 | 0x14 |
GPIOD_LCKR | 0x4001 0C18 | 0x18 |
因为stm32最小板仅有ABC三个引脚系列。0-7位为CRL,8-15位为CRH,相对基址的偏移即从基址开始多少十六进制后是此寄存器地址的开始,不同的寄存器有不同的地址,需要去访问该地址,就需要查表。
时钟寄存器在APB2线
因此后续代码都是0x00000002对应的是0010
高位同理
1.4接线
1.4.1 将串口USB转TTL线与stm32核心板连接如图所示
1.4.2 并且要设.BOOT0 与 BOOT1 配置启动方式。
BOOT1=x | BOOT0=0 | 从用户闪存启动,这是正常的工作模式。 |
BOOT1=0 | BOOT0=1 | (ISP方式)从系统存储器启动,这种模式启动的程序功能由厂家设置。 |
BOOT1=1 | BOOT0=1 | 从内置SRAM启动,这种模式可以用于调试. |
如图所示
1.5 程序下载
1.6 总的实际连线图
因为本文主要用到PA12,PB1,PC14引脚。
二、用C语言寄存器实现流水灯
2.1 工程文件
2.1.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.1.2加入设备
2.2 编译生成hex文件
2.3 用mcuisp烧入stm32如1.5步骤所示。
2.4 实验成果
三、汇编语言实现流水灯
3.1 汇编代码
1.s
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
3.2添加工程文件
因为自带starup启动文件因此不需要添加设备
3.3 进行编译生成hex文件
3.4 烧录程序
3.5实验成果
四、心得
深刻的了解了在动手方面自己的不足,但是通过查阅资料与请教,慢慢的理解了stm32的工作原理,以及串行口的转换关系,真正意义上的掌握了软硬件结合的实验,整个实验操作下来其实也就代码比较的繁琐,而实验过程与原理是很清晰容易理解的。
参考文献