概述:STM32微控制器上旋转编码器和计数传感器的应用,着重介绍了通过中断驱动实现的增量计数以及通过OLED显示模块进行计数显示的功能。
代码内容:
对射式红外传感器计次Encoder.c#include "stm32f10x.h" // Device headeruint16_t CountSensor_Count;/** * 函 数:计数传感器初始化 * 参 数:无 * 返 回 值:无 */void CountSensor_Init(void)//初始化{ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //开启GPIOB时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //开启AFIO时钟 //EXTI与NVIC不需要开启时钟 //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);//初始化GPIOB外设 //AFIO配置 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14); //EXTI配置 EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line = EXTI_Line14;//选择中断源位置 EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//选择事件还是中断 //EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发(离开后触发+1) //EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿触发 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//上升下降都触发 EXTI_Init(&EXTI_InitStructure);//初始化EXTI //NVIC配置 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组(两位抢占,两位相应) NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//选择芯片对应通道 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//0-3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//0-3 NVIC_Init(&NVIC_InitStructure); }/** * 函 数:获取计数传感器的计数值 * 参 数:无 * 返 回 值:计数值,范围: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) == 1 || GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14) == 0) //上升沿若设置为0,则离开加一 //下降沿若设置为1,则挡住加一 //若上升下降都触发若设置为 0|1,则挡住加一,离开也加一 //这里的判断是为了防止数据跳跃幅度大 //传感器输出高电平灭,输出低电平亮 //传感器输入高电平亮,输入低电平灭 { CountSensor_Count++; } EXTI_ClearITPendingBit(EXTI_Line14);//完成中断后清除中断标志位 } }main.c#include "stm32f10x.h" // Device header#include "Delay.h"#include "OLED.h"#include "CountSensor.h"uint8_t KeyNum;int main(void){ OLED_Init(); CountSensor_Init(); OLED_ShowString(1,1,"Count:"); while(1) { OLED_ShowNum(1,7,CountSensor_Get(),5); } }旋转编码器计次Encoder.c#include "stm32f10x.h" // Device headerint16_t Encoder_Count;/** * 函 数:旋转编码器初始化 * 参 数:无 * 返 回 值:无 */void Encoder_Init(void){ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //开启GPIOB时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //开启AFIO时钟 //EXTI与NVIC不需要开启时钟 //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);//初始化GPIOB外设 //AFIO配置 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);//配置PB^1端口 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);//配置PB^0端口 //EXTI配置 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;//下降沿触发 //EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿触发 //EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//上升下降都触发 EXTI_Init(&EXTI_InitStructure);//初始化EXTI //NVIC配置 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组(两位抢占,两位相应) NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//选择芯片端口0对应通道 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//0-3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//0-3 NVIC_Init(&NVIC_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;//选择芯片端口1对应通道 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//0-3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//0-3 NVIC_Init(&NVIC_InitStructure);}/** * 函 数:EXTI0外部中断函数 * 参 数:无 * 返 回 值:无 * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行 * 函数名为预留的指定名称,可以从启动文件复制 * 请确保函数名正确,不能有任何差异,否则中断函数将不能进入 */void EXTI0_IRQHandler(void)//端口0触发中断(左旋){ if(EXTI_GetITStatus(EXTI_Line0) == SET)//检查EXTI是否被置位SET,如果置位则进行中断程序 { if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0)//进行判断另一个引脚 { Encoder_Count--; } EXTI_ClearITPendingBit(EXTI_Line0);//清除标志位 }}/** * 函 数:EXTI1外部中断函数 * 参 数:无 * 返 回 值:无 * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行 * 函数名为预留的指定名称,可以从启动文件复制 * 请确保函数名正确,不能有任何差异,否则中断函数将不能进入 */void EXTI1_IRQHandler(void)//端口1触发中断(右旋){ if(EXTI_GetITStatus(EXTI_Line1) == SET)//检查EXTI是否被置位SET,如果置位则进行中断程序 { if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0) == 0)//进行判断另一个引脚 { Encoder_Count++; } EXTI_ClearITPendingBit(EXTI_Line1);//清除标志位 }}/** * 函 数:旋转编码器获取增量值 * 参 数:无 * 返 回 值:自上此调用此函数后,旋转编码器的增量值 */int16_t EncoderCount_Get(void){ int16_t Tmp; /*使用Tmp变量作为中继,目的是返回Encoder_Count后将其清零*/ /*在这里,也可以直接返回Encoder_Count 但这样就不是获取增量值的操作方法了 也可以实现功能,只是思路不一样*/ Tmp = Encoder_Count; Encoder_Count = 0; return Tmp;}main.c#include "stm32f10x.h" // Device header#include "Delay.h"#include "OLED.h"#include "Encoder.h"int16_t Num;int main(void){ OLED_Init(); Encoder_Init(); OLED_ShowString(1,1,"Num:"); while(1) { Num += EncoderCount_Get();//这里的函数得到的是对当前数进行操作(加或减)的增量值 OLED_ShowSignedNum(1,5,Num,5);//这里要显示有符号数 } }
[color=var(--text-primary)][color=var(--tw-prose-body)]用于STM32F10x系列微控制器的旋转编码器和计数传感器的程序。这两个模块都是用于监测旋转运动的设备,一个用于计数,一个用于获取增量值。 下面是对代码的一些建议: 共同点:初始化函数: 对旋转编码器和计数传感器的初始化函数进行了合理的设置,包括GPIO、EXTI、NVIC等配置。 中断处理函数: 中断处理函数对应的中断优先级等设置也较为合理。 主函数: 主函数中通过调用相关的获取函数,并使用OLED显示结果,整体结构清晰。
旋转编码器模块:计数传感器模块:- [color=var(--tw-prose-bold)]计数:
计数传感器中断处理函数中对计数的逻辑也较为清晰,通过判断当前引脚状态来进行计数。
建议与改进:中断处理函数时间: 中断处理函数最好不要执行过长时间的操作,尤其是在嵌套中断的情况下。可以在中断处理函数中设置标志位,然后在主循环中处理。 硬件相关操作: 尽量不要在中断函数中执行和主函数相同的硬件相关操作,以免造成冲突。 OLED显示频率: 如果OLED的显示频率过高,可能会影响整体性能。可以考虑使用定时器中断来控制OLED的更新频率。 可读性: 在代码中加入注释,对一些关键的逻辑进行说明,提高代码的可读性。 错误处理: 在中断处理函数中可以添加错误处理,例如对计数值的溢出进行处理。 模块化: 如果可能,可以将不同功能划分成模块,提高代码的模块化程度。
以上建议是基于目前提供的代码而言的,具体的改进还需要结合实际应用场景和需求。
|