main.c
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
int main(void) {
//来到这里的时候,系统时钟已经被配置成72M
RGB_GPIO_Config(RGB_R); // 初始化红色 LED
RGB_GPIO_Config(RGB_G); // 初始化绿色 LED
RGB_GPIO_Config(RGB_B); // 初始化蓝色 LED
// RGB_ON(RGB, RED);
// RGB_OFF(RGB,RED);
// RGB_ON(RGB,BLUE);
// RGB_OFF(RGB,BLUE);
//
// RGB_ON(RGB,GREEN);
// RGB_OFF(RGB,GREEN);
RGB_OFF(RGB, RED);
KEY_GPIO_Config();//单个按键初始化
while(1){
if(KEY1_Scan() == 1)
{
RGB_ON(RGB, RED);
}
}
}
key.c
#include "key.h"
void KEY_GPIO_Config(){
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA,&GPIO_InitStruct);
}
uint8_t KEY1_Scan(){
if((GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))==1){
while((GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))==1);
return 1;
}
else{
return 0;
}
}
key.h
#ifndef __KEY_H
#define __KEY_H
#include "stm32f10x.h"
void KEY_GPIO_Config(void);
uint8_t KEY1_Scan(void);
#endif
led.c
#include "led.h"
void RGB_GPIO_Config(RGB_Type RGBType) {
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
if (RGBType == RGB_R) {
GPIO_InitStruct.GPIO_Pin = RED;
} else if (RGBType == RGB_G) {
GPIO_InitStruct.GPIO_Pin = GREEN;
} else if (RGBType == RGB_B) {
GPIO_InitStruct.GPIO_Pin = BLUE;
}
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
RGB_OFF(RGB,GPIO_InitStruct.GPIO_Pin);// 电路原因,让灯不会因为 GPIO 引脚初始化而亮
}
led.h
#ifndef __LED_H
#define __LED_H
#include "stm32f10x.h"
#define RED GPIO_Pin_5
#define BLUE GPIO_Pin_1
#define GREEN GPIO_Pin_0
#define RGB GPIOB
#define RGB_ON(Port, color) GPIO_ResetBits(Port, color)
#define RGB_OFF(Port, color) GPIO_SetBits(Port, color)
/*
#define LED_OFF(A, B) GPIO_ResetBits(A, B)
#define LED_OFF(GPIOx, GPIO_PIN) GPIO_ResetBits(GPIOx, GPIO_PIN)
都可以,效果一样,前后对应即可
#define LED_OFF(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
这个不行,要改成下面这个
#define LED_OFF(GPIOx, GPIO_Pin) GPIO_ResetBits(GPIOx, GPIO_Pin
在这个宏定义中,我们去掉了参数列表中的类型声明,只保留了参数名。
这样做是因为在宏定义中,我们只需要指定参数的名字而不需要具体的类型。
参数的类型应该在调用宏的地方指定。
请注意,宏定义中的参数名 GPIOx 和 GPIO_Pin 只是标识符,
用于在宏调用时接收传递的实际参数。
在调用宏的时候,你需要传递正确的参数,即正确的 GPIO 引脚和引脚编号。
*/
/*#define: 这是宏定义的关键字,它告诉编译器后面是一个宏定义。
LED_OFF: 这是宏的名称,你可以根据需要给宏取一个有意义的名称。
在这个例子中,我们将宏定义为 LED_OFF,表示用于熄灭 LED。
(A, B): 这是宏的参数列表,宏可以接受多个参数,用逗号分隔。
在这个例子中,宏有两个参数 A 和 B。这些参数是占位符,
用于在宏被调用时接收传递的实际参数。
GPIO_ResetBits(A, B): 这是宏的替换文本。宏在被调用时,
会将参数 A 和 B 替换到这里。
在这个例子中,GPIO_ResetBits(A, B) 表示调用了 GPIO_ResetBits 函数,
其中 A 和 B 分别替换为宏被调用时传递的实际参数。
通过这个宏定义,你可以在代码中使用 LED_OFF(GPIOx, GPIO_Pin)
来熄灭 LED。在实际调用宏时,
预处理器会将 GPIOx 和 GPIO_Pin 替换为宏定义中的 A 和 B 参数,
然后展开成 GPIO_ResetBits(GPIOx, GPIO_Pin),
从而实现了简便的 LED 熄灭操作。
*/
typedef enum {
RGB_R,
RGB_G,
RGB_B,
// 可以添加其他 LED 类型
} RGB_Type;
void RGB_GPIO_Config(RGB_Type RGBType);
#endif /*__BSP_LED_H*/
这是一个按键的电路图,我将按照本电路图进行讲解。
void KEY_GPIO_Config(){
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA,&GPIO_InitStruct);
}
-
void KEY_GPIO_Config()
这是一个函数的声明,表明这是一个没有参数和返回值的函数,用于配置按键所用的 GPIO 引脚。 -
GPIO_InitTypeDef GPIO_InitStruct;
这是一个结构体变量,类型是GPIO_InitTypeDef
,它用来存储 GPIO 初始化的配置信息。 -
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
这是一个函数调用,它用来使能GPIOA
对应的外设时钟。RCC
表示寄存器时钟控制器,APB2Periph_GPIOA
表示GPIOA
外设的时钟,ENABLE
是一个宏定义,表示使能。 -
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
这一行代码将GPIO_InitStruct
结构体中的GPIO_Mode
成员设置为GPIO_Mode_IN_FLOATING
,表示该 GPIO 引脚将被配置为浮空输入模式。 -
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
这一行代码将GPIO_InitStruct
结构体中的GPIO_Pin
成员设置为GPIO_Pin_0
,表示配置的是 GPIOA 的 Pin 0。 -
GPIO_Init(GPIOA, &GPIO_InitStruct);
这是一个函数调用,它将前面配置好的结构体内容应用于GPIOA
,从而完成对 GPIOA 引脚的初始化。
总之,这段代码是用来初始化一个 GPIO 引脚作为按键输入的配置。它首先使能了 GPIOA 外设的时钟,然后配置了 GPIO 引脚的工作模式为浮空输入模式(表示未连接任何电平时的状态)。最后,它将初始化配置应用到指定的 GPIO 引脚上。这样,你就可以通过读取这个 GPIO 引脚的状态来检测按键的按下。
uint8_t KEY1_Scan() {
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1) {
// 等待按键释放,进行按键消抖
while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1);
return 1; // 按键按下
}
return 0; // 按键未按下
}
-
uint8_t KEY1_Scan() {
:这是函数的开始,它声明了一个名为KEY1_Scan
的函数,该函数返回一个uint8_t
类型的值(即一个字节,范围在 0 到 255 之间)。 -
if ((GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)) == 1)
这是一个条件语句,它检查 GPIOA 的 Pin 0 是否为高电平(1)。GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)
是一个函数调用,用于读取 GPIOA 引脚 Pin 0 的状态。如果引脚状态为高电平,则进入条件判断的代码块。 -
while ((GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)) == 1);
这是一个循环语句,它在按键仍然处于按下状态时进行循环。它不断地检查 GPIOA 引脚 Pin 0 的状态,直到它变为低电平(0)为止。这是一个简单的按键消抖机制,确保只有一次按键按下被记录。 -
return 1;
如果检测到按键按下并且经过消抖,函数返回1
,表示按键按下。 -
else
如果 GPIOA 引脚 Pin 0 不是高电平,那么执行else
分支。 -
return 0;
在这个分支中,函数返回0
,表示按键没有按下。
总之,这段代码实现了按键的扫描函数 KEY1_Scan()
。它会检测 GPIOA 引脚 Pin 0 的状态,如果按键被按下且消抖结束,函数返回 1
,否则返回 0
。这个函数可以判断是否有按键按下,并且根据需要执行相应的操作。
如果你的按键没有消抖电容可以增加一个软件消抖的方式来达到按键消抖的效果
uint8_t KEY1_Scan(){
if((GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))==1){
delay(20);
while((GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))==1);
return 1;
}
else{
return 0;
}
}
这里增加了一个delay函数,来自于一个自己添加的dealy文件
delay.c
#include "stm32f10x.h"
#include "delay.h"
void delay(uint16_t time) // delay(1000)延时1秒
{
uint16_t i=0;
while(time--)
{
i=12000;
while(i--);
}
}
void initSysTick(void)
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //设置时钟源8分频
SysTick->CTRL |=SysTick_CTRL_TICKINT_Msk; //使能中断
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开定时器
SysTick->LOAD = 9; //随意设置一个重装载值
}
/**
*功能:微秒级延时函数
*参数:xus:具体延时时间(单位:微秒)
*返回值:None
*/
void delay_us(u32 xus)
{
SysTick->LOAD = 9* xus; //记9次为1us,1000次为1ms
SysTick->VAL = 0; //计数器归零
while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待计数完成
}
/**
*功能:毫秒级别函数延时
*参数:xus:具体延时时间(单位:微秒)
*返回值:None
*/
void delay_ms(u32 xms)
{
SysTick->LOAD = 9000; //记9次为1us,1000次为1ms
SysTick->VAL = 0; //计数器归零
while(xms--)
{
while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待单次计数完成
}
}
delay.h
#ifndef _DELAY_H_
#define _DELAY_H_
#include "stm32f10x.h"
void delay(uint16_t time); // delay(1000)?ó3ùò???
void initSysTick(void);
void delay_us(u32 xus);
void delay_ms(u32 xms);
#endif
在在需要使用延时函数的地方在开头#include "delay"就可以使用这个文件里面的函数了。