TIM定时器的应用——利用旋转编码器实现操控呼吸灯——点灯大王篇!
一,实验准备
1,实验目的
本实验通过对TIM定时器PWM的学习,操控旋转编码器来对呼吸灯的亮度进行控制。
2,呼吸灯的原理
呼吸灯是让灯光呈现由亮到暗再到亮,类似人呼吸般节奏变化的灯光效果,其原理主要基于 PWM(脉冲宽度调制)技术 ,以常见的 LED 呼吸灯为典型。
3,旋转编码器的认识
旋转编码器是将旋转的机械量转换为电信号输出的传感器,常见类型有光电式、磁电式 ,按编码方式分为增量式和绝对式,以下是其原理介绍:
磁电式增量编码器
-
结构组成:有一个带磁性材料的旋转码盘(磁极交替分布 ),以及磁敏元件(如霍尔元件 )。
- 工作过程:码盘旋转时,磁极交替靠近磁敏元件,使磁敏元件周围磁场发生变化,根据霍尔效应等原理,磁敏元件会输出与磁场变化相关的电信号,经处理后得到类似光电式增量编码器的 A、B 相脉冲信号,用于判断旋转方向和计算旋转角度。 增量式编码器的特点是能反映旋转的相对变化,断电后位置信息丢失,再次工作需重新计数或找参考点。
二,工程创建
1,新建工程
依旧是轻车熟路的点击左上角的File,然后点击STM32 Project,选择收藏的STM32F103C8T6型号芯片。
之后将工程名称命名为“PWM_Encoder”,然后完成工程创建。
2,引脚参数配置
首先我们需要梳理一下本次项目需要使用的外设模块:
- 一个定时器的三个通道设置为PWM模式来控制三盏LED灯
- 一个GPIO引脚用于连接按键控制切换LED灯
- 一个定时器设置为编码器模式来控制旋转编码器
- 一个I2C外设来设置OLED屏幕
因此我们需要对这些外设一步一步配置。
别忘了开启外部高速时钟和SWD调试端口,Crystal/Ceramic Resonator意为晶体 / 陶瓷谐振器 ,即外部晶振,开启之后来到时钟树将时钟频率改为72MHz。
之后为三个LED小灯开启外部时钟,然后模式选择PWM1模式,预分频PSC改为72,自动重装载值ARR改为100,其余参数保持不变即可,这样我们就配置好了TIM3。
然后为旋转编码器开启TIM1,模式直接选择为编码器模式,分频数选择为2分频即可,然后需要将旋转编码器的其中一个引脚的输出脉冲反相一下,原因之后大家试过就知道了(桀桀桀)
再然后 给我们的OLED屏幕设置一下I2C,直接选择I2C模式即可,下方速度可以选择快速模式。
最后为按键选择一个GPIO引脚即可,模式设置为上拉输入模式,这里我选择的是GPIOB11引脚
这样所有的引脚参数都配置完毕了,整体的效果就是这样的
最后别忘了到Project Manager处勾选为每一个外设单独创建一个.c文件和对应的.h文件,然后使用快捷键Ctrl键+S保存并生成初始化代码!
三,代码实现
1,代码思想
在我们小学2年级的时候便学过了,调节呼吸灯的亮度是通过调节PWM的占空比来实现的,但是占空比是什么呢,简单来说就是一个脉冲信号的高电平与低电平的比值,占空比小,相当于电压降低,那么小灯自然而然也就变暗,当占空比增大时,相当于电压升高,小灯就亮了!
而捕获 / 比较寄存器CCR的值与我们设置的自动重装载值ARR的比值便是占空比,也就是说,我们只需要改变CCR的值便可以改变占空比从而实现改变小灯亮度的目的
根据我们的需求可以知道,要通过旋转编码器来操控LED的灯光亮度,那我们可以想到,获取旋转编码器的计数值作为LED灯对应通道的CCR值,这样,我们便将旋转编码器的计数值与小灯的亮度联系在了一起。
再之后就是通过按键达到选择小灯的目的,这就非常简单了,只需要简单将三个小灯的定时器通道序号添加在一个数组中,每次按下按键,将当前操控的定时器通道关闭,同时自加一位,开启下一个定时器通道即可。
2,代码实现
将需要使用的头文件声明
#include "oled.h"
#include <stdio.h>
将需要用到的变量声明
int counter=0; //用于存储旋转编码器计数值
char message[20]="",R[20]="",B[20]="",Y[20]="";//用于为OLED屏幕拼接字符串来输出
int Channel_i=0; //初始为操控Channel_0的LED灯
uint32_t Channels[3]={TIM_CHANNEL_1,TIM_CHANNEL_2,TIM_CHANNEL_3};//将三个小灯的对应通道加在一个数组中,用于按键的选择
初始化OLED屏幕,并开启旋转编码器及LED对应模式
HAL_Delay(20);//延时20ms等待OLED屏幕开机
OLED_Init();
HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL); //开启旋转编码器的a,b脚
HAL_TIM_PWM_Start(&htim3, Channels[Channel_i]);//开启LED的PWM模式,通道初始选择为通道0
将旋转编码器的计数值存储在counter变量中,并且将计数值限制在0~100中,使其能够与LED灯的占空比对应上
counter=__HAL_TIM_GET_COUNTER(&htim1); //读取旋转编码器的计数值
if(counter>60000){ //保证旋转编码器的值在0~100之间,以便对应LED灯的亮度
counter=0;
__HAL_TIM_SET_COUNTER(&htim1,0);
}
else if(counter>100){
counter=100;
__HAL_TIM_SET_COUNTER(&htim1,100);
}
将变量counter的值当做LED灯的CCR值来使用
__HAL_TIM_SET_COMPARE(&htim3,Channels[Channel_i],counter); //将旋转编码器输出的数变为LED小灯的占空比,从而操控小灯的亮度
之后来到按键选择模块
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_11)==GPIO_PIN_RESET){ //按下按键来选择旋转编码器控制哪个LED灯
HAL_Delay(10);
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_11)==GPIO_PIN_RESET){
HAL_TIM_PWM_Stop(&htim3, Channels[Channel_i]);
Channel_i=(Channel_i+1)%3; //自加后对3取余保证在0~2中做出选择
HAL_TIM_PWM_Start(&htim3, Channels[Channel_i]);
}
while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_11)==GPIO_PIN_RESET);//等待按键抬起
}
最后就是OLED显示功能,这里大家可以直接去调用网上各种大佬的驱动库,然后使用取模软件为我们需要输出的汉字进行取模,需要使用的依旧是sprintf函数,这一部分并不影响功能使用,大家可以按照自己的想法去大胆的操作。
OLED_NewFrame();
sprintf(message,"亮度: %d%%",counter);
OLED_PrintString(45, 0, message, &font16x16, OLED_COLOR_NORMAL);
OLED_DrawRectangle(13, 25, 101, 16, OLED_COLOR_NORMAL); //画一个空心长方形
OLED_DrawFilledRectangle(13,26,counter, 15, OLED_COLOR_NORMAL); //画一个进度条
sprintf(R,"红灯");
sprintf(B,"蓝灯");
sprintf(Y,"黄灯");
if(Channel_i==0){
OLED_PrintString(15, 0, B, &font16x16, OLED_COLOR_NORMAL);
}
else if(Channel_i==1){
OLED_PrintString(15, 0, Y, &font16x16, OLED_COLOR_NORMAL);
}
else{
OLED_PrintString(15, 0, R, &font16x16, OLED_COLOR_NORMAL);
}
OLED_ShowFrame();
HAL_Delay(100);
3,检查编译下载程序
点击上方的编译图标,检查代码完整性,之后点击Run将代码烧录,编译下载一气呵成!
四,硬件连接
硬件连接非常简单,唯一值得注意的就是旋转编码器的连接,上面的VCC和GND直接和面包板的正负极连接,下方的A,B脚与TIM1的1,2通道连接,C脚也是共地即可,按键的一脚接地,另一端与PB11相连,三个LED小灯按照颜色来对应,并且正极接在单片机GPIO口上。
五,实验现象
将程序烧入单片机中,可以观察到最开始从TIM_CHANNEL_0的蓝灯开始控制
顺时针旋转编码器,蓝灯的亮度逐渐变亮,并且进度条也逐渐增加
按下按键,切换LED灯,并且占空比保持上一LED灯的值,屏幕上的灯的颜色也相应切换。
实验现象符合预期,实验顺利完成!!!
六,实验小结
本次实验与前几期跨度有些大,本来应该是从单独的呼吸灯或者TIM定时器的原理开始分析的,但是博主上周有事出门,加上也有些懈怠了,所以直接大跨步来到了旋转编码器控制呼吸灯,所以和前几篇看起来不是那么的连贯,不过这也是笔记嘛,最近学的确实是这个,就记下了。
实验现象非常有意思,定时器也是非常实用重要的,可以结合各种外设实现各种各样意想不到的功能,通过本次实验可以发现,HAL库确实比标准库方便许多,居然还有专门的Encoder编码器模式,而且各种初始化的参数,例如预分频数及自动重装值的设置,直接可以设置,不需要调用各种繁琐的初始化函数,方便快捷了许多。本次的知识点不算太多,就是对旋转编码器的认识,对定时器几种参数的配置,还有模式的选择,当然本次并没有涉及到定时器的内部模式,感兴趣的小伙伴可以自行研究,也许博主之后勤快起来了会补上这一部分。
理工科最重要的就是实践了,希望大家能够看完感兴趣的小项目后可以自己试着操作一下,说不定会有不同的体会哦,说到实践,当然少不了一套好用的STM32开发板套件了,这里我推荐大家上闲鱼,有博主亲手设计焊接的STM32开发板,说不定还能买到博主同款二手开发套件。