使用硬件电路
从此开始,需要开始使用模块化的编程方式(即是建立.c与.h文件)
一、新建组
1.新建组Hardware用于存放硬件驱动代码
2.新建LED.c与LED.h,并且在
(1)LED.c中加入
#include "stm32f10x.h" // Device header
(2)在LED.h中加入防止头文件重复包含的代码:
#ifndef _LED_H
#define _LED_H
XXXXXXXXXXXXXXXXXXXX
#endif
用于声明的函数放在"XXXXX"处,而且需要再打完#endif代码后回车,在其后面空上一行(由于该模式无法空行,特此提示)
二、在LED.c中写入LED初始化文件代码
1.GPIOA端口1/2的初始化该代码,与点亮LED中的代码基本相同,只不过是作为函数表示出来,使用和删除更加方便
void LED_Init ()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init (GPIOA,&GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_1 | GPIO_Pin_2);
}
2.在.h文件中
加入以下代码:防止头文件重复包含
#ifndef _LED_H
#define _LED_H
#endif
另外#endif需要在后面加上一行空行,减少警告
3.本处共使用两个LED灯,分别接在GPIOA的1、2号位上。使用两个按键,分别接在GPIOB的1、11号位上。
分别利用GPIO_ResetBits(设低电平)与GPIO_SetBits(设高电平)两个函数。其实直接调用两个函数也没有关系,但是为了更为简洁,阅读时的方便,就这样设置了。同样,也作为自己设置函数的联系,类似于C语言学习时期利用函数输出Holle World一样.
void LED1_NO(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
void LED1_OFF(void)
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);
}
之后,同理设置GPIOA的2号位的LED灯的开关函数.
4.如图所示在头文件中进行声明
#ifndef _LED_H
#define _LED_H
void LED_Init (void);
void LED1_NO(void);
void LED1_OFF(void);
void LED2_NO(void);
void LED2_OFF(void);
#endif
5.在主函数中调用这几个函数,并加入Delay.h用于延时,实现类似于流水灯的效果
int main()
{
LED_Init();
while (1)
{
LED1_NO();
LED2_OFF();
Delay_ms(500);
LED1_OFF();
LED2_NO();
Delay_ms(500);
}
}
三、建立Key.h 文件,用于接受按键相关函数
1.由图可知,两个按键分别连在PB1与PB11上,即需要初始化这两个端口,并且为了能够获取这两个端口输入的值,所以需要配置为上拉输入模式。初始化方式与前面初始化A1A2端口类似但是此处是作为上拉输入模式初始化端口,其他差别不大
void Key_Init (void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
2.建立函数,用于存储按键按下带来的信息
①在建立函数之前,先深入了解一下输入函数。
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
a.uint8_t GPIO_ReadInputDataBit(),读取输入数据寄存器的某一位引脚的数据,符合该题仅有两个引脚安有按键的条件,是我们之后会用到的函数
b.uint16_t GPIO_ReadInputData(),读取输入数据寄存器的整个外设,如一整个GPIOB的输入信号。
c.uint8_t GPIO_ReadOutputDataBit(),读取输出数据寄存器的某一位引脚数据,用于探测程序此时在该引脚处输出的电平高低,用于下一步的操作
d.uint16_t GPIO_ReadOutputData(),读取输出寄存器的整个外设
②在这里,我们使用GPIO_ReadInputDataBit函数。
a.定义函数,返回类型为uint8_t,在主函数总使用KeyNum来接受这个值。
uint8_t Key_GetNum(void);
b.我们要区别两个按键按下是的效果,那么可以定义,PB1按键为1号按键,PB11按键为2号按键。对应的,在没有任何按键被按下的情况下,返回值KeyNum为0,保持静默状态。当点击后1号按键后赋值为一,2号按键赋值为2,以此来区分。
c.但是我们使用的按键一般是机械按键,也就是说会有震荡导致按下以及松手的一瞬间都会有一定的震荡,干扰单片机对信息的识别。故而需要加入延时函数,延时20ms即可。而我们在生活中一般都是松手后灯才会亮灭,所以需要加入一个循环函数来检查是否已经松手。按键1与按键2的检测逻辑相同
uint8_t Key_GetNum(void)
{
uint8_t KeyNum = 0;
if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0)
{
Delay_ms(20);
while (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0);
Delay_ms(20);
KeyNum = 1;
}
if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11 ) == 0)
{
Delay_ms(20);
while (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11) == 0);
Delay_ms(20);
KeyNum = 2;
}
return KeyNum;
}
③定义一个全局变量来接受Key_GetNum的值,在主函数中引用两个函数,按下按键1保持1号灯亮,按下2号按键使其熄灭
uint8_t KeyNum;
int main()
{
Key_Init();
LED_Init();
while (1)
{
KeyNum = Key_GetNum();
if (KeyNum == 1)
{
LED1_ON();
}
if (KeyNum == 2)
{
LED1_OFF();
}
}
}
④但是如果是让两个按键全部都只服务于LED1又会显得LED2没用,我们接下来会让按键1控制LED1的亮灭,按键2控制LED2的亮灭。
a.这样我们需要一个取反函数,在知道当前LED的状态的前提下,对它的状态进行取反:我们需要先前提及的函数GPIO_ReadOutputDataBit()读取输出数据寄存器的某一位引脚数据
void LED1_Turn(void)
{
if (GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_1) == 0)
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);
}
else
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
}
b.函数Key_GetNum输出的值都只是有即时效应,按下松手后KeyNum就会恢复默认值0,直到按键1/2再次被按下。此时,我们根据再次按下获得的返回值确定对哪个LED进行取反。
uint8_t KeyNum;
int main()
{
Key_Init();
LED_Init();
while (1)
{
KeyNum = Key_GetNum();
if (KeyNum == 1)
{
LED1_Turn();
}
if (KeyNum == 2)
{
LED2_Turn();
}
}
}