测试平台:STM32F103C8T6 库函数使用标准版

在许多应用中进行人机交互通常也会使用按键功能进行设置或地震地震某个参数。
这次使用带有按键功能地 EC 11旋转编码器进行设计长按、短按、双击、旋转递增递减功能。
为了节省MCU利用率资源自然我们使用中断+通用定时器的方式进行 不使用轮询方式进行设计。

电路设计。


说明 SW1为按键功能 WS2、SW3为可扭动的旋转编码器。
可扭动的旋转编码器,连接的电容为100pF电容值,实际情况可使用示波器工具进行测量方波信号以获得完美的脉冲信号。

引脚中断初始化。

//----------------旋转编码器旋转变量----------------//
extern int Encoder_status;   //旋转编码器当前状态
extern u8  Hxe8_Twinkle;     //传递数码管闪烁时间extern u16 Hxe8_paragraph;   //传递数码管位选段显示
extern u32 Hxe8_data;        //传递数码管显示数据
//----------------脉冲参数变量的----------------//
extern u32 Seg_pul_frequent;
extern u32 Seg_pul_frequent_mid;extern u32 Seg_pul_accel;
extern u32 Seg_Pul_accel_mid;extern u16 Pul_frequent[2];
extern u16 Pul_accel[2];u8 Kup;                         //旋钮锁死标志(1为锁死)
u8 Kt;                          //存放按键对比值
u16 Cou;                        //初始锁死计数器//----------------旋转编码器按钮变量----------------//
static u16 Key_upCnt = 0;                                      //按键弹起后计数值
static u16 Key_holdon_ms = 0;                              //按下的时长
u16  Key_second_down = 0;                                      //按键再次按下计数标志
u8   Flag_ec11_key_shotclick = 0;           //EC11按键短按动作标志
u8   Flag_ec11_key_longclick = 0;           //EC11按键长按动作标志
u8   Flag_ec11_key_doubleclick = 0;         //EC11按键双击动作标志
u8   Key_fall_flag = 0;                                            //外部中断按键按下标志
u16  Key_up_flag = 0;                                              //按键弹起标志//----------------当前显示的界面参数----------------//
u8 EC11_NUM_SW = 0;                                                  //记录处于在哪个设置界面中,目前只有一个界面
u8 Ec11_set_she = 0;                                               //进入设置参数,目前尚未添加此功能u8 Flash_refreshclick = 0;                                   //数据有改动标志
/*** 函数功能: EC11旋转编码器初始化* 输入参数: 无* 返 回 值: 无* 说    明:以外部触发上升沿中断方式检测*/
void Encoder_init(void)
{GPIO_InitTypeDef GPIO_InitStructure;EXTI_InitTypeDef EXTI_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);        //外部中断,需要使能AFIO时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);    GPIO_InitStructure.GPIO_Pin  = ENCODER_CLK;            //当做旋钮的时钟 输入外部触发中断GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;        //选择IO接口工作方式上拉电阻  GPIO_Init(ENCODER_GPIO, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin  = ENCODER_DT;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;        //选择IO接口工作方式上拉电阻    GPIO_Init(ENCODER_GPIO, &GPIO_InitStructure);   GPIO_InitStructure.GPIO_Pin  = ENCODER_KEY;            //当做旋钮的时钟 输入外部触发中断GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;        //选择IO接口工作方式上拉电阻    GPIO_Init(ENCODER_KEY_GPIO, &GPIO_InitStructure);   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);     NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;               //使能按键所在的外部中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;        //抢占优先级2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;             //子优先级1NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                     //使能外部中断通道NVIC_Init(&NVIC_InitStructure); //GPIOA3 中断线以及中断初始化配置 旋转转动GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource3);EXTI_InitStructure.EXTI_Line=EXTI_Line3;                                 //链接到外部中断3组EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling ;    //上升沿触发EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);                            //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);    NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;       //使能按键所在的外部中断通道   进一步确认是否优先级搞事情NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x03;        //抢占优先级2NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;              //子优先级2NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                     //使能外部中断通道 NVIC_Init(&NVIC_InitStructure);//GPIOC14 中断线以及中断初始化配置 按键按钮按下 HH 这个是确认按钮   GG在使用中会卡死状态屏蔽掉没事GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);EXTI_InitStructure.EXTI_Line = EXTI_Line1;                              //链接到外部中断14组EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;                    //下降沿触发EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);                        //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器GPIO_SetBits(ENCODER_GPIO, ENCODER_CLK);   GPIO_SetBits(ENCODER_GPIO, ENCODER_DT);GPIO_SetBits(ENCODER_KEY_GPIO, ENCODER_KEY);
}

