ロータリーエンコーダを4個まで接続し、それぞれの回転状態や回転速度を液晶ディスプレイ(LCD)に出力するプログラムの試作と実験の記録
EC12E2420801 24クリックタイプ
EC12E2430803 ノンクリックタイプ
RE-160F-40E3-(L)A-24P 1回転24パルス出力
AVRマイコン ATmega328P
積層セラミックコンデンサ 0.1uF 7個
カーボン抵抗器 10kΩ 1/4W 3個
I2C接続液晶ディスプレイ 16文字×2行 AQM1602XA-RN-GBW ( & DIP化キット)
ユニバーサル基板
/* * Rotary encoder 0 (B) : INT0/PD2 (PIN 4) * Rotary encoder 0 (A) : PD1 (PIN 3) * * Rotary encoder 1 (A) : INT1/PD3 (PIN 5) * Rotary encoder 1 (B) : PD4 (PIN 6) * * Rotary encoder 2 (B) : PCINT6/PB6 (PIN 9) * Rotary encoder 2 (A) : PCINT7/PB7 (PIN 10) * * Rotary encoder 3 (B) : PCINT21/PD5 (PIN 11) * Rotary encoder 3 (A) : PCINT22/PD6 (PIN 12) */#include <stdio.h>#include <stdlib.h>#include <util/delay.h>#include <avr/io.h>#include <avr/interrupt.h>/* * Rotary Encoder Counter */int8_t RECOUNT[4] = {0, 0, 0, 0}; // ロータリーエンコーダ0~3のカウンタ/** Rotary Encoder Input port Function*/static inline uint8_t isRE0Ahigh(void) { return ( PIND & (1<<PD2) ); }static inline uint8_t isRE0Bhigh(void) { return ( PIND & (1<<PD1) ); }static inline uint8_t isRE1Ahigh(void) { return ( PIND & (1<<PD3) ); }static inline uint8_t isRE1Bhigh(void) { return ( PIND & (1<<PD4) ); }static inline uint8_t isRE2Ahigh(void) { return ( PINB & (1<<PB7) ); }static inline uint8_t isRE2Bhigh(void) { return ( PINB & (1<<PB6) ); }static inline uint8_t isRE3Ahigh(void) { return ( PIND & (1<<PD6) ); }static inline uint8_t isRE3Bhigh(void) { return ( PIND & (1<<PD5) ); }static inline void wait(volatile unsigned long i){ while (i-- > 0);}/* * initialize */// PORTstatic inline void initIo(void){ PORTD = (1<<PD1) | (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD6) | (1<<PD7); // weak pull up resistor enagle on PD1,2,3,6,7 PORTB = (1<<PB6) | (1<<PB7); // weak pull up resistor enagle on PB6,7 DDRD = 0b00000000; // PD1234567 as Input, others Output DDRB = 0b00111111; // PORTB67 as Input, others Output DDRC = 0b11111110; // PORTC (PC0:Input) }/* * External Interrupt INT0,1 */static inline void initInt01(void) // INT0 割込みの設定{// EICRA |= (1<<ISC01) | (1<<ISC11); // INT0 Falling Edge, INT1 Falling edge EICRA = 0b00001010;// INT0 Falling Edge, INT1 Falling edge EIMSK |= (1<<INT0) | (1<<INT1); // Enable Interrupt 0, 1 //EIMSK |= (1<<INT0); // Enable Interrupt 0}ISR(INT0_vect)// INT0 割込みが発生したときの処理{ if (isRE0Bhigh()) { // エンコーダ出力BがHighのとき RECOUNT[0]++; //時計回り } else { // エンコーダ出力BがLowのとき RECOUNT[0]--; //反時計回り }}ISR(INT1_vect)// INT1 割込みが発生したときの処理{ _delay_ms(5); //チャタリング防止 EIFR |= (1<<INTF1); //チャタリング防止の後で、割り込みフラグをクリア if (isRE1Bhigh()) { // エンコーダ出力BがHighのとき RECOUNT[1]++; //時計回り } else { // エンコーダ出力BがLowのとき RECOUNT[1]--; //反時計回り }}/* * External Interrupt PCINT0,1 */static inline void initPcint02(void) // PCINT0,2 割込みの設定{ PCICR = (1<<PCIE0) | (1<<PCIE2);// PCINT0,2 pin change PCMSK0 |= (1<<PCINT7); // PCINT7 enable PCMSK2 |= (1<<PCINT22); // PCINT22 enable}ISR(PCINT0_vect)// PCINT0(PCINT0~7) 割込みが発生した(ロータリーエンコーダが動いた)ときの処理{ _delay_ms(5); //チャタリング防止 PCIFR |= (1<<PCIF0); //チャタリング防止の後で、割り込みフラグをクリア if (!isRE2Ahigh()) { // エンコーダ出力AがLowのとき if (isRE2Bhigh()) { // エンコーダ出力BがHighのとき RECOUNT[2]++; //時計回り } else { // エンコーダ出力BがLowのとき RECOUNT[2]--; //反時計回り } }}ISR(PCINT2_vect)// PCINT2(PCINT16~23) 割込みが発生した(ロータリーエンコーダが動いた)ときの処理{ _delay_ms(5); //チャタリング防止 PCIFR |= (1<<PCIF2); //チャタリング防止の後で、割り込みフラグをクリア if (!isRE3Ahigh()) { // エンコーダ出力AがLowのとき if (isRE3Bhigh()) { // エンコーダ出力BがHighのとき RECOUNT[3]++; //時計回り } else { // エンコーダ出力BがLowのとき RECOUNT[3]--; //反時計回り } }}/* * main */int main(void){ initIo(); // 出力の初期化 initInt01(); // INT0,1の初期化 initPcint02(); // PCINT0,2の初期化 sei(); // 割り込み処理開始 while (1) {// 割り込みにより、エンコーダの回転状態はRECOUNT[0]〜[3]に格納される。
// 時計回りの回転は+1、反時計回りの回転は-1として加算される。
} return 0;}ロータリーエンコーダで、回転速度を調べる方法
Timer0割込みを1ms間隔で発生させる。信号Aが立下がったときの変数tの値をt0として記憶しておく。
次に信号Aが立下がったときの変数tの値をt1とする。
信号Aの立下がりの間隔は、1/24回転に相当するため、ロータリーエンコーダが1回転する時間[ms]は次式で計算できる。
24 * ( t1 - t0 )
60秒(60000ms)間の回転数である回転速度n[min-1]が次の式で計算できる。
n = 60000 / (24 * ( t1 - t0 ) )
なお、変数tは32ビットの符号なし整数であると、tの値が0xffffffffの次は0x00000000となり、元に戻ってしまう。
このため、t0 > t1のときは、nの計算に次式を使う。
n = 60000 / (24 * ( 0xffffffff - t0 + t1 ) )
#include <stdio.h>#include <stdlib.h>#include <util/delay.h>#include <avr/io.h>#include <avr/interrupt.h>/* * Rotary Encoder Counter */int8_t RECOUNT[4] = {0, 0, 0, 0}; // ロータリーエンコーダ0~3のカウンタuint32_t REstartTime[4] = {0, 0, 0, 0}; // rotationnal speed check start time of Rotary Encoder 0~3 uint32_t RESPEED[4] = {0, 0, 0, 0}; // rotationnal speed of Rotary Encoder 0~3 // rotational time check flag started (1) stop(0)int8_t RECHECKTIME[4] = {0, 0, 0, 0}; // time check flag of Rotary Encoder 0~3/* * Rotary Encoder Input port Function */static inline uint8_t isRE0Ahigh(void) { return ( PIND & (1<<PD2) ); }static inline uint8_t isRE0Bhigh(void) { return ( PIND & (1<<PD1) ); }static inline uint8_t isRE1Ahigh(void) { return ( PIND & (1<<PD3) ); }static inline uint8_t isRE1Bhigh(void) { return ( PIND & (1<<PD4) ); }static inline uint8_t isRE2Ahigh(void) { return ( PINB & (1<<PB7) ); }static inline uint8_t isRE2Bhigh(void) { return ( PINB & (1<<PB6) ); }static inline uint8_t isRE3Ahigh(void) { return ( PIND & (1<<PD6) ); }static inline uint8_t isRE3Bhigh(void) { return ( PIND & (1<<PD5) ); }static inline void wait(volatile unsigned long i){ while (i-- > 0);}static inline uint8_t isSwitchOn(void){ return ( !( PIND & (1<<PD2) ) );}static inline void ledOn(void){ PORTD |= (1<<PD0);}static inline void ledOff(void){ PORTD &= ~(1<<PD0);}static inline void initTimer0(void)// TIMER0の初期化 約1ミリ秒ごとにタイマ割込みが発生する{ // CTC mode , TOP is OCR0A TCCR0A = (1 << WGM01) | (0 << WGM00) ; // I/O clock prescaler N = 8 TCCR0B = (0 << WGM02) | (0 << CS02) | (1 << CS01) | (0 << CS00); // Timer0 on OCR0A compare match interrupt enable TIMSK0 |= ( 1<< OCIE0A ); // set TOP value . Interrupt every 1 ms OCR0A = 125; // OCR0A = interrupt period [s] * f_clkio [Hz] / I/O clock prescaler N}/* * Interrupt */volatile uint32_t T0count; // Timer0割込み発生回数のカウンタ#define TIMER0_1SEC 1000ISR(TIMER0_COMPA_vect)// Timer0 の割込み処理{ T0count++;}/* * Timer 0 control function */void resetTimer0(void){ T0count = 0; // Reset Timer0 counter}void waitTimer0(uint32_t timelimit)// wait until timelimit{ T0count = 0; while (T0count < timelimit);}/* * initialize */// PORTstatic inline void initIo(void){ PORTD = (1<<PD1) | (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5) | (1<<PD6) | (1<<PD7); // weak pull up resistor enagle on PD1,2,3,6,7 PORTB = (1<<PB6) | (1<<PB7); // weak pull up resistor enagle on PB6,7 DDRD = 0b00000000; // PD1234567 as Input, others Output DDRB = 0b00111111; // PORTB67 as Input, others Output DDRC = 0b11111110; // PORTC (PC0:Input) }/* * Rotary Encoder Revolution speed */uint32_t getRpm(uint32_t startTime, uint32_t endTime)/* * startTime : 計測開始時間 * endTime : 計測終了時間 */{ uint32_t revspeed; // 回転速度[min-1] uint32_t timeInterval; // 計測開始時間と終了時間の差 if (startTime > endTime) { timeInterval = 0xffffffff - startTime + endTime; } else { timeInterval = endTime - startTime; } revspeed = 60000 / (24 * timeInterval); return revspeed;}/* * External Interrupt INT0,1 */static inline void initInt01(void) // INT0 割込みの設定{// EICRA |= (1<<ISC01) | (1<<ISC11); // INT0 Falling Edge, INT1 Falling edge EICRA = 0b00001010;// INT0 Falling Edge, INT1 Falling edge EIMSK |= (1<<INT0) | (1<<INT1); // Enable Interrupt 0, 1 //EIMSK |= (1<<INT0); // Enable Interrupt 0}ISR(INT0_vect)// INT0 割込みが発生したときの処理{ uint32_t t0countNow; // 計時したときのT0countの値 if (RECHECKTIME[0] == 0) { RECHECKTIME[0] = 1; // 計時開始 REstartTime[0] = T0count; } else { t0countNow = T0count; RESPEED[0] = getRpm(REstartTime[0], t0countNow); // 回転速度を更新 RECHECKTIME[0] = 0; //計時終了 } if (isRE0Bhigh()) { // エンコーダ出力BがHighのとき RECOUNT[0]++; //時計回り } else { // エンコーダ出力BがLowのとき RECOUNT[0]--; //反時計回り }}ISR(INT1_vect)// INT1 割込みが発生したときの処理{ uint32_t t0countNow; // 計時したときのT0countの値 _delay_ms(5); //チャタリング防止 EIFR |= (1<<INTF1); //チャタリング防止の後で、割り込みフラグをクリア if (RECHECKTIME[1] == 0) { RECHECKTIME[1] = 1; // 計時開始 REstartTime[1] = T0count; } else { t0countNow = T0count; RESPEED[1] = getRpm(REstartTime[1], t0countNow); // 回転速度を更新 RECHECKTIME[1] = 0; //計時終了 } if (isRE1Bhigh()) { // エンコーダ出力BがHighのとき RECOUNT[1]++; //時計回り } else { // エンコーダ出力BがLowのとき RECOUNT[1]--; //反時計回り }}/* * External Interrupt PCINT0,1 */static inline void initPcint02(void) // PCINT0,2 割込みの設定{ PCICR = (1<<PCIE0) | (1<<PCIE2);// PCINT0,2 pin change PCMSK0 |= (1<<PCINT7); // PCINT7 enable PCMSK2 |= (1<<PCINT22); // PCINT22 enable}ISR(PCINT0_vect)// PCINT0(PCINT0~7) 割込みが発生した(ロータリーエンコーダが動いた)ときの処理{ uint32_t t0countNow; // 計時したときのT0countの値 _delay_ms(5); //チャタリング防止 PCIFR |= (1<<PCIF0); //チャタリング防止の後で、割り込みフラグをクリア if (!isRE2Ahigh()) { // エンコーダ出力AがLowのとき if (RECHECKTIME[2] == 0) { RECHECKTIME[2] = 1; // 計時開始 REstartTime[2] = T0count; } else { t0countNow = T0count; RESPEED[2] = getRpm(REstartTime[2], t0countNow); // 回転速度を更新 RECHECKTIME[2] = 0; //計時終了 } if (isRE2Bhigh()) { // エンコーダ出力BがHighのとき RECOUNT[2]++; //時計回り } else { // エンコーダ出力BがLowのとき RECOUNT[2]--; //反時計回り } }}ISR(PCINT2_vect)// PCINT2(PCINT16~23) 割込みが発生した(ロータリーエンコーダが動いた)ときの処理{ uint32_t t0countNow; // 計時したときのT0countの値 _delay_ms(5); //チャタリング防止 PCIFR |= (1<<PCIF2); //チャタリング防止の後で、割り込みフラグをクリア if (!isRE3Ahigh()) { // エンコーダ出力AがLowのとき if (RECHECKTIME[3] == 0) { RECHECKTIME[3] = 1; // 計時開始 REstartTime[3] = T0count; } else { t0countNow = T0count; RESPEED[3] = getRpm(REstartTime[3], t0countNow); // 回転速度を更新 RECHECKTIME[3] = 0; //計時終了 } if (isRE3Bhigh()) { // エンコーダ出力BがHighのとき RECOUNT[3]++; //時計回り } else { // エンコーダ出力BがLowのとき RECOUNT[3]--; //反時計回り } }}enum {STAGE_OFF, STAGE_ON}; // OCR1A Started or Stopped/* * main */int main(void){ initIo(); // 出力の初期化 initTimer0(); // Timer0の初期化 initInt01(); // INT0,1の初期化 initPcint02(); // PCINT0,2の初期化 sei(); // 割り込み処理開始 while (1) {// 割り込みにより、エンコーダの回転状態はRECOUNT[0]〜[3]に格納される。
// 時計回りの回転は+1、反時計回りの回転は-1として加算される。
// 割り込みにより、エンコーダの回転速度[min-1]はRESPEED[0]〜[3]に格納される。
} return 0;}