MPLABX XC8でロータリーエンコーダを使うプログラムをテストします。
ロータリーエンコーダの使い方としては、割り込みを使う方法と使わない方法があります。
今回は、割り込みを使わない方法をテストします。wsnakのブログに掲載されているArduino用のプログラムをXC8用に移植し、若干の改良を行いました。
回路図です。電源と通信関係は、前の記事と同じです。ロータリーエンコーダは、RB0とRB1に接続します。
PORTBはPIC側でウィークプルアップして、プルアップ抵抗を省略しています。回転の結果は数字のカウントアップ・カウントダウンをTeraTermで表示します。
ブレッドボードです。ロータリーエンコーダは、秋月電子で販売しているものです。
プログラムです。シリアル通信関係のヘッダファイル serial.h とソースファイルserial.c は、前の記事を参照してください。
ロータリーエンコーダの回転方向判定のしくみについては、JH7UBCブログで解説しています。ご覧ください。
wsnakさんのプログラムのままですと、回してもカウントしないことが時々あります。そこで、befDatとcurDatが一致しないときに1ms待つことにしまいた。これによってほぼカウントロスがなくなりました。更にbefDatとcurDatが一致した回数が原版では、5回以上でフラッグを立てるようになっていたのですが、2回以上一致でフラッグをたてるようにしても十分カウントロスがなくなりました。
このプログラムは、PICのアセンブラに移植してAD9850SGで利用しています。大変よく動作してくれています。
/*
* File: main.c
* Author: JH7UBC Keiji Hata
* PIC16F88 EX3
* ロータリーエンコーダ制御(割り込みを使わない)
* Created on 2018/11/22
*/
* File: main.c
* Author: JH7UBC Keiji Hata
* PIC16F88 EX3
* ロータリーエンコーダ制御(割り込みを使わない)
* Created on 2018/11/22
*/
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include "serial.h"
#include <stdio.h>
#include <stdlib.h>
#include "serial.h"
/* RB0 ロータリーエンコーダA
* RB1 ロータリーエンコーダB
* RB3 シリアル通信RX
* RB5 シリアル通信TX
*/
* RB1 ロータリーエンコーダB
* RB3 シリアル通信RX
* RB5 シリアル通信TX
*/
// CONFIG1
#pragma config FOSC = INTOSCIO
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config BOREN = OFF
#pragma config LVP = OFF
#pragma config CPD = OFF
#pragma config WRT = OFF
#pragma config CCPMX = RB3
#pragma config CP = OFF
#pragma config FOSC = INTOSCIO
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config BOREN = OFF
#pragma config LVP = OFF
#pragma config CPD = OFF
#pragma config WRT = OFF
#pragma config CCPMX = RB3
#pragma config CP = OFF
// CONFIG2
#pragma config FCMEN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
#pragma config IESO = OFF
#define _XTAL_FREQ 8000000
#define ECA RB0
#define ECB RB1
#define ECA RB0
#define ECB RB1
/*ロータリーエンコーダ関係定義*/
unsigned char curDat;
unsigned char befDat = 0;
unsigned char rotDir = 0;
int Count = 0;
unsigned char inputMatch;
unsigned char matchCnt;
unsigned char rotPat = 0;
unsigned char curDat;
unsigned char befDat = 0;
unsigned char rotDir = 0;
int Count = 0;
unsigned char inputMatch;
unsigned char matchCnt;
unsigned char rotPat = 0;
/*エンコーダの回転方向判定*/
signed char CheckEnc(unsigned dat){
rotPat <<=2; //左に2bitシフト
rotPat |= (dat & 0x03);//datの下位2bitを加える
if(rotPat == 0x4B){
return 1; //時計回り(CW))
}else if(rotPat == 0x87){
return -1; //反時計回り(CCW)
}else{
return 0; //どちらでもない
}
}
signed char CheckEnc(unsigned dat){
rotPat <<=2; //左に2bitシフト
rotPat |= (dat & 0x03);//datの下位2bitを加える
if(rotPat == 0x4B){
return 1; //時計回り(CW))
}else if(rotPat == 0x87){
return -1; //反時計回り(CCW)
}else{
return 0; //どちらでもない
}
}
/*メインプログラム*/
void main() {
ANSEL = 0x00; //アナログポートを使わない
OSCCON = 0b01110000;//内部クロック8MHz
TRISA = 0b00100000; //RA5はinput 他はoutput
TRISB = 0b00000111; //RB0-RB2はinput 他はoutput
PORTA = 0x00; //PORTA初期化
PORTB = 0x00; //PORTB初期化
OPTION_REGbits.nRBPU = 0;//PORTB Pull up
serial_init(); //AUSART設定
/*ロータリーエンコーダ初期値設定*/
curDat = 0;
if(ECA){
befDat |= 2;
}
if(ECB){
befDat |= 1;
}
/*ロータリーエンコーダ処理*/
while(1){
signed char val;
curDat = 0;
if(ECA){
curDat |= 2;
}
if(ECB){
curDat |= 1;
}
if(befDat == curDat){
//befDatとcurDatが一致したときの処理
if(!inputMatch){
matchCnt++;
if(matchCnt >=2){ //2回以上一致したらフラッグを立てる
inputMatch = 1;
val = CheckEnc(curDat);
if(val != 0){
Count += val;
printf("%d\r\n",Count);
}
}
}
}else{
//befDatとcuDatが一致しなかった時の処理
__delay_ms(1);//1ms待つ
befDat = curDat;
matchCnt = 0;
inputMatch = 0;
}
}
}
ANSEL = 0x00; //アナログポートを使わない
OSCCON = 0b01110000;//内部クロック8MHz
TRISA = 0b00100000; //RA5はinput 他はoutput
TRISB = 0b00000111; //RB0-RB2はinput 他はoutput
PORTA = 0x00; //PORTA初期化
PORTB = 0x00; //PORTB初期化
OPTION_REGbits.nRBPU = 0;//PORTB Pull up
serial_init(); //AUSART設定
/*ロータリーエンコーダ初期値設定*/
curDat = 0;
if(ECA){
befDat |= 2;
}
if(ECB){
befDat |= 1;
}
/*ロータリーエンコーダ処理*/
while(1){
signed char val;
curDat = 0;
if(ECA){
curDat |= 2;
}
if(ECB){
curDat |= 1;
}
if(befDat == curDat){
//befDatとcurDatが一致したときの処理
if(!inputMatch){
matchCnt++;
if(matchCnt >=2){ //2回以上一致したらフラッグを立てる
inputMatch = 1;
val = CheckEnc(curDat);
if(val != 0){
Count += val;
printf("%d\r\n",Count);
}
}
}
}else{
//befDatとcuDatが一致しなかった時の処理
__delay_ms(1);//1ms待つ
befDat = curDat;
matchCnt = 0;
inputMatch = 0;
}
}
}
TeraTermには、次のようにロータリーエンコーダを時計回りに回すと数字がカウントアップされ、反時計回りに回すと数字がカウントダウンされて表示されます。
次は、割り込みを利用してロータリーエンコーダを使う方法をやってみます。