Quantcast
Channel: JH7UBCブログ
Viewing all 440 articles
Browse latest View live

PIC12F675 Lチカ

$
0
0
 MPLABX XC8 PICKit3環境でPICのプログラミングの勉強をしています。

 今回からPICの品種を8pin PICのPIC12F675にします。

 Webで、「MPLABX PIC12F675」を検索すると、「MPLABX ではPIC12F675は使えない」という記事が目につきました。
 これは、困ったと思い、記事を読むと、PIC12F675(629)を内蔵クロックで使った場合、0x3FF番地にメーカーが書き込んだクロック周波数の補正値(キャリブレーション値)がプログラムを書きこむときに消えてしまうことが原因のようです。

 ただ、この記事読むと、MPLABXとPICkit2の組み合わせのようです。PICKit3ならどうなんだろうと思い、テストしました。

 私は、PIC12F629と12F675を今2個ずつ持っています。このキャリブレーション値が消えた場合困るので、以前からこの値を調べて、PICの腹にテプラで張り付けています。

イメージ 1

 一応こんな準備をして、PIC12F675でLチカのテストをしました。

 PMLABXはV5.05、XC8はV1.45、PICkit3(ANBE製)という環境です。

 回路図です。GPIO2に接続したLEDを0.5秒ごとに点滅させます。電源は乾電池で3.0Vです。

イメージ 2

 ブレッドボードです。

イメージ 3

 プログラムです。

#include <xc.h>
#include <stdio.h>
#include <stdlib.h>

// CONFIG
#pragma config FOSC = INTRCIO   //内蔵クロック4MHzを使う
#pragma config WDTE = OFF       //
#pragma config PWRTE = ON       //
#pragma config MCLRE = OFF      //
#pragma config BOREN = OFF      //
#pragma config CP = OFF         //
#pragma config CPD = OFF        //

#define _XTAL_FREQ 4000000

void main() {
     CMCON  = 0x07 ;           // コンパレータは使わない
     ANSEL  = 0b00000000 ;     // すべてデジタI/O
     TRISIO = 0b00000000 ;     // ピンは全て出力に割当てる(GP3は入力のみ)
     GPIO   = 0x00 ;           // 出力ピンの初期化
     while(1) {
          GPIO2 = 1 ;          // LED ON
          __delay_ms(500) ;
          GPIO2 = 0 ;          // LED OFF
          __delay_ms(500) ;
     }
}

 このプログラムで問題なくLEDが点滅しました。

 MPLABX XC8 PICkit3の組み合わせなら大丈夫なようです。

PIC12F675 割り込みを使ったLチカ

$
0
0
 PIC12F675の例題2として、TMR0割り込みを使ってLEDを1秒ごとに点滅させます。

 PIC12F675は、8bitタイマーTMR0と16bitタイマーTMR1を持っています。今回はTMR0を使います。

 内蔵クロック4MHzを使いますので、命令サイクルは、1/4MHz×4 = 1us となります。

 TMR0のプリスケーラを1:32にすれば、1us×32×256 = 8192us = 8.192 ms ごとにオーバーフローが発生し、割り込みがかかります。
 割り込みは、1秒間に122回かかりますので、122をカウンタにセットして割り込みの度にディクリメントし、カウンタが0になったら1秒とします。

 実際には、8.192ms × 122 = 999.4msです。誤差は1%以下です。

 回路は、前の記事と同じです。

 プログラムです。

#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
// CONFIG
#pragma config FOSC = INTRCIO   //内蔵クロック4MHzを使う
#pragma config WDTE = OFF       //
#pragma config PWRTE = ON       //
#pragma config MCLRE = OFF      //
#pragma config BOREN = OFF      //
#pragma config CP = OFF         //
#pragma config CPD = OFF        //
#define _XTAL_FREQ 4000000

unsigned cnt1s;
unsigned flag_1s;

/* 割り込み処理 */
void interrupt isr(){
    INTCONbits.T0IF = 0;       //TMR0割り込みフラッグクリア
    cnt1s--;                   //cnt1s ディクリメント
    if(cnt1s == 0){            //1s経過したら
        cnt1s = 122;           //カウンタ再セット
        flag_1s = 1;           //フラッグを立てる
    }
}

void main() {
     CMCON  = 0x07 ;           // コンパレータは使わない
     ANSEL  = 0b00000000 ;     // すべてデジタI/O
     TRISIO = 0b00000000 ;     // ピンは全て出力に割当てる(GP3は入力のみとなる)
     GPIO   = 0x00 ;           // 出力ピンの初期化
    
     INTCONbits.T0IE = 1;      // TMR0割り込み許可
     INTCONbits.GIE = 1;       // 全割り込み許可
     /* TMR0関係設定 */
     OPTION_REGbits.T0CS = 0;   // TMR0のソースは、内部命令サイクル
     OPTION_REGbits.PSA = 0;    // プリスケーラをTMR0に設定
     OPTION_REGbits.PS2 = 1;    // プリスケーラ 1:32
     OPTION_REGbits.PS1 = 0;
     OPTION_REGbits.PS0 = 0;
    
     cnt1s = 122;   // 8.192ms×122 = 999.4ms
     flag_1s = 0;   // 1sec経過フラッグ
    
     while(1) {
         if(flag_1s){    //1s経過フラッグが立ったら
          flag_1s = 0;       // 1s経過フラッグクリア
          GPIO2 = 1 - GPIO2; // LED ON/OFF
         }
     }
}
-----------------------------

 各レジスタの設定ですが、例えば
 OPTION_REGbits.T0CS = 0;  は、T0CS = 0;でもOKです。

 なぜ、長い記述にしたかといいますと、XC8コンバイラは、プログラム編集中にリアルタイムでディバック、サポートをしてくれます。

 ですから、OPTION_REGbits. まで入力すると各ビットの名称を候補としてウインドウに表示してくれます。

 その候補の中から選べば、間違いはありません。この便利な機能を使うために、レジスタ・ビット名が長くなっています。記述は長くなるのですが、後で見てもどのレジスタか分かりやすくなります。

PIC12F675 内蔵クロック周波数のキャリブレーション

$
0
0
 MPLABX XC8 PICkit3の環境で、PIC12F675にプログラムが書けて、ほっとしたとこです。

 Lチカ、割り込みを使ったLチカと進んできて、次の課題は、PIC12F675が持っていない機能であるシリアル通信とI2C通信の実験をやろうと思っています。

 それらのタイミングをとるのに、内臓クロック周波数の精度を調べてみようと思います。

 CONFIGで、FOSC = INTRCCLK とすれば、クロックの1/4の周波数(1MHz)が、GP4に出力されます。

 これを利用して、クロック周波数を周波数カウンタ(自作です)測定してみると

イメージ 1

 なんと、30%以上ござがあります。キャリブレーション値がOSCCALレジスタに書き込まれていないようです。

 さて、この環境で、どうしたらキャリブレーション値をPICに書き込むことができるのでしょうか。

 Web上で方法を探してみると、MPLABXのProject propatiesのXC8 Linkerを開いて設定するようです。

 さっそく、Calibration oscillator にチェックを入れ、Alternate oscillator calibratiom value に記録しておいたメーカー設定値「341C」を入れてみました。

イメージ 2

 赤い文字のエラーメッセージがでました。±00~99までの数値でないとだめなようです。

 1C=28なので、「3428」をキャリブレーション値にセットして(これでいいのか、疑問)、プログラムを書き込んで、周波数をチェックすると

イメージ 3

 誤差約3%になりました。この後、3420~3480の間で、周波数を測定してみましたが、最初の値が一番誤差が小さいようでした。

 クロック周波数ですが観察してみると、RC発振回路なので、100Hz台まで、ちょこちょこと周波数が変化します。

 でも、通信関係程度ならこの精度でもなんとかなるかな、と考えています。

 メーカーキャリブレーション値を消してしまった場合の対策につ紹介してるサイトは、いくつか見つかりました。

 今回は、メーカーキャリブレーション値を記録しておいたので、助かりました。
 

PIC12F675 シリアル通信テスト

