前言
最近想重温一下32标准库的内容,所以打算写几篇博客梳理一遍之前学过的知识点,图片和代码都是参考江科大的,江科大32教程非常不错,不管是小白还是大佬想学习32标准库都可以看他b站的课程。
一、EXIT外部中断
中断系统
中断执行流程:
STM32中断:
NVIC基本结构:
NIVC优先级分组:
EXIT简介:
EIXT基本结构:
AFIO复用IO口:
EXIT框图:
(1)对射式红外传感器计次
面包板接线:
代码示例:
CountSensor.c
#include "stm32f10x.h" // Device header uint16_t CountSensor_Count; //全局变量,用于计数 /** * 函 数:计数传感器初始化 * 参 数:无 * 返 回 值:无 */ void CountSensor_Init(void) { /*开启时钟*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟,外部中断必须开启AFIO的时钟 /*GPIO初始化*/ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB14引脚初始化为上拉输入 /*AFIO选择中断引脚*/ GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//将外部中断的14号线映射到GPIOB,即选择PB14为外部中断引脚 /*EXTI初始化*/ EXTI_InitTypeDef EXTI_InitStructure; //定义结构体变量 EXTI_InitStructure.EXTI_Line = EXTI_Line14; //选择配置外部中断的14号线 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //指定外部中断线使能 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //指定外部中断线为中断模式 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //指定外部中断线为下降沿触发 EXTI_Init(&EXTI_InitStructure); //将结构体变量交给EXTI_Init,配置EXTI外设 /*NVIC中断分组*/ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2 //即抢占优先级范围:0~3,响应优先级范围:0~3 //此分组配置在整个工程中仅需调用一次 //若有多个中断,可以把此代码放在main函数内,while循环之前 //若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置 /*NVIC配置*/ NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量 NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //选择配置NVIC的EXTI15_10线 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定NVIC线路的抢占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1 NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设 } /** * 函 数:获取计数传感器的计数值 * 参 数:无 * 返 回 值:计数值,范围:0~65535 */ uint16_t CountSensor_Get(void) { return CountSensor_Count; } /** * 函 数:EXTI15_10外部中断函数 * 参 数:无 * 返 回 值:无 * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行 * 函数名为预留的指定名称,可以从启动文件复制 * 请确保函数名正确,不能有任何差异,否则中断函数将不能进入 */ 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); //清除外部中断14号线的中断标志位 //中断标志位必须清除 //否则中断将连续不断地触发,导致主程序卡死 } }
CountSensor.h
#ifndef __COUNT_SENSOR_H #define __COUNT_SENSOR_H void CountSensor_Init(void); uint16_t CountSensor_Get(void); #endif
main.c
#include "stm32f10x.h" // Device header #include "OLED.h" #include "CountSensor.h" int main(void) { /*模块初始化*/ OLED_Init(); //OLED初始化 CountSensor_Init(); //计数传感器初始化 /*显示静态字符串*/ OLED_ShowString(1, 1, "Count:"); //1行1列显示字符串Count: while (1) { OLED_ShowNum(1, 7, CountSensor_Get(), 5); //OLED不断刷新显示CountSensor_Get的返回值 } }
OLED驱动代码在上一篇发过,这里就不发了,链接:STM32标准库基础知识-CSDN博客
(2)旋转编码器计次
旋转编码器简介:
硬件原理图:
面包板接线:
代码示例:
Encoder.c
#include "stm32f10x.h" // Device header int16_t Encoder_Count; //全局变量,用于计数旋转编码器的增量值 /** * 函 数:旋转编码器初始化 * 参 数:无 * 返 回 值:无 */ void Encoder_Init(void) { /*开启时钟*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟,外部中断必须开启AFIO的时钟 /*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); //将PB0和PB1引脚初始化为上拉输入 /*AFIO选择中断引脚*/ GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//将外部中断的0号线映射到GPIOB,即选择PB0为外部中断引脚 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//将外部中断的1号线映射到GPIOB,即选择PB1为外部中断引脚 /*EXTI初始化*/ EXTI_InitTypeDef EXTI_InitStructure; //定义结构体变量 EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1; //选择配置外部中断的0号线和1号线 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //指定外部中断线使能 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //指定外部中断线为中断模式 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //指定外部中断线为下降沿触发 EXTI_Init(&EXTI_InitStructure); //将结构体变量交给EXTI_Init,配置EXTI外设 /*NVIC中断分组*/ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2 //即抢占优先级范围:0~3,响应优先级范围:0~3 //此分组配置在整个工程中仅需调用一次 //若有多个中断,可以把此代码放在main函数内,while循环之前 //若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置 /*NVIC配置*/ NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量 NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //选择配置NVIC的EXTI0线 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定NVIC线路的抢占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1 NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设 NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; //选择配置NVIC的EXTI1线 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定NVIC线路的抢占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //指定NVIC线路的响应优先级为2 NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设 } /** * 函 数:旋转编码器获取增量值 * 参 数:无 * 返 回 值:自上此调用此函数后,旋转编码器的增量值 */ int16_t Encoder_Get(void) { /*使用Temp变量作为中继,目的是返回Encoder_Count后将其清零*/ /*在这里,也可以直接返回Encoder_Count 但这样就不是获取增量值的操作方法了 也可以实现功能,只是思路不一样*/ int16_t Temp; Temp = Encoder_Count; Encoder_Count = 0; return Temp; } /** * 函 数:EXTI0外部中断函数 * 参 数:无 * 返 回 值:无 * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行 * 函数名为预留的指定名称,可以从启动文件复制 * 请确保函数名正确,不能有任何差异,否则中断函数将不能进入 */ void EXTI0_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line0) == SET) //判断是否是外部中断0号线触发的中断 { /*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/ 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号线的中断标志位 //中断标志位必须清除 //否则中断将连续不断地触发,导致主程序卡死 } } /** * 函 数:EXTI1外部中断函数 * 参 数:无 * 返 回 值:无 * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行 * 函数名为预留的指定名称,可以从启动文件复制 * 请确保函数名正确,不能有任何差异,否则中断函数将不能进入 */ 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 "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定时器
TIM简介:
定时器类型:
高级定时器框图:
通用定时器框图:
基本定时器框图:
定时中断基本结构:
预分频器时序:
计数器时序:
PSC(预分频器的值),ARR(计数周期的值)
例如定时一秒,晶振频率为72mHz,则1 = 72mHz / (PSC+1) / (ARR+1)
PSC可为7200-1,ARR可为10000-1
计数器无序装时序:
计数器有序装时序:
STM32标准库外部中断和定时器知识点总结-2