目录
一、寄存器位移点灯
根据之前博客中介绍的寄存器/地址映射,在这里采用寄存器位移方法点灯就比较简单了。我们采用的工具仍然是Keil,STM32最小核心板,面包板,若干杜邦线以及LED。
【问题描述】
通过配置STM32的PA5、PB9、PC14端口,使其周期性地输出高低电平,采用通用推挽输出模式,最高输出时钟频率为2Mhz。进而驱动LED灯的亮灭,实现流水灯的功能。
【问题分析】
1. 首先,要使对应IO口的时钟使能;
2. 其次,要操作寄存器,一定要知道寄存器是谁,在哪里。因此第二步骤,配置寄存器地址;
3. 之后,配置端口的输入输出模式;
4.如果要实现周期性的亮灭,需要加入延迟函数delay();
5. 最后,编译成功后,将程序烧录到单片机中。
【问题解答】
1.使对应IO口的时钟使能
经过查阅STM32F103x的芯片手册的发现:复位和时钟控制的起始地址为0x4002 1000
,控制GPIO等外设的寄存器为APB2外设时钟,因此加上偏移量0x18
,得到使能时钟地址为0x4002 1018
。
查询APB外设时钟位定义,发现位2、位3、位4分别使能GPIOA、GPIOB、GPIOC。
-------------------------------代码如下------------------------------
//----------------APB2使能时钟寄存器 ---------------------
#define CLKENR *((volatile unsigned int *)0x4002 1018)
CLKENR|=1<<2|1<<3|1<<4; //APB2-GPIOA、GPIOB、GPIOC外设时钟使能
【语句解释】
关键词:volatile
当地址是IO端口的时候,读写这个地址是不能对它进行缓存的,这是相对于某些嵌入式中有cache才有这个。比如写这个IO端口的时候,如果没有这个volatile,很可能由于编译器的优化,会先把值先写到一个缓冲区,到一定时候再写到io端口,这样就不能使数据及时的写到IO端口,有了volatile说明以后,就不会再经过cache,write buffer这种,而是直接写到IO端口,从而避免了读写IO端口的延时。
简单来说:让对volatile 变量的存取不能缓存到寄存器,每次使用时需要重新存取。
2.寄存器地址配置
查询数据手册,得到GPIOA、GPIOB、GPIOC的起始地址为0x40010800、0x40010C00、0x40011000
;
-------------------------------代码如下------------------------------
//----------------GPIOA寄存器地址 -----------------------
#define GPIOA *((unsigned volatile int*)0x40010800)
//----------------GPIOB寄存器地址 -----------------------
#define GPIOB *((unsigned volatile int*)0x40010C00)
//----------------GPIOC寄存器地址 -----------------------
#define GPIOC *((unsigned volatile int*)0x40011004)
3.配置输入输出模式以及最高输出速率
STM32中,用低寄存器(GPIOx_CRL)来配置引脚Px0-Px7, 用高寄存器(GPIOx_CRH)来配置引脚Px8-Px15。因此在本次实验中用到的三个引脚A5、B9、C14。端口A5需要用低寄存器(GPIOx_CRL)来配置,B9、C14需要高寄存器(GPIOx_CRH)来配置。
根据数据手册,可得低寄存器(GPIOx_CRL)基于寄存器地址偏移量为0x00
,高寄存器(GPIOx_CRH)在寄存器地址偏移量为0x04
;输出寄存器基于GPIO寄存器地址偏移量为0x0C
---------------------配置寄存器地址代码如下--------------------
//----------------GPIOA配置寄存器 -----------------------
#define GPIOA_CRL *((unsigned volatile int*)0x40010800)
#define GPIOC_ODR</