芯片:stm32f407ZET6X
功能:实现两按键分别控制四个灯和一个蜂鸣器
思路:将寄存器设置封装成函数,体会固件库的实现原理
踩坑记录:下载程序后发现与PA0连接的按键,还没按下按键,就出现了灯闪,蜂鸣器响,正常逻辑应该是按下按键,PA0口接地,此时才灯亮。
原因分析:因为初始化PA0口时没有使能A组的时钟(RCC_AHB1ENR |= (0x01); 漏写或还未写),所以,上拉设置对A组IO口无效,我发现GPIO口在未使能的状态下,是低电平,猜想默认是下拉吗???。对于输入口,不要尝试置位或复位,因为输入模式下,GPIOx->GPIOx_IDR(端口输入寄存器)是只读的。
//#include "reg_stm32.h"
/**/
#define PRI_BASEADDR 0x40000000
/*AHB1总线时钟使能相关*/
#define AHB1_BASEADDR (PRI_BASEADDR + 0x20000)
#define RCC_BASEADDR (AHB1_BASEADDR+0x3800)
#define RCC_AHB1ENR *((unsigned int *)(RCC_BASEADDR+0x30))//使能时钟寄存器
#define GPIOA_BASEADDR (AHB1_BASEADDR+0x0000)// IO口基地址
#define GPIOE_BASEADDR (AHB1_BASEADDR+0x1000)
#define GPIOF_BASEADDR (AHB1_BASEADDR+0x1400)
//构造一个结构体
typedef struct {
unsigned int GPIOx_MODER; //0x00
unsigned int GPIOx_OTYPER; //0x04
unsigned int GPIOx_OSPEEDR;//0x08
unsigned int GPIOx_PUPDR; //0x0c
unsigned int GPIOx_IDR; //0x10,只读
unsigned int GPIOx_ODR; //0x14
//...
}GPIOx_TypeDef;
#define GPIOA ((GPIOx_TypeDef *)GPIOA_BASEADDR)//宏名代表结构体地址
#define GPIOE ((GPIOx_TypeDef *)GPIOE_BASEADDR)
#define GPIOF ((GPIOx_TypeDef *)GPIOF_BASEADDR)
/*针对输出口的初始化,BEEP和LED*/
void GPIO_Out_Init(GPIOx_TypeDef * GPIOx, unsigned char PIN)
{
//使能RCC时钟,**重点**
RCC_AHB1ENR |= (0x01<<4);//使能E组GPIO口
RCC_AHB1ENR |= (0x01<<5);//使能F组GPIO口
//端口模式,输出--01
GPIOx->GPIOx_MODER &= ~(0x03<<PIN*2);//多位,先清零,后置位
GPIOx->GPIOx_MODER |= (0x01<<PIN*2);//后置位
//如果是输出模式,则输出类型---推挽--x=0
GPIOx->GPIOx_OTYPER &= ~(0x01<<PIN);//与0得0
//输出速度
GPIOx->GPIOx_OSPEEDR &= ~(unsigned int)(0x03<<PIN*2);
GPIOx->GPIOx_OSPEEDR |= (unsigned int)(0x01<<PIN*2);
//5.配置 PF8 引脚为上拉 ,这个影响不大可以忽略
GPIOx->GPIOx_PUPDR &= ~(unsigned int)(0x03<<PIN*2);
GPIOx->GPIOx_PUPDR |= (unsigned int)(0x01<<PIN*2);
}
/*针对输入口的初始化,KEY0~KEY4*/
void GPIO_In_Init(GPIOx_TypeDef * GPIOx, unsigned char PIN)
{
//使能RCC时钟,**重点**
RCC_AHB1ENR |= (0x01<<4);//使能E组GPIO口
RCC_AHB1ENR |= (0x01<<5);//使能F组GPIO口
RCC_AHB1ENR |= (0x01); //使能A组GPIO口,输入模式就是只读,不可再ResetBits或SetBits设置IO口**重点**
//配置IO口为输入模式
GPIOx->GPIOx_MODER &= ~(0x03<<PIN*2);
//设置为上拉
GPIOx->GPIOx_PUPDR &= ~(unsigned int)(0x03<<PIN*2);
GPIOx->GPIOx_PUPDR |= (unsigned int)(0x01<<PIN*2);
}
//输出1
void GPIO_SetBits(GPIOx_TypeDef *GPIOx ,unsigned char Pin)
{
GPIOx->GPIOx_ODR |= (0x01<<Pin);
}
//输出0
void GPIO_ResetBits(GPIOx_TypeDef *GPIOx ,unsigned char Pin)
{
GPIOx->GPIOx_ODR &= ~(0x01<<Pin);
}
//读取按键所连接端口的电平
unsigned int GPIO_ReadInputDataBit(GPIOx_TypeDef *GPIOx ,unsigned char Pin)
{
//判断某一组的 某一个 GPIO 口的电平
if( GPIOx->GPIOx_IDR & (0x1<<Pin) )
{
return 1;
}else{
return 0;
}
}
void BEEP_Init(void)
{
GPIO_Out_Init(GPIOF, 8);//蜂鸣器
//初始不响,**重点**
GPIO_ResetBits(GPIOF ,8);
}
void LED_Init(void)
{
GPIO_Out_Init(GPIOF, 9);//led0
GPIO_Out_Init(GPIOF, 10);//led1
GPIO_Out_Init(GPIOE, 13);//led2
GPIO_Out_Init(GPIOE, 14);//led3
//初始不亮,**重点**
GPIO_SetBits(GPIOF ,9);
GPIO_SetBits(GPIOF ,10);
GPIO_SetBits(GPIOE ,13);
GPIO_SetBits(GPIOE ,14);
}
void KEY_Init(void)
{
GPIO_In_Init(GPIOA ,0);//key0
GPIO_In_Init(GPIOE ,2);
GPIO_In_Init(GPIOE ,3);
GPIO_In_Init(GPIOE ,4);//key3
//初始端口值,给其一个确定的状态,**重点**
GPIO_SetBits(GPIOA ,0);
GPIO_SetBits(GPIOE ,2);
GPIO_SetBits(GPIOE ,3);
GPIO_SetBits(GPIOE ,4);
}
int main(void)
{
//初始化准备
BEEP_Init();
LED_Init();
KEY_Init();
//实现功能
while(1)
{
if(GPIO_ReadInputDataBit(GPIOA ,0) == 0)
{
GPIO_ResetBits(GPIOF ,9);
GPIO_ResetBits(GPIOF ,10);
GPIO_ResetBits(GPIOE ,13);
GPIO_ResetBits(GPIOE ,14);
GPIO_SetBits(GPIOF ,8);
}
if( GPIO_ReadInputDataBit(GPIOE ,2) == 0 )
{
GPIO_SetBits(GPIOF ,9);
GPIO_SetBits(GPIOF ,10);
GPIO_SetBits(GPIOE ,13);
GPIO_SetBits(GPIOE ,14);
GPIO_ResetBits(GPIOF ,8);
}
}
return 0;
}
总结:寄存器是基于触发器的,触发器的赋值是一定需要时钟的,而寄存器的时钟是由总线时钟提供的,就是说没有总线时钟的话,你给寄存器值它是不会读入的。使能时钟这根弦时刻要绷紧,就像心脏时刻需要跳动一样