$
0
0
 PIC12F675の内蔵クロック周波数のキャリブレーションの方法が分かり、クロック周波数の精度を誤差5%以下に調整できました。

 それでは、PIC12F675で、シリアル通信をやってみましょう。PIC12F675は、MSSPなどのデータ通信機能を持っていませんので、プログラムでシリアル通信を実現します。(ソフトウェアUARTといいます)

 プログラムは、海外のサイト(Saeed's Blog)を参考にしました。(というかほとんどコピーさせてもらいました)

 回路図です。GP0を送信にGP1を受信に使います。

イメージ 1

 テストプログラムです。通信速度(Baudrate)は1200bpsにしました。パソコンからの文字を受信して、その文字を送信する、いわゆるエコーバックのプログラムです。
----------------------------------------------------
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>

// CONFIG
#pragma config FOSC = INTRCIO   // 内蔵クロック4MHz
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config BOREN = OFF
#pragma config CP = OFF
#pragma config CPD = OFF

#define _XTAL_FREQ 4000000
#define Baudrate 1200
#define OneBitDelay (1000000 / Baudrate)
#define HalfBitDelay (1000000 / Baudrate / 2)
#define TX GP0
#define RX GP1

//シリアル通信ポートの初期化
void serial_init(){
    TRISIO0 = 0;    //GP0 output
    TRISIO1 = 1;    //GP1 input
    TX = 1;         //アイドル状態でTXpin High
}

//1文字送信
void putch(unsigned char byte){
    // Send start bit
    TX = 0;
    __delay_us(OneBitDelay);
    for(unsigned char i=0;i<8;i++){
        if((byte >> i)&0x1){
            TX = 1;
        }else{
            TX = 0;
        }
        __delay_us(OneBitDelay);
    }
    //Send stop bit
    TX = 1;
    __delay_us(OneBitDelay);
}

//1文字受信
unsigned char getch(){
    unsigned char RCdata = 0;
    while(RX == 1); //wait for start bit
    __delay_us(OneBitDelay);
    __delay_us(HalfBitDelay);
    for(unsigned char i=0;i<8;i++){
        if(RX == 1){
            RCdata += (1<<i);
        }
        __delay_us(OneBitDelay);
    }
    if(RX == 1){
        __delay_us(OneBitDelay);
        return RCdata;
    }else{
        __delay_us(OneBitDelay);
        return 0x00;
    }
}

//メインプログラム
void main() {
    unsigned char ch;
    CMCON = 0x07;   //comparater OFF
    ANSEL = 0x00;   //All GPIO is digital I/O
    TRISIO = 0b00001000;    //GP3 is input otheris  outpiut
    GPIO = 0x00;    // clear I/O port
    serial_init();  // UART initialize
   
    while(1){
        ch = getch();   //1文字受信
        putch(ch);      //1文字送信
    }
}
--------------------------------------------

 TeraTermで送受信した画面です。
イメージ 2

 送信関数をputch()としましたので、printf構文が使えます。この構文を使うとプログラムサイズは大きくなりますが、数値、文字をいろいろな形式で送信できます。

 通信速度2400bps,4800bpsとあげて、テストしてみましたが、文字化けしてしまいました。

 OnebitDelayの数値を調整すれば、速度アップもできると思いますが、とりあえず、シリアル通信でデータを送信したいときには、1200bpsと速度は遅いですが、このプログラムで送受信できます。

アンテナ支柱倒壊

$
0
0
 昨日午後から、湿った雪が約20cm降りました。朝、アンテナはどうかと思い外を見ると3.5MHzのアンテナの支柱が倒れていました。湿った雪がエレメントについて、その重みで倒れたようです。

イメージ 1

 畑の隅に立てた支柱ですので、畑の土が湿ってゆるみ、雪の重みに耐えられなかったようです。

 支柱を立て直し、支えの足場パイプも取り付け、修復しました。

イメージ 2

 もっと一杯7雪が降ってくれると、雪が支柱を支えてくれます。

 昨年は、3..6MHzのトラップコイルに届くほど、雪がたまりました。Hi

 いよいよ冬本番です。

 夜になり、21時からのFCWAのCW交信会の準備をしていると、SWRがほぼ∞になり、マッチングがとれません。

 夜で、雪も降っていて、原因をつきとめることができず、今日の交信会は、残念ながら出られません。

 明日、もう一度確認してみましょう。

アンテナ点検

$
0
0
 今日は、久しぶりに朝から良い天気になりました。

 さっそ、不具合を生じた3.5M/1.9MインバーテッドV アンテナの点検をしました。

 まず、給電部を目視で確認

イメージ 1

 特に問題ないようです。

 3.5Mhzのトラップコイルを確認。

イメージ 2

 だいぶ傷んできました。接触などを確認すると大丈夫そう。

 アンテナアナライザーBR-200でSWRを確認。

 3.5MHz

イメージ 3

 約2です。

 1.9MHz

イメージ 4

 約2.5 こんなものでしょうか。

 どちらも以前の値と同じです。

 では、先日の不調の原因はなんだったんだろうか・。

 前に、トラップコイルの接触不良で、SWRが上がったことがありました。

 今回もトラップコイル周りが原因と考えています。

 今シーズンは、このまま、だましだまし使って、来年は、このコイルを作り替えようと思っています。

PIC12F675 I2C address scanner

$
0
0
 PIC12F675は、I2Cの信号を発生する機能を持っていません。そこで、ソフト的にI2Cを行うことになります。

 あちこちのWebサイトを参考にして、start condition, 1 bye 送信,stop conditionのプログラムを組み、0b10101010を送って、波形を観察してみました。

イメージ 1

 SCLのパルス幅は10usです。速度は約20kHzです。

 ACKは、スレーブから応答があった時は、SDAが0になります。

 大丈夫そうなので、このソフトI2Cと以前に試したシリアル通信を組み合わせて、I2C アドレス スキャナを作ってみました。表示の仕方は、Travel and softwareというサイトを参考にさせていただきました。

 今回は、秋月電子のI2C LCDモジュールAQM0802Aのアドレスを調べてみます。

 回路図です。

イメージ 4

 I2Cバスのプルアップ抵抗は、AQM0802Aモジュールに内蔵されていますので、つけていません。

 AQM0802AのVDDは3.3Vなので、単3電池2本で電源としています。

 SWを押すとscanが始まります。

 ブレッドボードです。

イメージ 2

 スキャン結果は、TeraTermに表示されます。Baudrateは1200bpsです。

 ACKが0の時、アドレスに[ ]がつきます。なお、アドレス00~07と78~7Fはシステムで予約されたアドレスですので、スキャンしません。

イメージ 3

 3Eに[ ]がついています。AQM0802Aのスレーブアドレスは、3Eです。

 実際にアドレスとして送るときには、3Eを左に1ビットシフトし LSBに0 をセットして、7Cを送ります。

 プログラムです。シリアル通信は、送信だけにしています。

#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
// CONFIG
#pragma config FOSC = INTRCIO  // 内蔵クロック4MHz
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config BOREN = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#define _XTAL_FREQ 4000000

#define TX GPIO0
#define RX GPIO1
#define SW GPIO2
#define SDA GPIO4
#define SCL GPIO5
#define SDA_ON GPIO4 = 1
#define SDA_OFF GPIO4 = 0
#define SCL_ON GPIO5 = 1
#define SCL_OFF GPIO5 = 0
#define SDA_OUT TRISIO4 = 0
#define SDA_IN TRISIO4 = 1
#define SCL_OUT TRISIO5 = 0
#define SCL_IN TRISIO5 = 1
#define wait __delay_us(10)
#define Baudrate 1200
#define OneBitDelay (1000000 / Baudrate)
#define HalfBitDelay (1000000 / Baudrate / 2)
#define start_addr 0b00000111
#define end_addr 0b01111000

void serial_init(){
    TRISIO0 = 0;    //GP0 output
    TRISIO1 = 1;    //GP1 input
    TX = 1;         //アイドル状態でTXpin High
}
void putch(unsigned char byte){
    // Send start bit
    TX = 0;
    __delay_us(OneBitDelay);
    for(unsigned char i=0;i<8;i++){
        if(byte & 0x1){
            TX = 1;
        }else{
            TX = 0;
        }
        __delay_us(OneBitDelay);
        byte >>=1;
    }
    //Send stop bit
    TX = 1;
    __delay_us(OneBitDelay);
}

void I2C_init(){
    SDA_IN;
    SCL_IN;
    SDA_ON;       
    SCL_ON;
    }

void I2C_start(){
    SDA_OUT;
    SCL_OUT;
    SDA_ON;       
    SCL_ON;
    wait;
    SDA_OFF;
    wait;
    SCL_OFF;
}

void I2C_stop(){
    SDA_OFF;
    SCL_OFF;
    wait;
    SCL_ON;
    wait;
    SDA_ON;
    SDA_IN;
    SCL_IN;
}

unsigned char I2C_write(unsigned char byte){
    unsigned char ACK = 0;
    for(unsigned char i=0;i<8;i++){
        SCL_OFF;
        wait;
        if(byte & 0x80){
            SDA_ON;
        }else{
            SDA_OFF;
        }
        byte <<=1;
        wait;
        SCL_ON;
        wait;
        SCL_OFF;
    }
    //Get ACK
    SDA_IN;
    wait;
    SCL_ON;
    wait;
    ACK = SDA;
    SCL_OFF;
    SDA_OUT;
    return ACK;
}

void main() {
    CMCON = 0x07;   //コンパレータを使わない
    ANSEL = 0x00;   //アナログポートを使わない
    TRISIO = 0b00110110;    //GP1,GP2,GP4,GP5 input GP0,GP3 output
    I2C_init();     //I2C初期化
    serial_init();  //シリアル通信初期化
   
    while(SW);  //SWが押されたらscan start
   
    printf("I2C Address Scan\r\n");
   
    for(unsigned char i=0;i<0x7f;i+=0x10){
        for(unsigned char j=0;j<=0xf;j++){
            unsigned char i2c_adr = i+j;
            if(i2c_adr <= start_addr || i2c_adr >= end_addr){
                printf(" -- ");
            }else{
                I2C_start();
                unsigned char acknowledge = I2C_write(i2c_adr<<1);
                I2C_stop();
                if(acknowledge == 0){
                printf("[%02X]",i2c_adr);
                }else{
                printf(" %02X ",i2c_adr);
                }
            }
        }
            printf("\r\n");
    }
   
    while(1){
        }
}

PIC12F675 AQM0802A表示テスト

$
0
0
 前の記事のとおりPIC12F675でI2C通信ができるようになりましたので、秋月電子で販売しているI2C LCD AQM0802Aに文字を表示するテストをします。

 回路図です。AQM0802Aは、モジュールを使用しますので、プルアップ抵抗は内蔵しています。電源は電池2本です。

イメージ 1

 AQM0802AにI2Cでコマンドまたは、文字を送るフォーマットは次の通りです。

イメージ 2

 AQM0802Aの初期化の手順です。

イメージ 3

 これらを元にプログラムします。なお、こちらのサイトがたいへん参考になりました。

 プログラムです。
--------------------------------------
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
/* PIC12F675--AQM0802A
 * GP4--SCL
 * GP5--SDA
 */
// CONFIG
#pragma config FOSC = INTRCIO  // Internal OSC 4MHz
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config BOREN = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#define _XTAL_FREQ 4000000
#define SDA GPIO4
#define SCL GPIO5
#define SDA_ON GPIO4 = 1
#define SDA_OFF GPIO4 = 0
#define SCL_ON GPIO5 = 1
#define SCL_OFF GPIO5 = 0
#define SDA_OUT TRISIO4 = 0
#define SDA_IN TRISIO4 = 1
#define SCL_OUT TRISIO5 = 0
#define SCL_IN TRISIO5 = 1
#define LCD_addr 0x7C   //3E+0
#define wait __delay_us(10) //wait 10us
#define wait_time __delay_us(30)    //LCD wait time 

/* I2C initialize */
void I2C_init(){
    SDA_IN;
    SCL_IN;
    SDA_ON;       
    SCL_ON;
    }

/* start condition */
void I2C_start(){
    SDA_OUT;
    SCL_OUT;
    SDA_ON;       
    SCL_ON;
    wait;
    SDA_OFF;
    wait;
    SCL_OFF;
}

/* stop condition */
void I2C_stop(){
    SDA_OFF;
    SCL_OFF;
    wait;
    SCL_ON;
    wait;
    SDA_ON;
    SDA_IN;
    SCL_IN;
}

/* I2C write 1byte */
unsigned char I2C_write(unsigned char byte){
    unsigned char ACK = 0;
    for(unsigned char i=0;i<8;i++){
        SCL_OFF;
        wait;
        if(byte & 0x80){
            SDA_ON;
        }else{
            SDA_OFF;
        }
        byte <<=1;
        wait;
        SCL_ON;
        wait;
        SCL_OFF;
    }
    //Get ACK
    SDA_IN;
    wait;
    SCL_ON;
    wait;
    ACK = SDA;
    SCL_OFF;
    SDA_OUT;
    return ACK;
}

/* write command */
void LCD_cmd(unsigned char cmd){
    unsigned char ackn;
    I2C_start();          //start condition
    ackn = I2C_write(LCD_addr);  //send slave address
    ackn = I2C_write(0x00);      //send control byte
    ackn = I2C_write(cmd);       //send command
    I2C_stop();           //stop condition
}
 
/* write charactor */
void LCD_char(unsigned char dat){
    unsigned char ackn;
    I2C_start();          //start condition
    ackn = I2C_write(LCD_addr);  //send slave address
    ackn = I2C_write(0x40);      //send control byte
    ackn = 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);
}

