一、简单入门
1.什么是寄存器
①概念 寄存器是中央处理器内的组成部分。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址。 数据寄存器 存放数据,不同的数据存放在不同寄存器里。 指令、地址寄存器 指令、地址寄存器与数据寄存器类似,里边存放的都是0和1,毕竟单片机也只认识机器码,机器码都是0或1,只是特别的规定下,数据寄存器里面存放的0和1表示数据,指令寄存器里存放的表示指令。
2.寄存器寻址
通过查看数据手册,可以找到寄存器地址。 分为以下几步: ①找到GPIOB的基地址 ②找到端口输入寄存器的地址偏移 ③找到数据位置
3.操作寄存器点亮LED
同样分为几步: ①配置时钟使能 ②配置为通用输出 ③点亮LED需要输出低电平 ④使用直接赋值的方式写寄存器的地址 有些抽象对吧?下面会有实例的。
二、使用STM32F103C8芯片
1.打开时钟
①GPIO的地址 ②时钟的地址
即0x40021018,则打开三个IO口的时钟需要将三个位都置1
// 打开时钟
RCC_APB2ENR |= (1<<3); // 打开 GPIOB 时钟
RCC_APB2ENR |= (1<<4); // 打开 GPIOC 时钟
RCC_APB2ENR |= (1<<2); // 打开 GPIOA 时钟
2.初始化
GPIO口有八种模式:
输入浮空 输入上拉 输入下拉 模拟输入 开漏输出 推挽式输出 推挽式复用功能 开漏复用功能
这里使用推挽输出; 端口1-7为低,端口8-15为高。每个引脚由四个位控制。 以GPIOB和0号引脚(B0)为例,将其设置为推挽输出,并设置最大速度为10MHz,则将控制B0的四个位设置为0001;
// 最后四位变为0001
GPIOB_CRL |= (1<<0); // 最后一位变1
GPIOB_CRL &= ~(0xE<<0); // 倒数2、3、4位变0
对于GPIOB的B0、GPIOC的C15、GPIOA的A0,设置如下
// 配置 GPIO 口为推免输出
// GPIOB----最后四位为0001
GPIOB_CRL |= (1<<0); // 最后一位变1
GPIOB_CRL &= ~(0xE<<0); // 倒数2、3、4位变0
// GPIOC----前四位为0001
GPIOC_CRH |= (1<<28); // 第四位变1
GPIOC_CRH &= ~(0xE0000000); // 前三位变0
// GPIOA----最后四位为0001
GPIOA_CRL |= (1<<0); // 最后一位变1
GPIOA_CRL &= ~(0xE<<0); // 倒数2、3、4位变0
3.设置低电平
对于GPIOB的B0、GPIOC的C15、GPIOA的A0,设置如下
GPIOB_ODR &= ~(1<<0); //最后一位变为0
GPIOC_ODR &= ~(1<<15); //倒数16位变为0
GPIOA_ODR &= ~(1<<0); //最后一位变为
三、代码编程
1.创建项目
①创建好项目,要注意芯片选择STM32F103下的STM32F103C8 ②添加一个c文件,注意勾选下面的两个选项
2.C语言实现
源码:
#define GPIOB_BASE 0x40010C00
#define GPIOC_BASE 0x40011000
#define GPIOA_BASE 0x40010800
#define RCC_APB2ENR (*(unsigned int *)0x40021018)
#define GPIOB_CRL (*(unsigned int *)0x40010C00)
#define GPIOC_CRH (*(unsigned int *)0x40011004)
#define GPIOA_CRL (*(unsigned int *)0x40010800)
#define GPIOB_ODR (*(unsigned int *)0x40010C0C)
#define GPIOC_ODR (*(unsigned int *)0x4001100C)
#define GPIOA_ODR (*(unsigned int *)0x4001080C)
void SystemInit(void);
void Delay_ms(volatile unsigned int);
void Delay_ms( volatile unsigned int t)
{
unsigned int i;
while(t--)
for (i=0;i<800;i++);
}
int main(){
RCC_APB2ENR |= (1<<3);
RCC_APB2ENR |= (1<<4);
RCC_APB2ENR |= (1<<2);
GPIOB_CRL |= (1<<0);
GPIOB_CRL &= ~(0xE);
GPIOC_CRH |= (1<<28);
GPIOC_CRH &= ~(0xE0000000);
GPIOA_CRL |= (1<<0);
GPIOA_CRL &= ~(0xE);
GPIOB_ODR |= (1<<0);
GPIOC_ODR |= (1<<15);
GPIOA_ODR |= (1<<0);
while(1){
GPIOB_ODR &= ~(1<<0);
Delay_ms(1000000);
GPIOB_ODR |= (1<<0);
Delay_ms(1000000);
GPIOC_ODR &= ~(1<<15);
Delay_ms(1000000);
GPIOC_ODR |= (1<<15);
Delay_ms(1000000);
GPIOA_ODR &= ~(1<<0);
Delay_ms(1000000);
GPIOA_ODR |= (1<<0);
Delay_ms(1000000);
}
}
void SystemInit(){
}
3.汇编语言实现
源码:
RCC_APB2ENR EQU 0x40021018;配置RCC寄存器,时钟,0x40021018为时钟地址
GPIOB_BASE EQU 0x40010C00
GPIOC_BASE EQU 0x40011000
GPIOA_BASE EQU 0x40010800
GPIOB_CRL EQU 0x40010C00
GPIOC_CRH EQU 0x40011004
GPIOA_CRL EQU 0x40010800
GPIOB_ODR EQU 0x40010C0C
GPIOC_ODR EQU 0x4001100C
GPIOA_ODR EQU 0x4001080C
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 ; Top of Stack
DCD Reset_Handler ; Reset Handler
AREA |.text|, CODE, READONLY
THUMB
REQUIRE8
PRESERVE8
ENTRY
Reset_Handler
bl LED_Init;bl:带链接的跳转指令。当使用该指令跳转时,当前地址(PC)会自动送入LR寄存器
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;B:无条件跳转。
LED_Init;LED初始化
PUSH {R0,R1, LR};R0,R1,LR中的值放入堆栈
;控制时钟
LDR R0,=RCC_APB2ENR;LDR是把地址装载到寄存器中(比如R0)。
ORR R0,R0,
LDR R1,=RCC_APB2ENR
STR R0,[R1]
;初始化GPIOA_CRL
LDR R0,=GPIOA_CRL
BIC R0,R0,
LDR R1,=GPIOA_CRL
STR R0,[R1]
LDR R0,=GPIOA_CRL
ORR R0,
LDR R1,=GPIOA_CRL
STR R0,[R1]
;将PA0置1
MOV R0,
LDR R1,=GPIOA_ORD
STR R0,[R1]
;初始化GPIOB_CRL
LDR R0,=GPIOB_CRL
BIC R0,R0,
LDR R1,=GPIOB_CRL
STR R0,[R1]
LDR R0,=GPIOB_CRL
ORR R0,
LDR R1,=GPIOB_CRL
STR R0,[R1]
;将PB0置1
MOV R0,
LDR R1,=GPIOA_ORD
STR R0,[R1]
;初始化GPIOC
LDR R0,=GPIOC_CRH
BIC R0,R0,
LDR R1,=GPIOC_CRH
STR R0,[R1]
LDR R0,=GPIOC_CRH
ORR R0,
LDR R1,=GPIOC_CRH
STR R0,[R1]
;将PC15置1
MOV R0,
LDR R1,=GPIOC_ORD
STR R0,[R1]
POP {R0,R1,PC};将栈中之前存的R0,R1,LR的值返还给R0,R1,PC
LED_ON_A
PUSH {R0,R1, LR}
MOV R0,
LDR R1,=GPIOA_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_OFF_A
PUSH {R0,R1, LR}
MOV R0,
LDR R1,=GPIOA_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_ON_B;亮灯
PUSH {R0,R1, LR}
MOV R0,
LDR R1,=GPIOB_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_OFF_B;熄灯
PUSH {R0,R1, LR}
MOV R0,
LDR R1,=GPIOB_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_ON_C;亮灯
PUSH {R0,R1, LR}
MOV R0,
LDR R1,=GPIOC_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_OFF_C;熄灯
PUSH {R0,R1, LR}
MOV R0,
LDR R1,=GPIOC_ORD
STR R0,[R1]
POP {R0,R1,PC}
Delay
PUSH {R0,R1, LR}
MOVS R0,
MOVS R1,
MOVS R2,
DelayLoop0
ADDS R0,R0,
CMP R0,
BCC DelayLoop0
MOVS R0,
ADDS R1,R1,
CMP R1,
BCC DelayLoop0
MOVS R0,
MOVS R1,
ADDS R2,R2,
CMP R2,
BCC DelayLoop0
POP {R0,R1,PC}
NOP
END
四、电路及实现效果
1.连接电路
对于USB转TTL模块和stm32f103c8t6连接
GND — GND 3v3 — 3v3 TXD — A10 RXD — A9
2.连接开发板
因为我使用了ST-Link,所以只需要在Keil上设置一番就好了; 但是要注意,可能会出现以下情况: 需要安装驱动!具体的地址在参考链接中给出。 操作继续! 点击如下按钮 software pack 对应位置出现你的支持包就可以啦! 接下来,连接好开发板,然后点击调试就可以了
3.运行结果
五、总结
在这次的学习过程中,我了解到了寄存器相关知识,学会了STM系列芯片的入门,成功点亮了流水灯,对于时钟等概念有了更加清晰地认识,能够更加熟练灵活的运用它们。
参考链接 【驱动安装及keil使用】win10 stm32 stlink驱动安装,检测不到芯片,下载不了程序 STM32寄存器的简介、地址查找,与直接操作寄存器 STM32从地址到寄存器
|