GPIO | 红外探测
小车的循迹功能就是通过4路红外探测黑线来实现的
GPIO 简介
GPIO就是从STM32芯片上引出的引脚,可以通过拉高/拉低电平等方式简单粗暴地控制,也可以使用TIM、UART等系统资源控制,也可以检测GPIO端口电平的高低来实现数据输入。
GPIO输出--以点亮LED为例
编程顺序
- 使能GPIO端口时钟
- 初始化GPIO引脚
- 控制GPIO输出高、低电平
代码分析
1.在.h文件中列出需要的GPIO的宏
编程时使用宏定义便于提高程序的可移植性
每个.h文件都include这个头文件
#include "stm32f10x.h"
/* 定义LED连接的GPIO端口, 用户只需要修改下面的代码即可改变控制的LED引脚 */
#define LED1_GPIO_PORT GPIOC /* GPIO端口 */
#define LED1_GPIO_CLK RCC_APB2Periph_GPIOC /* GPIO端口时钟 */
#define LED1_GPIO_PIN GPIO_Pin_2 /*LED1在C2上*/
#define LED2_GPIO_PORT GPIOC /* GPIO端口 */
#define LED2_GPIO_CLK RCC_APB2Periph_GPIOC /* GPIO端口时钟 */
#define LED2_GPIO_PIN GPIO_Pin_3 /*LED2在C3上*/
/*两种控制GPIO电平的宏定义方法*/
/* 使用标准的固件库控制IO*/
#define LED1(a) if (a) \
GPIO_SetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);\
else \
GPIO_ResetBits(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED2(a) if (a) \
GPIO_SetBits(LED2_GPIO_PORT,LED2_GPIO_PIN);\
else \
GPIO_ResetBits(LED2_GPIO_PORT,LED2_GPIO_PIN)
//SetBits为高电平,ResetBits为低电平
/* 直接操作寄存器的方法控制IO */
#define digitalHi(p,i) {p->BSRR=i;} //输出为高电平
#define digitalLo(p,i) {p->BRR=i;} //输出低电平
#define digitalToggle(p,i) {p->ODR ^=i;} //输出反转状态
/* 定义控制IO的宏 */
#define LED1_TOGGLE digitalToggle(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_HI digitalHi(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_LO digitalLo(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED2_TOGGLE digitalToggle(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_HI digitalHi(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_LO digitalLo(LED2_GPIO_PORT,LED2_GPIO_PIN)
//在实际使用中也可以根据开发板LED实际亮灭所需电平情况定义为LED1_ON.LED1_OFF
2.在.c文件中编写GPIO初始化函数
void LED_GPIO_Config(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启LED相关的GPIO外设时钟*/
RCC_APB2PeriphClockCmd( LED1_GPIO_CLK | LED2_GPIO_CLK , ENABLE);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*调用库函数,初始化GPIO*/
GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED2_GPIO_PIN;
/*调用库函数,初始化GPIO*/
GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);
/* 关闭所有led灯 */
GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
/* 关闭所有led灯 */
GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
}
这个初始化函数是利用库文件写的,首先要打开GPIO端口的时钟,然后向GPIO_InitTypeDef结构体里填入想要配置的相关信息,最后用GPIO_Init()完成配置
3.GPIO端口的8种工作模式
1、4种输入模式
(1)GPIO_Mode_IN_FLOATING 浮空输入
(2)GPIO_Mode_IPU 上拉输入
(3)GPIO_Mode_IPD 下拉输入
(4)GPIO_Mode_AIN 模拟输入
2、4种输出模式
(5)GPIO_Mode_Out_OD 开漏输出(带上拉或者下拉)
(6)GPIO_Mode_AF_OD 复用开漏输出(带上拉或者下拉)
(7)GPIO_Mode_Out_PP 推挽输出(带上拉或者下拉)
(8)GPIO_Mode_AF_PP 复用推挽输出(带上拉或者下拉)
最常用的就是前三种输入模式(根据外设实际情况而定),和复用推挽输出,速度多选50MHZ和10MHZ
编程顺序
- 使能GPIO端口时钟
- 初始化GPIO引脚
- 控制GPIO输出高、低电平
代码分析
1.在.h文件中列出需要的GPIO的宏
编程时使用宏定义便于提高程序的可移植性
每个.h文件都include这个头文件
#include "stm32f10x.h"
/* 定义LED连接的GPIO端口, 用户只需要修改下面的代码即可改变控制的LED引脚 */
#define LED1_GPIO_PORT GPIOC /* GPIO端口 */
#define LED1_GPIO_CLK RCC_APB2Periph_GPIOC /* GPIO端口时钟 */
#define LED1_GPIO_PIN GPIO_Pin_2 /*LED1在C2上*/
#define LED2_GPIO_PORT GPIOC /* GPIO端口 */
#define LED2_GPIO_CLK RCC_APB2Periph_GPIOC /* GPIO端口时钟 */
#define LED2_GPIO_PIN GPIO_Pin_3 /*LED2在C3上*/
/*两种控制GPIO电平的宏定义方法*/
/* 使用标准的固件库控制IO*/
#define LED1(a) if (a) \
GPIO_SetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);\
else \
GPIO_ResetBits(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED2(a) if (a) \
GPIO_SetBits(LED2_GPIO_PORT,LED2_GPIO_PIN);\
else \
GPIO_ResetBits(LED2_GPIO_PORT,LED2_GPIO_PIN)
//SetBits为高电平,ResetBits为低电平
/* 直接操作寄存器的方法控制IO */
#define digitalHi(p,i) {p->BSRR=i;} //输出为高电平
#define digitalLo(p,i) {p->BRR=i;} //输出低电平
#define digitalToggle(p,i) {p->ODR ^=i;} //输出反转状态
/* 定义控制IO的宏 */
#define LED1_TOGGLE digitalToggle(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_HI digitalHi(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_LO digitalLo(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED2_TOGGLE digitalToggle(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_HI digitalHi(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_LO digitalLo(LED2_GPIO_PORT,LED2_GPIO_PIN)
//在实际使用中也可以根据开发板LED实际亮灭所需电平情况定义为LED1_ON.LED1_OFF
2.在.c文件中编写GPIO初始化函数
void LED_GPIO_Config(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启LED相关的GPIO外设时钟*/
RCC_APB2PeriphClockCmd( LED1_GPIO_CLK | LED2_GPIO_CLK , ENABLE);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*调用库函数,初始化GPIO*/
GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED2_GPIO_PIN;
/*调用库函数,初始化GPIO*/
GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);
/* 关闭所有led灯 */
GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
/* 关闭所有led灯 */
GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
}
这个初始化函数是利用库文件写的,首先要打开GPIO端口的时钟,然后向GPIO_InitTypeDef结构体里填入想要配置的相关信息,最后用GPIO_Init()完成配置
3.GPIO端口的8种工作模式
1、4种输入模式
(1)GPIO_Mode_IN_FLOATING 浮空输入
(2)GPIO_Mode_IPU 上拉输入
(3)GPIO_Mode_IPD 下拉输入
(4)GPIO_Mode_AIN 模拟输入
2、4种输出模式
(5)GPIO_Mode_Out_OD 开漏输出(带上拉或者下拉)
(6)GPIO_Mode_AF_OD 复用开漏输出(带上拉或者下拉)
(7)GPIO_Mode_Out_PP 推挽输出(带上拉或者下拉)
(8)GPIO_Mode_AF_PP 复用推挽输出(带上拉或者下拉)
最常用的就是前三种输入模式(根据外设实际情况而定),和复用推挽输出,速度多选50MHZ和10MHZ
GPIO输入 以红外探测为例
红外传感器使用方法
通过查阅使用说明或者淘宝详情得知,当灵敏度调整正确时,在白色处输出低电平,LED亮;在黑色处输出高电平,LED灭。所以设计程序应该设置GPIO为上拉输入或浮空输入,即当探测到黑线时产生输入。这里我选择上拉输入,因为上拉输入更适合调整输入后产生的动作对应的延迟。
另外使用 GPIO_ReadInputDataBit(GPIOx_PORT,GPIOx_PIN) 来读取上拉输入。
XUNJI.h 里面的定义
//IN 1 2 3 4 - A12 A2 A4 A5
//循迹5就是前面壁障的红外探头
#define XUNJI1_GPIO_CLK RCC_APB2Periph_GPIOA
#define XUNJI1_GPIO_PORT GPIOA
#define XUNJI1_GPIO_PIN GPIO_Pin_12
#define XUNJI2_GPIO_CLK RCC_APB2Periph_GPIOA
#define XUNJI2_GPIO_PORT GPIOA
#define XUNJI2_GPIO_PIN GPIO_Pin_2
#define XUNJI3_GPIO_CLK RCC_APB2Periph_GPIOA
#define XUNJI3_GPIO_PORT GPIOA
#define XUNJI3_GPIO_PIN GPIO_Pin_4
#define XUNJI4_GPIO_CLK RCC_APB2Periph_GPIOA
#define XUNJI4_GPIO_PORT GPIOA
#define XUNJI4_GPIO_PIN GPIO_Pin_5
#define XUNJI5_GPIO_CLK RCC_APB2Periph_GPIOC
#define XUNJI5_GPIO_PORT GPIOC
#define XUNJI5_GPIO_PIN GPIO_Pin_0
#define R1 GPIO_ReadInputDataBit(XUNJI1_GPIO_PORT,XUNJI1_GPIO_PIN)
#define R2 GPIO_ReadInputDataBit(XUNJI2_GPIO_PORT,XUNJI2_GPIO_PIN)
#define L2 GPIO_ReadInputDataBit(XUNJI3_GPIO_PORT,XUNJI3_GPIO_PIN)
#define L1 GPIO_ReadInputDataBit(XUNJI4_GPIO_PORT,XUNJI4_GPIO_PIN) // L1 L2 R2 R1
#define F1 GPIO_ReadInputDataBit(XUNJI5_GPIO_PORT,XUNJI5_GPIO_PIN)
配置XUNJI.c,初始化GPIO端口,写读取GPIO输入函数
void xunji_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( XUNJI1_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = XUNJI1_GPIO_PIN|XUNJI2_GPIO_PIN|XUNJI3_GPIO_PIN|XUNJI4_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(XUNJI1_GPIO_PORT , &GPIO_InitStructure);
RCC_APB2PeriphClockCmd( XUNJI5_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = XUNJI5_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(XUNJI5_GPIO_PORT , &GPIO_InitStructure);
}
void Read_xunji_Date(void)
{
L1;
L2;
R2;
R1;
F1;
}
在 main.c 中,可以用循环和if语句来读取红外传感器的值并作出指令
while(1)
{
Read_xunji_Date();
if(L1==0&&L2==1&&R2==1&&R1==0)//1为探测到黑线,0为白线
{
Go();
delay_ms(100);
}
}
具体的循迹指令可以参考源文件
但是主要是要理解循迹的逻辑,具体的代码还要根据实际跑道进行优化