/* main program */
void main() {
    CMCON = 0x07;   //comparater disable
    ANSEL = 0x00;   //all port is digital
    TRISIO = 0b00110000;    //GP4,GP5 input GP0,GP1,GP2,GP3 output
    I2C_init();     //I2C initialize
    LCD_init();     //LCD initialize
   
    LCD_home();
    printf("JH7UBC");
   
    unsigned char count = 0;
    while(1){
        LCD_2line();
        printf("%3d",count);
        __delay_ms(1000);
        count++;
    }
}
--------------------------------------

 表示結果です。1行目にコールサイン、2行目に数字のカウントアップを表示しました。

イメージ 4

 文字、数字を表示するのに、printf()構文を使えるようにするために、putch()関数を定義しました。

 これで、簡単に文字列、様々なフォーマットで数値を表示できます。

 ただ、難点はプログラムサイズが大きくなることです。今回は、プログラムのメモリ占有率は、約72%に達しています。この問題は、メモリ容量の大きいPICを使うことで解決できます。

 無料で使用できるMPLBXを使っていますので、文句は言えませんが、有料版なら最適化をして、プログラムサイズが小さくなり、速度も向上するのでしょうね。(やったことがないので分かりませんが・・・)


QSOパーティ参加

$
0
0
 今日、午後2時頃から約1時間QSOパーティに参加しました。

 今年の初無線です。

 7MHzのCWで縦振電鍵を使い、のんびりと22局と交信しました。

 N6TI NORIさんからもコールをいただき、今年の初DXになりました。

 今年も自分のペースで、季節、コンディションに合わせて、アマチュア無線を楽しんでいきたいと思います。

 今年もどうぞよろしくお願いいたします。

PIC12F675 A/Dコンバータ テスト

$
0
0
 今年最初の実験は、PIC12F675のA/Dコンバータを使うテストです。

 AN0の電圧をA/D変化して、変換した数値(0~1023)をI2C LCD AQM0802Aに表示します。

 回路図です。電源は、電池2本で3Vです。

イメージ 1

ブレッドボードです。

イメージ 2

 プログラムです。

 AQM0802Aの表示に必要なI2C関係関数とLCD表示関数は、前の記事と同じです。

 ここでは、main()のみ掲載します。

------------------------------------------------------------------
/* main program */
void main() {
    CMCON = 0x07;   //comparater disable
    TRISIO = 0b00110001;    //GP0,GP4,GP5 input GP1,GP2,GP3 output
    ANSEL = 0b00000001;     //select AN0
    ADCON0bits.VCFG = 0;    //Vref = VDD
    ADCON0bits.ADFM = 1;    //ADRES 右詰め
    ADCON0bits.ADON = 1;    //A/D converter ON
    ANSELbits.ADCS0 = 1;    //A/D変換クロック4MHz 16Tosc 4.0us
    ANSELbits.ADCS1 = 0;
    ANSELbits.ADCS2 = 1;
   
    I2C_init();     //I2C initialize
    LCD_init();     //LCD initialize
   
    LCD_home();
    printf("A/D TEST");
   
    /* select AN0*/
    ADCON0bits.CHS0 = 0;
    ADCON0bits.CHS1 = 0;
   
    while(1){
        __delay_us(50); //wait 50us
        ADCON0bits.GO_nDONE = 1;    //A/D convert start
   
        while(ADCON0bits.GO_nDONE);   //wait for A/D convert complete
        unsigned int val = ADRESH;
        val = (val << 8) + ADRESL;
        LCD_2line();
        printf("%4d",val);
        __delay_ms(500);
    }
}
------------------------------------------------
0.5secごとにA/Dコンバータの値を表示します。

