ロータリーエンコーダを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
*/
// PORT
static 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 1000
ISR(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
*/
// PORT
static 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;
}