JH7UBCブログ

アマチュア無線 電子工作 家庭菜園など趣味のブログです

PIC16F1705 ロータリーエンコーダ テスト

2019-04-19 10:22:13 | PIC16F1705

 PIC16F1705でロータリーエンコーダを利用するためのテストをします。

 ロータリーエンコーダの回転方向反転には、いくつか方法がありますが、今回は、IOC(ピン状態変化割り込み)を使う方法です。

 回転方向の判定方法を簡単に説明します。

 ロータリーエンコーダを回転させるとロータリーエンコーダのA,B端子の電圧は下の図のように変化します。(A,B端子ともプルアップされています。)

 AとBの値の変化は、回転方向により異なります。BAの値の前の値を左に1ビットシフトし、今の値との排他的ORをとると、下位2ビットの値が時計回りなら00か01になり、反時計回りなら10か11になります。このことを利用して、回転方向を判定することができます。

 

 回路図です。

 ロータリーエンコーダは、RC4とRC5に接続しました。この2つのピンは、PIC側でウィークプルアップしておきます。

(PIV16F1705では、すべてのI/Oピンがプルアップ可能です。)

 回転方向判定結果(RIGHT,LEFT)は、シリアル変換モジュールFT234Xを通してパソコンのTeraTermに表示させます。

(PPSを使ってRA0にTXをRA1にRXを割り当てました)

ブレッドボードです。

判定結果(TeraTermの画面)です。

 プログラムです。

 通信速度は、9600bpsです。クロックは、内臓クロック8MHz×4=32MHzです。

--------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
 
// CONFIG1
#pragma config FOSC = INTOSC    //内部クロックを使う
#pragma config WDTE = OFF       //ウォッチドックタイマー無効
#pragma config PWRTE = ON       //パワーアップタイマーを有効にする
#pragma config MCLRE = OFF      //MCLRピンをRA3として使用する
#pragma config CP = OFF         //プログラムメモリを保護しない
#pragma config BOREN = ON       //ブラウンアウトリセットを有効にする
#pragma config CLKOUTEN = OFF   //クロック出力を無効とし、RA4ピンとして使用する
#pragma config IESO = OFF       //内部・外部クロックの切り替えでの起動を行わない
#pragma config FCMEN = OFF      //外部クロックを監視しない
// CONFIG2
#pragma config WRT = OFF        //フラッシュメモリを保護しない
#pragma config PPS1WAY = OFF    //ロック解除シーケンスで何度でもPPSLOCKをセット/クリアできる
#pragma config ZCDDIS = ON      //ゼロクロス検出回路無効
#pragma config PLLEN = ON       //×4PLLを動作させる
#pragma config STVREN = ON      //スタックオーバーフローリセットを行う
#pragma config BORV = HI        //ブラウンアウトリセット電圧を高(2.7V)に設定
#pragma config LPBOR = OFF      //低消費電力ブラウンアウトリセット無効
#pragma config LVP = OFF        //低電圧プログラミングを行わない
 
#define _XTAL_FREQ 32000000     //クロック32MHz
 
/* ロータリーエンコーダ関係定義*/
#define ECA RC5 //エンコーダA
#define ECB RC4 //エンコーダB
unsigned char EA;
unsigned char EB;
volatile unsigned char curDat;
volatile unsigned char befDat;
volatile signed char count= 0;
 
void serial_init(unsigned long BR){
    TX1STA = 0x24;   //SYNC=0 TXEN = 1 BRGH = 1
    BRG16 = 1;       //BRG 16bit mode
    RC1STA = 0x90;   //非同期、継続受信可
    unsigned int X= _XTAL_FREQ/BR/4 - 1;
    SP1BRGH = X / 256;
    SP1BRGL = X % 256;
}
 
void putch(unsigned char byte){
    while(!TXIF);
    TX1REG = byte;
}
 
void interrupt isr(){
    IOCIF = 0;   //割り込みフラッグクリア
    IOCCF4 = 0;
    IOCCF5 = 0;
    __delay_ms(2);
    curDat = ECA + (ECB<<1);
    if (befDat != curDat){
        unsigned char d = ((befDat<<1)^curDat) & 3; //回転方向判定
        if(d < 2){
            count++;
        }else{
            count--;
        }
        befDat = curDat;
    }
    if(count >= 4){
        printf("RIGHT\r\n");
        count = 0;
    }else if(count <= -4){
        printf("LEFT\r\n");
        count = 0;
    }
}
 