定时器2初始化

/*** 函数功能: 配置TIM2定时器初始化* 输入参数: 无* 返 回 值: 无* 说    明:无*/
void Tim2_init(void)
{TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;NVIC_InitTypeDef NVIC_InitStructure; /*-------------------中断优先级配置初始化-------------------*/                        //中断优先级会影响/* 设置中断组为0 */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);     /* 设置中断来源 */NVIC_InitStructure.NVIC_IRQChannel = ADVANCE_TIM2_IRQ; /* 设置主优先级为 0 */NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;    /* 设置抢占优先级为3 */NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);  /*-------------------TIM2配置初始化-------------------*/ /* 开启TIM2_CLK即内部时钟CK_INT=72M */RCC_APB1PeriphClockCmd(ADVANCE_TIM2_CLK, ENABLE);   /* 自动重装载寄存器周的值(计数值) */TIM_TimeBaseStructure.TIM_Period=ADVANCE_TIM2_PERIOD;/* 累计 TIM_Period个频率后产生一个更新或者中断时钟预分频数为71,则驱动计数器的时钟CK_CNT = CK_INT / (71+1)=1M */TIM_TimeBaseStructure.TIM_Prescaler= ADVANCE_TIM2_PRESCALER;/* 计数器计数模式,基本定时器TIM8向上计数 */TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; /* 初始化定时器TIM2 */TIM_TimeBaseInit(ADVANCE_TIM2, &TIM_TimeBaseStructure);/* 清除计数器中断标志位 */TIM_ClearITPendingBit(ADVANCE_TIM2, TIM_IT_Update);/* 使能计数器 */TIM_Cmd(ADVANCE_TIM2, ENABLE);/* 开启计数器中断 */TIM_ITConfig(ADVANCE_TIM2,TIM_IT_Update,ENABLE);
}

旋转编码器。中断服务函数触发,因为编码器的特性,扭动一步 AB必定产生电平的变化也导致中断的产生。
后判断 AB相,哪个脉冲先运行即可判断出顺时针或逆时针并做好标记

 /*** 函数功能: 外部EXTI3中断服务函数对应关系是PB3-编码器的A相ENCODER_DT = PB4-编码器的B相* 输入参数: 无* 返 回 值: 无* 说    明:Encoder_status变量状态:* -------------------------------- 0:无动作;                                                             * -------------------------------- 1:正转;* -------------------------------- -1:反转;* -------------------------------- 3:按着按键正转;* -------------------------------- -3:按着按键反;*/
