1、初識編碼器,像示波器的旋轉按鈕,可左旋右旋,還可以按下,我們使用的是Grayhill編碼器,如下圖所示:
從圖中可以看出,該編碼器一共有6個IO,從1-6分別爲GND,GND,C,B,A,VCC,。
其中VCC和GND接3.3V和GND,A、B對應旋轉時電平的跳變IO,C對應按下時電平的跳變IO。
(1) 硬件電路設計上,爲了方便代碼編寫與理解,最好把編碼器的A、B接到單片機相鄰的GPIO中,即使用同一個中斷處理函數,比如本設計A、B分別接到PB12和PB11。C接任一個GPIO,當成中斷使用。
(2) 順時針和逆時針旋轉編碼器時,慢慢旋轉一個單位(手指頭會有明顯旋轉到位的感覺),用萬用表測量A、B對應的IO電平,並記錄下來。旋轉一圈爲止。如下表是我使用的編碼器,在順、逆時針旋轉一圈時所記錄下的IO電平(旋4次即滿一圈):
(實際使用中,旋轉了24小格才滿一圈,這裏只記錄方法,不記錄確切的值,需要自己測量)
|
|
旋轉前 |
旋1/4圈 |
旋2/4圈 |
旋3/4圈 |
旋4/4圈 |
順時針 |
A電平 |
0 |
1 |
1 |
0 |
0 |
B電平 |
0 |
0 |
1 |
1 |
0 |
|
逆時針 |
A電平 |
0 |
0 |
1 |
1 |
0 |
B電平 |
0 |
1 |
1 |
0 |
0 |
表1 順、逆時針旋轉編碼器時A、B對應IO電平
2、A、B對應的IO初始化成中斷雙邊沿觸發方式,如下所示:
void KeyA_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the EXTI_PB1 Clock */
RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB , ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //浮空輸入
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource12); //配置PB12管腳爲外部中斷線路用
EXTI_InitStructure.EXTI_Line = EXTI_Line12; //配置爲外部中斷線2
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //配置爲中斷請求
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //輸入線路下降沿爲中斷請求
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中斷
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC_Group:先佔優先級2位,從優先級2位
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //配置爲外部中斷2中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先佔優先級爲1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //從優先級爲2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中斷通道
NVIC_Init(&NVIC_InitStructure);
}
void KeyB_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the EXTI_PB1 Clock */
RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB , ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //浮空輸入
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource11); //配置PB11管腳爲外部中斷線路用
EXTI_InitStructure.EXTI_Line = EXTI_Line11; //配置爲外部中斷線11
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //配置爲中斷請求
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //輸入線路下降沿爲中斷請求
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中斷
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC_Group:先佔優先級2位,從優先級2位
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //配置爲外部中斷2中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先佔優先級爲1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //從優先級爲2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中斷通道
NVIC_Init(&NVIC_InitStructure);
}
3、根據表1設計的中斷處理函數如下所示,使用TurningDir變量來標識檢測到的旋轉方向,TurnLeft表示逆時針(對應數值可以理解爲減),TurnRight表示順時針(對應數值可以理解爲加):
typedef enum {TurnNone=0,TurnRight , TurnLeft}TurnDir ;
TurnDir TurningDir=TurnNone;//default turning dir = none
void EXTI15_10_IRQHandler(void)
{
u8 KeyAValue=0;
u8 KeyBValue=0;
//key left & right
if (EXTI_GetITStatus(EXTI_Line12) != RESET)
{
EXTI_ClearITPendingBit(EXTI_Line12);
KeyAValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12);
KeyBValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
// printf("AB=%d,%d\r\n",KeyAValue,KeyBValue);
if(KeyAValue==1)
{
if(KeyBValue==0)
{
TurningDir=TurnLeft;
}
else
{
TurningDir=TurnRight;
}
}
else if(KeyAValue==0)
{
if(KeyBValue==1)
{
TurningDir=TurnLeft;
}
else
{
TurningDir=TurnRight;
}
}
}
if (EXTI_GetITStatus(EXTI_Line11) != RESET)
{
EXTI_ClearITPendingBit(EXTI_Line11);
KeyAValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12);
KeyBValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
if(KeyBValue==1)
{
if(KeyAValue==0)
{
TurningDir=TurnRight;
}
else
{
TurningDir=TurnLeft;
}
}
else if(KeyBValue==0)
{
if(KeyAValue==1)
{
TurningDir=TurnRight;
}
else
{
TurningDir=TurnLeft;
}
}
}
}
編碼器旋鈕的按下功能,即C,可以把它當成一個普通的按鍵,軟件設置成上拉輸入,按下時爲低電平,彈起時爲高,同樣也使用中斷操作,這裏就不再描述。
至此,編碼器軟件設計完成。經過實測,能完美檢測左旋右旋。
如果你有什麼問題,歡迎留言。
我們還建了一個QQ羣,可以討論相關內容,羣號是:597254771