/* main program */
void main() {
    CMCON = 0x07;   //comparater disable
    TRISIO = 0b00110001;    //GP0,GP4,GP5 input GP1,GP2,GP3 output
    ANSEL = 0b00000001;     //select AN0
    ADCON0bits.VCFG = 0;    //Vref = VDD
    ADCON0bits.ADFM = 1;    //ADRES 右詰め
    ADCON0bits.ADON = 1;    //A/D converter ON
    ANSELbits.ADCS0 = 1;    //A/D変換クロック4MHz 16Tosc 4.0us
    ANSELbits.ADCS1 = 0;
    ANSELbits.ADCS2 = 1;
   
    I2C_init();     //I2C initialize
    LCD_init();     //LCD initialize
   
    LCD_home();
    printf("A/D TEST");
   
    /* select AN0*/
    ADCON0bits.CHS0 = 0;
    ADCON0bits.CHS1 = 0;
   
    while(1){
        __delay_us(50); //wait 50us
        ADCON0bits.GO_nDONE = 1;    //A/D convert start
   
        while(ADCON0bits.GO_nDONE);   //wait for A/D convert complete
        unsigned int val = ADRESH;
        val = (val << 8) + ADRESL;
        LCD_2line();
        printf("%4d",val);
        __delay_ms(500);
    }
}
-------------------------------------

0.5secごとにA/Dコンバータの値(0~1023)をLCDに表示します。

今回、Vrefは、VDDとしていますが、しっかりとした電圧源に接続すれば、デジタル電圧計ができますね。

PIC12F675のプログラミング練習はこれくらいにして、新しいPICの品種に進みたいと思います。
 

PIC16F883 シリアル通信テスト

$
0
0
 PIC16F883は、PIC16F88とほぼ同じ程度の機能を持っていますが、I/Oが24ポートもあり、Arduinoなどと同じような使い方ができそうです。

 まず、シリアル通信をテストします。

 回路図です。USBシリアル変換モジュールFT234Xを使います。クロックは精度を考えて、20MHzのセラロックにしています。

イメージ 1

 ブレッドボードです。

イメージ 2

 プログラムです。PIC16F883では、SPBRGとSPBRGHを合わせて16bitで設定できます。

 そこで、BRG16 = 1として、16bitモードにします。

 これにより、Baud rateは、300~115200bpsまで設定できます。

--------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>

// CONFIG1
#pragma config FOSC = HS
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
#pragma config LVP = OFF
// CONFIG2
#pragma config BOR4V = BOR40V
#pragma config WRT = OFF
#define _XTAL_FREQ 20000000
void serial_init(unsigned long BR){
    TXSTA = 0x20;   //SYNC=0 TXEN = 1 BRGH = 0
    BRG16 = 1;      //BRG 16bit mode
    RCSTA = 0x90;   //非同期、継続受信可
    unsigned int X= _XTAL_FREQ/BR/16 - 1;
    SPBRGH = X / 256;
    SPBRG = X % 256;
}
void putch(unsigned char byte){
    while(!TXIF);
    TXREG = byte;
}
unsigned char getch(){
    while(!RCIF);
    return RCREG;
}
unsigned char getche(){
    unsigned char c;
    c = getch();
    putch(c);
    return c;
}

void main() {
    ANSEL = 0;  //アナログポートを使わない
    ANSELH = 0;
    TRISA = 0b00000000;
    TRISB = 0b00000000;
    TRISC = 0b10000000; //RC7 input=RX
    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;
   
    serial_init(57600);
    while(1){
        getche();
    }
}
----------------------------------------------------

 Baud rateを変えて、実際にテストしてみました。

 300~57600bpsでは問題ありませんでしたが、115200bpsでは文字化けしてしまいました。

 実用上はこれで大丈夫ですね。

PIC16F883 AQM0802A表示テスト

$
0
0
 PIC16F883は、MSSPモジュールを持っていますので、そのI2Cモードで、I2C LCD AQM0802Aに文字を表示するテストをします。

 回路図です。PIC16F883は、内臓クロック8MHzで動作させ、電源は電池2本、3Vです。

イメージ 1

 ブレッドボードです。

イメージ 2

 プログラムです。LCD関係の関数は、PIC12F675でAQM0802A表示テストをした時と同じです。
 1行目にJH7UBCを表示し、2行目に0~255の数値をカウントアップします。

----------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>

// CONFIG1
#pragma config FOSC = INTRC_NOCLKOUT
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
#pragma config LVP = OFF
// CONFIG2
#pragma config BOR4V = BOR40V
#pragma config WRT = OFF
#define _XTAL_FREQ 8000000
#define LCD_addr 0x7C   //3E+0

/* I2C 初期化*/
void I2C_init(){
    SSPCON = 0x28;  //SSPEN = 1,I2C Master Mode
    SSPSTATbits.SMP = 1;    //標準速度モード(100KHz)
    SSPADD = 19;    //Fosc/(4*Clock)-1  Clock=100kHz,Fosc=8MHz
}
/* スタートコンディション */
void I2C_start(){
    SEN = 1;
    while(SEN);
}
/* ストップコンディション */
void I2C_stop(){
    SSPIF = 0;
    PEN = 1;
    while(PEN);
    SSPIF = 0;
}
/* I2Cに1byte送信 */
unsigned char I2C_write(unsigned char dat){
    SSPIF = 0;
    SSPBUF = dat;
    while(!SSPIF);
    return ACKSTAT;
}
/* write command */
void LCD_cmd(unsigned char cmd){
    unsigned char ackn;
    I2C_start();          //start condition
    ackn = I2C_write(LCD_addr);  //send slave address
    ackn = I2C_write(0x00);      //send control byte
    ackn = I2C_write(cmd);       //send command
    I2C_stop();           //stop condition
}
 
