基于c8t6的平衡小车(CubeMX+MDK)(1)OLED的多级菜单显示
OLED的多级菜单显示
OLED控制之旋转编码器兼按键
按键
- 按键状态
想想按键有哪些状态呢,按下,断开?,这是最基本的KEYSTATUS,按键还可以有短按,长按,一直按,按多次,这都可以产生不同的按键标志 - 扫描按键
在while循环中不断读取GPIO的数据占用了cpu,那有没有好的办法可以即使读取按键状态又不影响主程序的运行呢?
有的兄弟,有的,请看按键扫描:
使用定时器内部计时,触发中断,在中断回调函数中扫描按键状态,通过按键扫描函数区分各种按键状态;
在主程序中调用handle设置标志位。根据不同标志位,执行不同逻辑,控制不同外设。(如何配置?)
- 我用短按做确定,进入下一级菜单
- 我用长按做退出,返回上一级菜单
按键扫描速度非常快,并且扫描时候一次按压可能被扫描很多次,那么如何在有效操作时间内按压记录次数呢(坑+1)
旋转编码器
- 旋转编码器的状态
一样的思路,旋转编码器有哪些状态呢?静止,顺时针转,逆时针转,按下按键(有的编码器是没有的,这取决于外部电路)。这些状态如何获取呢
速食者跳过
曲折
一开始我的想法是判断__HAL_TIM_IS_TIM_COUNTING_DOWN(&htimx)的返回值来获取其旋转状态,但是经过尝试后发现:
这个函数的返回值取决于上次的变化,也就是说上次是顺时针转,哪怕转完静止,调用该函数的返回值依旧是1,令我十分纳闷。
- 硬件上
用示波器测AC相的电压,其静止状态下为VCC的电压值,旋转编码器,其信号变为方波,方波的频率随着旋转速度变化而变化。这证明信号是表征其静止状态的。 - 软件上
我看了__HAL_TIM_IS_TIM_COUNTING_DOWN(&htimx)的函数定义和查阅其他佬的文章后知道了这个函数的原理:该函数通过访问TIM的控制寄存器1的DIR位来判断计数器的计数方向,也就是说计数方向向下(DIR=1),函数返回1,计数向上(DIR=0),函数返回0;
因此其无法直接判断旋转编码器状态
在编码器接口模式下,定时器硬件会根据编码器两个通道(A相和B相)的相位关系自动设置DIR位:
- 获取编码器状态
设置变量last和current用__HAL_TIM_SET_COUNTER(&htimx, 0)获取,比较current和last的大小获取按键状态
接下来就很简单了,用TIM1配置为encode Mode,设置为TI1单独检测,TI1触发后读取TI2的电平值,判断左右旋转,设置不同标志位控制OLED(如何使用?)
- 逆时针旋转,切换同级下一项
- 顺时针旋转,切换同级上一项
OLED的多级菜单
上一级菜单和下一级菜单之间是什么关系呢,我的思考方式是把菜单对象当作树中的节点,创建多叉树,菜单的遍历即为树的遍历
好了,那如何实现呢?
树结构的菜单实现???????????(挖个坑,待我再学一学)
这个项目的多级菜单我只是用MenuIndex这个变量去控制菜单的显示,尽管不便于移植,但是逻辑简单。后续按键标志位的控制均是在控制MenuIndex这个变量实现切换菜单。
CUBEMX配置
编码器
- 通用定时器(TIM2,TIM3,TIM4)和高级定时器(TIM1)均可以设置为Encoder Mode,
- Encoder Mode模式下,GPIO有编码模式自定义,无需额外设置。
- 编码器函数
HAL_TIM_Encoder_Start(&htimx, TIM_CHANNEL_ALL);开启encoder接口
__HAL_TIM_GET_COUNTER(&htimx)的返回值得到编码器的脉冲数count
__HAL_TIM_IS_TIM_COUNTING_DOWN(&htimx)的返回值得到编码器的旋转方向direction,1为逆时针,0为顺时针
__HAL_TIM_SET_COUNTER(&htimx, 0)将计数器归0
TIM1时钟设置和RCC会产生冲突报错,暂未找到冲突原因,但用旋转编码器测试TIM1的Encoder Mode无误(坑+1)
TIM内部计时
- 使能TIM时钟为内部时钟,使能NVIC管理中断,创建自定义回调函数
HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)供HAL_TIM_IRQHandler()调用
记得检查触发中断的定时器
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htimx){}
}

浙公网安备 33010602011771号