学习32是一个循序渐进的过程,通过点灯可以了解到许许多多的知识,今天先讲解按键点灯,明天如果有时间会通过对比按键,写中断控制点灯的程序。
实现按键点灯的第一步还是先看原理图:
原理:当按键按下去后,会向芯片输入高电平,芯片得到之后,继而向LED灯输出低电平,则可以达到按键控制LED灯的效果。
下面我先把代码放在一起运行,这样比较直观,我用的还是GPIO_ResetBits()函数控制芯片输出低电平,但是存在一个bug,就是按键按下去,就无法退回去了,也就点亮以后无法熄灭了,必须按复位键才可以。
#include "stm32f10x.h"
/*简单延时函数*/
void delay(uint32_t i)
{
while(i--);
}
/*按键GPIO配置*/
void KEY_GPIO_Init(void)
{
//引入GPIO结构体
GPIO_InitTypeDef GPIO_InitStructure;
//打开控制KEY1的GPIOA外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC, ENABLE);
/*选择要控制的GPIOA的引脚*/
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_13 ;
/*设置引脚模式为浮空输入*/
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
/*设置引脚速率为50MHz*/
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
/*调用库函数,初始化GPIOA*/
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_Init(GPIOC,&GPIO_InitStructure);
/*按键置0,设置为最初的状态*/
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
GPIO_ResetBits(GPIOC,GPIO_Pin_13);
}
/*LED_GPIO配置*/
void LED_GPIO_Init(void)
{
//引入GPIO结构体
GPIO_InitTypeDef GPIO_InitStructure;
//打开控制LED的GPIOB外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/*选择要控制的GPIOB的引脚*/
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5|GPIO_Pin_1 ;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz*/
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
/*调用库函数,初始化GPIOB*/
GPIO_Init(GPIOB,&GPIO_InitStructure);
/*按键置1,设置为最初的状态*/
GPIO_SetBits(GPIOB,GPIO_Pin_5|GPIO_Pin_1);
}
/*主函数*/
int main(void)
{
LED_GPIO_Init();//LED初始化
KEY_GPIO_Init();//KEY初始化
while(1)
{
/*如果按键1按下去,红灯点亮*/
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)//使用引脚读入函数
{
delay(99999);//延时是为了按键消抖
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
}
/*如果按键2按下去,绿灯点亮*/
if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13)==1)
{
delay(99999);
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
}
}
}
这里使用的外设还是RCC外设和GPIO外设,所以只要添加这两个库文件就可以了
从原理图可以得到,它加了电容,可以达到硬件消抖的作用,所以可以不用软件延时消抖。
下面我换另一种方式控制按键点灯,文件也趋于模块化,方便修改
1. key.h
#ifndef __KEY_H //如果没有定义头文件
#define __KEY_H //则定义头文件
#include "stm32f10x.h"
//定义引脚
#define KEY1_GPIO_CLK RCC_APB2Periph_GPIOA
#define KEY1_GPIO_PORT GPIOA
#define KEY1_GPIO_PIN GPIO_Pin_0
#define KEY2_GPIO_CLK RCC_APB2Periph_GPIOC
#define KEY2_GPIO_PORT GPIOC
#define KEY2_GPIO_PIN GPIO_Pin_13
//定义按键,按下为高电平
#define KEY_ON 1
#define KEY_OFF 0
void KEY_GPIO_Config(void);
int KEY_Scanf(GPIO_TypeDef* GPIOX,uint16_t GPIO_PIN);
#endif //结束
2. key.c
#include "stm32f10x.h"
#include "key.h"
/*配置按键GPIO*/
void KEY_GPIO_Config(void)
{
//引入GPIO结构体
GPIO_InitTypeDef GPIO_InitStructure;
//打开控制KEY1和KYE2的GPIO外设时钟
RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK, ENABLE);
/*选择要控制的KEY1的引脚*/
GPIO_InitStructure.GPIO_Pin=KEY1_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;/*设置引脚模式为浮空输入*/
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; /*设置引脚速率为50MHz*/
GPIO_Init(KEY1_GPIO_PORT,&GPIO_InitStructure);/*初始化控制按键KEY1*/
/*选择要控制的KEY2的引脚*/
GPIO_InitStructure.GPIO_Pin=KEY2_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;/*设置引脚模式为浮空输入*/
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; /*设置引脚速率为50MHz*/
GPIO_Init(KEY2_GPIO_PORT,&GPIO_InitStructure);/*初始化控制按键KEY2*/
}
/*按键检测,当有按键按下(KEY_ON),向芯片输入高电平*/
int KEY_Scanf(GPIO_TypeDef* GPIOX,uint16_t GPIO_PIN)
{
/*检测按键是否被按下*/
if(GPIO_ReadInputDataBit(GPIOX,GPIO_PIN)==KEY_ON)
{
/*等待按键释放,按一次就被置位高电平*/
while(GPIO_ReadInputDataBit(GPIOX,GPIO_PIN)==KEY_ON);
return KEY_ON;
}
else
return KEY_OFF;//返回值表现在哪里
}
3. led.h
#ifndef __LED_H //如果没有定义头文件
#define __LED_H //则定义头文件
#include "stm32f10x.h"
/*定义引脚*/
//红色
#define LED1_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED1_GPIO_PORT GPIOB
#define LED1_GPIO_PIN GPIO_Pin_5
//绿色
#define LED2_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED2_GPIO_PORT GPIOB
#define LED2_GPIO_PIN GPIO_Pin_0
//蓝色
#define LED3_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED3_GPIO_PORT GPIOB
#define LED3_GPIO_PIN GPIO_Pin_1
//定义LED的高低电平(根据原理图可知电平低电平点亮LED,所以用0N比较好看些)
#define ON 0
#define OFF 1
/* 直接操作寄存器的方法控制IO */
#define digitalHi(p,i) {p->BSRR=i;} //输出为高电平
#define digitalLo(p,i) {p->BRR=i;} //输出低电平
#define digitalToggle(p,i) {p->ODR ^=i;} //输出反转状态
//定义控制LED的宏
#define LED1_TOGGLE digitalToggle(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_OFF digitalHi(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_ON digitalLo(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED2_TOGGLE digitalToggle(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_OFF digitalHi(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_ON digitalLo(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED3_TOGGLE digitalToggle(LED3_GPIO_PORT,LED3_GPIO_PIN)
#define LED3_OFF digitalHi(LED3_GPIO_PORT,LED3_GPIO_PIN)
#define LED3_ON digitalLo(LED3_GPIO_PORT,LED3_GPIO_PIN)
void LED_GPIO_Config(void);
#endif //结束
4.led.c
#include "stm32f10x.h"
#include "led.h"
void LED_GPIO_Config(void)
{
//引入GPIO结构体
GPIO_InitTypeDef GPIO_InitStructure;
//打开控制LED的GPIO外设时钟
RCC_APB2PeriphClockCmd(LED1_GPIO_CLK | LED2_GPIO_CLK | LED3_GPIO_CLK,ENABLE);
/*选择要控制的GPIOX的引脚*/
GPIO_InitStructure.GPIO_Pin=LED1_GPIO_PIN|LED2_GPIO_PIN|LED3_GPIO_PIN;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz*/
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
/*调用库函数,初始化GPIOB*/
GPIO_Init(GPIOB,&GPIO_InitStructure);
/*按键置0,设置为最初的状态*/
GPIO_SetBits(GPIOB,LED1_GPIO_PIN|LED2_GPIO_PIN|LED3_GPIO_PIN);
}
5.main.c
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
int main(void)
{
/* LED端口初始化 */
LED_GPIO_Config();
LED3_ON;//直接点亮蓝灯LED3,无按键控制,最初的状态,这里只是为了两个按键都按下去之后,可以出现白灯效果
/* KEY端口初始化 */
KEY_GPIO_Config();
while(1)
{
if(KEY_Scanf(KEY1_GPIO_PORT,KEY1_GPIO_PIN)==KEY_ON)
{
LED1_TOGGLE;//红灯
}
if(KEY_Scanf(KEY2_GPIO_PORT,KEY2_GPIO_PIN)==KEY_ON)
{
LED2_TOGGLE;//绿灯
}
}
}
第二个程序用到了寄存器控制LED,虽然寄存器不需要深究,但也要稍微了解一下。
寄存器:暂时存放数据的地方,数据在芯片中的表现形式是地址,地址用一串16进制的数字表示,寄存器形象一点理解就是房子,每个房子都有自己独特的地址(数据)。
#define digitalHi(p,i) {p->BSRR=i;} //输出为高电平
#define digitalLo(p,i) {p->BRR=i;} //输出低电平
#define digitalToggle(p,i) {p->ODR ^=i;} //取反
这里用到了三个寄存器BSRR、BRR、ODR,那么它们有什么作用?
GPIO_SetBits()和GPIO_ResetBits()两个库函数就是通过修改BSRR,BRR寄存器的值来实现对引脚的设置。
BSRR:端口位设置/清除寄存器,只能写,既能控制管脚为高电平(设置),也能控制管脚为低电平(清除),对寄存器高16bit 写1 ,对应管脚为低电平;对寄存器低16bit写1对应管脚为高电平;写 0 ,无动作。
BRR:端口位清除寄存器,只能写,只能使管脚状态为低电平(清除),对寄存器写1,相应管脚为低电平;写0,无动作。
如此看来,想要给GPIO引脚写入高/低电平有BSRR一个寄存器就可以实现, BRR 寄存器就显得比较多余的,而实际上,在最新的 STM32F4 系列 MCU 的 GPIO 寄存器中,已经找不到 BRR 寄存器了,仅保留了 BSRR 寄存器用于实现端口输出高低电平。
ODR:输出数据寄存器,可读可写,既能控制管脚为高电平,也能控制管脚为低电平。
位写1,GPIO 管脚为高电平;位写0, GPIO管脚为低电平。
一个 ODR 寄存器控制了一组(16位)的 GPIO 输出。因此,设置ODR也能控制引脚输出高/低电平。但是,由于对 ODR 寄存器的读写操作必须以 16 位的形式进行,也就是说要修改的话,把GPIO一组16个引脚都修改了,无法针对单个引脚,而如果非要使用 ODR 改写数据来控制输出时,须采用“读-改-写”的形式进行。
uint32_t temp;//设定一个暂时存储变量
temp = GPIOA->ODR;//将0DR的值赋给temp
temp = temp | GPIO_Pin_6;
GPIOA->ODR = temp;
设置单个 IO 口输出时,使用 BSRR 进行操作会更加方便;如果需要对单个IO口进行 Toggle(反转/取反) 操作时,使用 ODR 寄存器进行操作的比较方便。
位写1,GPIO 管脚为高电平;位写0, GPIO管脚为低电平。
在C语言中“->”代表什么?
->:指向结构体成员运算符。
#include
// 结构体的声明与定义
struct Data
{
char name[10];
int age;
char sex;
}person;
void main()
{
int i;
// 此处就是指向结构体成员运算符(->)的用法
i = person->age; // 提取结构体成员变量age的值(某人的年龄),并赋值给变量i
}
最后再说一下仿真器配置,因为一开始我将程序烧录到开发板,必须先按下复位键才可以执行程序,其实就是仿真器没有配置好,直接上图就会懂了
备注:由于小白杨才疏学浅,寄存器那一部分仍有些许迷惑,请大佬指点迷津,弄懂之后,会重新修改,希望在此能遇到志同道合的朋友,一起学习技术。**