void EXTI3_IRQHandler(void)
{if(EXTI_GetITStatus(EXTI_Line3) != RESET){Encoder_status = 0;if(GPIO_ReadInputDataBit(ENCODER_GPIO,ENCODER_CLK)) Kup = 0;           //判断旋钮是否解除锁死if(!GPIO_ReadInputDataBit(ENCODER_GPIO,ENCODER_CLK) && Kup == 0)  //判断是否旋转旋钮,同时判断是否有旋钮锁死{ delay_us(100);Kt=GPIO_ReadInputDataBit(ENCODER_GPIO,ENCODER_DT);                            //把旋钮另一端电平状态记录delay_ms(2);                                                                                                    //延时if(!GPIO_ReadInputDataBit(ENCODER_GPIO,ENCODER_CLK))                      //去抖{ if(Kt!=0){                                                                                                                   //用另一端判断左或右旋转Encoder_status = 1;   //顺时针转//--------编码器顺时针动作代码--------//Clockwise();                                            //旋转编码器顺时针动作代码//--------编码器顺时针动作结束区--------//}else{Encoder_status = -1;//逆时针转转//--------编码器逆时针动作代码--------//Anticlockwise();//--------编码器逆时针动作结束区--------// }  /* 由于数码管刷新频率存在缺陷将此段进行屏蔽Cou=0;                              //初始锁死判断计数器while(!GPIO_ReadInputDataBit(ENCODER_GPIO,ENCODER_CLK)&&Cou<60000)//等待放开旋钮,同时累加判断锁死{                                                                                                       Cou++;Kup=1;delay_us(20); }*/                }}/*    //取消按键按下状态旋转编码器的触发if(GPIO_ReadInputDataBit(ENCODER_KEY_GPIO,ENCODER_KEY) == 0)        //在按键按下状态旋转编码器{if(Encoder_status == 1 ){Encoder_status = 3;DIG3= !DIG3;}else if(Encoder_status == -1){Encoder_status = -3;DIG4= !DIG4;}}*/EXTI_ClearITPendingBit(EXTI_Line3);    //清除LINE3上的中断标志位 }
}

按键中断服务函数。

  /*** 函数功能: EXTI_2外部服务中断函数* 输入参数: 无* 返 回 值: 无* 说    明:无*/
void EXTI1_IRQHandler(void)
{if(EXTI_GetITStatus(EXTI_Line1) != RESET){if(GPIO_ReadInputDataBit(ENCODER_KEY_GPIO, ENCODER_KEY) == 0) //按键 EXTI_2{Key_fall_flag = 1;                             //外部中断按键按下标志}EXTI_ClearFlag(EXTI_Line1);EXTI_ClearITPendingBit(EXTI_Line1); //清除 LINE15 上的中断标志位}
}

此处为判断按键短按长按双击 通过定时器,累加变量判断各种时间标志。

 /*** 函数功能: Timer2定时器服务中断函数* 输入参数: 无* 返 回 值: 无* 说    明:无*/
void TIM2_IRQHandler(void)
{if(TIM_GetITStatus(TIM2,TIM_IT_Update)!= RESET)           //检查指定的TIM中断发生与否:TIM 中断源 {TIM_ClearITPendingBit(TIM2,TIM_IT_Update);            //清除TIMx的中断待处理位:TIM 中断源 if(Key_fall_flag == 1)                                                        //发生按键按下事件{if(GPIO_ReadInputDataBit(ENCODER_KEY_GPIO, ENCODER_KEY) == 0) //按键持续按下{if(Key_holdon_ms <= 2000)                                   //按下按键时间开始计时{Key_holdon_ms++;}else if(Key_holdon_ms > 2000)                        //按键按下到2000ms就判断长按时间成立,生成长按标志{Key_holdon_ms = 0;                                //清除长按动作EC11按键动作计数器Flag_ec11_key_shotclick = 0;            //清除EC11按键短按动作标志Flag_ec11_key_longclick = 1;       //置位EC11按键长按动作标志Flag_ec11_key_doubleclick = 0;     //清除EC11按键双击动作标志Key_fall_flag = 0;                             //外部中断按键按下标志Key_up_flag = 0;                                   //按键弹起标志Key_upCnt = 0;                                         //按键弹起后计数值          Encoder_status = 5;                                //旋转编码器当前状态//--------编码器长按动作代码--------//Long_press();//旋转编码器长按动作代码//--------编码器长按作结束区--------//}}else                                                                           //按键抬起{if(Key_holdon_ms > 10 && Flag_ec11_key_longclick == 0 ) //按下时间大于50ms,按键时间到达消抖时间{Key_up_flag = 1;                                                            //单击抬起按键后,生成按键抬起标志 Key_second_down ++;                                                 //按键再次按下计数标志if(Key_second_down >= 200 && Key_upCnt<200)  {                                                               Key_holdon_ms = 0;                             //清除长按动作EC11按键动作计数器Flag_ec11_key_shotclick = 1;            //置位EC11按键短按动作标志Flag_ec11_key_longclick = 0;       //清除EC11按键长按动作标志Flag_ec11_key_doubleclick = 0;     //清除EC11按键双击动作标志Key_fall_flag = 0;                             //外部中断按键按下标志Key_second_down = 0;                           //再次按下标志位Key_up_flag = 0;                                      //按键弹起标志Key_upCnt = 0;                                         //按键弹起后计数值          Encoder_status = 4;                                //旋转编码器当前状态//--------编码器短按动作代码--------//Press();    //旋转编码器短按动作代码//--------编码器短按作结束区--------//          }else if(Key_upCnt>400)                      //如果在规定双击时间内超时按下按键{                                                         //认为按键是双击动作Key_holdon_ms = 0;                              //清除长按动作EC11按键动作计数器Flag_ec11_key_shotclick = 0;            //清除EC11按键短按动作标志Flag_ec11_key_longclick = 0;       //清除EC11按键长按动作标志Flag_ec11_key_doubleclick = 1;     //置位EC11按键双击动作标志Key_fall_flag = 0;                             //外部中断按键按下标志Key_second_down = 0;                           //再次按下标志位Key_up_flag = 0;                                      //按键弹起标志Key_upCnt = 0;                                         //按键弹起后计数值          Encoder_status = 2;                                //旋转编码器当前状态//--------编码器双击动作代码--------//Double_click(); //旋转编码器双击动作代码//--------编码器双击作结束区--------//                                      }}else  //按键持续时间小于50ms,忽略{Key_holdon_ms = 0;                                //清除长按动作EC11按键动作计数器Flag_ec11_key_shotclick = 0;            //清除EC11按键短按动作标志Flag_ec11_key_longclick = 0;       //置位EC11按键长按动作标志Flag_ec11_key_doubleclick = 0;     //清除EC11按键双击动作标志Key_fall_flag = 0;                             //外部中断按键按下标志Key_up_flag = 0;                                   //按键弹起标志Key_upCnt = 0;                                         //按键弹起后计数值          Encoder_status = 0;                                //旋转编码器当前状态}}}if(Key_up_flag)//单击抬起后,启动计数,计数到500msKey_upCnt++;if(Key_upCnt > 500){Key_upCnt = 0;Key_up_flag = 0;//标记为弹起}       }
}

下面为各种操作状态所要执行的函数。

  /*** 函数功能: 旋转编码器顺时针动作代码* 输入参数: 无* 返 回 值: 无* 说    明:无*/
void Clockwise (void)
{}/*** 函数功能: 旋转编码器逆时针动作代码* 输入参数: 无* 返 回 值: 无* 说    明:无*/
void Anticlockwise (void)
{}/*** 函数功能: 旋转编码器长按动作代码* 输入参数: 无* 返 回 值: 无* 说    明:无*/
void Long_press (void)
{}/*** 函数功能: 旋转编码器双击动作代码* 输入参数: 无* 返 回 值: 无* 说    明:无*/
void Double_click (void)
{}/*** 函数功能: 旋转编码器短按动作代码* 输入参数: 无* 返 回 值: 无* 说    明:无*/
void Press (void)
{}

旋转编码器EC11_支持长按、短按、双击、顺时针逆时针相关推荐

  1. 单片机基于独立按键的屏幕翻页/功能选择方案(支持长按,短按,双击操作)

    功能概述 本按键方案硬件部分由两个独立按键组成, 在移植后能够适配市面上绝大部分单片机. 独立按键分为A, B两键, 轻击A键代表上一页, 轻击B键代表下一页, 同时开发者可自由定义双击, 长按操作的 ...

  2. 三行代码按键消抖 独立按键 矩阵按键 长按 短按 双击

    九层妖塔 起于垒土 直接跳转到三行代码 三行代码按键消抖 独立按键 矩阵按键 长按 短按 双击 一.基本理论 0.按键的常见名词:  ①按键抖动  ②按键稳定闭合时间 1.按键的扫描: 2.按键的消抖 ...

  3. EC11旋转编码器电路和程序

    现在手里的项目使用到了旋转编码器,目前只使用了旋转功能,按钮功能没用到,电路图如下: 说一下为什么这么画,原因很简单,旋钮并不需要供电,它做的工作是把原来的高电平拉低,或者不改变高电平状态,这就是为什 ...

  4. ec11编码器c语言程序,EC11旋转编码器电路和程序

    现在手里的项目使用到了旋转编码器,目前只使用了旋转功能,按钮功能没用到,电路图如下: 说一下为什么这么画,原因很简单,旋钮并不需要供电,它做的工作是把原来的高电平拉低,或者不改变高电平状态,这就是为什 ...

  5. 让linux支持EC11等旋转编码器

    最近,产品基本定型了,所以开始着手升级版.说是升级版,只是在操作上面有所修改.产品需要使用旋钮进行人机交互.对linux驱动十窍通了九窍,一窍不通,所以从最低级的开始看起,然后把gpio_key.c这 ...

  6. 11旋转编码器原理_旋转编码器的原理是什么?增量式编码器和绝对式编码器有什么区别?...

    先给出结论,最重要的区别在于:增量式编码器没有记忆,断电重启必须回到参考零位,才能找到需要的位置,而绝对式编码器,有记忆,断电重启不用回到零位,即可知道目标所在的位置. 接下来细说一下,主要包含如下的 ...

  7. 旋转编码器详解(主要讨论增量式编码器与绝对式编码器)

    参考资料:旋转编码器的原理是什么?增量式编码器和绝对式编码器有什么区别? 0. 前言 我们在选电机时,非常注重电机的扭矩和尺寸,因为这直接决定了电机是否能按规定的运动模式拖动负载,能不能很好地布置在有 ...

  8. 旋钮编码器c代码_旋转编码器EC11驱动

    旋转编码器EC11驱动 /*-------->>>>>>>>--------注意事项:EC11旋转编码器的扫描时间间隔控制在1~4ms之间, 否则5ms ...

  9. EC11旋转编码器驱动程序

    EC11驱动程序 不多做介绍,百度上很多相关的介绍 首先,放上参考文档 代码有正转.反转.按下正转.按下反转.单机.双击.长按和长安松手检测,有什么问题可以在下方留言 我是对该文档代码进行一些修改,偏 ...

  10. 旋转编码器:如何在 Arduino 上使用 Keys KY-040 编码器

    旋转编码器是一种输入设备,您可以在任一方向连续旋转.当您转动设备时,它会生成数字脉冲,以使用两个相控输出信号显示旋转方向.这两个输出还指示单个位置运动,因此您可以在控制面板中使用它们来增加或减少参数. ...

最新文章

  1. 每日一皮:公司来了个程序员鼓励师...
  2. ajax异步查询demo,ASP.NET中AJAX的异步加载(Demo演示)
  3. Shell脚本的学习笔记一:变量
  4. 图论复习——dfs树,点双,边双,强连通分量
  5. 对人工智能神经网络的认识
  6. ## CSP 201609-2 火车购票购买,C语言版(90分版)
  7. 蓝桥杯 ADV-113 算法提高 c++_ch02_02
  8. The “QtRunWork“ task returned false but did not log an error
  9. 2018网易内推 堆棋子 规律题
  10. h5调用原生摄像头扫描二维码
  11. c语言php环境搭建,c语言从入门到放弃php从入门到放弃系列-01php环境的搭建
  12. c语言编写墓碑上的字符,恶的大写字母是什么
  13. OpenGL ES 绘制图片
  14. 数据结构pintia_如何学好数据结构,去哪里刷题?
  15. dedecms织梦后台不显示模块管理和辅助插件
  16. React项目前端开发总结
  17. Jedis 读写效率测试 hgetAll hmset
  18. 伯克利开源 Confluo 替代 Kafka;ofo:现场退押与线上无异
  19. mysql怎么查看网站后台帐号密码_mysql怎么查看网站后台帐号密码
  20. 计算机图形学基础:双向反射分布函数 BRDF

热门文章

  1. K8S资源quota配置引起的问题
  2. sheet中没有getcolumns()方法吗_痘痘能去除吗?有没有效果比较好的祛痘方法
  3. flink集成springboot案例_集成-Apache Flink+Spring Boot
  4. 《异度神剑2》与柏拉图的精神世界略考(上)
  5. 计算MMMM年WW周是YYYY年MM月DD日(周一)到YYYY年MM月DD日(周日)(以JS为例)
  6. 从西洋跳棋开始机器学习
  7. android安装出现问题怎么解决方案,安卓手机安装软件提示解析程序包时出现问题怎么解决...
  8. Create React App无eject配置(react-app-rewired 和 customize-cra)
  9. 六年级计算机课件,六年级信息技术上册课件.ppt
  10. 泰坦尼克号数据挖掘项目实战——Task6 模型调优