旋轉(zhuǎn)編碼器是一種將旋轉(zhuǎn)位移轉(zhuǎn)換為一連串?dāng)?shù)字脈沖信號(hào)的旋轉(zhuǎn)式傳感器。這些脈沖用來控制角位移。讀數(shù)系統(tǒng)通常采用差分方式,即將兩個(gè)波形一樣但相位差為180°的不同信號(hào)進(jìn)行比較,以便提高輸出信號(hào)的質(zhì)量和穩(wěn)定性。讀數(shù)是在兩個(gè)信號(hào)的差別基礎(chǔ)上形成的,從而消除了干擾。
01模塊來源
資料下載鏈接:https://pan.baidu.com/s/18pp1KaT2V_llizWvdIXtKA?pwd=8889
資料提取碼:8889
02 規(guī)格參數(shù)
模塊的廠家資料下載請(qǐng)查看百度網(wǎng)盤鏈接
工作電壓:5V
工作電流:1MA
模塊尺寸:18 x 25 mm
旋轉(zhuǎn)角度: 360度
通信協(xié)議:相位差
管腳數(shù)量:5 Pin(2.54mm間距排針)
03移植過程
我們的目標(biāo)是在立創(chuàng)·CW32F030C8T6開發(fā)板上能夠判斷旋轉(zhuǎn)方向、旋轉(zhuǎn)次數(shù)和是否按下的功能。首先要獲取資料,查看數(shù)據(jù)手冊(cè)應(yīng)如何實(shí)現(xiàn),再移植至我們的工程。
3.1查看資料
旋轉(zhuǎn)編碼器是通過兩個(gè)引腳的相位差,實(shí)現(xiàn)的旋轉(zhuǎn)方向判斷(以后的CLK引腳統(tǒng)一稱呼為A相,DT引腳為B相)
當(dāng)是順時(shí)針旋轉(zhuǎn)時(shí),A相超前B相90度,即A相為下降沿時(shí),B相為低電平;A相為上升沿時(shí),B相為高電平。
當(dāng)是逆時(shí)針旋轉(zhuǎn)時(shí),B相超前A相90度,即A相為下降沿時(shí),B相為高電平;A相為上升沿時(shí),B相為低電平。
而EC11按旋轉(zhuǎn)的輸出動(dòng)作可以分為兩種。
一種是轉(zhuǎn)兩格,A、B端輸出一個(gè)完整脈沖(轉(zhuǎn)一格就只是由低電平->高電平或由高電平->低電平);
一種就是轉(zhuǎn)一格,A、B對(duì)C端輸出一個(gè)完整脈沖。
轉(zhuǎn)一格半個(gè)脈沖
轉(zhuǎn)一格完整脈沖
因此我們只需檢測(cè)A相或者B相有發(fā)生高低電平跳變時(shí),就判斷另一相狀態(tài),來決定旋轉(zhuǎn)方向。根據(jù)以下真值表,可以發(fā)現(xiàn):
當(dāng)兩相同時(shí)為上升沿或者同時(shí)為下降沿時(shí),則為順時(shí)針;
當(dāng)兩相不同時(shí)為上升沿或者不同時(shí)為下降沿時(shí),則為逆時(shí)針;
下B相 右A相 | 上升沿 | 下降沿 |
---|---|---|
上升沿 | 順時(shí)針 | 逆時(shí)針 |
下降沿 | 逆時(shí)針 | 順時(shí)針 |
旋轉(zhuǎn)編碼器是機(jī)械結(jié)構(gòu)的,是機(jī)械結(jié)構(gòu)就避免不了在旋轉(zhuǎn)或者按下時(shí)有抖動(dòng),這里采用定時(shí)器每隔10ms掃描一次編碼器是否有動(dòng)作,實(shí)現(xiàn)10ms內(nèi)的消抖。
在中斷服務(wù)函數(shù)中,根據(jù)真值表確定旋轉(zhuǎn)的方向。
3.2引腳選擇
該模塊有5個(gè)引腳,具體引腳連接見 表 各引腳連接。
3.3移植至工程
打開自己的工程。(這里工程參考見入門手冊(cè)工程模板)
移植步驟中的導(dǎo)入.c和.h文件與第二章的第1小節(jié)【DHT11溫濕度傳感器】相同,只是將.c和.h文件更改為ec11.c與ec11.h。這里不再過多講述,移植完成后面修改相關(guān)代碼。
在文件ec11.c中,編寫如下代碼。
/*
* Change Logs:
* Date Author Notes
* 2024-06-19 LCKFB-LP first version
*/
#include "ec11.h"
#include "stdio.h"
/******************************************************************
* 函 數(shù) 名 稱:Encoder_GPIO_Init
* 函 數(shù) 說 明:旋轉(zhuǎn)編碼器引腳初始化
* 函 數(shù) 形 參:無
* 函 數(shù) 返 回:無
* 作 者:LC
* 備 注:使用定時(shí)器每10Ms掃描一次是否有旋轉(zhuǎn),即通過定時(shí)器進(jìn)行消抖
******************************************************************/
void Encoder_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct; // GPIO初始化結(jié)構(gòu)體
BTIM_TimeBaseInitTypeDef BTIM_TimeBaseInitStruct; // 定時(shí)器初始化結(jié)構(gòu)體
RCC_GPIO_ENABLE(); // 使能GPIO時(shí)鐘
BTIM_RCC_ENABLE(); // 使能BTIM時(shí)鐘
// 禁止中斷,以安全地配置NVIC
__disable_irq();
// 開啟BTIM1中斷,并關(guān)聯(lián)到NVIC
NVIC_EnableIRQ(BSP_TIMER_IRQ);
// 允許中斷,恢復(fù)中斷狀態(tài)
__enable_irq();
GPIO_InitStruct.Pins = GPIO_ENCODER_SW| // GPIO引腳
GPIO_ENCODER_LCK|
GPIO_ENCODER_DT;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT_PULLUP; // 上拉輸入
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; // 輸出速度高
GPIO_Init(PORT_GPIO, &GPIO_InitStruct); // 初始化
// 配置定時(shí)器模式、周期和預(yù)分頻器
BTIM_TimeBaseInitStruct.BTIM_Mode = BTIM_Mode_TIMER; // 設(shè)置為定時(shí)器模式
BTIM_TimeBaseInitStruct.BTIM_Period = 625 - 1; // 設(shè)置周期,使得定時(shí)器每10ms產(chǎn)生一次溢出中斷
BTIM_TimeBaseInitStruct.BTIM_Prescaler = BTIM_PRS_DIV1024; // 預(yù)分頻器設(shè)置為1024,以降低時(shí)鐘頻
// 使用上述配置初始化定時(shí)器BTIM1
BTIM_TimeBaseInit(BSP_TIMER, &BTIM_TimeBaseInitStruct);
// 使能BTIM1的溢出中斷
BTIM_ITConfig(BSP_TIMER, BTIM_IT_OV, ENABLE);
// 啟動(dòng)定時(shí)器BTIM1
BTIM_Cmd(BSP_TIMER, ENABLE);
}
/******************************************************************
* 函 數(shù) 名 稱:Encoder_Scanf
* 函 數(shù) 說 明:判斷旋轉(zhuǎn)編碼器是否有往哪一個(gè)方向旋轉(zhuǎn)
* 函 數(shù) 形 參:無
* 函 數(shù) 返 回:1=正轉(zhuǎn) 2=反轉(zhuǎn)
* 作 者:LC
* 備 注:哪一邊正轉(zhuǎn)哪一邊反轉(zhuǎn)不需要太在意,你說的算
******************************************************************/
char Encoder_Scanf(void)
{
static GPIO_PinState EC11_CLK_Last= GPIO_Pin_RESET; //EC11的LCK引腳上一次的狀態(tài)(A相)
static GPIO_PinState EC11_DT_Last = GPIO_Pin_RESET; //EC11的DT引腳上一次的狀態(tài)(B相)
char ScanResult = 0;
//當(dāng)A發(fā)生跳變時(shí)采集B當(dāng)前的狀態(tài),并將B與上一次的狀態(tài)進(jìn)行對(duì)比。
if(GET_CLK_STATE !=EC11_CLK_Last)
{ //若A 0->1 時(shí),B 1->0 正轉(zhuǎn);若A 1->0 時(shí),B 0->1 正轉(zhuǎn);
//若A 0->1 時(shí),B 0->1 反轉(zhuǎn);若A 1->0 時(shí),B 1->0 反轉(zhuǎn)
if(GET_CLK_STATE == 1) //EC11_A和上一次狀態(tài)相比,為上升沿
{
//EC11_B和上一次狀態(tài)相比,為下降沿
if((EC11_DT_Last == 1)&&(GET_DT_STATE == 0))
ScanResult = 1; //正轉(zhuǎn)
//EC11_B和上一次狀態(tài)相比,為上升沿
if((EC11_DT_Last == 0)&&(GET_DT_STATE == 1))
ScanResult = 2; //反轉(zhuǎn)
//>>>>>>>>>>>>>>>>下面為正轉(zhuǎn)一次再反轉(zhuǎn)或反轉(zhuǎn)一次再正轉(zhuǎn)處理<<<<<<<<<<<<<<<<//
//A上升沿時(shí),采集的B不變且為0
if((EC11_DT_Last == GET_DT_STATE)&&(GET_DT_STATE == 0))
ScanResult = 1; //正轉(zhuǎn)
//A上升沿時(shí),采集的B不變且為1
if((EC11_DT_Last == GET_DT_STATE)&&(GET_DT_STATE == 1))
ScanResult = 2; //反轉(zhuǎn)
}
else //EC11_A和上一次狀態(tài)相比,為下降沿
{
//EC11_B和上一次狀態(tài)相比,為下降沿
if((EC11_DT_Last == 1)&&(GET_DT_STATE == 0))
ScanResult = 2; //反轉(zhuǎn)
//EC11_B和上一次狀態(tài)相比,為上升沿
if((EC11_DT_Last == 0)&&(GET_DT_STATE == 1))
ScanResult = 1; //正轉(zhuǎn)
//>>>>>>>>>>>>>>>>下面為正轉(zhuǎn)一次再反轉(zhuǎn)或反轉(zhuǎn)一次再正轉(zhuǎn)處理<<<<<<<<<<<<<<<<//
//A上升沿時(shí),采集的B不變且為0
if((EC11_DT_Last == GET_DT_STATE)&&(GET_DT_STATE == 0))
ScanResult = 2; //反轉(zhuǎn)
//A上升沿時(shí),采集的B不變且為1
if((EC11_DT_Last == GET_DT_STATE)&&(GET_DT_STATE == 1))
ScanResult = 1; //正轉(zhuǎn)
}
EC11_CLK_Last = GET_CLK_STATE; //更新編碼器上一個(gè)狀態(tài)暫存變量
EC11_DT_Last = GET_DT_STATE; //更新編碼器上一個(gè)狀態(tài)暫存變量
return ScanResult; //返回值的取值: 0:無動(dòng)作; 1:正轉(zhuǎn); 2:反轉(zhuǎn);
}
return 0;
}
/******************************************************************
* 函 數(shù) 名 稱:Encoder_Sw_Down
* 函 數(shù) 說 明:判斷編碼器是否被按下
* 函 數(shù) 形 參:無
* 函 數(shù) 返 回:0=沒有被按下 1=被按下
* 作 者:LC
* 備 注:請(qǐng)注意消抖
******************************************************************/
unsigned char Encoder_Sw_Down(void)
{
//沒有按下
if( GET_SW_STATE == GPIO_Pin_SET )
{
delay_ms(100);//消抖
return 0;
}
else//按下
{
delay_ms(100);//消抖
// printf("downrn");
return 1;
}
}
/******************************************************************
* 函 數(shù) 名 稱:Encoder_Rotation_left
* 函 數(shù) 說 明:左旋轉(zhuǎn)服務(wù)函數(shù)。當(dāng)編碼器左轉(zhuǎn)時(shí),需要執(zhí)行的操作
* 函 數(shù) 形 參:無
* 函 數(shù) 返 回:向左旋轉(zhuǎn)次數(shù)
* 作 者:LC
* 備 注:無
******************************************************************/
int Encoder_Rotation_left(void)
{
static int left_num = 0;//左轉(zhuǎn)次數(shù)
left_num++;
/* 你的代碼寫在此處 */
printf("left num = %drn",left_num);
/* 你的代碼寫在此處 */
return left_num;
}
/******************************************************************
* 函 數(shù) 名 稱:Encoder_Rotation_left
* 函 數(shù) 說 明:右旋轉(zhuǎn)服務(wù)函數(shù)。當(dāng)編碼器右轉(zhuǎn)時(shí),需要執(zhí)行的操作
* 函 數(shù) 形 參:無
* 函 數(shù) 返 回:向右旋轉(zhuǎn)次數(shù)
* 作 者:LC
* 備 注:無
******************************************************************/
int Encoder_Rotation_right(void)
{
static int right_num = 0;//右轉(zhuǎn)次數(shù)
right_num++;
/* 你的代碼寫在此處 */
printf("right num = %drn",right_num);
/* 你的代碼寫在此處 */
return right_num;
}
/************************************************
函數(shù)名稱 : BSP_TIMER_IRQHandler
功 能 : 基本定時(shí)器中斷服務(wù)函數(shù)
參 數(shù) : 無
返 回 值 : 無
作 者 : LC
*************************************************/
void BSP_TIMER_IRQHANDLER(void)
{
static char dat = 0;
if(BTIM_GetITStatus(BSP_TIMER, BTIM_IT_OV) == SET)
{
dat = Encoder_Scanf();//掃描編碼器是否扭動(dòng)
if( dat != 0 )//如果有轉(zhuǎn)動(dòng)
{
if( dat == 2 )
{
Encoder_Rotation_left();
}
else
{
Encoder_Rotation_right();
}
}
}
BTIM_ClearITPendingBit(BSP_TIMER, BTIM_IT_OV);
}
在文件ec11.h中,編寫如下代碼。
/*
* Change Logs:
* Date Author Notes
* 2024-06-19 LCKFB-LP first version
*/
#ifndef _BSP_ENCODER_H_
#define _BSP_ENCODER_H_
#include "board.h"
#define RCC_GPIO_ENABLE() __RCC_GPIOA_CLK_ENABLE()
#define PORT_GPIO CW_GPIOA
//SW引腳
#define GPIO_ENCODER_SW GPIO_PIN_7
//CLK引腳
#define GPIO_ENCODER_LCK GPIO_PIN_6
//DT引腳
#define GPIO_ENCODER_DT GPIO_PIN_4
//獲取CLK引腳的狀態(tài)
#define GET_CLK_STATE GPIO_ReadPin(PORT_GPIO, GPIO_ENCODER_LCK)
//獲取DT引腳的狀態(tài)
#define GET_DT_STATE GPIO_ReadPin(PORT_GPIO, GPIO_ENCODER_DT)
//獲取SW引腳的狀態(tài)
#define GET_SW_STATE GPIO_ReadPin(PORT_GPIO, GPIO_ENCODER_SW)
//定時(shí)器掃描
#define BTIM_RCC_ENABLE() __RCC_BTIM_CLK_ENABLE() // 使能定時(shí)器時(shí)鐘
#define BSP_TIMER CW_BTIM1 // 定時(shí)器
#define BSP_TIMER_IRQ BTIM1_IRQn // 定時(shí)器中斷
#define BSP_TIMER_IRQHANDLER BTIM1_IRQHandler // 定時(shí)器中斷服務(wù)函數(shù)
void Encoder_GPIO_Init(void);//旋轉(zhuǎn)編碼器初始化
unsigned char Encoder_Sw_Down(void);//編碼器是否按下
int Encoder_Rotation_left(void);//左轉(zhuǎn)服務(wù)函數(shù)
int Encoder_Rotation_right(void);//右轉(zhuǎn)服務(wù)函數(shù)
#endif
04移植驗(yàn)證
在自己工程中的main主函數(shù)中,編寫如下。
/*
* Change Logs:
* Date Author Notes
* 2024-06-19 LCKFB-LP first version
*/
#include "board.h"
#include "stdio.h"
#include "bsp_uart.h"
#include "ec11.h"
int32_t main(void)
{
board_init(); // 開發(fā)板初始化
uart1_init(115200); // 串口1波特率115200
Encoder_GPIO_Init();
printf("encoder demo startrn");
while(1)
{
if( Encoder_Sw_Down() == 1 )//旋轉(zhuǎn)編碼器被按下
{
printf("Encoder downrn");
}
}
}
移植現(xiàn)象:向右旋轉(zhuǎn)10次,向左旋轉(zhuǎn)10次,按下一次。
模塊移植成功案例代碼:
鏈接:https://pan.baidu.com/s/18_4-IfR_pzyy1QvyQE7XkQ?pwd=LCKF
提取碼:LCKF