void main() {
    OSCCON = 0b01110000 ;     // 内部クロック8MHz ×4=32MHz
    ANSELA = 0b00000000 ;     // AN0-AN3を使わない
    ANSELC = 0b00000000 ;     // AN4-AN6を使わない
    TRISA  = 0b00000010 ;     // RA1は入力他は出力
    TRISC  = 0b00110000 ;     // RC4,RC5は入力、他は出力
    PORTA  = 0b00000000 ;     // PORTAクリア
    PORTC  = 0b00000000 ;     // PORTCクリア
/* TX RXピンの割り当て*/
    RA0PPS = 0x14;            //RA0にTXを割り当てる。
    RXPPS = 0x01;             //RXをRA1に割り当てる。
   
    OPTION_REGbits.nWPUEN=0;  // ウィークプルアップ許可
    WPUC = 0b00110000;        // RC4,RC5をプルアップ
       
    /* IOC割り込み設定 */
    IOCCN = 0b00110000;     //RC4,RC5立下り割り込み設定
    IOCCP = 0b00110000;     //RC4,RC5立ち上がり割り込み設定
    IOCIE = 1;              //IOC割り込み許可
    PEIE = 1;               //周辺割り込み許可
    GIE = 1;                //全割り込み許可
    
    serial_init(9600);        // Serial通信初期化とBaud Rateの設定
   
    /* Roatry Encoder 初期値 */
    befDat = ECA + (ECB<<1);
   
    while(1){
    }
}

PIC16F1705 OPアンプテストその2

2019-04-14 19:49:08 | PIC16F1705

 PIC16F1705の内蔵OPアンプで反転増幅のテストをします。

 OPA1を使い、OP1CONを次のように設定します。

 OPA1を有効にします。OP1EN=1

 OPA1の-入力をOPA1IN-ピン(RC1)に接続します。OPA1UG=0

 スピードは、Lowに設定。OPA1SG=0

 OPA1の+入力は、バイアス電圧としてFVR Buffer2に接続します。OPA1NCH=11

 FVR(固定参照電圧)は、2.024Vに設定します。

 

 次の回路図ように抵抗とコンデンサを接続して、増幅率2倍の反転増幅とします。

 

 ブレッドボードです。

 INPUTに自作SGから1000Hzの信号を入力して、OPA1OUTの出力をパソコンオシロpicoscopeで観測しました。

 

 青がINPUTで、1Vp-pです。

 赤がOUTPUTで、2Vp-pです。

 プログラムです。main関数のみを示します。

void main() {
    OSCCON = 0b01110010 ;     // 内部クロック8MHz
    ANSELA = 0b00000000 ;     // AN0-AN3を使わない
    ANSELC = 0b00100000 ;     // AN5はアナログピン
    TRISA  = 0b00000000 ;     // 全て出力
    TRISC  = 0b00000010 ;     // RC1は入力、他は出力
    PORTA  = 0b00000000 ;     // PORTAクリア
    PORTC  = 0b00000000 ;     // PORTCクリア
   
    OPA1CON = 0b10000011;   //OPA1EN=1,OPA1UG=0,OPA1NCH=11
    FVRCON = 0b10001000;    //FVREN=1,CDAFVR=10
      
    while(1){

    }
}
 

PIC16F1705 OPアンプテストその1

2019-04-13 11:16:47 | PIC16F1705

 PIB16F1705には2つのOPアンプが内蔵されています。これは、このPICの大きな特徴です。

 OPアンプのテスト1として、PIC内のFVR(Fixed Voltage Reference)の電圧、1.024V,2.048V,4.096VをOPA1をボルテージフォロアにして、外部に取り出してみます。

 OPAモジュールは、下の図のような構造になっています。設定は、OPAxCONで行います。(x=1,2)

 

 OPA1を有効にするには、OPA1CONレジスタのbit7(OPA1EN)=1とします。

 OPA1をFVR Buffer2に接続するには、OPA1CONレジスタのbit1:0=11とします。

 OPA1をボルテージフォロア接続にするには、OPA1CONレジスタのbit4(OPA1UG)=1とします。

 今回は、OPアンプのスピードは関係ないので、OPA1SP=0(Low Speed)とします。

 次に、FVRの設定は、FVRCONレジスタで行います。

 FVRCONレジスタのbi7(FVREN)=1として、FVRを有効にします。

 参照電圧は、FVRCONレジスタのbit3:2(CDAFVR)で行い、

 01=1.024V

 10=2.048V

 11=4.096V

 となります。

 CDAFVRの値を変えて、出力(OPA1OUT=RC2)を測定してみました。

 CDFVR=01=1.024Vの時

 

 CDAFVR=10=2.048Vの時 

 CDAFVR=11=4.096Vの時

 FVR(固定参照電圧)を外部に取り出すことができました。(かなり正確です)

 メイン関数だけを示します。(今回は、内部クロック8MHzとしています。CDAFVRは、4.096V出力になっています。)

