目录
学习51我们从最简单的点灯开始,同样32也是,首先我们从最简单的寄存器控制点亮LED开始。
1、配置端口输出数据寄存器(ODR)
控制LED灯:给PC2/PC3输出低电平,LED灯亮;给PC2/PC3输出高电平,LED灯会灭。
那么如何控制端口:我们要选择GPIO寄存器的端口输出数据寄存器,该寄存器共有32位,高16位始终为0。因为控制端口是PC2/3,所以,外设基地址按照GPIOC的去写。
绝对地址=外设基地址+偏移地址
0X4001 1000+0C=0X4001 100C
注意:找到绝对地址之后,该绝对地址在系统看来只是一个数,所以我们要对他进行类型转换,让系统知道我们是对该地址里的内容进行操作。(unsigned int *)0x4001 100c:把后面的数转换为unsigned int指针类型,编译器就会认为这串数是个地址;*(unsigned int *)0x4001 100c表示该地址里的值。
*(unsigned int *)0X4001100C &= ~(1<<2);
*(unsigned int *)0X4001100C &= ~(1<<3);
PC2亮灭是对ODR2进行修改(左移2位),PC3亮灭是对ODR3进行修改(左移3位)。
还要注意的是,绝对地址的中间不能添加空格符!但是这样是烧写进去之后灯不会亮,是因为我们还没配置CRL寄存器。
2、配置低寄存器(CRL)
这个寄存器来配置GPIO端口的输入输出模式。
由于是RC2/3所以是CNF2和MODE2来控制,CNF2取决于MODE的配置,MODE选择01,CNF2选择00(通用推挽输出模式)所以是0001(注意一个小方框里面就是2位)。
3、配置复位和时钟控制寄存器(RCC)
配置到这里,但是灯还是不会亮。因为stm32为了节省功耗,让每个外设都对应时钟,在上电复位之后,所有时钟都是关闭的。所以,如果你想使用GPIO这个外设,就需要把GPIO外设的时钟给打开。 由于GPIO是属于APB2的,所以用到APB2 外设时钟使能寄存器(RCC_APB2ENR);如果是属于APB1的外设,就用APB1外设时钟使能寄存器(RCC_APB1ENR)。
寻找RCC寄存器的绝对地址:偏移地址0X18,RCC属于AHB,起始地址为0X4002 1000,绝对地址为0X4002 1018
我们用到端口C,且置1是让时钟打开。让第4位置1即可。
*(unsigned int *)0x40021018 |= (1<<4);
4、拓展:如何让PC2和PC3两个LED灯同时闪烁?
CRL寄存器和ODR寄存器两个端口都要配置
#include "stm32f10x.h"
void soft_delay(unsigned int count);
int main(void)
{
// &= ~用来清0(给某一位单独置0)
// |= 用来给某一位单独置1
//配置RCC寄存器,使能GPIO口的时钟
*(unsigned int *)0x40021018 |= (1<<4);
//配置CRL寄存器,配置为推挽输出
*(unsigned int *)0x40011000 |= (1<<(4*3));
*(unsigned int *)0x40011000 |= (1<<(4*2));
//配置ODR寄存器,决定I/O口输出低或高电平
//*(unsigned int *)0x4001100C &= ~(1<<2);
while(1)
{
*(unsigned int *)0x4001100C &= ~(1<<2); //亮
*(unsigned int *)0x4001100C &= ~(1<<3); //亮
soft_delay(0xfffff);
*(unsigned int *)0x4001100C |= (1<<2); //灭
*(unsigned int *)0x4001100C |= (1<<3); //灭
soft_delay(0xfffff);
}
}
void soft_delay(unsigned int count)//延时函数,但是不知道延时多长时间
{
for(;count!=0;count--);
}
void SystemInit(void)
{
}
至此,点灯工作完毕,灯开始流水闪烁。
---------------------------------------------------------------------------------------------------------------------------------
5、总结: GPIO功能框图和常用的寄存器
GPIO:是通用输入输出端口的简称,软件可控制的引脚。
(1)CRL和CRH寄存器
这两个都是32位的寄存器,因为STM32的GPIO口不像51单片机一样只有高低电平两个参数,STM32的每个GPIO口都有八种输出方式,通过寄存器配置确定是哪一种方式,除此之外,还有输出的速度,也是GPIO口的一个重要的参数。所以,要想描述STM32的一个GPIO的状态,就需要四个位(八种输出方式和三种输出速度),STM32的一个GPIO组有16个GPIO口,CRL和CRH都拥有32个位,CRL负责描述一个GPIO组的低八位GPIO(32/8 = 4,刚好够用),CRH负责描述一个GPIO组的高八位GPIO(同样,32/8 = 4,刚好够用)。
(2)IDR和ODR寄存器
1.IDR是一个端口输入数据寄存器,只使用了低16位,该寄存器为只读寄存器,并且只能以16位数据的形式读出,作用是可以通过其读出寄存器的状态是低还是高,要想知道具体是哪个寄存器的话,就需要看具体那个位的值了,在固件库中,通过
GPIO_ReadInputDataBit函数实现:
比如要读取GPIOA.5的状态,那么方法是:
返回值是 1(Bit_SET)或者 0(Bit_RESET);
2.ODR 是一个端口输出数据寄存器,也只用了低 16 位。该寄存器为可读写,从该寄存器读出来的数据可以用于判断当前 IO 口的输出状态。在固件库中设置 ODR 寄存器的值来控制 IO 口的输出状态是通过函数 GPIO_Write 来实现的:
该函数一般用来往一次性一个 GPIO 的多个端口设值。
(3)端口位清楚寄存器(BRR)
某一位置1:清除对应的位为0(复位)
(4)端口位设置/清除寄存器(BSRR)
高16位:清除,置1:清除对应的位为0(复位)
低16位:设置,置1:设置对应的位为1
BRR和BSRR这两个寄存器配置好之后会把数据传送给ODR,这一位的数据就是0或者1.
6、总结:什么是推挽输出,什么是开漏输出
(1)推挽输出:
1、可以输出高低电平,用于连接数字器件,高电平由VDD决定,低电平由VSS决定。
2、推挽结构指两个三极管受两路互补的信号控制,总是在一个导通的时候另外一个截止,优点开关效率效率高,电流大,驱动能力强。
3、输出高电平时,电流输出到负载,叫拉电流,可以理解成推,输出低电平时,负载电流流向芯片,叫灌电流,即挽。
(2)开漏输出:
1、只能输出低电平,不能输出高电平。
2、如果要输出高电平,则需要外接上拉。
3、开漏输出具有“线与”功能,一个为低,全部为低,多用于I2C和SMBUS总线。
7、配置GPIO初始化结构体步骤
(1)选定具体的GPIO(选择哪个I/O口)
(2)配置GPIO工作模式(CRL和CRH寄存器)(CRL控制低8位I/O口,CRH控制高8位I/O口)
(3)控制GPIO输出高低电平(ODR、BRR和BSRR)