GPIO通用输入输出口
GPIO:8种输入输出模式
浮空输入 | 可读取引脚电平,若引脚悬空,电平不确定 |
上拉输入 | 可读取引脚电平,内部接上拉电阻,悬空时默认为高电平 |
下拉输入 | 可读取引脚电平,内部接下拉电阻,悬空时默认为低电平 |
模拟输入 | GPIO无效,引脚直接接入内部ADC |
开漏输出 | 可输出引脚电平,高电平为高阻态,低电平接VSS |
推挽输出 | 可输出引脚电平,高电平接VDD,低电平接VSS |
复用开漏输出 | 由片上外设提供,高电平为高阻态,低电平接VSS |
复用推挽输出 | 由片上外设提供,高电平接VDD,低电平接VSS |
GPIO: 引脚电平在0-3.3v之间,部分可以容忍5v的电平
GPIO:在输出模式下可以控制端口输出高低电平,驱动外部设备
GPIO:输入模式下可以读取端口高低电平和电压,获取外部输入的数据
GPIO:函数
复位GPIO | void GPIO_DeInit(GPIO_TypeDef* GPIOx); |
复位AFIO | void GPIO_AFIODeInit(void); |
初始化GPIO | void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct); |
结构体初始化 | void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct); |
获取输入数据寄存器中的一位 | 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); |
把指定的端口设置为高电平 | void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); |
GPIO 把指定的端口设置为低电平 | void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); |
GPIO写入数据位,根据第三个参数的值设置指定的端口 | void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal); |
第一个参数指定端口,第二个参数可以同时对16个端口进行写入操作 | void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal); |
RCC:时钟
开启AHB外设时钟控制 | void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState); |
开启RCCAPB2外设时钟控制 | void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState); |
开启RCCAPB1外设时钟控制 | void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState); |
GPIO外设控制
1:第一步开启RCCAPB2外设时钟控制
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
2: 初始化GPIO结构体
GPIO_InitTypeDef GPIO_InitStructure; //初始化GPIO结构体
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO模式,赋值为推挽输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO引脚,赋值为第0号引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度,赋值为50MHz
#if
GPIO8种输入输出模式
typedef enum
{
GPIO_Mode_AIN = 0x0, // 模拟输入
GPIO_Mode_IN_FLOATING = 0x04, //浮空输入
GPIO_Mode_IPD = 0x28, //上拉输入
GPIO_Mode_IPU = 0x48, //下拉输入
GPIO_Mode_Out_OD = 0x14, //开漏输出
GPIO_Mode_Out_PP = 0x10, //推挽输出
GPIO_Mode_AF_OD = 0x1C, //复用开漏输出
GPIO_Mode_AF_PP = 0x18 //复用推挽输出
}GPIOMode_TypeDef;
// GPIO的引脚每一个GPIO口有16个引脚
#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */
#define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */
#define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */
#define GPIO_Pin_5 ((uint16_t)0x0020) /*!< Pin 5 selected */
#define GPIO_Pin_6 ((uint16_t)0x0040) /*!< Pin 6 selected */
#define GPIO_Pin_7 ((uint16_t)0x0080) /*!< Pin 7 selected */
#define GPIO_Pin_8 ((uint16_t)0x0100) /*!< Pin 8 selected */
#define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */
#define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */
#define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */
#define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */
#define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */
#define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */
#define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */
#define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */
// GPIO输入输出的频率
typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
#endif
3:初始化GPIO
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO:ResetBits控制灯的闪烁
接线图工程模版
#include "stm32f10x.h" // Device header
#include "Delay.h"
// 操作GPIO 1: 使用rcc开启gpio时钟
// 使用GPIO_Init函数初始化GPIO
// 使用输入或输出函数控制GPIO口
int main(void){
// 开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// GPIO的结构体
GPIO_InitTypeDef GPIO_InitStructure;
// 选择输出模式为推挽输出
// 选择输出模式为推挽输出PP表示的是推挽输出模式,OD表示开漏输出模式
// 推挽输出高电平和低电平均有输出能力,开漏输出自由在低电平的情况下才有驱动能力
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
// 选择输出的引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
// 输出速度
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// GPIO初始化结构体的地址放到GPIO——Init的第二个参数
GPIO_Init(GPIOA,&GPIO_InitStructure);
// GPIO设置A0口的灯亮,最后一个参数Bit_RESET表示的是清除端口的值为0,清除端口的值为1
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);// Bit_RESET时LED灯是点亮状态,当为Bit_SET时LED灯是熄灭状态
while(1){
// 实现led灯光闪烁的功能需要在主循环中写上点亮led灯熄灭led灯光的操作并在while中循环
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);
Delay_ms(500);
// 添加延时函数进行延时
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);
Delay_ms(500);
/* 使用GPIO点亮led灯
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
Delay_ms(500);
GPIO_SetBits(GPIOA,GPIO_Pin_0);
Delay_ms(500);
*/
}
}
GPIO:控制LED灯闪烁
#include "stm32f10x.h" // Device header
#include "Delay.h"
// 操作GPIO 1: 使用rcc开启gpio时钟
// 使用GPIO_Init函数初始化GPIO
// 使用输入或输出函数控制GPIO口
int main(void){
// 开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// GPIO的结构体
GPIO_InitTypeDef GPIO_InitStructure;
// 选择输出模式为推挽输出
// 选择输出模式为推挽输出PP表示的是推挽输出模式,OD表示开漏输出模式
// 推挽输出高电平和低电平均有输出能力,开漏输出自由在低电平的情况下才有驱动能力
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
// 选择输出的引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
// 输出速度
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// GPIO初始化结构体的地址放到GPIO——Init的第二个参数
GPIO_Init(GPIOA,&GPIO_InitStructure);
// GPIO设置A0口的灯亮,最后一个参数Bit_RESET表示的是清除端口的值为0,清除端口的值为1
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);// Bit_RESET时LED灯是点亮状态,当为Bit_SET时LED灯是熄灭状态
while(1){
// 实现led灯光闪烁的功能需要在主循环中写上点亮led灯熄灭led灯光的操作并在while中循环
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);
Delay_ms(500);
// 添加延时函数进行延时
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);
Delay_ms(500);
/* 使用GPIO点亮led灯
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
Delay_ms(500);
GPIO_SetBits(GPIOA,GPIO_Pin_0);
Delay_ms(500);
*/
}
}
GPIO:控制蜂鸣器发声
#include "stm32f10x.h" // Device header
#include "Delay.h"
/*
1: 操作GPIO 1: 使用rcc开启gpio时钟
2: 使用GPIO_Init函数初始化GPIO
3: 使用输入或输出函数控制GPIO口
*/
int main(void){
// 开启GPIOA时钟,该程序连接的是GPIOA的端口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
// GPIO的结构体
GPIO_InitTypeDef GPIO_InitStructure;
// 选择输出模式为推挽输出PP表示的是推挽输出模式,OD表示开漏输出模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
// 选择输出的引脚,GPIO_Pin_All将16个端口都设置为推挽输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
// 输出速度
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// GPIO初始化结构体的地址放到GPIO——Init的第二个参数
GPIO_Init(GPIOB,&GPIO_InitStructure);
while(1){
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
Delay_ms(700);
}
}
STM32库函数使用方式
第一种方式是,打开头文件,拉到最下面,查看有哪些函数,然后查看函数的定义函数的使用方法是
第二种方式,查看库函数用户手册,所有函数的介绍个使用方法
第三种方式,最后一种方式是百度,借助别人的代码进行使用
GPIO:LED流水灯
步骤:1:开启RCCAPB2外设时钟控制
2:初始化GPIO结构体
3: 初始化GPIO
接线原理图
代码实现
#include "stm32f10x.h" // Device header
#include "Delay.h"
/*
1: 操作GPIO 1: 使用rcc开启gpio时钟
2: 使用GPIO_Init函数初始化GPIO
3: 使用输入或输出函数控制GPIO口
*/
uint16_t arr[16] = {0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080};
int main(void){
// 函数的声明
void LED_Run(void);
void LED_All(void);
// 开启GPIOA时钟,该程序连接的是GPIOA的端口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// GPIO的结构体
GPIO_InitTypeDef GPIO_InitStructure;
// 选择输出模式为推挽输出PP表示的是推挽输出模式,OD表示开漏输出模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
// 选择输出的引脚,GPIO_Pin_All将16个端口都设置为推挽输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
// 输出速度
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// GPIO初始化结构体的地址放到GPIO——Init的第二个参数
GPIO_Init(GPIOA,&GPIO_InitStructure);
while(1){
//LED_Run();
LED_All();
}
}
void LED_Run(void){
//控制A号引脚,对引脚的位置进行按位取反
GPIO_Write(GPIOA,~0x0001); // 0000 0000 0000 0001 二进制转换为16进制的写法
Delay_ms(500);
GPIO_Write(GPIOA,~0x0002); // 0000 0000 0000 0010 二进制转换为16进制的写法
Delay_ms(500);
GPIO_Write(GPIOA,~0x0004); // 0000 0000 0000 0100 二进制转换为16进制的写法
Delay_ms(500);
GPIO_Write(GPIOA,~0x0008); // 0000 0000 0000 1000 二进制转换为16进制的写法
Delay_ms(500);
GPIO_Write(GPIOA,~0x0010); // 0000 0000 0001 0000 二进制转换为16进制的写法
Delay_ms(500);
GPIO_Write(GPIOA,~0x0020); // 0000 0000 0010 0000 二进制转换为16进制的写法
Delay_ms(500);
GPIO_Write(GPIOA,~0x0040); // 0000 0000 0100 0000 二进制转换为16进制的写法
Delay_ms(500);
GPIO_Write(GPIOA,~0x0080); // 0000 0000 1000 0000 二进制转换为16进制的写法
Delay_ms(500);
}
void LED_All(void){
// for循环
int len = sizeof(arr)/sizeof(arr[0]);
int i = 0;
for(i = 0; i< len; i++){
GPIO_Write(GPIOA,~arr[i]); // 0000 0000 0000 0001 二进制转换为16进制的写法
Delay_ms(500);
}
}
GPIO:按键控制LED亮灭
步骤:1:创建LED.C文件和LED.H文件
2: 创建KEY.H文件和KEY.C文件
3:开起RCC外设时钟控制
4: 分别初始化GPIO
5: 编写实现功能函数
接线图
KEY.C文件
#include "stm32f10x.h" // Device header
#include "Delay.h"
void Key_Init(void)
{
// RCCAPB2外设时钟控制,开启GPIOB的外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟
//GPIO初始化
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);
}
uint8_t Key_GetNum(void)
{
uint8_t KeyNum = 0; //定义变量,默认键码值为0
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) //读PB1输入寄存器的状态,如果为0,则代表按键1按下
{
Delay_ms(20); //延时消抖
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0); //等待按键松手
Delay_ms(20); //延时消抖
KeyNum = 1; //置键码为1
}
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0) //读PB11输入寄存器的状态,如果为0,则代表按键2按下
{
Delay_ms(20); //延时消抖
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0); //等待按键松手
Delay_ms(20); //延时消抖
KeyNum = 2; //置键码为2
}
return KeyNum; //返回键码值,如果没有按键按下,所有if都不成立,则键码为默认值0
}
KEY.H文件
#ifndef __KEY_H__
#define __KEY_H__
void Key_Init(void);
uint8_t Key_GetNum(void);
#endif
LED.C文件
#include "stm32f10x.h" // Device header
/**
* 函 数:LED初始化
* 参 数:无
* 返 回 值:无
*/
void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
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); //将PA1和PA2引脚初始化为推挽输出
GPIO_SetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_2); //设置PA1和PA2引脚为高电平
}
/**
* 函 数:LED1开启
* 参 数:无
* 返 回 值:无
*/
void LED1_ON(void)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_1); //设置PA1引脚为低电平
}
/**
* 函 数:LED1关闭
* 参 数:无
* 返 回 值:无
*/
void LED1_OFF(void)
{
GPIO_SetBits(GPIOA, GPIO_Pin_1); //设置PA1引脚为高电平
}
/**
* 函 数:LED1状态翻转
* 参 数:无
* 返 回 值:无
*/
void LED1_Turn(void)
{
if (GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1) == 0) //获取输出寄存器的状态,如果当前引脚输出低电平
{
GPIO_SetBits(GPIOA, GPIO_Pin_1); //则设置PA1引脚为高电平
}
else //否则,即当前引脚输出高电平
{
GPIO_ResetBits(GPIOA, GPIO_Pin_1); //则设置PA1引脚为低电平
}
}
/**
* 函 数:LED2开启
* 参 数:无
* 返 回 值:无
*/
void LED2_ON(void)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_2); //设置PA2引脚为低电平
}
/**
* 函 数:LED2关闭
* 参 数:无
* 返 回 值:无
*/
void LED2_OFF(void)
{
GPIO_SetBits(GPIOA, GPIO_Pin_2); //设置PA2引脚为高电平
}
/**
* 函 数:LED2状态翻转
* 参 数:无
* 返 回 值:无
*/
void LED2_Turn(void)
{
if (GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_2) == 0) //获取输出寄存器的状态,如果当前引脚输出低电平
{
GPIO_SetBits(GPIOA, GPIO_Pin_2); //则设置PA2引脚为高电平
}
else //否则,即当前引脚输出高电平
{
GPIO_ResetBits(GPIOA, GPIO_Pin_2); //则设置PA2引脚为低电平
}
}
LED.H文件
#ifndef __LED_H
#define __LED_H
void LED_Init(void);
void LED1_ON(void);
void LED1_OFF(void);
void LED1_Turn(void);
void LED2_ON(void);
void LED2_OFF(void);
void LED2_Turn(void);
#endif
main.c文件
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h"
#include "KEY.h"
uint8_t KeyNum; //定义用于接收按键键码的变量
int main(void)
{
LED_Init(); //LED初始化
Key_Init(); //按键初始化
while (1)
{
KeyNum = Key_GetNum(); //获取按键键码
if (KeyNum == 1) //按键1按下
{
LED1_Turn(); //LED1翻转
}
if (KeyNum == 2) //按键2按下
{
LED2_Turn(); //LED2翻转
}
}
}
GPIO:光敏传感器控制蜂鸣器
步骤:1:分别初始化蜂鸣器和光敏电阻的GPIO,并开启RCC外设手时钟控制的时钟
2:编写函数实现在光线较暗的情况下蜂鸣器发出声音
接线图
BUZZER.C和BUZZER.H
Buzzzer.c
#include "stm32f10x.h" // Device header
void Buzzer_Init(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOA的时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //将PA1和PA2引脚初始化为推挽输出
/*设置GPIO初始化后的默认电平*/
GPIO_SetBits(GPIOB, GPIO_Pin_12); //设置PA1和PA2引脚为高电平
}
void Buzzer_ON(void)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12); //设置PA1引脚为低电平
}
void Buzzer_OFF(void)
{
GPIO_SetBits(GPIOB, GPIO_Pin_12); //设置PA1引脚为高电平
}
void Buzzer_Turn(void)
{
if (GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_12) == 0) //获取输出寄存器的状态,如果当前引脚输出低电平
{
GPIO_SetBits(GPIOB, GPIO_Pin_12); //则设置PA1引脚为高电平
}
else //否则,即当前引脚输出高电平
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12); //则设置PA1引脚为低电平
}
}
Buzzer.h
#ifndef __BUZZER_H__
#define __BUZZER_H__
void Buzzer_Init(void);
void Buzzer_ON(void);
void Buzzer_OFF(void);
void Buzzer_Turn(void);
#endif
LightSensor.c 和 LightSensor.h
LightSensor.c
#include "stm32f10x.h" // Device header
void LightSensor_Init(void){
// 初始化程序开启GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB, GPIO_Pin_13);
}
// 编写返回端口值的函数
uint8_t LightSensor_Get(void){
return GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13);
}
LightSensor.h
#ifndef __LIGHT_SENSOR_H__
#define __LIGHT_SENSOR_H__
void LightSensor_Init(void);
uint8_t LightSensor_Get(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "Buzzer.h"
#include "LightSensor.h"
int main(void)
{
// 初始化蜂鸣器
Buzzer_Init();
// 初始化光敏传感器
LightSensor_Init();
while (1)
{
if(LightSensor_Get() == 1){ // 光线比较暗的情况下
Buzzer_ON(); // 蜂鸣器鸣叫
}else{
Buzzer_OFF();
}
}
}
EXTI中断
中断的定义:
在主程序运行的过程中,出现特定的中断触发条件(也就是中断源)使CPU暂停当前正在运行的程序转而去处理中断程序,处理完成后又返回之前暂停的程序继续执行
中断的优先等级,当有多个中断源同时申请中断时,CPU会根据当前中断源的轻重缓急进行裁决,优先响应更加紧急的中断源。
中断嵌套:当一个中断程序正在运行时,又有一个更紧急优先级更高的中断源申请中断,GPU再次暂停当前的中断程序,转而去处理新的中断程序,处理完成后依次进行返回。
中断的执行流程
EXTI:外部中断函数
重定义初始化,恢复默认状态 | void EXTI_DeInit(void); |
中断初始化 | void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct); |
结构体初始化引出参数 | void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct); |
软件触发外部中断 | void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line); |
获取指定的中断标志位 | FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line); |
清除指定的中断标志位 | void EXTI_ClearFlag(uint32_t EXTI_Line); |
获取中断标志位 | ITStatus EXTI_GetITStatus(uint32_t EXTI_Line); |
清除中断挂起标志位 | void EXTI_ClearITPendingBit(uint32_t EXTI_Line); |
AFIO:中断引脚选择和复用功能引脚重映射
AFIO函数
复位AFIO的外设 | void GPIO_AFIODeInit(void); |
锁定GPIO配置 | void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); |
AFIO事件输出配置(使用不多) | void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource); |
AFIO的事件输出功能,使用不多 | void GPIO_EventOutputCmd(FunctionalState NewState); |
配置引脚重映射,第一个是重映射的方式,第二个参数是新的状态 | void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState); |
配置AFIO的数据选择器,选择中断引脚 | void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource); |
和以太网有关 | void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface); |
1:AFIO中断引脚选择:是一个中断引脚选择器可以在前面GPIO外设的16个引脚中选择一个连接到EXTI边沿检测及控制中所以
2:相同的Pin不能同时触发中断,因为PA0,PB0,PC0通过AFIO选择后只有其中一个可以连接到EXTI的通道0上,PB1,PC1等也只有一个可以接入EXTI上
复用功能引脚重映射
NVIC:嵌套中断向量控制器->用于统一分配中断优先级和管理中断
NVIC函数:
选择中断分组,参数为中断分组的方式 | void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); |
根据结构体里面指定的参数初始化NVIC | void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct); |
设置中断向量表 | void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset); |
系统低功耗配置 | void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState); |
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource); |
NVIC的基本结构
NVIC优先级
def : nvic的优先级可以由优先级寄存器的4为决定,可以对这4位进行切分,分为高N为的抢占式优先级和低n位的响应式优先级(4-n),抢占式优先级可以嵌套中断,响应式优先级高的可以优先排队,抢占式优先级和响应式优先级相同的可以按照中断号排队。
分组方式 | 抢占式优先级 | 响应式优先级 |
0 | 0 取值为0 | 4取值为0-15 |
1 | 1取值为0-1 | 3取值为0-7 |
2 | 2取值为0-3 | 2取值为0-3 |
3 | 3取值为0-7 | 1取值为0-1 |
4 | 4取值为0-15 | 0 取值为0 |
响应式优先级:也可以称为插队式优先级哪个优先级高优先处理哪个
抢占式优先级:优先级高的可以优先被处理,相当于CPU可以暂时中断当前处理的程序,优先处理优先级更高的程序,该程序执行完成后再执行原先没有执行完毕的程序,也可以称之为嵌入式中断优先级
EXTI:对射式红外传感器统计次数
步骤:
1: 开启RCCAPB和RCCAFIO中断引脚控制
2: 配置GPIO输入输出
3: 配置EXTI外部中断
4: 配置NVIC(嵌套中断向量控制器)
接线图:
CounterSensor.c
#include "stm32f10x.h"
uint16_t CountSensor_Count;
void CountSensor_Init(void){
// 开启RCC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
// 配置AFIO中断引脚选择
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
// 初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
// 引出GPIO参数
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_14 ;
GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
// 初始化GPIO
GPIO_Init(GPIOB,&GPIO_InitStructure);
// AFIO中断引脚选择
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
// 初始化中断引脚选择
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line14;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure);
// NVIC中断分组,配置抢占式优先级和响应式优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// NVIC配置
// 初始化NVIC结构体
NVIC_InitTypeDef NVIC_InitStructure;
// 将NVIC的结构体全都引出来
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; // 配置NVIC中断线
NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE ; //使能NVIC中断线
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1 ; // 配置NVIC的抢占式优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 配置NVIC的响应式优先级
}
// 获取传感器计数的值
uint16_t CountSensor_Get(void){
return CountSensor_Count;
}
//编写中断函数
void EXTI15_10_IRQHandler(void){
if(EXTI_GetITStatus(EXTI_Line14) == SET){ // 判断是外部中断14号线发生中断
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14) == 0){
CountSensor_Count ++; // 每次触发一次中断自动增加一次
}
// 清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
CounterSensor.h
#ifndef __COUNT_SENSOR_H__
#define __COUNT_SENSOR_H__
void CountSensor_Init(void);
void EXTI15_10_IRQHandler(void);
uint16_t CountSensor_Get(void);
#endif
KEY.C
#include "stm32f10x.h" // Device header
#include "Delay.h"
// 按键初始化
void Key_Init(void){
// 开启RCCAPB外设时钟控制
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
// 初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
//配置GPIO输入输出模式
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
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
// 按键控制
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 =1;
}
return KeyNum;
}
KEY.H
#ifndef __KEY_H__
#define __KEY_H__
void Key_Init(void);
uint8_t Key_GetNum(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "CounterSensor.h"
int main(void)
{
// 模块初始化
OLED_Init();
CountSensor_Init();
// 显示静态字符串
OLED_ShowString(1,1,"Count:");
while (1)
{
OLED_ShowNum(1,7,CountSensor_Get(),5);
}
}
EXTI:外部中断旋转编码器计次
旋转编码器简介
旋转编码器原理:输出一个正交波形,两个波形之间相差的角度为90度,识别编码器是正向选装还是反向旋转。
旋转编码器的硬件电路
接线图
步骤:
1:RCC开启APB2与AFIO时钟
2:开启GPIO
3:配置外部中断EXTI
4:配置NVIC
5:使能NVIC
Encoder.c 代码
#include "stm32f10x.h" // Device header
int16_t Encoder_Count;
void Encoder_Init(void){
// 开启GPIO和AFIO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
// 开启GPIO时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_0 | GPIO_Pin_1 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
// 配置中断引脚
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line =EXTI_Line0 | EXTI_Line1 ;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger =EXTI_Trigger_Falling;
// 配置NVIC中断引脚控制
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 配置中断分组
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
// 配置中断通道
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructure);
}
// 使用中间变量对选装编码器的计次进行初始化
int16_t Encoder_Get(void){
int16_t temp;
temp = Encoder_Count;
Encoder_Count = 0;
return temp;
}
// 第一个中断
void EXTI0_IRQHandler(void){
//判断中断是否发生
if(EXTI_GetITStatus(EXTI_Line0) == SET){
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) //PB0的下降沿触发中断,此时检测另一相PB1的电平,目的是判断旋转方向
{
Encoder_Count --; //此方向定义为反转,计数变量自减
}
}
EXTI_ClearITPendingBit(EXTI_Line0); //清除外部中断0号线的中断标志位
//中断标志位必须清除
//否则中断将连续不断地触发,导致主程序卡死
}
}
void EXTI1_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line1) == SET) //判断是否是外部中断1号线触发的中断
{
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) //PB1的下降沿触发中断,此时检测另一相PB0的电平,目的是判断旋转方向
{
Encoder_Count ++; //此方向定义为正转,计数变量自增
}
}
EXTI_ClearITPendingBit(EXTI_Line1); //清除外部中断1号线的中断标志位
//中断标志位必须清除
//否则中断将连续不断地触发,导致主程序卡死
}
}
Encoder.h代码
#ifndef __ENCODER_H__
#define __ENCODER_H__
void Encoder_Init(void);
int16_t Encoder_Get(void);
#endif
main.c代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"
int16_t Num; //定义待被旋转编码器调节的变量
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Encoder_Init(); //旋转编码器初始化
/*显示静态字符串*/
OLED_ShowString(1, 1, "Num:"); //1行1列显示字符串Num:
while (1)
{
Num += Encoder_Get(); //获取自上此调用此函数后,旋转编码器的增量值,并将增量值加到Num上
OLED_ShowSignedNum(1, 5, Num, 5); //显示Num
}
}
编译后没有发生错误
TIM定时器
定时器基本概念:
定时器是计算机系统中的一种重要的计时和事件触发机制。它是一种硬件或软件组件,用于按照预定的时间间隔执行任务、触发事件或生成中断。
定时器的主要功能包括:
1. 计时功能:定时器可以通过内部或外部的时钟源来测量时间间隔。它可以提供毫秒、微秒、纳秒等不同精度的计时。
2. 事件触发功能:定时器可以设置一个固定的时间间隔,当时间间隔到达时,就会触发预先定义的事件,例如发送一个中断信号、执行特定的任务或调用一个回调函数。
3. 周期性执行任务:定时器可以反复执行同一个任务,例如定期执行一个程序或周期性地发送数据。这对于需要按固定时间间隔进行重复操作的应用程序非常有用。
4. 超时检测:定时器可以用于超时检测,例如在网络通信中,可以设置一个定时器来检测数据包是否在预定的时间内到达,如果没有到达,则可以重新发送数据包或采取其他应对措施。
5. 节能功能:在一些移动设备和嵌入式系统中,定时器可以用于管理功耗。通过定时器,设备可以在空闲或非活动状态下进入省电模式,并在预定的时间间隔后自动唤醒。
定时器可以由硬件实现,如专用的定时器芯片或计时器模块,也可以由操作系统或编程语言提供的定时器库函数进行软件实现。
总之,定时器在计算机系统中扮演着重要的角色,用于计时、事件触发、周期性执行任务和超时检测等应用。它们在各个领域中都有广泛的应用,包括操作系统、通信、网络、嵌入式系统、游戏开发等。
定时器的基本类型
基本定时器框图
基本定时器的理解
通用定时器程序框图
....
定时器的基本结构
计数器的计数频率公式:
计数器计数频率 : ck_cnt = ck-psc /(psc + 1)
计数器溢出频率: ck-cnt_ov = ck-psc /(psc + 1) / (arr + 1)
定时器:TIM编码器接口
def:编码器接口定义
1: 编码器接口可以接收增量(正交)编码器信号,根据编码器产生的正交脉冲信号自动控制CNT自增或自减,从而指示编码器的位置,旋转方向和旋转速度。
2:每个高级定时器和通用定时器都拥有一个编码器接口
3:两个通用的引脚分别借用了输入捕获通道1和输入捕获通道二
正交编码器波形
中间相差大概90度
正向旋转
边沿 | 另一相状态 |
A相上升沿 | B相低电平 |
A相下降沿 | B相高电平 |
B相上升沿 | A相高电平 |
B相下降沿 | A相低电平 |
反向旋转
边沿 | 另一相状态 |
A相上升沿 | B相高电平 |
A相下降沿 | B相低电平 |
B相上升沿 | A相低电平 |
B相下降沿 | A相高电平 |
旋转编码器计数:正转是向上计数,反转是向下计数
编码器接口的基本结构
1:不反向根据高低电平输出波形
2:反向取反高低电平反转
配置GPIO的输入模式:
配置GPIO的输入模式是可以看接在这个引脚的外部模块输出电平,如果外部模块默认高电平就选择上拉模式,如果外部模块默认低电平就选择下拉模式,配置引脚的电平和外部的默认模式保持一致
定时器:旋转编码器计数编码
接线图
Encoder.c代码
#include "stm32f10x.h" // Device header
int16_t Encoder_Count;
void Encoder_Init(void){
// RCC开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
// 开起AFIO中断引脚控制
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
// GPIO初始化
GPIO_InitTypeDef GPIO_InitStruce;
// 选择模式
GPIO_InitStruce.GPIO_Mode =GPIO_Mode_IPU;
// 选择引脚
GPIO_InitStruce.GPIO_Pin =GPIO_Pin_0 | GPIO_Pin_1 ;
// 选择时钟频率
GPIO_InitStruce.GPIO_Speed = GPIO_Speed_50MHz;
// 初始化GPIO
GPIO_Init(GPIOB,&GPIO_InitStruce);
// 将外部中断0号线映射到GPIOB,选择PB0作为中断引脚
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);
// 将外部中断1号线映射到GPIOB,选择PB1作为中断引脚
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);
/*
中断初始化
*/
// 定义结构体变量
EXTI_InitTypeDef EXTI_InitStructure;
// 选择外部中断0号线和1号线
EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;
// 使能外部中断线
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
// 选择外部中断的模式
EXTI_InitStructure.EXTI_Mode =EXTI_Mode_Interrupt ;
// 选择外部中断的触发方式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
// 初始化外部中断
EXTI_Init(&EXTI_InitStructure);
// 配置NVIC分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 配置NVIC
NVIC_InitTypeDef NVIC_InitStructure;
// 选择NVIC的EXTI0号线
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
// 使能外部中断线
NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
// 使能外部中断线
NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructure);
}
int16_t Encoder_Get(void){
int16_t Temp;
Temp = Encoder_Count;
Encoder_Count = 0;
return Temp;
}
void EXTI0_IRQHandler(void){
if(EXTI_GetITStatus(EXTI_Line0) == SET){
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0) == 0){
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) //PB0的下降沿触发中断,此时检测另一相PB1的电平,目的是判断旋转方向
{
Encoder_Count --; //此方向定义为反转,计数变量自减
}
}
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
void EXTI1_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line1) == SET) //判断是否是外部中断1号线触发的中断
{
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) //PB1的下降沿触发中断,此时检测另一相PB0的电平,目的是判断旋转方向
{
Encoder_Count ++; //此方向定义为正转,计数变量自增
}
}
EXTI_ClearITPendingBit(EXTI_Line1); //清除外部中断1号线的中断标志位
//中断标志位必须清除
//否则中断将连续不断地触发,导致主程序卡死
}
}
Encoder.h
#ifndef __ENCODER_H__
#define __ENCODER_H__
void Encoder_Init(void);
int16_t Encoder_Get(void);
#endif
main.c代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"
int16_t Num; //定义待被旋转编码器调节的变量
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Encoder_Init(); //旋转编码器初始化
/*显示静态字符串*/
OLED_ShowString(1, 1, "Num:"); //1行1列显示字符串Num:
while (1)
{
Num += Encoder_Get(); //获取自上此调用此函数后,旋转编码器的增量值,并将增量值加到Num上
OLED_ShowSignedNum(1, 5, Num, 5); //显示Num
}
}
......