void main() {
    OSCCON = 0b01110010 ;     // 内部クロック8MHz
    ANSELA = 0b00000000 ;     // アナログ使わない
    ANSELC = 0b00000000 ;     //
    TRISA  = 0b00000000 ;     // 全て出力
    TRISC  = 0b00000000 ;     // 全て出力
    PORTA  = 0b00000000 ;     // PORTAクリア
    PORTC  = 0b00000000 ;     // PORTCクリア
   
    OPA1CON = 0b10010011;   //OPA1EN=1,OPA1UG=1,OPA1NCH=11
    FVRCON = 0b10001100;    //FVREN=1,CDAFVR=11
   
   
    while(1){
      
    }
}


PIC16F1705 AQM0802A表示テスト

2019-04-10 09:26:59 | PIC16F1705

 PIC16F1705で、I2C LCD AQM0802Aの表示テストをします。

 PIC16F1705はデフォルトでSCLはRC0に、SDAはRC1に割り当てられていますので、AQM0802AのSCLとSDAを下の回路図のように接続します。

 電源は、乾電池2本(3V)です。I2Cのプルアップ抵抗は、AQM0802Aモジュールに内蔵されていますので、つけていません。

 ブレッドボードです。

 1行目に当局のコールサインが表示され、2行目に数字のカウントアップが表示されます。

 プログラムです。

 I2Cのピンの設定ですが、SCLは出力設定、SDAは出力と入力両方の設定が必要です。

 SCL,SDAピンともTRISCは入力に設定します。

-----------------------------------------------------------------

/*
 * File:   main.c
 * Author: JH7UBC Keiji Hata
 * PIC16F1705 AQM0802A表示テスト
 * Created on 2019/04/09
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
 
// CONFIG1
#pragma config FOSC = INTOSC    //内部クロックを使う
#pragma config WDTE = OFF       //ウォッチドックタイマー無効
#pragma config PWRTE = ON       //パワーアップタイマーを有効にする
#pragma config MCLRE = OFF      //MCLRピンをRA3として使用する
#pragma config CP = OFF         //プログラムメモリを保護しない
#pragma config BOREN = ON       //ブラウンアウトリセットを有効にする
#pragma config CLKOUTEN = OFF   //クロック出力を無効とし、RA4ピンとして使用する
#pragma config IESO = OFF       //内部・外部クロックの切り替えでの起動を行わない
#pragma config FCMEN = OFF      //外部クロックを監視しない
 
// CONFIG2
#pragma config WRT = OFF        //フラッシュメモリを保護しない
#pragma config PPS1WAY = OFF    //ロック解除シーケンスで何度でもPPSLOCKをセット/クリアできる
#pragma config ZCDDIS = ON      //ゼロクロス検出回路無効
#pragma config PLLEN = ON       //×4PLLを動作させる
#pragma config STVREN = ON      //スタックオーバーフローリセットを行う
#pragma config BORV = HI        //ブラウンアウトリセット電圧を高(2.7V)に設定
#pragma config LPBOR = OFF      //低消費電力ブラウンアウトリセット無効
#pragma config LVP = OFF        //低電圧プログラミングを行わない
#define _XTAL_FREQ 32000000     //クロック32MHz
#define LCD_addr 0x7C   //3E+0

/* I2C 初期化*/
void I2C_init(){
    SSP1CON1 = 0x28;        //SSPEN = 1,I2C Master Mode
    SSP1STATbits.SMP = 1;   //標準速度モード(100KHz)
    SSP1ADD = 0x4F;         //Fosc/(4*Clock)-1  Clock=100kHz,Fosc=32MHz
}
 
/* スタートコンディション */
void I2C_start(){
    SSP1CON2bits.SEN = 1;
    while(SSP1CON2bits.SEN);
}
 
/* ストップコンディション */
void I2C_stop(){
    SSP1IF = 0;
    SSP1CON2bits.PEN = 1;
    while(SSP1CON2bits.PEN);
    SSP1IF = 0;
}
 
/* I2Cに1byte送信 */
void I2C_write(unsigned char dat){
    SSP1IF = 0;
    SSP1BUF = dat;
    while(!SSP1IF);
}
 
