之前做過(guò)一個(gè)LED調(diào)光的項(xiàng)目,這次想拿XR806來(lái)實(shí)現(xiàn),后續(xù)打算加入遠(yuǎn)程控制的功能。這個(gè)項(xiàng)目使用旋轉(zhuǎn)編碼器來(lái)調(diào)節(jié)LED的亮度,基本原理是MCU識(shí)別編碼器的旋轉(zhuǎn)方向和步數(shù),調(diào)節(jié)PWM輸出占空比,從而實(shí)現(xiàn)亮度調(diào)節(jié)。
識(shí)別其旋轉(zhuǎn)方向和步數(shù),要考慮消除抖動(dòng),否則會(huì)出現(xiàn)識(shí)別錯(cuò)誤,導(dǎo)致系統(tǒng)不穩(wěn)定,這和按鍵是類似的。
Github上有對(duì)應(yīng)的Arduino庫(kù),注意它的開(kāi)源協(xié)議是GNU GPL V3!本人曾經(jīng)移植到STM32,現(xiàn)已移植到XR806,效果良好。旋轉(zhuǎn)編碼器相關(guān)代碼如下:
頭文件re.h源碼:
/*
* Rotary encoder library for Arduino.
* Port to XR806 by Zixun Chen.
*/
#ifndef _ROTARY_ENCODER_H_
#define _ROTARY_ENCODER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
// 根據(jù)編碼器的輸出類型來(lái)選擇是否定義RE_HALF_STEP
#define RE_HALF_STEP
// 旋轉(zhuǎn)編碼器通常外接上拉電阻,對(duì)應(yīng)空閑電平是00B。如果外接電阻是下拉的,需要定義RE_PINS_PULL_DOWN
// #define RE_PINS_PULL_DOWN
#define DIR_NONE 0 // 尚無(wú)完整有效的步進(jìn)
#define DIR_CW 0x10 // 順時(shí)針步進(jìn)
#define DIR_CCW 0x20 // 逆時(shí)針步進(jìn)
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; // 內(nèi)部變量,保存狀態(tài)機(jī)狀態(tài)
} REHandle_t;
void RotaryEncoderInit(REHandle_t *REVal); // 初始化
void RotaryEncoderProcess(REHandle_t *REVal); // 讀取步進(jìn)
#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 0x5
const 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 0x6
const 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},
};
#endif
static uint8_t ReadPinLevel(GPIO_Port GPIOx, GPIO_Pin PINy); // 內(nèi)部函數(shù),讀取引腳電平
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);
// 初始化狀態(tài)機(jī)
REVal- >State=R_START;
}
void RotaryEncoderProcess(REHandle_t *REVal) {
uint8_t pinstate;
// 讀取AB端電平
pinstate=(ReadPinLevel(REVal- >GPIO_B, REVal- >PIN_B)< 1) |
ReadPinLevel(REVal- >GPIO_A, REVal- >PIN_A);
// 狀態(tài)機(jī)操作
REVal- >State=ttable[REVal- >State & 0xf][pinstate];
// 返回編碼器步進(jìn)信息
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,需要反轉(zhuǎn)引腳電平
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,對(duì)應(yīng)PWM_CH2。開(kāi)發(fā)環(huán)境基于FreeRTOS,XR806 SDK在 ~/tools/
目錄下。在 ~/tools/xr806_sdk/project/demo/
目錄下新建 tryre
文件夾,并在其中添加源代碼,makefile等文件,然后按照教程編譯鏈接下載即可。主要代碼如下:
頭文件main.h:
#ifndef __MAIN_H
#define __MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
// 需要包含的頭文件
#include < stdio.h >
#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亮度等級(jí)
static void RotaryScan(void); // 編碼器識(shí)別與處理
// 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_ECT2
static int max_duty_ratio; // PWM計(jì)數(shù)上限
static void PWMCycleModeSet(void); // PWM重復(fù)輸出模式初始化
static HAL_Status PWMDutyRatioSet(int val); // 設(shè)置PWM輸出占空比
// FreeRTOS配置
#define TASK_RE_PRIO 1
#define TASK_RE_STK_SIZE 200
static TaskHandle_t TaskRE_Handler=NULL;
static void TaskCreation(void); // 創(chuàng)建任務(wù)
static void TaskRE(void *pvParameters); // 編碼器識(shí)別任務(wù)
int main(void)
{
printf("Rotary encoder & PWM demo.rn"); // 串口輸出相關(guān)信息
RotaryEncoderInit(&REVal); // 初始化編碼器
PWMCycleModeSet(); // 初始化PWM
PWMDutyRatioSet(max_duty_ratio*step/STEPMAX); // 設(shè)置PWM輸出占空比
TaskCreation(); // 創(chuàng)建任務(wù)
// 任務(wù)調(diào)度不需要用戶指定
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"); // 任務(wù)創(chuàng)建成功
}
taskEXIT_CRITICAL();
}
static void TaskRE(void *pvParameters)
{
while (1) {
RotaryScan(); // 識(shí)別編碼器步進(jìn)
vTaskDelay(10); // 延遲10(ms)
}
}
static void RotaryScan(void)
{
RotaryEncoderProcess(&REVal); // 識(shí)別編碼器步進(jìn)
if(DIR_CW == REVal.RetVal) { // 順時(shí)針步進(jìn)
if(step< STEPMAX) {
step++; // 增大亮度,上限是STEPMAX
PWMDutyRatioSet(max_duty_ratio*step/STEPMAX);
printf("%d ", step);
}
} else if(DIR_CCW == REVal.RetVal) {
if(step >STEPMIN) {
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復(fù)用,官方例程里面缺了這一部分
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時(shí)鐘源
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__);
}
// 設(shè)置占空比
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);
}
-
旋轉(zhuǎn)編碼器
+關(guān)注
關(guān)注
5文章
159瀏覽量
25990 -
LED調(diào)光
+關(guān)注
關(guān)注
0文章
61瀏覽量
16323 -
GNU
+關(guān)注
關(guān)注
0文章
143瀏覽量
17512 -
MCU控制
+關(guān)注
關(guān)注
0文章
48瀏覽量
6757 -
PWM輸出
+關(guān)注
關(guān)注
1文章
66瀏覽量
5204
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論