STM32-GPIO输入输出-红外探测
STM32-GPIO输入输出-红外探测

STM32-GPIO输入输出-红外探测

GPIO | 红外探测

小车的循迹功能就是通过4路红外探测黑线来实现的

GPIO 简介

GPIO就是从STM32芯片上引出的引脚,可以通过拉高/拉低电平等方式简单粗暴地控制,也可以使用TIM、UART等系统资源控制,也可以检测GPIO端口电平的高低来实现数据输入。

GPIO输出--以点亮LED为例

编程顺序

  1. 使能GPIO端口时钟
  2. 初始化GPIO引脚
  3. 控制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

编程顺序

  1. 使能GPIO端口时钟
  2. 初始化GPIO引脚
  3. 控制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);
      }  
}

具体的循迹指令可以参考源文件

但是主要是要理解循迹的逻辑,具体的代码还要根据实际跑道进行优化

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注