/* write command */
void LCD_cmd(unsigned char cmd){
    I2C_start();          //start condition
    I2C_write(LCD_addr);  //send slave address
    I2C_write(0x00);      //send control byte
    I2C_write(cmd);       //send command
    I2C_stop();           //stop condition
}
 
/* write charactor */
void LCD_char(unsigned char dat){
    I2C_start();          //start condition
    I2C_write(LCD_addr);  //send slave address
    I2C_write(0x40);      //send control byte
    I2C_write(dat);       //send data
    I2C_stop();           //stop condition
}
 
/* LCD initialize */
void LCD_init(){
    __delay_ms(40); //40ms wait
    LCD_cmd(0x38);  //8bit,2line
    LCD_cmd(0x39);  //IS=1 : extention mode set
    LCD_cmd(0x14);  //Internal OSC Frequency
    LCD_cmd(0x70);  //Contrast set
    LCD_cmd(0x56);  //Power/ICON/Contrast Control
    LCD_cmd(0x6C);  //Follower control
    __delay_ms(200);//200ms wait
    LCD_cmd(0x38);  //IS=0 : extention mode cancel
    LCD_cmd(0x0C);  //Display ON
    LCD_cmd(0x01);  //Clear Display
    __delay_ms(2);  //wait more than 1.08ms
}
 
/* Clear Display */
void LCD_clear(){
    LCD_cmd(0x01);
    __delay_ms(1);
    __delay_us(80);
}
 
/* Return Home `*/
void LCD_home(){
    LCD_cmd(0x02);
    __delay_ms(1);
    __delay_us(80);
}
 
/* Cursor 2line top */
void LCD_2line(){
    LCD_cmd(0xC0);
}
 
/* write 1 charactor to LCD  */
void putch(unsigned char ch){
    LCD_char(ch);
}
 
/* write string */
void putstr(const unsigned char *s){
    while(*s){
        LCD_char(*s++);
    }     
}
 
void main() {
OSCCON = 0b01110000 ; // 内部クロック8MHz ×4=32MHz
ANSELA = 0b00000000 ; // AN0-AN3を使わない
ANSELC = 0b00000000 ; // AN4-AN6を使わない
TRISA = 0b00000000 ; // 全て出力
TRISC = 0b00000011 ; // RC0,RC1は入力、他は出力
PORTA = 0b00000000 ; // PORTAクリア
PORTC = 0b00000000 ; // PORTCクリア

/* I2Cピン設定 */
RC0PPS = 0x10; //RC0をSCLに割り当てる(出力)
SSPDATPPS = 0x11;//SSPDATにRC4を割り当てる(入力)
RC1PPS = 0x11; //RC1をSDAに割り当てる(出力)

I2C_init();
LCD_init();

printf("JH7UBC");
unsigned char count = 0;

while(1){
LCD_2line();
printf("%4d",count);
__delay_ms(1000);
count++;
}
}
 
------------------------------------------------------------------------
 
 PIC16F1705は、PPSモジュールを内蔵していますので、I2Cのピンも他に設定できると思って、やってみたのですが、他のピンでは表示できませんでした。
 
 I2Cを他のピンに設定する方法をご存知の方、教えていただけるとありがたいです。

PIC16F1705 シリアル通信テスト

2019-04-06 09:37:21 | PIC16F1705

 PIC16F1705でのシリアル通信のテストをします。

 いつものように、USBシリアル変換モジュールFT234Xを使い、パソコンの通信ソフトTeraTermとの間でのシリアル通信テストです。電源(5V)もFT234Xから供給します。

 PIC16F1705の特徴の一つは、PPSモジュールを内蔵していることです。PPSとは、Peripheral Pin Selectのことで、各機能の入出力ピンを自由に設定できるのです。(デジタルI/Oのみ)

 今回は、USARTのTX(出力)をRA0に、RX(入力)をRA1に割り当ててみます。PPSについては、PIC16F1705の説明書のP137とP139とP141に説明があるのですが、英文のこともあり、今一つ分かりませんでしたが、「きむ茶工房ガレージハウス」の16F1705覚書を読んで、ようやく理解できました。

 設定の方法です。

 まず、入力ピンの選択は、XXXPPSレジスタ群で行います。XXXは、入力機能でINT,T0CKI,T1CKI,T1G,CCP1,CCP2,COG,SSPCLK,SSPDAT,SSPSS,RX,CK,CLCIN0,CLCIN1,CLCIN2,CLCIN3です。

