Development kit:MDK5.14
IDE:UV4
MCU:STM32F103C8T6
一、增量式旋轉編碼器
1、簡介
- 編碼器(encoder)是將信號(如比特流)或數據進行編制、轉換為可用以通訊、傳輸和存儲的信號形式的設備。編碼器把角位移或直線位移轉換成電信號,前者稱為碼盤,后者稱為碼尺。
- 按照讀出方式編碼器可以分為接觸式和非接觸式兩種;
- 按照工作原理編碼器可分為增量式和絕對式兩類。增量式編碼器是將位移轉換成周期性的電信號,再把這個電信號轉變成計數脈沖,用脈沖的個數表示位移的大小。絕對式編碼器的每一個位置對應一個確定的數字碼,因此它的示值只與測量的起始和終止位置有關,而與測量的中間過程無關。
- 旋轉編碼器是集光機電技術於一體的速度位移傳感器。它將被測的角位移直接轉換成數字信號(高速脈沖信號)。
- 我們通常用的是增量型編碼器,可將旋轉編碼器的輸出脈沖信號直接輸入給PLC,利用PLC的高速計數器對其脈沖信號進行計數,以獲得測量結果。不同型號的旋轉編碼器,其輸出脈沖的相數也不同,有的旋轉編碼器輸出A、B、Z三相脈沖,有的只有A、B相兩相,最簡單的只有A相。A、B為相差90度的脈沖,Z相信號在編碼器旋轉一圈只有一個脈沖,通常用來做零點的依據,連接時要注意PLC輸入的響應時間。旋轉編碼器還有一條屏蔽線,使用時要將屏蔽線接地,提高抗干擾性。
- 信號輸出有正弦波(電流或電壓),方波(TTL、HTL),集電極開路(PNP、NPN),推拉式多種形式,其中TTL為長線差分驅動(對稱A,A-;B,B-;Z,Z-),HTL也稱推拉式、推挽式輸出,編碼器的信號接收設備接口應與編碼器對應。信號連接—編碼器的脈沖信號一般連接計數器、PLC、計算機,PLC和計算機連接的模塊有低速模塊與高速模塊之分,開關頻率有低有高。如單相聯接,用於單方向計數,單方向測速。A.B兩相聯接,用於正反向計數、判斷正反向和測速。A、B、Z三相聯接,用於帶參考位修正的位置測量。A、A-,B、B-,Z、Z-連接,由於帶有對稱負信號的連接,在后續的差分輸入電路中,將共模噪聲抑制,只取有用的差模信號,因此其抗干擾能力強,可傳輸較遠的距離。對於TTL的帶有對稱負信號輸出的編碼器,信號傳輸距離可達150米。
- 線數:旋轉一周信號線輸出脈沖數,也成為分辨率
2、型號
博主此篇文章使用的旋轉編碼器型號有兩種。
型號 | 供電電壓 | 輸出方式 | 分辨率 | 品牌/廠家 | 輸出電平 |
S20-1000型拉線位移傳感器 | 5-24V | NPN集電極開路 | 400 | Omron/歐姆龍 | |
DT100E40R1024-HI 增量型大孔徑編碼器 |
5-30V | HTL(推挽式) | 1024 | 冬蒞自動化技術(上海)有限公司 www.shdongli.cn |
高:VCC*70% 低:1V |
二、硬件設計
由於增量型編碼器輸出脈沖,可以直接接入PLC單片機,利用PLC高速計數器進行測量。將編碼器接入STM32單片機需要一個定時器資源,並將定時器配置為編碼器模式,稍后會簡要介紹配置方法。根據傳感器的輸出方式,進而來判斷是否需要進行信號預處理。對於集電極開路輸出的傳感器接入必須使用上拉電阻。注意STM32大多數引腳的輸入電壓都在3.3V左右,盡量不要過壓。如果電壓較大,最好加隔離電路,比如6N136光耦,類似下圖
我在這里並沒有使用隔離電路,而是對傳感器統一使用了5V供電,並在信號接入端人為加了3.3V上拉(上拉電阻1K),這種接口電路在我自己設計的采集板上統一留出了8個。編碼器應用中並未使用Z相,僅使用了A、B兩相進行編碼器計數。按照線序說明接入STM32的定時器引腳,PA6、PA7對應TIM3的CH1、CH2通道,PB6、PB7對應TIM4的CH1、CH2通道。對於第二種傳感器的推挽輸出方式而言,實驗證明這種接入電路也是可行的。(具體電路沒有進行分析,只是圖個方便。但是5*70%=3.5V應該是勉強可以直接接入stm32引腳的)
三、軟件設計
1、STM32定時器編碼器模式簡介
參考官方文檔,摘錄幾個要點:
- 編碼器接口模式基本上相當於使用了一個帶有方向選擇的外部時鍾。
- 兩個輸入TI1和TI2被用來最為增量編碼器的接口,TI1FP1和TI2FP2是TI1和TI2在通過濾波器和極性控制后的信號,計數器由每次在TI1FP1和TI2FP2的有效跳變驅動,根據跳變順序,產生計數脈沖和方向信號,計數器向上或向下計數,同時硬件對TIMx_CR1寄存器的DIR位進行相應設計
- IC1、TI1概念理解:TI1是定時器輸入通道,IC1是輸入捕獲通道(兩者可以交錯)。配置CCMR1寄存器的CC1S位可以指定IC1的映射方式。可配置濾波器和預分頻器。
- 計數方向與編碼器信號的關系
2、軟件實現

