之前做过一个led调光的项目,这次想拿xr806来实现,后续打算加入远程控制的功能。这个项目使用旋转编码器来调节led的亮度,基本原理是mcu识别编码器的旋转方向和步数,调节pwm输出占空比,从而实现亮度调节。
识别其旋转方向和步数,要考虑消除抖动,否则会出现识别错误,导致系统不稳定,这和按键是类似的。
github上有对应的arduino库,注意它的开源协议是gnu gpl v3!本人曾经移植到stm32,现已移植到xr806,效果良好。旋转编码器相关代码如下:
头文件re.h源码:
/* * rotary encoder library for arduino. * port to xr806 by zixun chen. */#ifndef _rotary_encoder_h_#define _rotary_encoder_h_#ifdef __cplusplusextern c {#endif#include main.h// 根据编码器的输出类型来选择是否定义re_half_step#define re_half_step// 旋转编码器通常外接上拉电阻,对应空闲电平是00b。如果外接电阻是下拉的,需要定义re_pins_pull_down// #define re_pins_pull_down#define dir_none 0 // 尚无完整有效的步进#define dir_cw 0x10 // 顺时针步进#define dir_ccw 0x20 // 逆时针步进typedef struct { // 定义编码器a端所连的gpio引脚 gpio_port gpio_a; gpio_pin pin_a; // 定义编码器b端所连的gpio引脚 gpio_port gpio_b; gpio_pin pin_b; uint8_t retval; // 保存返回值 uint8_t state; // 内部变量,保存状态机状态} rehandle_t;void rotaryencoderinit(rehandle_t *reval); // 初始化void rotaryencoderprocess(rehandle_t *reval); // 读取步进#ifdef __cplusplus}#endif#endif // _rotary_encoder_h_源文件re.c:
/* rotary encoder handler for arduino. * * copyright 2011 ben buxton. licenced under the gnu gpl version 3. * contact: bb@cactii.net * * port to xr806 by zixun chen. */#include re.h/* * the below state table has, for each state (row), the new state * to set based on the next encoder output. from left to right in, * the table, the encoder outputs are 00, 01, 10, 11, and the value * in that position is the new state to set. */#define r_start 0x0#ifdef re_half_step// use the half-step state table (emits a code at 00 and 11)#define r_ccw_begin 0x1#define r_cw_begin 0x2#define r_start_m 0x3#define r_cw_begin_m 0x4#define r_ccw_begin_m 0x5const unsigned char ttable[6][4] = { // r_start (00) {r_start_m, r_cw_begin, r_ccw_begin, r_start}, // r_ccw_begin {r_start_m | dir_ccw, r_start, r_ccw_begin, r_start}, // r_cw_begin {r_start_m | dir_cw, r_cw_begin, r_start, r_start}, // r_start_m (11) {r_start_m, r_ccw_begin_m, r_cw_begin_m, r_start}, // r_cw_begin_m {r_start_m, r_start_m, r_cw_begin_m, r_start | dir_cw}, // r_ccw_begin_m {r_start_m, r_ccw_begin_m, r_start_m, r_start | dir_ccw},};#else// use the full-step state table (emits a code at 00 only)#define r_cw_final 0x1#define r_cw_begin 0x2#define r_cw_next 0x3#define r_ccw_begin 0x4#define r_ccw_final 0x5#define r_ccw_next 0x6const unsigned char ttable[7][4] = { // r_start {r_start, r_cw_begin, r_ccw_begin, r_start}, // r_cw_final {r_cw_next, r_start, r_cw_final, r_start | dir_cw}, // r_cw_begin {r_cw_next, r_cw_begin, r_start, r_start}, // r_cw_next {r_cw_next, r_cw_begin, r_cw_final, r_start}, // r_ccw_begin {r_ccw_next, r_start, r_ccw_begin, r_start}, // r_ccw_final {r_ccw_next, r_ccw_final, r_start, r_start | dir_ccw}, // r_ccw_next {r_ccw_next, r_ccw_final, r_ccw_begin, r_start},};#endifstatic uint8_t readpinlevel(gpio_port gpiox, gpio_pin piny); // 内部函数,读取引脚电平void rotaryencoderinit(rehandle_t *reval) { gpio_initparam gpio_initval={0}; // 初始化gpio引脚 gpio_initval.driving=gpio_driving_level_1; gpio_initval.mode=gpiox_pn_f0_input; gpio_initval.pull=gpio_pull_none; hal_gpio_init(reval- >gpio_a, reval- >pin_a, &gpio_initval); hal_gpio_init(reval- >gpio_b, reval- >pin_b, &gpio_initval); // 初始化状态机 reval- >state=r_start;}void rotaryencoderprocess(rehandle_t *reval) { uint8_t pinstate; // 读取ab端电平 pinstate=(readpinlevel(reval- >gpio_b, reval- >pin_b)< gpio_a, reval- >pin_a); // 状态机操作 reval- >state=ttable[reval- >state & 0xf][pinstate]; // 返回编码器步进信息 reval- >retval=reval- >state & 0x30;}static uint8_t readpinlevel(gpio_port gpiox, gpio_pin piny){ gpio_pinstate rdpin; rdpin=hal_gpio_readpin(gpiox, piny); #ifdef re_pins_pull_down // 如果定义re_pins_pull_down,需要反转引脚电平 if(gpio_pin_high==rdpin) { return 0; } else { return 1; } #else // re_pins_pull_down if(gpio_pin_high==rdpin) { return 1; } else { return 0; } #endif // re_pins_pull_down}编码器a端和b端分别连接pa12和pa13,使用板载led即可,引脚是pa21,对应pwm_ch2。开发环境基于freertos,xr806 sdk在 ~/tools/目录下。在 ~/tools/xr806_sdk/project/demo/ 目录下新建 tryre 文件夹,并在其中添加源代码,makefile等文件,然后按照教程编译链接下载即可。主要代码如下:
头文件main.h:
#ifndef __main_h#define __main_h#ifdef __cplusplusextern c {#endif// 需要包含的头文件#include #include driver/chip/hal_gpio.h#include driver/chip/hal_pwm.h#include re.h#include freertos.h#include task.h#ifdef __cplusplus}#endif#endif /* __main_h */main.c:
#include main.h// 定义编码器占用的gpio引脚static rehandle_t reval={ .gpio_a=gpio_port_a, .pin_a=gpio_pin_12, .gpio_b=gpio_port_a, .pin_b=gpio_pin_13};static const uint8_t stepmax=10, stepmin=0;static uint8_t step=0; // 控制led亮度等级static void rotaryscan(void); // 编码器识别与处理// pwm输出初始化#define pwm_channel pwm_group1_ch2#define pwm_mode pwm_cycle_mode#define pwm_gpio_port gpio_port_a#define pwm_gpio_pin gpio_pin_21#define pwm_gpio_mode gpioa_p21_f4_pwm2_ect2static int max_duty_ratio; // pwm计数上限static void pwmcyclemodeset(void); // pwm重复输出模式初始化static hal_status pwmdutyratioset(int val); // 设置pwm输出占空比// freertos配置#define task_re_prio 1#define task_re_stk_size 200static taskhandle_t taskre_handler=null;static void taskcreation(void); // 创建任务static void taskre(void *pvparameters); // 编码器识别任务int main(void){ printf(rotary encoder & pwm demo.rn); // 串口输出相关信息 rotaryencoderinit(&reval); // 初始化编码器 pwmcyclemodeset(); // 初始化pwm pwmdutyratioset(max_duty_ratio*step/stepmax); // 设置pwm输出占空比 taskcreation(); // 创建任务 // 任务调度不需要用户指定 return 0;}static void taskcreation(void){ basetype_t xret = null; taskenter_critical(); xret = xtaskcreate((taskfunction_t )taskre, (const char *)taskre, (uint16_t)task_re_stk_size, (void *)null, (ubasetype_t)task_re_prio, (taskhandle_t *)&taskre_handler); if(pdpass == xret) { printf(taskre created!rn); // 任务创建成功 } taskexit_critical();}static void taskre(void *pvparameters){ while (1) { rotaryscan(); // 识别编码器步进 vtaskdelay(10); // 延迟10(ms) }}static void rotaryscan(void){ rotaryencoderprocess(&reval); // 识别编码器步进 if(dir_cw == reval.retval) { // 顺时针步进 if(stepstepmin) { step--; // 减小亮度,下限是stepmin pwmdutyratioset(max_duty_ratio*step/stepmax); printf(%d , step); } }}static void pwmcyclemodeset(void){ // 初始化硬件所需变量声明 gpio_initparam io_param = {0}; hal_status status = hal_error; pwm_clkparam clk_param = {0}; pwm_chinitparam ch_param = {0}; // 配置gpio复用,官方例程里面缺了这一部分 io_param.driving = gpio_driving_level_1; io_param.mode = pwm_gpio_mode; io_param.pull = gpio_pull_none; hal_gpio_init(pwm_gpio_port, pwm_gpio_pin, &io_param); // 配置pwm时钟源 clk_param.clk = pwm_clk_hosc; clk_param.div = pwm_src_clk_div_1; status = hal_pwm_groupclkcfg(pwm_channel / 2, &clk_param); if (status != hal_ok) { printf(%s(): %d, pwm group clk config errorn, __func__, __line__); } // 配置pwm模式,频率和极性 ch_param.hz = 1000; ch_param.mode = pwm_mode; ch_param.polarity = pwm_highleve; max_duty_ratio = hal_pwm_chinit(pwm_channel, &ch_param); if (max_duty_ratio == -1) { printf(%s(): %d, pwm ch init errorn, __func__, __line__); } // 设置占空比 status = hal_pwm_chsetdutyratio(pwm_channel, max_duty_ratio / 2); if (status != hal_ok) { printf(%s(): %d, pwm set duty ratio errorn, __func__, __line__); } // 使能通道 status = hal_pwm_enablech(pwm_channel, pwm_mode, 1); if (status != hal_ok) { printf(%s(): %d, pwm ch enable errorn, __func__, __line__); }}static hal_status pwmdutyratioset(int val){ return hal_pwm_chsetdutyratio(pwm_channel, val);}
华为正在开发“世界上第一台”5G电视
有几种方法可以快速判断PCB设计的质量
伺服电机转矩和什么有关?
2018年工业机器人的核心点还是量的提升
网工必备的SecureCRT如何进行下载和安装?
【XR806开发板试用】使用编码器进行调光
FLIR红外热像仪助力减少电动汽车火灾发生
PUA光固化涂料研究
电池隔膜工艺要求特点有哪些?
基于TDA1521的集成立体声放大器电路
基于PCI Express 2.0的系统互连交换机设备系统设计
锂电池制造工艺的要点介绍
多维科技推出 AMR132x 和 AMR134x AMR 磁开关传感器系列
Arduino UNO 1.3寸OLED扩展板应用介绍
水开报警线路图
波峰焊传送系统的技术要求以及结构分类
深圳八月底前率先实现5G网络全覆盖
新冠疫情引发物联网应用市场更多深思
微机电系统陀螺仪工作原理
慧能泰半导体eMarker HUSB332D开启4脚emarker时代 适用于USB Type-C线缆