/* write charactor */
void LCD_char(unsigned char dat){
    unsigned char ackn;
    I2C_start();          //start condition
    ackn = I2C_write(LCD_addr);  //send slave address
    ackn = I2C_write(0x40);      //send control byte
    ackn = 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() {
    ANSEL = 0;  //アナログポートを使わない
    ANSELH = 0;
    OSCCON = 0x70;  //内蔵クロック8MHz
    TRISA = 0b00000000;
    TRISB = 0b00000000;
    TRISC = 0b00011000; //RC3(SCL),RC4(SDA)input
    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;
   
    I2C_init();
    LCD_init();
    putstr("JH7UBC");
    unsigned char count = 0;
   
    while(1){
        LCD_2line();
        printf("%3d",count);
        count++;
        __delay_ms(500);
    }
}

PIC16F883 I2Cインターフェース付きLCD1602

$
0
0
 PIC16F883でI2Cインターフェース付きLCD1602の表示テストをしました。

 Arduinoで実験済みのプログラムを移植しました。Arduinoの記事は、こちら

 回路図です。

イメージ 1

ブレッドボードです。表示された文字がちょっと見ずらいです。すみません。

イメージ 2

プログラムです。I2Cインターフェースを使ってLCD1602に文字を表示する原理は、JH7UBCホームページのこちらの記事を見てください。
---------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
// CONFIG1
#pragma config FOSC = INTRC_NOCLKOUT
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
#pragma config LVP = OFF
// CONFIG2
#pragma config BOR4V = BOR40V
#pragma config WRT = OFF
#define _XTAL_FREQ 8000000
#define LCD_EN 0b00000100   //Enable
#define LCD_BL 0b00001000   //Back Light
#define LCD_CMD 0x00
#define LCD_CHR 0x01
#define LCD_LINE1 0x80
#define LCD_LINE2 0xC0
#define LCD_addr 0x4E   //0x27<<1
#define SW RB0
#define start_addr 0b00000111
#define end_addr 0b01111000

/* I2C 初期化*/
void I2C_init(){
    SSPCON = 0x28;  //SSPEN = 1,I2C Master Mode
    SSPSTATbits.SMP = 1;    //標準速度モード(100KHz)
    SSPADD = 19;    //Fosc/(4*Clock)-1  Clock=100kHz,Fosc=8MHz
}
/* スタートコンディション */
void I2C_start(){
    SEN = 1;
    while(SEN);
}
/* ストップコンディション */
void I2C_stop(){
    SSPIF = 0;
    PEN = 1;
    while(PEN);
    SSPIF = 0;
}
/* I2Cに1byte送信 */
unsigned char I2C_write(unsigned char dat){
    SSPIF = 0;
    SSPBUF = dat;
    while(!SSPIF);
    return ACKSTAT;
}
void Write_data(unsigned char data){
    unsigned char ackn;
    I2C_start();
    ackn = I2C_write(LCD_addr);
    ackn = I2C_write(data | LCD_EN | LCD_BL);
    ackn = I2C_write(data | LCD_BL);
    I2C_stop();
    __delay_us(100);
}
void LCD_write(unsigned char bits,unsigned char mode){
    //send High 4bits
    Write_data((bits & 0xF0) | mode);
    //send Low 4bits
    Write_data(((bits << 4) & 0xF0) | mode);
}
void LCD_init(){
    LCD_write(0x33,LCD_CMD);
    LCD_write(0x32,LCD_CMD);
    LCD_write(0x06,LCD_CMD);
    LCD_write(0x0C,LCD_CMD);
    LCD_write(0x28,LCD_CMD);
    LCD_write(0x01,LCD_CMD);
    __delay_ms(1);
}
void LCD_clear(){
    LCD_write(0x01,LCD_CMD);
    __delay_ms(1);
}
void LCD_home(){
    LCD_write(0x02,LCD_CMD);
    __delay_ms(1);
}
void LCD_cursor(unsigned char x,unsigned char y){
    if (y == 0){
        LCD_write(LCD_LINE1+x,LCD_CMD);
    }
    if (y == 1){
        LCD_write(LCD_LINE2+x,LCD_CMD);
    }
}
void LCD_char(unsigned char byte){
    LCD_write(byte,LCD_CHR);
}
void putch(unsigned char ch){
    LCD_char(ch);
}
void main() {
    ANSEL = 0;  //アナログポートを使わない
    ANSELH = 0;
    OSCCON = 0x70;  //内蔵クロック8MHz
    TRISA = 0b00000000;
    TRISB = 0b00000000;
    TRISC = 0b00011000; //RC3(SCL),RC4(SDA)input
    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;
   
    I2C_init();
    LCD_init();
   
    printf("Hello World!");
    LCD_cursor(5,1);
    printf("JH7UBC");
   
    while(1){
    }
}
-----------------------------------------------
 今回のトラブルシューティング

 すでにArduinoで実験済みのプログラムです。簡単に動作すると思ったのですが、どっこい、電源を入れてもまったく表示しません。

 回路は簡単ですから、接続に間違いはないはず。

 うーーん。表示されない原因が分からない。

 Arduinoでやってみると、ちゃんと表示されます。

 I2C1の信号はちゃんと出ているのか、picoスコープで確認すると問題ありません。

 いろいろ確認に1日かかりました。

 一夜明けて、はっつと気づきました。SDAとSCLの配線が逆になっているでは、ありませんか。

 入れ替えたら、ぱっと表示されました。

 正しく配線したはず、という思い込みでした。

 反省。

 さて、次に進みましょう。

PIC16F883 AD9834DDS VFO

$
0
0
 PIC16F883とAD9834DDSを組み合わせてVFOを試作してみました。

 周波数表示は前の記事で実験したI2Cインターフェース付きのLCD1602です。

 ロータリーエンコーダのプログラムは、PIC16F88で実験したものを使います。(割り込みを使わない方法)

 AD9834DDSのコントロールは、Arduinoで実験済みのこちらのプログラムを若干変更して使います。コントロールの方法も書いてあります。

 回路図です。

イメージ 1

 AD9834のクロックは、67.108864MHzのクリスタルオシレータSG8002DBを使っています。

 AD9834に周波数データを送るフォーマットは、次の図のとおりです。

イメージ 2

 16ビットのデータを3回送ります。Arduinoでは、8ビットずつ送りましたが、今回は1回に16ビット送ります。

 周波数データの計算方法は、次の図の通りです。マスタクロックの周波数を67108864Hz=2^26とすることにより、計算が非常に簡単になります。

イメージ 3

 周波数を1MHz=1000000Hzとした場合のAD0934への信号です。SCLK(青)とSDATA(赤)の波形です。

イメージ 4

 今回は、VFOの初期周波数は7MHzとしました。

 ブレッドボードと発生させた信号を自作周波数カウンタで測定している様子です。電源は、FT234Xから供給しています。

イメージ 5

 プログラムです。今まで実験したものを組み合わせて使っています。若干長くなりました。

 プログラムのメモリ占有率は、38%です。

------------------------------------------------------
/*
 * File:   main.c
 * Author: Keiji
 * PIC16F883 AD9834DDS VFO
 * clock = 67.108864MHz
 * Created on 2019/01/15, 13:12
 */
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
// CONFIG1
#pragma config FOSC = INTRC_NOCLKOUT
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
#pragma config LVP = OFF
// CONFIG2
#pragma config BOR4V = BOR40V
#pragma config WRT = OFF
#define _XTAL_FREQ 8000000
/* AD9834DDS関係定義 */
#define FSYNC RA0
#define SCLK RA1
#define SDATA RA2
unsigned long Freq = 7000000;   //周波数初期値
unsigned long Freq_old;         //周波数の前の値
unsigned int Step = 1000;       //周波数STEP初期値
//LCD関係定義
#define LCD_EN 0b00000100   //Enable
#define LCD_BL 0b00001000   //Back Light
#define LCD_CMD 0x00
#define LCD_CHR 0x01
#define LCD_LINE1 0x80
#define LCD_LINE2 0xC0
#define LCD_addr 0x4E   //0x27<<1
/*ロータリーエンコーダ関係定義*/
#define ECA RB0
#define ECB RB1
unsigned char curDat;
unsigned char befDat = 0;
unsigned char rotDir = 0;
unsigned char inputMatch;
unsigned char matchCnt;
unsigned char rotPat = 0;
signed char val;
unsigned int STEP = 1000;   //周波数ステップ初期値
/* SW定義 */
#define SW_step RB2
#define SW_rit RB3
unsigned char step_val;
unsigned char step_old = 1;
/* I2C 初期化*/
void I2C_init(){
    SSPCON = 0x28;  //SSPEN = 1,I2C Master Mode
    SSPSTATbits.SMP = 1;    //標準速度モード(100KHz)
    SSPADD = 19;    //Fosc/(4*Clock)-1  Clock=100kHz,Fosc=8MHz
}
/* スタートコンディション */
void I2C_start(){
    SEN = 1;
    while(SEN);
}
/* ストップコンディション */
void I2C_stop(){
    SSPIF = 0;
    PEN = 1;
    while(PEN);
    SSPIF = 0;
}
/* I2Cに1byte送信 */
unsigned char I2C_write(unsigned char dat){
    SSPIF = 0;
    SSPBUF = dat;
    while(!SSPIF);
    return ACKSTAT;
}
/* LCDにデータを送る */
void Write_data(unsigned char data){
    unsigned char ackn;
    I2C_start();
    ackn = I2C_write(LCD_addr);
    ackn = I2C_write(data | LCD_EN | LCD_BL);
    ackn = I2C_write(data | LCD_BL);
    I2C_stop();
    __delay_us(100);
}
/* LCDにコマンドまたは文字を送る */
void LCD_write(unsigned char bits,unsigned char mode){
    //send High 4bits
    Write_data((bits & 0xF0) | mode);
    //send Low 4bits
    Write_data(((bits << 4) & 0xF0) | mode);
}
/* LCD初期化 */
void LCD_init(){
    LCD_write(0x33,LCD_CMD);
    LCD_write(0x32,LCD_CMD);
    LCD_write(0x06,LCD_CMD);
    LCD_write(0x0C,LCD_CMD);
    LCD_write(0x28,LCD_CMD);
    LCD_write(0x01,LCD_CMD);
    __delay_ms(1);
}
void LCD_clear(){
    LCD_write(0x01,LCD_CMD);
    __delay_ms(1);
}
void LCD_home(){
    LCD_write(0x02,LCD_CMD);
    __delay_ms(1);
}
void LCD_cursor(unsigned char x,unsigned char y){
    if (y == 0){
        LCD_write(LCD_LINE1+x,LCD_CMD);
    }
    if (y == 1){
        LCD_write(LCD_LINE2+x,LCD_CMD);
    }
}
void LCD_char(unsigned char byte){
    LCD_write(byte,LCD_CHR);
}
void putch(unsigned char ch){
    LCD_char(ch);
}
/* AD9834DDSに16ビット送信 */
void Data_send(unsigned long data){
    for(unsigned char i = 0;i<16;i++){
        if(data & 0x8000){
            SDATA = 1;
        }else{
            SDATA = 0;
        }
        __delay_us(1);
        SCLK = 0;
        __delay_us(1);
        SCLK = 1;
        data <<= 1;
    }
}
/* AD9834DDSに周波数データを送る */
void Fnc_DDS(unsigned long frequency){
    unsigned long wrk = frequency << 2;
    unsigned int wrk1,wrk2,wrk3;
   
    wrk1 = 0x2000;          //コントロールワード
    wrk2 = wrk & 0x3fff;    //周波数データ下位
    wrk2 = wrk2 | 0x4000;
    wrk3 = wrk >> 14;
    wrk3 = wrk3 & 0x3fff;   //周波数データ上位
    wrk3 = wrk3 | 0x4000;
    SCLK = 1;
    FSYNC = 0;
    Data_send(wrk1);
    Data_send(wrk2);
    Data_send(wrk3);
    FSYNC = 1;
}
/*エンコーダの回転方向判定*/
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 Freq_disp(unsigned long f){
    LCD_cursor(3,0);
    printf("%5d",f/1000);
    printf(".");
    printf("%03d",f%1000);
}
/* STEP表示 */
void Step_disp(unsigned int stp){
    LCD_cursor(4,1);
    if(stp == 1000){
        printf(" 1K");
    }else if(stp == 100){
        printf("100");
    }else{
        printf(" 10");
    }
}
/* STEP変更 */
void Step_change(){
    __delay_ms(10);
    if(STEP == 10){
        STEP = 1000;
    }else{
        STEP /= 10;
    }
    Step_disp(STEP);
}
void main() {
    ANSEL = 0;  //アナログポートを使わない
    ANSELH = 0;
    OSCCON = 0x70;  //8MHz
    TRISA = 0b00000000;
    TRISB = 0b00000111; //RB0 RB1 RB2input
    TRISC = 0b00011000; //RC3(SCL),RC4(SDA)input   
    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;
    OPTION_REGbits.nRBPU = 0;//PORTB Pull up
   
    I2C_init(); //I2C初期化
    LCD_init(); //LCD初期化
   
    //周波数初期値表示
    Freq_disp(Freq);
    Freq_old = Freq;
   
    //周波数STEP表示
    LCD_cursor(0,1);
    printf("STEP");
    Step_disp(STEP);
   
    //DDS制御信号初期化
    FSYNC = 1;
    SCLK = 1;
    //初期周波数設定
    Fnc_DDS(Freq);
   
    /*ロータリーエンコーダ初期値設定*/
    curDat = 0;
    if(ECA){
        befDat |= 2;
    }
    if(ECB){
        befDat |= 1;
    }
   
    while(1){
        /* STEP SWが押されたらSTEPを変更 */
        step_val = SW_step;
        if((step_val == 0)&&(step_old == 1)){
            Step_change();
        }
        step_old = step_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 == 1){
                        Freq += STEP;
                    }
                    if(val == -1){
                        Freq -= STEP;
                    }
                    if(Freq != Freq_old){   //周波数が変化したらDDSに周波数データを送り周波数を更新
                        Fnc_DDS(Freq); 
                        Freq_disp(Freq);
                        Freq_old = Freq;
                    }
                }
            }
        }else{
            //befDatとcuDatが一致しなかった時の処理
            __delay_ms(1);//1ms待つ
            befDat = curDat;
            matchCnt = 0;
            inputMatch = 0;
        }
    }
}
----------------------------------------------------