1 /******************************************** 2
3 TIM3、TIM4定時器編碼器模式配置 4
5 *********************************************/
6
7 #include "sys.h"
8 #include "encoder.h"
9
10 int TIM3ITCount=0;//中斷溢出次數
11 int TIM4ITCount=0;//中斷溢出次數
12
13 void TIM3_IRQHandler(void) 14 { 15 if(TIM3->SR&0x0001)//溢出中斷
16 { 17
18 } 19 TIM3->SR&=~(1<<0);//清除標志位
20 } 21
22 void TIM4_IRQHandler(void) 23 { 24 if(TIM4->SR&0x0001)//溢出中斷
25 { 26 } 27 TIM4->SR&=~(1<<0);//清除標志位
28 } 29
30 void TIM3_Encoder_Init() 31 { 32 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; 33 TIM_ICInitTypeDef TIM_ICInitStructure; 34 GPIO_InitTypeDef GPIO_InitStructure; 35 NVIC_InitTypeDef NVIC_InitStructure; 36
37 //時鍾使能
38 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能 TIM3 時鍾
39 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能 GPIOA 時鍾 40
41 //GPIO配置
42 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PA6、PA7設置
43 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入
44 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 45 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA 46
47 //定時器初始化
48 TIM_DeInit(TIM3); 49 TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); 50 TIM_TimeBaseStructure.TIM_Period =65535;//預裝載值
51 TIM_TimeBaseStructure.TIM_Prescaler = 0;//預分頻
52 TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ; 53 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 54 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 55
56 //編碼器模式配置
57 TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling); 58 TIM_ICStructInit(&TIM_ICInitStructure); 59 TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;
60 TIM_ICInit(TIM3, &TIM_ICInitStructure); 61
62 //初始化 NVIC 中斷優先級分組
63 NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 中斷
64 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占優先級 2 級
65 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //從優先級 0 級
66 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能
67 NVIC_Init(&NVIC_InitStructure); //初始化 NVIC 68
69 //中斷配置
70 TIM_ClearFlag(TIM3, TIM_FLAG_Update); 71 TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); 72
73 //Reset counter
74 TIM3->CNT = 0; 75 //使能定時器
76 TIM_Cmd(TIM3, ENABLE); 77 } 78
79 void TIM4_Encoder_Init() 80 { 81 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; 82 TIM_ICInitTypeDef TIM_ICInitStructure; 83 GPIO_InitTypeDef GPIO_InitStructure; 84 NVIC_InitTypeDef NVIC_InitStructure; 85
86 //時鍾使能
87 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //使能 TIM4 時鍾
88 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能 GPIOB 時鍾 89
90 //GPIO配置
91 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PB6、PB7設置
92 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入
93 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 94 GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 GPIOA 95
96 //定時器初始化
97 TIM_DeInit(TIM4); 98 TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); 99 TIM_TimeBaseStructure.TIM_Period =65535;//預裝載值
100 TIM_TimeBaseStructure.TIM_Prescaler = 0;//預分頻
101 TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ; 102 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 103 TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); 104
105 //編碼器模式配置
106 TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling); 107 TIM_ICStructInit(&TIM_ICInitStructure); 108 TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;
109 TIM_ICInit(TIM4, &TIM_ICInitStructure); 110
111 //初始化 NVIC 中斷優先級分組
112 NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4 中斷
113 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占優先級 2 級
114 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //從優先級 0 級
115 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能
116 NVIC_Init(&NVIC_InitStructure); //初始化 NVIC 117
118 //中斷配置
119 TIM_ClearFlag(TIM4, TIM_FLAG_Update); 120 TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); 121
122 //Reset counter
123 TIM4->CNT = 0; 124 //使能定時器
125 TIM_Cmd(TIM4, ENABLE); 126 }
3、注意事項
- 更新中斷可以不開。但是由於沒有使用Z相信號,所以這里保留更新中斷,實際上目前中斷里什么也沒做
- 由於是雙相下降沿計數,所以轉一次會輸出兩個脈沖,轉一周脈沖數為分辨率*2,注意處理。當然你也可以配置為其它計數倍數
- 這里預裝載65535,可改為線數*2,即可對轉一周的位置進行嚴格編碼
- TIMx->CNT的值即為編碼器的位置(並不一定是輸出脈沖值,跟旋轉方向和預裝載值有關,增量編碼器的零點是初始化自己定義的)
- GPIO配置為浮空輸入模式。對於集電極開路輸出的編碼器,如果你沒有外部上拉,則使用內部上拉,配置為上拉輸入模式即可。
參考資料: