STM32 寄存器点亮流水灯

本文详细介绍了如何使用STM32的GPIO寄存器控制LED灯实现流水灯效果,从理解寄存器映射、配置GPIO时钟、设置端口模式到编写C语言代码,一步步解析了整个流程。通过直接操作寄存器,展示了STM32F103C8T6芯片控制LED的底层实现。
摘要由CSDN通过智能技术生成

文章内容:以 STM32 最小系统核心板(STM32F103C8T6)+面板板+3只红绿蓝 LED 搭建电路,使用 GPIOB、GPIOC、GPIOD 这3个端口控制 LED 灯,轮流闪烁,间隔时长1秒。
开发环境:keil 5

1 初识寄存器

我们知道,存储器本身没有地址,给存储器分配地址的过程叫存储器映射,那什么叫寄存器映射?寄存器到底是什么?

1.1 寄存器

  • 在存储器 Block2 这块区域,设计的是片上外设,它们以四个字节为一个单元,共 32bit,每一个单元对应不同的功能。
  • 我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器
  • 这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射

1.2 STM32 的外设地址映射

1.2.1 总线基地址

  • 从存储器映射那张图的 Block2 可以看到,分为4大块,每块都有一个起始地址,这个起始地址就是基地址

  • 然后到下一块起始地址的时候就会和前一块地址出现偏差,这个差值就是偏移量,即相对基地址的偏移量

总线基地址如下图所示:

总线名称总线基地址相对外设基地址的偏移
APB10x4000 00000x0
APB20x4001 00000x0001 0000
AHB0x4001 80000x0001 8000

从上图可以看到 APB1 总线基地址是 0x4000 0000,相对外设基地址的偏移量是0,所以此总线也是外设 Block2 的基地址。

1.2.2 外设基地址

  • 每条总线上都会挂接着很多的外设,这些外设也会有自己的地址范围,XXX外设的首个地址即最低地址就是XXX外设的基地址,也称作XXX边界地址。
  • 有关STM32F10xxx 外设的具体边界地址可以参考《STM32中文参考手册》P28页第23节, 里面有详细的介绍。这里我们就以GPIO外设来讲解外设基地址,其他的外设也是同样分析。

GPIO外设基地址如下图所示:

外设名称外设基地址相对 APB2 总线的地址偏移
GPIOA0x4001 08000x0000 0800
GPIOB0x4001 0C000x0000 0C00
GPIOC0x4001 10000x0000 1000
GPIOD0x4001 14000x0000 1400
GPIOE0x4001 18000x0000 1800
GPIOF0x4001 1C000x0000 1C00
GPIOG0x4001 20000x0000 2000

1.2.3 外设寄存器地址

  • XXX外设的寄存器就分布在其对应的外设地址范围内。
  • 这里我们以GPIO外设为例,GPIO是通用输入输出端口的简称,可以通过软件来控制其输入和输出。GPIO有很多个寄存器,每一个都有特定的功能。每个寄存器为32bit,占四个字节,这些寄存器都是按顺序依次排列在外设的基地址上。寄存器的位置都以相对该外设基地址的偏移地址来描述。
    这里我们以GPIOC端口为例,来说明GPIO都有哪些寄存器,如下表所示:
寄存器名称寄存器地址相对 GPIOB 基址的偏移
GPIOB_CRL0x4001 0C000x00
GPIOB_CRH0x4001 0C040x04
GPIOB_IDR0x4001 0C080x08
GPIOB_ODR0x4001 0C0C0x0C
GPIOH_BSRR0x4001 0C100x10
GPIOH_BRR0x4001 0C140x14
GPIOH_LCKR0x4001 0C180x18

2 使用寄存器点灯

2.1 工程文件创建

2.1.1 新建工程文件

在 keil 里创建新工程文件可以参考笔者的另一条博文:基于 MDK 创建汇编语言的STM32工程并分析HEX文件

打开 keil ,先点击菜单栏的Project ,接着点击New uVision Project 创建新工程;
在弹出的窗口选择工程路径,输入工程名(可随意命名,最好全英文,这里命名为 LED ),并保存;
在新窗口中选择 CPU 型号,这里选择STM32F103C8 芯片。
在这里插入图片描述
run-time environment 窗口中,选择下图所示:
在这里插入图片描述

2.1.2 添加文件

在工程文件里添加 main.c 文件
在这里插入图片描述
接下来我们考虑如何使用寄存器进行代码编写。

2.2 配置 GPIO 端口

2.2.1 初识 GPIO

  • GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚,STM32 芯片 的 GPIO引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。
  • 最基本的输出功能是由 STM32 控制引脚输出高、低电平,实现开关控制,如把 GPIO 引脚接入到 LED 灯,那就可以控制 LED 灯的亮灭,引脚接入到继电器或三极管,那就可以 通过继电器或三极管控制外部大功率电路的通断。
  • 最基本的输入功能是检测外部输入电平,如把 GPIO 引脚连接到按键,通过电平高低区分按键是否被按下。

在这里插入图片描述

