基于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){}
}
基于c8t6的平衡小车(CubeMX+MDK)(1)OLED的多级菜单显示的更多相关文章
- 两轮自平衡小车双闭环PID控制设计
两轮自平衡小车的研究意义 ...
- 使用msp432搭建的平衡小车(一)
1.前言 笔者是一名大二学生曾经荒废一年学业,现在不断学习,所以有任何问题都希望讨论提出,你们的支持就是我的动力. 关于硬件搭建的步骤,笔者就不提网上方案太多了,笔者使用编码器电机,驱动采用tb661 ...
- 基于 SailingEase WinForm Framework 开发客户端程序(3:实现菜单/工具栏按钮的解耦及状态控制)
本系列文章将详细阐述客户端应用程序的设计理念,实现方法. 本系列文章以 SailingEase WinForm Framework 为基础进行设计并实现,但其中的设计理念及方法,亦适用于任何类型的客 ...
- Keil MDK 5.14 仿真时System Viewer菜单显示空白和Peripherals菜单无外设寄存器
keil mdk5.14新建工程进行仿真时,进入Debug环境发现System Viewer菜单显示空白,Peripherals菜单没有外设寄存器.如图1和图2所示.打开Oprons for Targ ...
- 基于STM32的平衡车机器人设计-硬件电路设计
今天分享一个STM32F103为主控的自平衡车机器人的硬件电路设计.(亲测完全可用,且没有任何问题) 电路原理图: 电源部分采用12V锂电池作为输入,分三路稳压,其中7805作为5V传感器的供电以及后 ...
- 基于visual studio 2017 以及cubemx 搭建stm32的开发环境(2)
主要解决 vs2017中,printf无法打印数据的问题. 在keil环境下正常使用printf功能,但是以下的重定向代码在vs2017下使用不了: #ifdef __GNUC__ /* With G ...
- 基于visual studio 2017 以及cubemx 搭建stm32的开发环境(0)
(1)安装visual studio 2017 官网下载安装即可 (2)安装visual GDB 链接:https://pan.baidu.com/s/1TgXI1BRQLAWiWlqCcIS9TA ...
- 基于visual studio 2017 以及cubemx 搭建stm32的开发环境(1)
参考如下文档: 传送门:http://www.stm32cube.com/article/128 如果链接不存在的话,下载我截屏好的图: 传送门:https://pan.baidu.com/s/1NC ...
- msp432搭建平衡小车(二)
前言 上一节掌握了使用pwm驱动电机,接下来介绍如何使用msp432读取mpu6050数据 正文 首先我们得知道mpu6050通信方式,由于mpu6050只能用i2c通信,所以学会使用msp432的i ...
- 基于Metronic的Bootstrap开发框架经验总结(11)--页面菜单的几种呈现方式
在常规的后台管理系统或者前端界面中,一般都有一个导航菜单提供给用户,方便选择所需的内容.基于Metronic的Bootstrap开发框架,是整合了Metroinc样式,以及Boostrap组件模块的内 ...
随机推荐
- 鸿蒙Next开发实战教程-使用WebSocket实现即时聊天
鸿蒙系统提供了WebSocket库,使用它可以很方面的实现即时聊天功能,今天就使用WebSocket来实现一个完整的聊天功能. 首先创建一个WebSocket实例: let ws = webSocke ...
- ZeRO:一种去除冗余的数据并行方案
ZeRO:一种去除冗余的数据并行方案 目前训练超大规模语言模型主要有两条技术路线: TPU + XLA + TensorFlow/JAX GPU + Pytorch + Megatron + Deep ...
- C++ STL vector预分配空间——resize和reserve
vector的resize:既分配了空间,也创建了对象,会调用构造函数 vector的reserve:reserve()表示容器预留空间,但不是真正的创建对象,需要通过insert()或push_ba ...
- JavaScript入门笔记day1
文章目录 啥是JavaScript JavaScript与HTML的结合方式 js文件在HTML中的位置 注释方式 变量 命名规范: 定义的关键字 To be a struggling Rick fo ...
- Manim动画渲染:从代码到屏幕的幕后故事
Manim是一个强大的动画制作库,它能够将简单的Python代码转化为精美的动画视频. 你是否好奇过,当你运行Manim代码时,背后的魔法是如何发生的呢? 今天,将重点介绍渲染过程中的三个关键步骤:S ...
- PC端自动化测试实战教程-7-pywinauto等待方法大集合 (详细教程)
1.简介 经过上一篇的学习和讲解想必小伙伴或者童鞋们已经意识到等待的重要性了吧.宏哥在上一篇中在start()后,加入适当的等待时间(如time.sleep()),让应用程序有足够的时间初始化窗口和U ...
- Java遍历Map对象的常见方法
在Java中遍历Map有不少的方法,我们看一下最常用的方法及其优缺点. 既然Java中的所有map都实现了Map接口,以下方法适用于任何map实现(HashMap, TreeMap, LinkedHa ...
- 记录一次自己用 AI 写IOS APP的经历
我是几乎没有移动端开发经验的.仅有的一点安卓开发经验还是十几年前没毕业的时候自己瞎折腾. 故事的起源是每天辅导我儿子功课时的暴跳如雷. 我儿子上一年级了,在语文的生词上落后得非常严重(当然可能是他同学 ...
- P4602 [CTSC2018] 混合果汁
贪心思想,整体二分+权值线段树解决. \(Step\ 1\) 首先将所有果汁的美味度按从大到小排序,若美味度高的果汁可以满足小朋友的两个需求,则储存答案. \(Step\ 2\) 不断二分果汁,并且枚 ...
- React 的 KeepAlive 实战指南:深度解析组件缓存机制
Vue 的 Keep-Alive 组件是用于缓存组件的高阶组件,可以有效地提高应用性能.它能够使组件在切换时仍能保留原有的状态信息,并且有专门的生命周期方便去做额外的处理.该组件在很多场景非常有用,比 ...