目录
一、什么是GPIO口
1、GPIO口的作用
GPIO是单片机与外界进行信息交换的唯一窗口;
2、GPIO口的描述和数量
STM32F407VG(E)T6有100引脚,其中包括5组IO口
5组:GPIOA,GPIOB,到E 每一组有0~15号(16个) //16*5==80
GPIOA:0 1 2 3......15 16个
GPIOB:0 1 2 3......15 16个
GPIOC:0 1 2 3......15 16个
GPIOD:0 1 2 3......15 16个
GPIOE:0 1 2 3......15 16个
PA5表示:GPIOA组的5号IO口
3、GPIO口控制器
GPIO控制器: 每组都有一个GPIO控制器控制16个IO口
二、如何配置GPIO口
GPIO框图:
1、GPIO口控制器框图
1.1 一个GPIO口可以配置几种模式
①输入
直接读(检测)IO口的电平状态
②通用输出
内核直接控制IO输出高低电平
③复用功能
a.复用输入:从IO进来的数据信号要经过片上外设(串口控制器),再到内核
b.复用输出:内核将数据信号发送到片上外设,片上外设再通过IO口传送出去
注:在M4中选用复用模式时没有严格的区分输入输出
④模拟模式
a.模拟输入:外界的模拟量(电压)通过IO口进入到ADC控制器,ADC控制器转换成数字量
b.模拟输出: 内核将数字信号发送到DAC上,DAC将数字信号转换成电压值,通过IO出去
1.2 通用输出和通用输入的细节说明
①通用输入部分
通用输入说明:
a.怎么读
①如何读取电平状态,通过读输入数据寄存器的对应位是0还是1,来确定IO高低电平
②上下拉电阻作用
在通用输入的时候,也就是在读某个IO的电平的时候, 一定要让此IO口先保持一个电平状态,这样才能检测到不同电平状态。
b.如何保持电平状态
①可以通过芯片内部的上下拉电阻,由于是弱上下拉一般不用
②硬件外界一个上拉电阻或者下拉电阻
c.读之前怎么配置
打开GPIOx的时钟
配置GPIOx的模式(输入模式 00 输出模式 01 复用模式 10 模拟模式 11) --->端口模式寄存器
配置GPIOx的上下拉(无上下拉-0 上拉-01 下拉-10) --->端口上下拉寄存器
读取GPIOx的数据--->端口输入数据寄存器
②通用输出部分
通用输出说明:
a.怎么输出
①输出原则
对输出数据寄存器的对应位写0 或 1,就可以控制对应编号的IO口输出低/高电平
②输出类型
推挽输出:IO口可以输出高电平,也可以输出低电平
开漏输出:IO口只能输出低电平
所以:在输出的时候,要选择推挽或者开漏
③关于场效应管(MOS管)的补充
④对于输出上下拉起到什么作用
- 放大电信号
- 降低功耗,但是电阻太大,电信号太弱,不用
b.输出之前怎么配置
打开GPIOx的时钟
配置GPIOx的模式(输入模式 00 输出模式 01 复用模式 10 模拟模式 11) --->端口模式寄存器
配置GPIOx的输出类型(推挽-0 开漏-1) --->端口输出类型寄存器
配置GPIOx的输出速度(2Mhz-00 25Mhz-01 50Mhz-10 ) --->端口输出速度寄存器
配置GPIOx的上下拉(无上下拉-0 上拉-01 下拉-10) --->端口上下拉寄存器
配置GPIOx的输出数据(具体看什么电平触发) --->端口输出数据寄存器
2、GPIO口寄存器描述
2.1 如何看寄存器
看中文参考手册---GPIO寄存器内容
2.2 知道哪些模式具体使用那些寄存器配置
①通用输入
端口模式寄存器
端口上下拉寄存器
端口输入数据寄存器
②通用输出
端口模式寄存器
端口输出类型寄存器
端口输出速度寄存器
端口上下拉寄存器
端口输出数据寄存器
③复用模式
输入:
端口模式寄存器
端口上下拉寄存器
复用功能寄存器
输出:
端口模式寄存器
端口输出类型寄存器
端口输出速度寄存器
端口上下拉寄存器
复用功能寄存器
④模拟模式
输入:
端口模式寄存器
输出:
端口模式寄存器
2.3 总结配置思路
在本篇着重输入输出,复用模拟在后面再继续细说.....
①输入:
打开GPIOx的时钟
配置GPIOx的模式
配置GPIOx的上下拉
②输出:
打开GPIOx的时钟
配置GPIOx的模式
配置GPIOx的输出类型
配置GPIOx的输出速度
配置GPIOx的上下拉
配置GPIOx的输出数据
三、具体使用GPIO口
1、关于LED灯的使用
需求1:控制核心板的四个LED灯亮灭
分析:
原理图:①具体管脚 ② 用到IO的模式灯 ③ 如何亮灭
LED1--------PC4
LED2--------PC5
LED3--------PC6
LED4--------PC7
管脚要配置通用输出模式---推挽输出
管脚输出低电平亮 输出高电平灭
思路:
所用IO的初始化配置
{
//端口模式配置------通用输出
//输出类型配置-------推挽
//输出速度配置-------2M
//上下拉配置-----------无
//输出数据寄存器配置-----关
}
LED灯初始化配置函数(四个灯):
/*
函数名: LED_init
函数功能:LED灯IO初始化
返回值:void
形参:void
函数说明:
LED1--------PC4
LED2--------PC5
LED3--------PC6
LED4--------PC7
管脚要配置通用输出模式---推挽输出
高电平熄灭,低电平点亮
*/
void LED_init(void)
{
// 打开GPIOC的时钟
RCC->AHB1ENR |= 1 << 2;
// 配置GPIOC的模式(输出模式 01)
GPIOC->MODER |= 0x55 << 8;
// 配置GPIOC的输出类型(推挽-0 开漏-1)
GPIOC->OTYPER &= ~(0xf << 4);//PC4-P7配置为推挽输出
// 配置GPIOC的输出速度(2Mhz-00 25Mhz-01 50Mhz-10)
GPIOC->OSPEEDR |= 0x55 << 8;
// 配置GPIOC的上下拉(无上下拉-00 上拉-01 下拉-10)
GPIOC->PUPDR &= ~(0xff << 8);
// 配置GPIOC的输出数据(具体看什么电平触发)
GPIOC->ODR |= 0xf << 4;
}
所有灯开函数
/*
函数名: ALL_LED_ON
函数功能:4个LED灯亮
返回值:void
形参:void
函数说明:
*/
void ALL_LED_ON(void)
{
LED_ON;//宏定义 #define LED_ON (GPIOC->ODR &= ~(0xf << 4))
}
所有灯关函数:
/*
函数名: ALL_LED_OFF
函数功能:4个LED灯灭
返回值:void
形参:void
函数说明:
*/
void ALL_LED_OFF(void)
{
LED_OFF;//宏定义 #define LED_OFF (GPIOC->ODR |= 0xf << 4)
}
某一个灯开函数:
/*
函数名: LEDx_ON
函数功能:某一个灯亮
返回值:void
形参:u8 x
函数说明:
*/
void LEDx_ON(u8 x)
{
switch(x)
{
case 1:LED1_ON;break;
case 2:LED2_ON;break;
case 3:LED3_ON;break;
case 4:LED4_ON;break;
}
}
某一个灯关函数:
/*
函数名: LEDx_OFF
函数功能:某一个灯灭
返回值:void
形参:u8 x
函数说明:
*/
void LEDx_OFF(u8 x)
{
switch(x)
{
case 1:LED1_OFF;break;
case 2:LED2_OFF;break;
case 3:LED3_OFF;break;
case 4:LED4_OFF;break;
}
}
需求2:流水灯函数(计数思想切换灯)
分析:
延时函数会影响其他功能的运行,故需要删掉延时函数,具体方法如下:
/**方案一**/
LED1_ON;
Delay_ms(500);
LED1_OFF;
LED2_ON;
Delay_ms(500);
LED2_OFF;
LED3_ON;
Delay_ms(500);
LED3_OFF;
LED4_ON;
Delay_ms(500);
LED4_OFF;
//缺点:1、灯多 2、延时影响其它功能的运行
//说明:while一圈,切换4个LED
/**方案二**/
LEDx_ON(n);
Delay_ms(100);
LEDx_OFF(n);
n++;
if(n > 4)
n = 1;
//缺点:2、延时影响其它功能的运行
//说明:while一圈,切换1个LED
/**方案三**/
LEDx_ON(n);
count++;
if(count == 200000)
{
LEDx_OFF(n);
n++;
if(n > 4)
{
n = 1;
}
count = 0;
}
//说明:while 200000圈,切换1个LED
封装流水灯函数:
/*
函数名: LED_flash
函数功能:控制流水灯速度
返回值:void
形参:u8 speed
函数说明:
计数思想切换灯
*/
void LED_flash(u8 speed)
{
static u8 n = 1;
static u32 count = 0;
LEDx_ON(n);
count++;
if(count >= 100000 * speed)
{
LEDx_OFF(n);
n++;
if(n > 4)
{
n = 1;
}
count = 0;
}
}
2、关于蜂鸣器的使用
需求:控制蜂鸣器的开关
分析:
原理图:①具体IO ②用到IO的模式 ③蜂鸣器如何启动
BEEP---------PE0
管脚要配置通用输出模式---推挽输出
高电平开 低电平关
思路:
蜂鸣器所用的IO口初始配置
{
//端口时钟使能
//端口模式配置
//端口输出类型
//端口输出速度
//上下拉
//输出数据寄存器---关---低
}
蜂鸣器IO初始化函数:
/*
函数名: Beep_init
函数功能:蜂鸣器IO初始化
返回值:void
形参:void
函数说明:
BEEP---------PE0
高电平开(NPN) 低电平关
管脚要配置通用输出模式---推挽输出
*/
void Beep_init(void)
{
//打开GPIOE的时钟
RCC->AHB1ENR |= 1 << 4;
//配置GPIOE的模式---输出01
GPIOE->MODER |= 1 << 0;
//配置GPIOE的输出类型---推挽0
GPIOE->OTYPER &= ~(1 << 0);
//配置GPIOE的速度---50hz
GPIOE->OSPEEDR |= 2 << 0;
//配置GPIOE的上下拉---无
GPIOE->PUPDR &= ~(3 << 0);
//配置GPIOE的输出数据
GPIOE->ODR &= ~(1 << 0);
}
3、关于按键的使用
需求1:四个按键控制四个LED
分析:原理图:①具体IO ②用到IO的模式 ③内核如何识别按键是否按下
通过原理图确定按键是配置为通用输入模式
KEY1-----------PA0 检测到高电平表示按键按下
KEY2-----------PE2 检测到低电平表示按键按下
KEY3-----------PE3 检测到低电平表示按键按下
KEY4-----------PE4 检测到低电平表示按键按下
思路:
按键所用IO初始化函数
{
//时钟使能 A E
//端口模式配置--------通用输入
//上下拉-----------------浮空
}
按键扫描函数
{
//检测按键是否按下
//延时消抖
//再次检测按键是否按下
//赋值键值
//等待按键松开
//返回键值
}
由于等待按键会影响其它功能的运行(卡住CPU),故需要优化程序
那直接删了等待按键松开这个步骤会怎样?
结果是每次按下按键,会不断返回所按下按键的按键值,这就造成了LED灯一直在闪。这不是我们想要的效果,那需要怎么优化呢?
优化要求:
1、不能卡死程序(不能等待按键松开)
2、按下一次按键只能返回一个键值(标志位思想)
解决方案:
按下按键锁住标志位,松开按键解锁标志位
当按下按键没有松开后,锁住标志位,不允许再识别按键;
当松开按键后,解锁标志位,下次按下按键又允许识别按键。
优化程序后的具体思路:
返回值类型 按键扫描函数(void)
{
//键值默认值
//定义静态标志位变量
if(按键按下)
{
//延时消抖
if(按键真的按下 && 标志位)
{
//赋值键值
//锁住标志位
}
if(松开按键)
{
//解锁标志位
}
}
//返回键值
}
按键IO初始化函数:
/*
函数名: Key_init
函数功能:按键IO初始化
返回值:void
形参:void
函数说明:
KEY1-----------PA0 检测到高电平表示按键按下
KEY2-----------PE2 检测到低电平表示按键按下
KEY3-----------PE3 检测到低电平表示按键按下
KEY4-----------PE4 检测到低电平表示按键按下
管脚要配置通用输入模式---浮空
*/
void Key_init(void)
{
// 打开GPIOA、GPIOE的时钟
RCC->AHB1ENR |= 1 << 0;
RCC->AHB1ENR |= 1 << 4;
// 配置GPIOA、GPIOE的模式(输入模式 00)
GPIOC->MODER &= ~(3 << 0);
GPIOE->MODER &= ~(0x3f << 4);
// 配置GPIOA、GPIOE的上下拉(无上下拉-00 上拉-01 下拉-10)
GPIOC->PUPDR &= ~(3 << 0);
GPIOE->PUPDR &= ~(0x3f << 4);
}
按键扫描函数:
/*
函数名: Key_scan
函数功能:按键扫描返回键值
返回值:u8
形参:void
函数说明:
按键扫描过程
1、检测按键按下
2、延时消抖
3、再次检测按键按下
4、赋值键值
5、等待按键松开
6、返回键值
*/
u8 Key_scan(void)
{
u8 key_val = 0xff;
static u8 key_flag = 1;
//检测按键按下
if((KEY1 || KEY2 || KEY3 || KEY4) && key_flag)
{
//延时消抖
Delay_ms(20);
//再次检测按键按下
if(KEY1)
{
//赋值键值
key_val = 1;
}
if(KEY2)
{
//赋值键值
key_val = 2;
}
if(KEY3)
{
//赋值键值
key_val = 3;
}
if(KEY4)
{
//赋值键值
key_val = 4;
}
//锁定标志位
key_flag = 0;
}
//判断按键抬起
if((!KEY1) && (!KEY2) && (!KEY3) && (!KEY4))
{
key_flag = 1;
}
//返回键值
return key_val;
}
需求2: 四个按键控制流水灯
分析:
按键1控制开
按键2控制关
按键3控制加速
按键4控制减速
u8 keynum;
u8 flag = 0;
u8 speed = 5;
while(1)
{
keynum = Key_scan();
switch(keynum)
{
case 1:flag = 1;break;//按键1打开流水灯
case 2:flag = 0;LED_OFF;break;//按键2关闭流水灯
case 3:speed -= 1;if(speed < 1){speed = 5;}break;//按键3加速
case 4:speed += 1;if(speed > 10){speed = 5;}break;//按键4减速
}
if(flag == 1)
{
LED_flash(speed);
}
}
优化需求:
关闭流水灯时候,调速是无作用的
u8 keynum;
u8 flag = 0;
u8 speed = 5;
while(1)
{
keynum = Key_scan();
switch(keynum)
{
case 1:flag = 1;break;//按键1打开流水灯
case 2:flag = 0;LED_OFF;break;//按键2关闭流水灯
case 3:if(flag){speed -= 1;if(speed < 1)speed = 5;}break;//按键3加速
case 4:if(flag){speed += 1;if(speed > 10)speed = 5;}break;//按键4减速
}
if(flag == 1)
{
LED_flash(speed);
}
}
4、关于传感器的使用
4.1 传感器分类:
通过检测电平信号类
通过ADC转换获取数据类
通过通信获取数据类
4.2 检测电平信号类传感器的使用
①火焰检测传感器
功能:
检测火焰 有无
引脚:
VCC----3.3V
GND----GND
DO -----PC9
如何获取信号或者数据:
检测到火焰,DO引脚就会出低电平,没有火焰,就会出高电平
DO脚接到主控芯片的PC9 IO口上
IO口配置为通用输入模式,
程序设计:
火焰传感器所用IO口初始化程序(火焰传感器初始化函数)
/*
函数名:flame_init
函数功能:火焰传感器IO初始化
返回值:void
形参:void
函数说明:
VCC --- 3.3V
GND --- GND
DO --- PC9
检测到低电平,表示有火焰;否则,反之
管脚要配置通用输入模式---浮空
*/
void flame_init(void)
{
//打开GPIOC的时钟
RCC->AHB1ENR |= 1 << 2;
//配置GPIOC的模式--输入模式---浮空
GPIOC->MODER &= ~(3 << 18);
//配置GPIOC的上下拉--无
GPIOC->PUPDR &= ~(3 << 18);
}
获取火焰传感器数据函数
/*
函数名:flame_data
函数功能:获取火焰传感器数据
返回值:u8
形参:void
函数说明:
返回0:检测到有火焰
返回1:检测到无火焰
*/
u8 flame_data(void)
{
if(GPIOC->IDR & (1 << 9))
{
return 1;
}
else
{
return 0;
}
}
②红外避障传感器
功能:
检测障碍物2cm~30cm
引脚:
VCC-------3.3V
GND-------GND
OUT-------PC8
如何获取信号或者数据:
检测到障碍物,OUT引脚就会出低电平,没有障碍物,就会出高电平
OUT脚接到主控芯片的PC8IO口上
IO口配置为通用输入模式,
检测到IO高低电平就知道是否有障碍物
程序设计:
传感器所有用IO口初始化配置(传感器初始配置)
/*
函数名:infra_init
函数功能:红外避障传感器IO初始化
返回值:void
形参:void
函数说明:
VCC --- 3.3V
GND --- GND
OUT --- PC8
检测到低电平,表示有火焰;否则,反之
管脚要配置通用输入模式---浮空
*/
void infra_init(void)
{
//打开GPIOC的时钟
RCC->AHB1ENR |= 1 << 2;
//配置GPIOC的模式--输入模式---浮空
GPIOC->MODER &= ~(3 << 16);
//配置GPIOC的上下拉--无
GPIOC->PUPDR &= ~(3 << 16);
}
传感器信号获取函数
/*
函数名:infra_data
函数功能:获取红外避障传感器数据
返回值:void
形参:u8
函数说明:
返回0:检测到有障碍物
返回1:检测到无火焰
*/
u8 infra_data(void)
{
if(GPIOC->IDR & (1 << 8))
{
return 1;
}
else
{
return 0;
}
}
总结:
GPIO口
- 什么是GPIO口
GPIO口的作用
GPIO口的描述和数量
GPIO口控制器
2.如何配置GPIO口
2.1 GPIO口控制器框图
一个GPIO口可以配置几种模式(能知道每种模式的使用场景)
通用输出和通用输入的细节说明
通用输入和通用输出 都需要配置什么
2.2 GPIO口寄存器描述
如何看寄存器
知道哪些模式具体使用那些寄存器配置
总结配置思路
1.3 设计程序
3.具体使用GPIO口
关于LED灯的使用
关于蜂鸣器的使用
关于按键的使用
关于传感器的使用