STM32F103寄存器方式点亮LED流水灯
文章目录
1、寄存器映射原理
存储器本身没有地址,给存储器分配地址的过程叫存储器映射;在存储器区域,设计的是片上外设,它们以四个字节为一个单元,共 32bit, 每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到 每个单元的起始地址,然后通过 C 语言指针的操作方式来访问这些单元,如果每次都是通 过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的 不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个 给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。
2、GPIO端口的初始化三步骤
1、时钟配置
由于 STM32的 外设很多,为了降低功耗,每个外设都对应着一个时钟,在芯片刚 上电的时候这些时钟都是被关闭的,如果想要外设工作,必须把相应的时钟打开。 STM32 的所有外设的时钟由一个专门的外设来管理,叫 RCC(reset and clockcontrol)
例如
// 开启 GPIOB 端口 时钟
RCC_APB2ENR |= (1<<3);
2、输入输出模式设置与最大速率设置
在 GPIO 外设中,控制端口高低控制寄存器 CRH 和 CRL 可以配置每个 GPIO 的工作模式和 工作的速度,每 4 个位控制一个 IO,CRH 控制端口的高八位,CRL 控制端口的低 8 位,具 体的看 CRH 和 CRL 的寄存器描述
例如
把 PB0 配置为通 用推挽输出,输出的速度为 10M
// 清空控制 PB0 的端口位
GPIOB_CRL &= ~( 0x0F<< (4*0));*
// 配置 PB0 为通用推挽输出,速度为 10M
GPIOB_CRL |= (1<<4*0);
在代码中,先把控制 PB0 的端口位清 0,然后再向它赋值“0001 b”,从而使 GPIOB0 引脚设置成输出模式,速度为 10M。
代码中使用了“&=~”、“|=”这种操作方法是为了避免影响到寄存器中的其它位, 因为寄存器不能按位读写,假如直接给 CRL 寄存器赋值:
GPIOB_CRL = 0x0000001;
这时 CRL 的的低 4 位被设置成“0001”输出模式,但其它 GPIO 引脚就有意见了,因 为其它引脚的 MODER 位都已被设置成输入模式。
3、寄存器ODR
例如
// PB0 输出低电平
GPIOB_ODR &= ~(1<<0);
3、程序设计思路
因为我购买的是STM32最小系统核心板(STM32F103C8T6),GPIOA与GPIOB都有0-15端口,GPIOC只有13-15号端口,所以为控制三个LED灯,采用GPIOA0、GPIOB0、GPIOC13三个端口来实现。
程序代码
#include "stm32f10x.h"
#include <stdio.h>
void SystemInit(void);
void delay(unsigned int a);
int main(void)
{
RCC_APB2ENR |= (1<<2); // 开启GPIOA端口时钟
RCC_APB2ENR |= (1<<3); // 开启GPIOB端口时钟
RCC_APB2ENR |= (1<<4); // 开启GPIOC端口时钟
//清空控制 PA0 的端口位
GPIOA_CRL &= ~(unsigned int)( 0x0F<< (4*0));
//清空控制 PB0 的端口位
GPIOB_CRL &= ~(unsigned int)( 0x0F<< (4*0));
//清空控制 PC13 的端口位
GPIOC_CRH &= ~(unsigned int)( 0x0F<< (4*5));
// 配置 PA0 为通用推挽输出,速度为 10M
GPIOA_CRL |= (1<<4*0);
// 配置 PB0 为通用推挽输出,速度为 10M
GPIOB_CRL |= (1<<4*0);
// 配置 PC13 为通用推挽输出,速度为 10M
GPIOC_CRH |= (1<<4*5);
while (1)
{
GPIOA_ODR = (1<<0); //红灯亮
GPIOB_ODR = (0<<0); //绿灯灭
GPIOC_ODR = (0<<13); //黄灯灭
delay(1000000); //延时1秒
GPIOA_ODR = (0<<0); //红灯灭
GPIOB_ODR = (1<<0); //绿灯亮
GPIOC_ODR = (0<<13); //黄灯灭
delay(1000000); //延时1秒
GPIOA_ODR = (0<<0); //红灯灭
GPIOB_ODR = (0<<0); //绿灯灭
GPIOC_ODR = (1<<13); //黄灯亮
delay(1000000); //延时1秒
}
}
void SystemInit(void)
{
}
// 函数为空,目的是为了骗过编译器不报错
void delay(unsigned int a)
{
for (; a != 0; a--);
}
//延时函数
程序解析
延时函数delay(),通过内部循环执行指令,一次指令大概1微秒,通过改变循环次数达到延时效果。
主程序main()中,首先要开启端口时钟,通过寄存器RCC_ APB2ENR来控制。然后用寄存器GPIOA_CRL和
GPIOB_CRL来控制A、B端口低8位的输出模式,寄存器GPIOC_CRH来控制C端口高8位的输出模式。寄存器GPIOA_CRL的地址是0x40010800、GPIOB_CRL的地址是0x40010C00、GPIOC_CRH的地址是0x40011004
在循环体中,控制端口输出数据寄存器GPIOx_ORD来输出高低电平,进而控制LED灯的熄灭和发光。每执行一种状态,通过延时函数延时一秒钟,切换到下一种状态,最终达到流水灯的效果。
4、软件仿真–示波器
教程参考 https://blog.csdn.net/weixin_43737995/article/details/98049869
我的仿真结果如下图
5、硬件测试
因烧录程序时,芯片连接超时,无法正常运行。原因是芯片内部的损坏。
6、总结
因芯片问题,感觉购买的芯片是伪劣产品,在烧录程序时不成功,无法进行硬件测试。在编写程序代码时,应该注意端口的配置要准确,端口时钟要第一时间开启,不然端口就没有输出或者输入。