RXをRA1に割り当てる場合は、RXCCP = 0x01;とします。(0x01は、RA1を指定する値です。説明書のP139を参照)

 次に出力の選択は、RxyPPSレジスタ群で行います。Rxyはピンの名称で、RA0,RA1,RA2,RA4,RA5,RC0,RC1,RC2,RC3,RC4,RC5です。(RA3はありません。)

 RA0にTXを割り当てる場合は、RA0CCP = 0x14;とします。(数値は、TXを指定する値です。説明書のP141を参照)

 回路図です。

ブレッドボードです。

TeraTermの画面です。(キーボードからのエコーバックです)

プログラムです。

以下のプログラムで、通信速度300bps~230400bpsで通信できることを確認しました。

--------------------------------------------------------------

/*
 * File:   main.c
 * Author: Keiji
 * PIC16F1705 シリアル通信テスト
 * Created on 2019/04/04
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
 
// CONFIG1
#pragma config FOSC = INTOSC    //内部クロックを使う
#pragma config WDTE = OFF       //ウォッチドックタイマー無効
#pragma config PWRTE = ON       //パワーアップタイマーを有効にする
#pragma config MCLRE = OFF      //MCLRピンをRA3として使用する
#pragma config CP = OFF         //プログラムメモリを保護しない
#pragma config BOREN = ON       //ブラウンアウトリセットを有効にする
#pragma config CLKOUTEN = OFF   //クロック出力を無効とし、RA4ピンとして使用する
#pragma config IESO = OFF       //内部・外部クロックの切り替えでの起動を行わない
#pragma config FCMEN = OFF      //外部クロックを監視しない
 
// CONFIG2
#pragma config WRT = OFF        //フラッシュメモリを保護しない
#pragma config PPS1WAY = OFF    //ロック解除シーケンスで何度でもPPSLOCKをセット/クリアできる
#pragma config ZCDDIS = ON      //ゼロクロス検出回路無効
#pragma config PLLEN = ON       //×4PLLを動作させる
#pragma config STVREN = ON      //スタックオーバーフローリセットを行う
#pragma config BORV = HI        //ブラウンアウトリセット電圧を高(2.7V)に設定
#pragma config LPBOR = OFF      //低消費電力ブラウンアウトリセット無効
#pragma config LVP = OFF        //低電圧プログラミングを行わない
 
#define _XTAL_FREQ 32000000     //クロック32MHz
 
void serial_init(unsigned long BR){
    TX1STA = 0x24;   //SYNC=0 TXEN = 1 BRGH = 1
    BRG16 = 1;       //BRG 16bit mode
    RC1STA = 0x90;   //非同期、継続受信可
    unsigned int X= _XTAL_FREQ/BR/4 - 1;
    SP1BRGH = X / 256;
    SP1BRGL = X % 256;
}
 
void putch(unsigned char byte){
    while(!TXIF);
    TX1REG = byte;
}
 
unsigned char getch(){
    while(!RCIF);
    return RC1REG;
}
 
unsigned char getche(){
    unsigned char c;
    c = getch();
    putch(c);
    return c;
}
 
void main() {
    OSCCON = 0b01110000 ;     // 内部クロック8MHz ×4=32MHz
    ANSELA = 0b00000000 ;     // AN0-AN3を使わない
    ANSELC = 0b00000000 ;     // AN4-AN6を使わない
    TRISA  = 0b00000010 ;     // RA1は入力他は出力
    TRISC  = 0b00000000 ;     // 全て出力
    PORTA  = 0b00000000 ;     // PORTAクリア
    PORTC  = 0b00000000 ;     // PORTCクリア
   
    /* TX RXピンの割り当て*/
    RA0PPS = 0x14;            //RA0にTXを割り当てる。
    RXPPS = 0x01;             //RXをRA1に割り当てる。
   
    serial_init(9600);        // Serial通信初期化とBaud Rateの設定
   
    while(1){
        getche();
    }
}
 
----------------------------------------------------------------------
 
入力ピンはデフォルト値が設定されていますので、ディフォルトと同じなら指定しなくともOKですが、出力ピンは、PPSで必ず指定しなければなりません。
このことが分からず、最初動作しなくて戸惑いました。
ちなみに、RXのディフォルトは、RC5です。
ディフォルトで使用する場合は、入力の設定は必要ありません。
 
PPSLOCKで、入出力ピン割り当てをロックすることもできるようですが、今後いろいろ実験しますので、この機能は試しませんでした。