第3回 A1CLUB STRAIGHT KEY コンテスト

$
0
0
 昨年10月28日に開催された第3回A1CLUB ストレートキーコンテストの賞状がメールで届きました。

 総合4位でした。2017年は総合10位でしたから、6位アップです。

 KEYの名称を8文字にしたこともあり、まずまずの結果でした。

 集計結果の詳細を見ると、自分がlコンテストナンバーをミスした数が6、相手が受信ミスした数が10とけっこう多く、反省点です。

 今年の反省を元に来年も頑張りたいと思います。


イメージ 1



PIC16F883 Si5351A VFO

$
0
0
 3チャンネル・クロックジェネレータSi5351Aを使ったVFOは、既にAeduinoやSTM32で製作済みです。

 今回は、MPLABX XC8の環境で、PIC16F883で7MHz VFOを作ってみました。

 Si5351Aの制御は、Arduinoのプログラムが、そのまま使えました。7MHzのVFOとしましたが、周波数は任意に設定できます。STEPは、ボタンを押すたびに1000Hz→100Hz→10Hz→1000Hzと循環します。

 回路図です。

 Si5351AとLCD AQM0602Aは、I2Cバスに接続します。プルアップ抵抗は、AQM0802Aモジュールに内蔵されています。

イメージ 1

 ブレッドボードです。

イメージ 2

 電源は、乾電池2本を使っています。

 プログラムです。ちょっと長いですが、参考までに掲載します。

 ロータリーエンコーダは、PORTBのピン変化割り込みを利用しています。PIC16F883では、RB0~RB7を利用できますが、割り込みを使うピンを
IOCB0 = 1;      //RB0を割り込みピンに指定
IOCB1 = 1;      //RN1を割り込みピンに指定
で指定しています。

 プログラムサイズは、メモリ4kワードの90%です。

--------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
// CONFIG1
#pragma config FOSC = INTRC_NOCLKOUT    //内蔵クロックを使う
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
#pragma config LVP = OFF
// CONFIG2
#pragma config BOR4V = BOR40V
#pragma config WRT = OFF
#define _XTAL_FREQ 8000000
/* LCD AQM0802関係定義 */
#define LCD_addr 0x7C   //3E<<1
/* ロータリーエンコーダ関係定義*/
#define ECA RB0 //エンコーダA
#define ECB RB1 //エンコーダB
unsigned char curDat;
unsigned char befDat;
signed char count= 0;
/* SW関係定義 */
#define SW_step RB2
unsigned int Step = 1000;       //Step初期値
unsigned char step_val;
unsigned char step_old = 1;