2.2.2 GPIO配置

  • 配置时钟
    查看《STM32中文参考手册》,可以看到时钟控制名字叫做 RCC,属于 AHB 总线。
    在这里插入图片描述
    手册P25页的系统结构可以看到时钟的从属关系,可以看出 AHB 总线包含 RCC 时钟控制,GPIO 是属于 APB2 的。
    在这里插入图片描述
    我们需要用到 GPIO 的A、B、C端口,查到其对应地址如下:
    在这里插入图片描述
    接下来,可以在手册 6.3.7小节找到 APB2 外设时钟使能寄存器(RCC_APB2ENR),偏移地址是0x18,所以 APB2 的地址就是0x4002 1018
    以及端口 A、B、C 的位数,如下:

在这里插入图片描述
通过自行查找,现在就可以对时钟进行使能:

//RCC的AHB1时钟使能寄存器地址,强制转换成指针
#define RCC_APB2ENR	*((unsigned volatile int*)0x40021018)

RCC_APB2ENR|=1<<2;		  	//APB2-GPIOA外设时钟使能
RCC_APB2ENR|=1<<3;		  	//APB2-GPIOB外设时钟使能
RCC_APB2ENR|=1<<4;		  	//APB2-GPIOC外设时钟使能
  • 输入输出模式设置

控制 LED 需要输出高电平或是低电平,所以需要配置为输出,所以我们这里需要配置为推挽输出。

STM32中,用端口配置寄存器(GPIOx_CRL)来配置引脚 Px0-Px7, 用端口配置寄存器(GPIOx_CRH)来配置引脚 Px8-Px15

我们回到流水灯问题,问题中我们需要 GPIOB、GPIOC、GPIOD 这3个端口控制红、黄、绿三个 LED 灯,所以这里配置三个引脚 PA 、PB 、PC 使用的寄存器是 GPIOB_CRH,下面我们来寻找对应的寄存器地址。
端口配置高寄存器时:
在这里插入图片描述

GPIOx,表示不管是 PA, PB 还是 PE,都能用。偏移地址是 0x04,意思是在基地址的基础上再加 0x04,所以,对于 GPIOB 来说就是 0x4001 0c04。

端口配置低寄存器时:
在这里插入图片描述

GPIOx_CRL 中包含 0-7 号引脚(控制GPIO低8位),每个引脚占用 4 个寄存器位。 低寄存器的偏移地址为0x00,MODE 位用来配置输出的速度, CNF 位用来配置各种输入输出模式。

对于GPIOA的A12、GPIOB的B1、GPIOC的C14,配置如下:

/*GPIOA寄存器地址,强制转换为指针*/
#define GPIOA_CRH	*((unsigned volatile int*)0x40010804)
/*GPIOB寄存器地址,强制转换为指针*/
#define GPIOB_CRL	*((unsigned volatile int*)0x40010C00)
/*GPIOC寄存器地址,强制转换为指针*/
#define GPIOC_CRH	*((unsigned volatile int*)0x40011004)

GPIOA_CRH&=0xFFF0FFFF;		/*设置位 清零*/
GPIOA_CRH|=0x00020000;		/*PA12推挽输出*/

GPIOB_CRL&=0xFFFFFF0F;		/*设置位 清零*/
GPIOB_CRL|=0x00000020;		/*PB1推挽输出*/

GPIOC_CRH&=0xF0FFFFFF;		/*设置位 清零*/
GPIOC_CRH|=0x02000000;   	/*PC14推挽输出,设置最大速度为2Mhz*/

点亮 LED 灯需要输出低电平,我们需要寻找端口输出寄存器:
在这里插入图片描述

#define	GPIOA_ODR	*((unsigned volatile int*)0x4001080C)
#define	GPIOB_ODR	*((unsigned volatile int*)0x40010C0C)
#define	GPIOC_ODR	*((unsigned volatile int*)0x4001100C)

GPIOA_ODR&=1<<12;		    	/*设置初始灯为亮*/
GPIOB_ODR|=0<<1;			    /*设置初始灯为灭*/
GPIOC_ODR|=0<<14;		    	/*设置初始灯为灭*/

2.3 执行 C 语言代码

2.3.1 C代码

  • 在之前的基础上再添加时延代码:
//延时函数
void  Delay_ms( volatile  unsigned  int  t)
{
     unsigned  int  i;
     while(t--)
         for (i=0;i<800;i++);
}
  • 整理之后的 C 代码如下,将其添加到工程文件的main.c文件中
/*GPIOA外设基地址*/
#define GPIOA_BASE 0x40010800
/*GPIOB外设基地址*/
#define GPIOB_BASE 0x40010C00
/*GPIOC外设基地址*/
#define GPIOC_BASE 0x40011000

/*RCC的AHB1时钟使能寄存器地址,强制转换成指针*/
#define RCC_APB2ENR	*((unsigned volatile int*)0x40021018)

/*GPIOA寄存器地址,强制转换为指针*/
#define GPIOA_CRH	*((unsigned volatile int*)0x40010804)
#define	GPIOA_ODR	*((unsigned volatile int*)0x4001080C)
/*GPIOB寄存器地址,强制转换为指针*/
#define GPIOB_CRL	*((unsigned volatile int*)0x40010C00)
#define	GPIOB_ODR	*((unsigned volatile int*)0x40010C0C)
/*GPIOC寄存器地址,强制转换为指针*/
#define GPIOC_CRH	*((unsigned volatile int*)0x40011004)
#define	GPIOC_ODR	*((unsigned volatile int*)0x4001100C)
	
