打印
[其他ST产品]

STM32旋转编码器和计数传感器应用:中断驱动的增量计数与计数显示

[复制链接]
109|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
狗啃模拟|  楼主 | 2024-2-23 12:58 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
概述: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显示结果,整体结构清晰。

旋转编码器模块:
  • 增量值获取: EncoderCount_Get 函数的设计较为巧妙,通过中继变量 Tmp 来获取增量值,并在函数内清零 Encoder_Count。
  • 编码器方向判断: 在中断处理函数中,对旋转方向的判断较为简洁,通过读取另一引脚的状态来判断。

计数传感器模块:
  • [color=var(--tw-prose-bold)]计数: 计数传感器中断处理函数中对计数的逻辑也较为清晰,通过判断当前引脚状态来进行计数。
建议与改进:
  • 中断处理函数时间: 中断处理函数最好不要执行过长时间的操作,尤其是在嵌套中断的情况下。可以在中断处理函数中设置标志位,然后在主循环中处理。
  • 硬件相关操作: 尽量不要在中断函数中执行和主函数相同的硬件相关操作,以免造成冲突。
  • OLED显示频率: 如果OLED的显示频率过高,可能会影响整体性能。可以考虑使用定时器中断来控制OLED的更新频率。
  • 可读性: 在代码中加入注释,对一些关键的逻辑进行说明,提高代码的可读性。
  • 错误处理: 在中断处理函数中可以添加错误处理,例如对计数值的溢出进行处理。
  • 模块化: 如果可能,可以将不同功能划分成模块,提高代码的模块化程度。

以上建议是基于目前提供的代码而言的,具体的改进还需要结合实际应用场景和需求。











使用特权

评论回复
沙发
呐咯密密| | 2024-2-23 13:16 | 只看该作者
整理一下格式和字体,太乱了,看不清

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

49

主题

648

帖子

0

粉丝