/* Si5351A関係定義*/
#define Si5351_ADDR 0xC0    //60<<1
#define MSNA_ADDR 26
#define MS0_ADDR 42
#define CLK0_CTRL 16
#define CLK1_CTRL 17
#define CLK2_CTRL 18
#define OUTPUT_CTRL 3
#define PLL_RESET 177
#define XTAL_LC 183
unsigned long Freq = 7000000;    //周波数初期値 7MHz
unsigned long Freq_old;         //周波数の前の値
const unsigned long XtalFreq = 25000000; //水晶発振器の周波数
unsigned long divider;
unsigned long PllFreq;
unsigned int mult;
unsigned long l;
float f;
unsigned long num;
const unsigned long denom = 1048575;
unsigned long P1;
unsigned long P2;
unsigned long P3;
/* I2C関係関数*/
/* I2C 初期化*/
void I2C_init()
{
    SSPCON = 0x28;  //SSPEN = 1,I2C Master Mode
    SSPSTATbits.SMP = 1;    //標準速度モード(100KHz)
    SSPADD = 19;    //Fosc/(4*Clock)-1  Clock=100kHz,Fosc=8MHz
}
/* スタートコンディション */
void I2C_start()
{
    SEN = 1;
    while(SEN);
}
/* ストップコンディション */
void I2C_stop()
{
    SSPIF = 0;
    PEN = 1;
    while(PEN);
    SSPIF = 0;
}
/* I2Cに1byte送信 */
void  I2C_write(unsigned char dat)
{
    SSPIF = 0;
    SSPBUF = dat;
    while(!SSPIF);
}
/* 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);
}
void Freq_disp(){
    LCD_home();
    printf("%8ld",Freq);
}
void Step_disp(){
    LCD_cmd(0xC4);  //cursol 4,1
    printf("%4d",Step);
}
void Step_change(){
    __delay_ms(10);
    if(Step==10){
        Step = 1000;
    }else{
        Step = Step / 10;
    }
    Step_disp();
    while(!SW_step);
    __delay_ms(10);
}
/* Si5351A関係関数*/
/* Si5351Aのレジスタにデータ送信*/
void Si5351_write(unsigned char Reg , unsigned char Data)
{
    I2C_start();
    I2C_write(Si5351_ADDR);
    I2C_write(Reg);
    I2C_write(Data);
    I2C_stop();
}
/* Si5351A初期化 */
void Si5351_init(){
    Si5351_write(OUTPUT_CTRL,0xFF); //Disable oll output
    Si5351_write(CLK0_CTRL,0x80);   //CLOCK0 power down
    Si5351_write(CLK1_CTRL,0x80);   //CLOCK1 power down   
    Si5351_write(CLK2_CTRL,0x80);   //CLOCK2 power down   
    Si5351_write(XTAL_LC,0x80);     //Crystal Load Capasitance=8pF
    Si5351_write(PLL_RESET,0xA0); //Reset PLLA and PLLB
    Si5351_write(CLK0_CTRL,0b01001111); //CLOCK0 Power up 8mA
    Si5351_write(OUTPUT_CTRL,0xFE); //Enable CLOCK0
}
//レジスタにパラメータP1,P2,P3を書き込む。
void Parameter_write(unsigned char REG_ADDR,unsigned long Pa1,unsigned long Pa2,unsigned long Pa3)
{
    Si5351_write(REG_ADDR + 0,(Pa3 & 0x0000FF00) >> 8);
    Si5351_write(REG_ADDR + 1,(Pa3 & 0x000000FF));
    Si5351_write(REG_ADDR + 2,(Pa1 & 0x00030000) >> 16);
    Si5351_write(REG_ADDR + 3,(Pa1 & 0x0000FF00) >> 8);
    Si5351_write(REG_ADDR + 4,(Pa1 & 0x000000FF));
    Si5351_write(REG_ADDR + 5,((Pa3 & 0x000F0000) >> 12) | ((Pa2 & 0X000F0000) >> 16));
    Si5351_write(REG_ADDR + 6,(Pa2 & 0x0000FF00) >> 8);
    Si5351_write(REG_ADDR + 7,(Pa2 & 0x000000FF));
}
void PLLA_set()
{
    divider = 900000000 / Freq;
    divider >>= 1;  //dividerは整数かつ偶数
    divider <<= 1;
    PllFreq = divider * Freq;
    mult = PllFreq / XtalFreq;
    l = PllFreq % XtalFreq;
    f = l;
    f *= denom;
    f /= XtalFreq;
    num = f;
    P1 = (unsigned long)(128 * ((float)num /(float)denom));
    P1 = (unsigned long)(128 * (unsigned long)mult + P1 - 512);
    P2 = (unsigned long)(128 * ((float)num / (float)denom));
    P2 = (unsigned long)(128 * num -denom * P2);
    P3 = denom;
    Parameter_write(MSNA_ADDR,P1,P2,P3);
}
void MS0_set()
{
    P1 = 128 * divider - 512;
    P2 = 0;
    P3 = 1;
    Parameter_write(MS0_ADDR,P1,P2,P3);
}
void interrupt isr(){
    RBIF = 0;   //RB割り込みフラッグクリア
    __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){
        Freq += Step;
        count = 0;
    }
    else if(count <= -4){
        Freq -= Step;
        count = 0;
    }
}
void main() {
    ANSEL = 0;  //アナログポートを使わない
    ANSELH = 0;
    OSCCON = 0x70;  //内蔵クロック8MHz
    TRISA = 0b00000000;
    TRISB = 0b00000111; //RB0,RB1,RB2 input
    TRISC = 0b00011000; //RC3(SCL),RC4(SDA)input
    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;
    OPTION_REGbits.nRBPU = 0;   //PORTB Pull up
   
    I2C_init();     //I2C initiatize
    LCD_init();     //LCD initialize
    Si5351_init();  //Si5351A initialize
   
    IOCB0 = 1;      //RB0を割り込みピンに指定
    IOCB1 = 1;      //RN1を割り込みピンに指定
    RBIE = 1;       //RBピン割り込み許可
    GIE = 1;        //全割り込み許可
   
    befDat = ECA + (ECB<<1);
   
    Freq_old = Freq;
    Freq_disp();
    PLLA_set();
    MS0_set();
    Step_disp();
       
    while(1){
        if(Freq != Freq_old){
        PLLA_set();
        MS0_set();
        Freq_disp();
        }
        Freq_old = Freq;
       
        step_val = SW_step;
        if((step_val == 0)&&(step_old == 1)){
            Step_change();
        }
        step_old = step_val;
    }
}
----------------------------------------------------
 

PIC12F1822 Lチカ

$
0
0
 MPLABX XC8環境で、最近のPICのプログラミングを勉強していきます。(MPLAB Xはv5.05をXC8はv1.45を使っています。)

 まず、8ピンPICのPIC12F1822を使ってみます。同じファミリーにPIC12F1840がありますが、プログラムメモリーとデータメモリの容量の違いだけです。(プログラムメモリは、1822が2kワード、1840が4kワード、データメモリは、1822が128バイト、1840が256バイトです)

 PIC16F675など旧タイプの8ピンPICとピン配列は同じですが、中身はかなり機能が強化されています。

 まずは、恒例のLチカ(LEDの点滅)をやってみましょう。

 回路図です。入出力ピンの名称は、GPIOではなく、RA0~RA5になり、容量性負荷の影響を受けないように、IO回路にラッチが導入されています。今回は、RA0のLEDを点滅させます。

イメージ 1

ブレッドボードです。電源(5V)は、USBシリアル変換モジュールFT234Xからとっています。

イメージ 2

 プログラムです。

  PIC12F1822のクロックは、DC~32MHzで動作します。内部クロックでもPLLを使えば32MHzクロックで動かせます。
 今回は、内臓クロック16MHzで動作させてみました。

 CONFIG1で
 FOSC = INTOSC
 CONFIG2で
 PLLEN = OFFとし
 OSCCON =0b01111010;に設定します。bit6~bit3でクロック周波数を設定します。
1111  16MHz
1110   8MHz
1101   4MHz
1100   2MHz
1011   1MHz
以下省略
です。


-----------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
// CONFIG1
#pragma config FOSC     = INTOSC   // 内部クロックを使う(INTOSC)
#pragma config WDTE     = OFF       // ウオッチドッグタイマーを使わない(OFF)
#pragma config PWRTE    = ON       // パワーアップタオイマーを有効にする(ON)
#pragma config MCLRE    = OFF      // 外部リセット信号は使用せずにデジタル入力(RA3)ピンとする(OFF)
#pragma config CP       = OFF      // プログラムメモリーを保護しない(OFF)
#pragma config CPD      = OFF      // データメモリーを保護しない(OFF)
#pragma config BOREN    = ON       // 電源電圧降下常時監視機能ON(ON)
#pragma config CLKOUTEN = OFF      // CLKOUTピンをRA4ピンで使用する(OFF)
#pragma config IESO     = OFF      // 外部・内部クロックの切替えでの起動はなし(OFF)
#pragma config FCMEN    = OFF      // 外部クロック監視しない(OFF)
// CONFIG2
#pragma config WRT    = OFF        // Flashメモリーを保護しない(OFF)
#pragma config PLLEN  = OFF        // 動作クロックを32MHzで動作させない(OFF)
#pragma config STVREN = ON         // スタックがオーバフローやアンダーフローしたらリセットをする(ON)
#pragma config BORV   = HI         // 電源電圧降下常時監視電圧(2.5V)設定(HI)
#pragma config LVP    = OFF        // 低電圧プログラミング機能使用しない(OFF)
#define _XTAL_FREQ 16000000

void main() {
    OSCCON = 0b01111010 ;     // 内部クロック16MHz
    ANSELA = 0b00000000 ;     // アナログは使用しない
    TRISA  = 0b00001000 ;     // ピンは全て出力(RA3は入力のみ)
    PORTA  = 0b00000000 ;     // 出力ピンの初期化
    while(1){
        RA0 = 1;
        __delay_ms(500);
        RA0 = 0;
        __delay_ms(500);
    }
}
-----------------------------------------------

 PICkit3で問題なく書き込みが出来、0.5秒ごとにLEDが点滅しました。

それではと32MHzで動作させてみました。
PLLEN = ON
#define _XTAL_FREQ 32000000
OSCCON = 0b01110000;
としてコンパイル・書き込みをしました。

問題なくLチカが動作しました。

PIC12F1822 シリアル通信テスト

$
0
0
 PIC12F1822は、シリアル通信のためのEUARTモジュールを持っています。USBシリアル変換モジュールFT234Xを介してパソコンの通信ソフトTeraTermの間で通信テストを行いました。

 RA0がTXピンになり、RA1がRXピンになります。このピン配置は、APFCONレジスタの設定で、RXピンをRA5に、TXピンをRA4に変更することができます。

イメージ 1

 ブレッドボードです。

イメージ 2

プログラムです。パソコンから送られた文字をエコーバックするプログラムです。