/*延时函数*/
void  Delay_ms( volatile  unsigned  int  t)
{
     unsigned  int  i;
     while(t--)
         for (i=0;i<800;i++);
}

/*主函数*/
int main()
{
	RCC_APB2ENR|=1<<2;		  	/*APB2-GPIOA外设时钟使能*/
	RCC_APB2ENR|=1<<3;		  	/*APB2-GPIOB外设时钟使能*/
	RCC_APB2ENR|=1<<4;		  	/*APB2-GPIOC外设时钟使能*/
	
	GPIOA_CRH&=0xFFF0FFFF;		/*设置位 清零*/
	GPIOA_CRH|=0x00020000;		/*PA12推挽输出*/
	GPIOA_ODR&=1<<12;		    	/*设置初始灯为亮*/
	
	GPIOB_CRL&=0xFFFFFF0F;		/*设置位 清零*/
	GPIOB_CRL|=0x00000020;		/*PB1推挽输出*/
	GPIOB_ODR|=0<<1;			    /*设置初始灯为灭*/	
	
	GPIOC_CRH&=0xF0FFFFFF;		/*设置位 清零*/
	GPIOC_CRH|=0x02000000;   	/*PC14推挽输出*/
	GPIOC_ODR|=0<<14;		    	/*设置初始灯为灭*/	
	while(1)
	{	
		GPIOA_ODR&= ~(1<<12);		/*PA12高电平*/
		Delay_ms(3000000);
		GPIOA_ODR|= (1<<12);		/*PA12低电平*/
		Delay_ms(3000000);
		
		GPIOB_ODR&= ~(1<<1);		/*PB1高电平*/
		Delay_ms(3000000);
		GPIOB_ODR|= (1<<1);		  /*PB1低电平*/
		Delay_ms(3000000);
		
		GPIOC_ODR&= ~(1<<14);		/*PC14高电平*/
		Delay_ms(3000000);
		GPIOC_ODR|= (1<<14);		/*PC14低电平*/
		Delay_ms(3000000);
	}
}

2.3.2 编译形成 hex 文件

  • 编译前准备
    点击 魔法棒 ,在 Output 界面下,勾选 Create HEX File ,点击OK,才能生成 hex 文件
    在这里插入图片描述
  • 点击编译,生成 hex 文件
    在这里插入图片描述

3 电路效果

3.1 设备选择

1、stm32 核心板103f 一块
2、usb 转串口一块
3、面包板一块
4、LED 红、黄、绿三色各一只,导线若干

3.2 连接电路

  • USB 转 TTL 模块stm32f103c8t6的连接如下图:
    在这里插入图片描述

GND-G
3V3-3.3
RXD-A9
TXD-A10

  • BOOT0 与 BOOT1 配置启动方式
    在这里插入图片描述
    将 boot0 置1,boot1 置0,并且要按下 reset 键

  • GPIO 端口与 LED 连接
    C 程序使用 GPIOA的A12、GPIOB的B1、GPIOC的C14端口连接。

  • 实际连接结果:
    在这里插入图片描述

注意:接线时尽量避免用杜邦线串联,可能接触不良或过程中松动。可用一根杜邦线,长度不够时可通过面包板卡槽连接两根杜邦线。

3.3 烧录运行

需要用到烧录软件,这里用的是 mcuisp 软件。可以从这得到:百度网盘下载链接:https://pan.baidu.com/s/1sjKJTMtg3cJLtsGOQhyJ3w 提取码:cyuu

注意:网盘里的 CH340-driver 驱动文件也需要安装!

3.3.1 烧录

  • USB转TTL模块插入电脑,打开mcuisp ,将之前生成的 hex 文件上传,选择DTR的低电平复位,RTs高电平进BootLoader
    在这里插入图片描述

  • 点击读器件信息,点击开始编程,成功的话右侧会出现一切正常。
    在这里插入图片描述

3.3.2 流水灯效果

在这里插入图片描述

4 总结

相信有很多小伙伴和我一样,都是第一次使用 STM32 板子进行开发,确实有点难度。通过整篇文章的篇幅也能看出,STM 使用寄存器操作过程比较复杂,尤其是在寻找寄存器地址时,需要对照参考手册查找,相当麻烦,其实,还有其他方法,比如通过 stm32CubeMX,配合 Keil 使用寄存器地址的方式进行操作,这就相较简单,我们之后可以尝试一下。此外,文章流水灯代码使用的是 C 语言编写,读者若是对嵌入式有兴趣,也可以试着使用汇编语言进行点灯。
第一次学习这种开发,如有不妥之处,还请读者斧正。

5 参考资料

1、STM32寄存器的简介、地址查找,与直接操作寄存器
2、基于汇编和C语言STM32流水灯依次闪烁
3、STM32串口下载程序
4、百度网盘STM32参考资料包(包括STM32中文参考手册)提取码:luha

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值