PIC12F1822は、内臓クロック8MHzとし、PLLEN = ONとして、クロック32MHzで動作させます。

非同期通信ですが、BRGH=1, BRG16=1とすることにより、広範囲のBaudレートに適合します。

このプログラムで、300bps~230.4Kbpsで通信ができることを確認しました。(このプログラムでは、9600bpsとしていあます)
putch()で定義していますので、printf()構文を使うことができます。

最近のPICは、小さいけれど力持ちですね。次は、I2Cのテストをします。

---------------------------------------------
#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
#pragma config CP       = OFF
#pragma config CPD      = OFF
#pragma config BOREN    = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO     = OFF
#pragma config FCMEN    = OFF
// CONFIG2
#pragma config WRT    = OFF
#pragma config PLLEN  = ON
#pragma config STVREN = ON
#pragma config BORV   = HI
#pragma config LVP    = OFF
#define _XTAL_FREQ 32000000

void serial_init(unsigned long BR){
    TXSTA = 0x24;   //SYNC=0 TXEN = 1 BRGH = 1
    BRG16 = 1;      //BRG 16bit mode
    RCSTA = 0x90;   //非同期、継続受信可
    unsigned int X= _XTAL_FREQ/BR/4 - 1;
    SPBRGH = X / 256;
    SPBRGL = X % 256;
}

void putch(unsigned char byte){
    while(!TXIF);
    TXREG = byte;
}

unsigned char getch(){
    while(!RCIF);
    return RCREG;
}

unsigned char getche(){
    unsigned char c;
    c = getch();
    putch(c);
    return c;
}

void main() {
    OSCCON = 0b01110000 ;     // 内部クロック8MHz
    ANSELA = 0b00000000 ;     // アナログは使用しない
    TRISA  = 0b00001010 ;     // RA1,RA3は入力、他は出力
    PORTA  = 0b00000000 ;     // 出力ピンの初期化
   
    serial_init(9600);       //Baud rate 9600bps
   
    while(1){
        getche();
    }
}
----------------------------------------

PIC12F1822 AQM0802A表示テスト

$
0
0
 PIC12F1822のMSSPモジュールのI2C機能を使って、I2C LCD AQM0802Aの表示テストをします。

 まずPIC12F1822のI2C機能のテストをしてみます。

 PIC12F1822のクロックは、内臓クロック8MHzをPLLで4倍してFosc=32MHzとし、I2Cは標準速度Clock=100KHzとするとI2CのBaud レートジェネレータの値FSSP1ADDは、

 SSP1ADD = Fosc / (4*Clock)-1 = 32000000 / (4 * 100000) -1 = 79 = 0x4F です。

 I2Cをマスターモードで使えるようにするためには、

 SSP1CON1 = 0x28;    //SSPrN = 1,I2Cマスターモード
 SSP1STATbits.SMP = 1;  //標準速度100kHz
 SSP1ADD = 0x4F;
とします。

 回路図です。電源は、乾電池2本で3Vとします。PIC12F1822は、RA1がSCL、RA2がSDAピンになります。

 プルアップ抵抗は、AQM0802Aモジュールに内蔵されているので、つけていません。

イメージ 1

 ブレッドボードです。

イメージ 2

 プログラムです。

 基本的には、PIC16F883の場合と同じです。

------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include <pic12f1822.h>
// CONFIG1
#pragma config FOSC     = INTOSC
#pragma config WDTE     = OFF
#pragma config PWRTE    = ON
#pragma config MCLRE    = OFF
#pragma config CP       = OFF
#pragma config CPD      = OFF
#pragma config BOREN    = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO     = OFF
#pragma config FCMEN    = OFF
// CONFIG2
#pragma config WRT    = OFF
#pragma config PLLEN  = ON
#pragma config STVREN = ON
#pragma config BORV   = HI
#pragma config LVP    = OFF
#define _XTAL_FREQ 32000000
#define LCD_addr 0x7C   //3E<<1

/* 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(){
    SEN = 1;
    while(SEN);
}
/* ストップコンディション */
void I2C_stop(){
    SSP1IF = 0;
    PEN = 1;
    while(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){
    unsigned char ackn;
    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
    ANSELA = 0b00000000 ;     // アナログは使用しない
    TRISA  = 0b00001110 ;     // RA1,RA2,RA3は入力、他は出力
    PORTA  = 0b00000000 ;     // 出力ピンの初期化
    I2C_init();
    LCD_init();
       
    putstr("JH7UBC");
    unsigned char count = 0;
   
    while(1){
        LCD_2line();
        printf("%3d",count);
        count++;
        __delay_ms(500);
     }
}

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

 PIC12F1822には、日本語の説明書があります。これはたいへん助かります。

 でも、膨大ですので、関係する部分のみ拾い読みしています。

 これを見てびっくりしたのは、レジスタのバンクの多さです。とても覚えきれるものではありません。

 旧タイプのPICは、アセンブラでプログラミングをしていました。バンクはそれほど多くないのですが、バンク切り替えに気を使いました。
 このバンク数の多さからも最近のPICは、やはりXC8などのCコンパイラが開発に適していると感じました。

PIC12F1822 A/Dコンバータのテスト

$
0
0
 PIC12F1822のA/Dコンバータの動作テストを行います。

 回路図です。表示には、AQM0802Aを使います。電源は乾電池2本(3V)です。
 A/Dコンバータ入力は、AN0(RA0)を使い、VDDの電圧を10KΩのボリュームで0~3Vの電圧を加えます。
 VREF=VDDとして、A/Dコンバータの動作をチェックします。

イメージ 1

 ブレッドボードです。
イメージ 2

 A/Dコンバータを使う手順は、

 1 アナログ入力にするピンを指定する。 ANSELA = 0b00000001;  //AN0(RA0)をアナログ入力にします。

 2 使うA/Dコンバータのチャンネルを指定します。またA/Dコンバータを有効にします。
   これは、ADCON0で行い、ADCON0 = 0b00000001; //bit6-bit2=0000でAN0を指定、bit0=1でADC有効になります。
 3 ADCON1のbit7で、結果を右詰めで格納するか、左詰めで格納するかを指定します。今回は右詰めにします。 また、bit6-bit4でA/D変換クロックを指定します(ADFM=1)。変換クロックは、1us以上なので、010=Fosc/32=1.0usとします。したがって、ADCON1= 0b10100000;となります。
 4 A/Dコンバータの準備ができる時間を待って、A/D変換をスタートします。(GO=10)

 5 A/D変換が終わるのを待ち、(GO=0になるのを待ちます)、結果をADRESHとADRESLから取り出します。

 プログラムです。0.5秒ごとにA/D変換値を表示します。I2C及びLCD表示部分は、前の記事と同じです。

--------------------------------------------
#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
#pragma config CP       = OFF
#pragma config CPD      = OFF
#pragma config BOREN    = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO     = OFF
#pragma config FCMEN    = OFF
// CONFIG2
#pragma config WRT    = OFF
#pragma config PLLEN  = ON
#pragma config STVREN = ON
#pragma config BORV   = HI
#pragma config LVP    = OFF
#define _XTAL_FREQ 32000000
#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(){
    SEN = 1;
    while(SEN);
}
/* ストップコンディション */
void I2C_stop(){
    SSP1IF = 0;
    PEN = 1;
    while(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){
    unsigned char ackn;
    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
    ANSELA = 0b00000001 ;     // RA0をアナログPORTに設定
    TRISA  = 0b00001111 ;     // RA0,RA1,RA2,RA3は入力、RA4RA5は出力
    PORTA  = 0b00000000 ;     // PORTの初期化
    LATA = 0b00000000;
    I2C_init();
    LCD_init();
   
    putstr("ADC TEST");
       
    /* A/Dコンバータ関係の設定 */
    ADCON0 = 0b00000001;    //AN0をselect,ADCイネーブル
    ADCON1 = 0b10100000;    //ADFM=1(右詰め),クロックFosc/32,VREF=VDD
   
    while(1){
        __delay_us(20);
        GO = 1;             //ADCスタート
        while(GO);          //ADC完了まで待つ
        unsigned int val = ADRESH;
        val = (val<<8) | ADRESL;
        LCD_2line();
        printf("%4d",val);
        __delay_ms(500);
     }
}
-------------------------------------------
10kΩのボリュームを回すと、1~1023の値を表示しました。正常に動作しています。

PIC12F1822は、固定参照電圧を内蔵していますので、次回それを使ったテストをしてみます。
Viewing all 440 articles
Browse latest View live