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

MPLABX XC8 PIC16F84A 例題4

$
0
0
 今回からPICをPIC16F84Aに変更しました。(PIC16F84とは最高クロックの違いだけです。)

 例題4としてLCD1602に文字を表示するプログラムを試しました。

 回路図です。クロックは10MHzとしました。

イメージ 1

ブレッドボードに文字が表示された様子です。LCD1602が手持ちがなかったので、LCD2004を使っています。

イメージ 2

プログラムです。

こちらのサイトのプログラムを使わせていただきました。(一部変更しています) ありがとうございます。

LCDのBUSY FLAGは、見ていません。

#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
/* PICとLCD1602(2004)との接続
 * PIC  LCD
 * RA0  RW
 * RA1  E
 * RA2  RS
 * RB0  D4
 * RB1  D5
 * RB2  D6
 * RB3  D7
 */

#define _XTAL_FREQ   10000000
#define RW  RA0
#define EN  RA1
#define RS  RA2
#define LCD_DATA    PORTB

// CONFIG
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (Power-up Timer is enabled)
#pragma config CP = OFF         // Code Protection bit (Code protection disabled)

/*STROBE信号*/
void LCD_STROBE(){
    EN = 1;
    EN = 0;
}
/* 4ビットモードでの8ビット分のデータ転送 */
void lcd_write(unsigned char c)
{
    __delay_us(40);
    LCD_DATA = (LCD_DATA & 0xf0) | ((c >> 4) & 0x0f);
    LCD_STROBE();
    LCD_DATA = (LCD_DATA & 0xf0) | (c & 0x0f);
    LCD_STROBE();
}
 
/* 画面クリア */
void lcd_clear(void)
{
    RS = 0;
    lcd_write(0x01);
    __delay_ms(2);
}
 
/* カーソル位置に文字列を出力 */
void lcd_puts(const char *s)
{
    RS = 1;    // write characters
    while(*s)
        lcd_write(*s++);
}
 
/* カーソル位置に1文字出力 */
void lcd_putch(char c)
{
    RS = 1;    // write characters
    lcd_write( c );
}
 
 /* カーソル位置の設定 y:行 x:桁*/
void lcd_locate(unsigned char y,unsigned char x){
    RS = 0;
    lcd_write(0x80 + (0x40 * y) +x);
}
void lcd_init(){
    RS = 0;
    EN = 0;
    RW = 0;
     __delay_ms(15);        /* 15ms待つ */
    LCD_DATA = 0b00000011;  /* 一旦8bitモード設定(初期化) */
    LCD_STROBE();
    __delay_ms(5);          /*5ms待つ */
    LCD_STROBE();
    __delay_us(200);        /*200us待つ */
    LCD_STROBE();
    __delay_us(200);        /*200us待つ */
    LCD_DATA = 0b00000010;    /* 4bitモード設定 */
    LCD_STROBE();
 
    lcd_write(0x28);          /* 2行表示 7ドットモードに設定 */
    lcd_write(0x0C);          /* 表示ON,カーソルOFF,カーソル点滅OFF */
    lcd_clear();              /* 画面クリア */
    lcd_write(0x06);          /* Entry Modeセット */
}
void main() {
//    __delay_ms(1000);
    TRISA = 0x00;   //PORTA 全てOUTPUT
    TRISB = 0x00;   //PORTB 全てOUTPUT
    lcd_init();     //LCD初期化
    lcd_locate(0,0);    //Cursor Home
    lcd_puts("Hello World!");
    lcd_locate(1,0);
    lcd_puts("JH7UBC");
 
    while(1){
    }
 
}

 今回のトラブルシューティング

 まず、分かっている方には、単純なことなのですが、関数を定義する前に関数を使うとエラーになります。

 Aeduino言語のプログラミングに慣れていたため、関数(サブルーチン)はどこにおいてもコンパイルしてくれると思い込んでいましたが、XC8では、上の行からコンパイルしていき、定義されていない関数がでてくるとエラーを出すようです。

 つまり、
 void main(){
   Test1();
}

 void Test1(){

}

と書くと、エラーになります。
これを

void Test1(){

}

void main(){
     Test1();
}

と書けば、エラーはでません。

単純なことですが、やってみないと分からないものですね。

今回のようなプログラムは、細かい関数は、ヘッダプログラムやライブラリを使えばもっとスマートに使えるようになると思いますが、それらの作り方や使い方がまだよく分かりません。

これから勉強していきたいと思います。

ストレートキーコンテストに参加

$
0
0
 今日15時から21時までの第3回A1 CLUB STRAIGHT KEY コンテストに参加しました。

 15時、コンテストのスタートは、7MHzからです。

 お空のコンディションは、秋というより初冬の感じです。3,4,5,6,8エリアが主に聞こえます。1,2,7,8,9,0エリアはまり聞こえません。バンドの低い方にレーダーノイズとおぼしきノイズがあり、BFなコンディションでした。

 A1クラブのコンテストは、QSYルールがありますので、初めは、CQを出してコールされたら交信し、1KHz以上QSYを繰り返しました。
 でも、これはちょっと効率が悪い。CQを出している局をコールして、交信して、続けてCQを出す。このパターンでしばらくQSO。

 コンディションが悪いせいか、交信局数が伸びません。17時まで7MHzで頑張って、その後3.5MHzに移動しました。
 3.5MHzも空電があり、こちらもあまり良いコンディションではありません。21時まで上記の2つの方法で交信を続けましたが、昨年の交信数に届きませんでした。

 このコンテストの特徴は、縦振電鍵など単一接点のキーを使い、コンテストナンバーがRST+キーの名称、そしてQSYルールです。

 キーの名称は長いほど、得点に有利ですので、各局とも8文字程度になり、聞き取りが難しくなります。

 年のせいか、聞き取り能力が落ちてきました。AGNを連発して、聞きなおすことが多くなりました。

 6時間連続での送受信は、若干疲れますが、手打ちのCWを十分楽しめました。交信していただいた各局、ありがとうございました。

MPLABX XC8 PIC16F84A 例題5

$
0
0
 MPLABX XC8の勉強は、少しずつ進んでいます。

 今回は、PIC16F84Aで7セグメントLEDの表示テストをします。

 プログラムは、こちらのサイトを参考にさせていただきました。

 回路図です。

 各セグメントとPICの接続は

 RB0 -- a
 RB1 -- b
 RB2 -- c
 RB3 -- d
 RB4 -- e
 RB5 -- f
 RB6 -- g
として、680Ωの電流制限抵抗を介して接続します。 

イメージ 2



 ブレッドボードです。7セグメントLEDは、手持ちの秋月電子で販売している超高輝度カソードコモン7セグメントLED C-533SRの#1だけを使ってテストしました。超高輝度ですので、電流制限抵抗は、680Ωとしました。

イメージ 1

プログラムです。
0から9の文字に対応するセグメントデータを配列に保存しておき、数字に対応するデータをPORTBにセットすることにより、数字を表示します。

1秒ごとに0から9までの数字を繰り返し表示するプログラムです。

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

// CONFIG
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (Power-up Timer is enabled)
#pragma config CP = OFF         // Code Protection bit (Code protection disabled)

#define _XTAL_FREQ 10000000

void main()
{
    unsigned char seg_data[10] = {
        // gfedcba      // 7セグメントLED用点灯パターン
        0b00111111,     // 0
        0b00000110,     // 1
        0b01011011,     // 2
        0b01001111,     // 3
        0b01100110,     // 4
        0b01101101,     // 5
        0b01111101,     // 6
        0b00000111,     // 7
        0b01111111,     // 8
        0b01101111      // 9
    };
    
    TRISA = 0x00;       // PORTAは全て出力
    TRISB = 0x00;       // PORTBは全て出力
    PORTA = 0x00; 
    PORTB = 0x00; 
    
    while(1){                       // 無限ループ
        for(unsigned char i=0;i<=9;i++){
            PORTB = seg_data[i];   //セグメントデータをPORTBにセット
            __delay_ms(1000);         // 1 s待つ
        }
    }
}

今回のトラブルシューティング

 プログラムをPICに書き込んで、PICをブレッドボードにセットして、電源スイッチ ON。

 あれ、0 を表示したまま変わらない。

 プログラムを見直してみましたが、問題なし。

 電源ONの前に一度ブレッドボードの回路をチェックしましたが、もう一度見直し。

 分かりました。クロック用のレゾネータへの配線がずれていました。

 それを直すと、0,1,2,3と数字が1秒ごとに変わりました。

 テストOKでした。

MPLABX XC8 PIC16F84A 例題6

$
0
0
 MPLABX XC8 でPIC16F84Aを使って、今まで勉強したことを応用して、ラーメンタイマー(3分タイマー)を作ってみました。

 表示は7セグメントLEDを使い、スタートボタンを押すと3分(3.00表示)から1秒ごとにカウントダウンし、0.00になったら、ピピピ ピピピとブザーを鳴らします。リセットを押すとまた3.00に戻ります。(スタートボタンは、もう一度押すとカウントストップします)

 電源は、電池2本(3V)です。

 回路図です。
イメージ 2

 7セグメントLEDは、超高輝度の3桁のものC-533SRを使い、ダイナミック点灯表示です。ダイナミック点灯と1秒カウントのために、TMR0割り込みを利用して、6.5536msごとに割り込みをかけています。

ブレッドボードです。

イメージ 1

 プログラムです。

/*
 * PICと7セグメントLEDとの接続
 * PIC  LED
 * RB0  A
 * RB1  B
 * RB2  C
 * RB3  D
 * RB4  E
 * RB5  F
 * RB6  G
 * RB7  DP
 * RA0 #1
 * RA1 #2
 * RA2 #3
 *
 * RA3は、START/STOPスイッチ
 * RA4には、BUZZERを接続する。
 * RESETは、MCLR
 */
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#define _XTAL_FREQ 10000000

// CONFIG
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (Power-up Timer is enabled)
#pragma config CP = OFF         // Code Protection bit (Code protection disabled)

 unsigned char digit = 1;
 unsigned char counter = 0;
 unsigned char timer_data[3] = {3,0,0};  //3min 00sec
 signed char work0,work1,work2;//timer用作業レジスタ
 unsigned char val = 1; //input value
 unsigned char old_val = 1;
 unsigned char state = 0; //swtch's state 0=stop
 unsigned char cnt1s = 153;// 6.5536×153 = 1002.7ms
 unsigned char T1s = 0;
 
 unsigned char seg_data[10] = {
        // gfedcba      // data for 7segment LED
        0b00111111,     // 0
        0b00000110,     // 1
        0b01011011,     // 2
        0b01001111,     // 3
        0b01100110,     // 4
        0b01101101,     // 5
        0b01111101,     // 6
        0b00000111,     // 7
        0b01111111,     // 8
        0b01101111      // 9
    };
 
 void interrupt isr(void){
    T0IF = 0; // TMR0割り込みクリア
    cnt1s --;
    if(cnt1s == 0){
        cnt1s = 153;//1sカウンタセット
        T1s = 1;//1s経過フラッグを立てる
    }
    digit <<= 1;
    counter ++;
    if(counter >= 3){
        digit = 1;
        counter = 0;
    }
    PORTA =(PORTA & 0xF0) | digit;
   
    if(digit == 1){
        PORTB = seg_data[timer_data[counter]] | 0x80;//digit0ならDPを表示
    }else{
        PORTB = seg_data[timer_data[counter]];
    }
}
 
//BUZZER音 RA4=0でBUZZER ON
 void beep(){
         while(1){
         RA4 = 0;//ピ
         __delay_ms(200);
         RA4 = 1;
         __delay_ms(200);
         RA4 = 0;//ピ
         __delay_ms(200);
         RA4 = 1;
         __delay_ms(200);
         RA4 = 0;//ピ
         __delay_ms(200);
         RA4 = 1;
         __delay_ms(600);
     }
 }
 void main() {    
     TRISA = 0b00001000;  //RA3 INPUT 他OUTPUT
     TRISB = 0x00;  //PORTB ALL OUTPUT
     PORTA = 0x00;  //PORTA clear
     PORTB = 0x00;  //PORTB clear
     RA4 = 1;//BUZZER OFF
    
     //TMR0関係のセットアップ
     OPTION_REG = 0x85;//prescaler 1:32 interrupt time interval 0.4us×64×256=6.5536ms
     TMR0 = 0x00;   //TMR0 clear
     T0IE = 1;      //TMR0 interrupt enable
     GIE = 1;       //general interrupt enable
   
     while(1){
         val = RA3;
         if((val == 0 )&&(old_val == 1)){
             state = 1 - state;
             __delay_ms(20);
         }
         old_val = val;
        
         if(state){
            if(T1s){
                T1s = 0;//1sフラッグクリア
                work0 = timer_data[0];//min
                work1 = timer_data[1];//10sec
                work2 = timer_data[2];//sec
                work2 --;
                if(work2 < 0){
                    work1 --;
                    work2 = 9;
                }
                if(work1 < 0){
                    work0 --;
                    work1 = 5;
                }
                if((work0 == 0)&&(work1 == 0)&&(work2 == 0)){
                    timer_data[2] = 0;
                    beep();//000になったら、ピピピ
                }
                timer_data[0] = work0;
                timer_data[1] = work1;
                timer_data[2] = work2;  
            }
         }
    }
 }

 今回のトラブルシューティング

 タイマーの基本的なプログラムができ、ブザー音ピピピを出すところで、トラブりました。

 RA4の罠です。RA0~RA3とRA4とでは、IOポートの構造が違うのです。

 RA0~RA3がFETのトーテムポール出力になっているのに対して、RA4は、オープンドレイン出力なのです。

 RA4には、電子ブザーを接続しています。出力0の時、ブザーが鳴るよう負論理になります。

 最後に、ダイナミック点灯に割り込みを使っているせいか、ブザー音のピピピがときどき、ピピーになったりします。
 プログラムで改善できると思うのですが、ま愛嬌ってことでご勘弁を。

 PIC16F84(84A)を使ってMPLABXとXC8の基本的なプログラミングの仕方が勉強できました。

 次は、PIC16F628Aを使って、シリアル通信、A/D変換、SPI、I2Cなどの勉強をしたいと思います。

PIC16F628A Blink

$
0
0
 今回からPICの品種をPIC16F628Aに変更します。

 このPICのファミリーは、627A, 628A,648Aの3種類あります。違いは、フラッシュメモリとEEPROMの容量の違いです。
 PIC16F628Aは、これまで使っていたPIC16F84(84A)に比べると多くの機能をもっています。その分、CONFIをはじめ、様々な設定が必要になります。

 とりあえず、動作確認のために、Lチカをやってみます。

 PIC16F628Aは、内臓発振器を持っていますので、外付けのレゾネータを省くことができます。(CONFIGで、FOSC = INTOSCIOにします。)発振周波数は、PCONレジスタの設定で、4MHzと47KHzを選ぶことができます。(ディフォルトは、4MHzですので、今回は、PCON設定はしません)

 更に4番ピンは、CONFIGの設定で、入力ピンRA5として使うことができます。(MCLRE = OFF)

 RA0~RA3はディフォルトでアナログ入力になっていますので、デジタルIOとして使う場合は、CMCONレジスタを

 0x07に設定します。

 回路図です。RB0に接続したLEDを0.5secごとに点滅させます。非常に簡単です。

イメージ 1

 ブレッドボードです。電源は、FT234からとっています。

イメージ 2

 プログラムです。

#include <xc.h>
#define _XTAL_FREQ 4000000

// CONFIG
#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTOSC oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF      // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

void main() {
    CMCON = 0x07;//comparators OFF
    TRISA = 0b00100000;//RA5は入力他は出力
    TRISB = 0x00;//PORTBは全て出力
    PORTA = 0x00;//PORTAクリア
    PORTB = 0x00;//PORTBクリア
    while(1){
        RB0 = 1;//
        __delay_ms(500);
        RB0 = 0;
        __delay_ms(500);
    }
}

PIC16F628A シリアル通信テスト

$
0
0
 PIC16F628AのUSARTを使ってシリアル通信を行います。

 MPLAB IDE アセンブラで既に経験済みですが、MPLABX XC8では、どのようなプログラムになるのでしょうか。

 シリアル通信のTX(送信)は、7番ピン(RB1)で、RX(受信)は、8番ピン(RB2)で、両方ともTRISは入力設定(1)にします。
 USBシリアル変換モジュールは、秋月電子のFT234Xを使います。(これすごく小さいです。)

 接続は、
 PIC  FT234X
 RX   TxD
 TX  RxD
 です。

 回路図です。電源は、FT224Xから5Vを供給します。FT234XのTxD出力は3.3Vで、入力は5VトレラントですからPICと直接接続できます。クロックは内蔵発振器で4MHzとします。

イメージ 1

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

 プログラムです。渓鉄さんのサイトを参考にさせていただきました。ありがとうございます。

 USARTの設定です。

 送信設定は、TXSTAで行います。非同期通信、高速サンプル指定(BRGH=1)ですので、TXSTA = 0x24とします。

 送信データをTXREGにセットすると自動的にシリアル送信されます。

 受信設定は、RCSTAで行います。非同期通信、継続受信ですので、RCSTA = 0x90とします。
 エラーチェックはしません。

 シリアルデータを受信するとRCIFが1になり、データはRCREGに保存されます。

 通信速度jは9600bpsとします。クロック4MHzで、BRDH=1ですので、SPBRG=25とします。

 この値の計算方法は、PIC16F628Aの説明書にかいてありますが、今回はサンプルにあった値をつかいました。

/*
 * File:   PIC16F628A_serial_com.c
 * Author: JH7UBC Keiji Hata
 * Created on 2018/11/05
 */
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#define _XTAL_FREQ 4000000

// CONFIG
#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 CP = OFF

/*1文字送信 */
void putch(unsigned char byte){
    while(!TXIF );
    TXREG = byte;
}

/* 1文字受信 エラーチェックなし*/
unsigned char getch(){
    while(!RCIF);
    return RCREG;

/* エコーバック付き入力 */
unsigned char getche(){
    unsigned char c;
    c = getch();
    putch(c);
    return c;
}

void main() {
    CMCON = 0x07;       //comparator OFF
    TRISA = 0b00010000; //RA5 input 他は output
    TRISB = 0b00000110; //RB1,RB2はinput 他はoutput
   
    /*USART設定*/
    TXSTA = 0x24;       //送信設定 非同期 高速
    RCSTA = 0x90;       //受信設定 非同期 継続受信
    SPBRG = 25;         //クロック4MHz レート9600bps
   
    unsigned char work;
    while(1){
        work = getch();
        printf("Echo: %c\r\n", work);
    }
}

 Tera Termを使ってテストをしました。キーボードで打った文字がエコーバックされます。

イメージ 3


オータムポエム

$
0
0
 我が家の家庭菜園は、トマト、ナス、ピーマンなどの後始末が終わり、残すはオータムポエム(アスパラ菜)だけになりました。

イメージ 1

 とり残したルッコラが大きくロゼット葉を広げています。このまま冬を越します。

イメージ 7

 オータムポエムは、アブラナ科の植物で、花はアブラナそっくりです。

イメージ 2

 花が咲いたところを茎からポキッと折って収穫します。おひたしにするとやわらかくて、おいしいです。

 この季節になると花が貴重になります。花にはハチやチョウがやってきます。これは何バチ?

イメージ 3

 ふと見ると葉にはカマキリ。

イメージ 4

 高度の低い日差しをうけて、残り少ない命をいつくしんでいるのでしょうか。

 あたりを見渡せば、家の前の雑木林が紅葉の盛りです。

イメージ 5

 里山も紅葉が今がピークで、間もなく長い冬がやってきます。そろそろシャックの雪囲いをしなければ・・・

イメージ 6




PIC16F628A PWMテスト

$
0
0
 PIC16F628Aを使ってPWM(Pulse Width Modulation)のテストをやってみます。

 Arduinoでは、簡単にPWMが使えますので、PWMを使った経験があるのですが、PICでは初めてなので、WebサイトやPIC16F628Aの説明書を見て、その構造と方法がある程度できました。(完全に理解したわけではありません。Hi)

 まず、ハードウェアですが、PWMは、PIC内のCCPモジュールで行われます。説明書に簡略した図がありました。

イメージ 1 
 PWM信号を作る源発振は、クロックの4倍の周期の信号です。(4・Tosc)

 その信号によって、TMR2がカウントアップされます。

 PWM信号の周期を決定するのはPR2レジスタ(8bit)です。

 TMR2とPR2は常時比較され、一致したらRB3(CCP1)ピンにHighが出力され、TMR2はリセットされます。

 一方、デューティサイクルを決定するのは、CCPR1LレジスタにCCP1COレジスタの5bit目と4bit目の値を加えた10bitのデータです。

 このデータは、CCPR1Hレジスタ(10bit)に送られ、その上位8bitが常時TMR2と比較され、一致したらRB3(CCP1)ピンにLowが出力されます。

 この2つの動作が繰り返され、PWM信号を発生します。TNR2には、プリセレクタを設定することができ、その値を含めて、各レジスタとPWM周期、デューティサイクル、PR2の関係は下の図のようになります。

イメージ 6



 mainプログラムを次のようにして、1000Hz デューティ比50%の信号を発生させてみました。クロックは内部クロック4MHzです。CONFI等は前の記事と同じです。

void main() {
    CMCON = 0x07;       //comparator OFF
    TRISA = 0b00100000; //RA5 input 他はoutput
    TRISB = 0x00;       //全てoutput
    PORTA = 0x00;       //PORTA初期化
    PORTB = 0x00;       //PORTB初期化
   
    /* PWM設定 */
    CCP1CON = 0b00001100;//PWMモード,CCP1X=0,CCP1Y=0
    T2CON = 0b00000001; //プリスケーラ1:4
    /*
     * PWM 周期設定 1000Hzに設定
     * PR2=(クロック周波数/(PWM周波数*4*プリスケーラ値)-1
     *    =(4000000/(1000*4*4))-1
     *    = 249
     */
    PR2 = 249;
    /*カウンタ・レジスタ初期化 */
    CCPR1L = 0;
    CCPR1H = 0;
    TMR2 = 0;
    /* ディーティセット */
    CCPR1L = 199;//デューティ比50%にセット 249/2=124.5なので、124をCCPR1Lにセット
    /* PWM START(TMR2 START) */
    TMR2ON = 1;
    while(1){
       
    }
}

 実際のPWM信号です。

イメージ 2

 周期は、1msよりやや短いですが、ほぼ希望通りデューティ比50%の信号が出ています。

 周波数を測ると

イメージ 3

 1010Hz~1012Hzでした。約1%の誤差です。内部クロックの誤差と思われます。周波数の精度を求めなければ、これで問題ないようです。

 デューティ比を変えて、20%と80%の時の波形です。

イメージ 4

イメージ 5

 OKですね。

 次は、周波数の精度を少し上げて、ドレミファソラシドの音階を発生させてみたいと思います。

 今回は、以下のサイトが大変参考になりました。




PIC16F628A PWM 音階発生

$
0
0
 PIC16F628AのPWMを利用して、ドレミファソラシドの音階を発生させてみます。

 周波数の精度を上げるために、クロックは、1MHzのクリスタルオシレータを使った外部発振としました。

 発生させたPWMは、RB3ピンに圧電ブザー(圧電スピーカー)を直付けして聞いてみます。

 回路図です。

イメージ 1

 ブレッドボードです。

イメージ 2

 プログラムです。

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

#define _XTAL_FREQ 1000000

// CONFIG
#pragma config FOSC = ECIO  //外部クロック
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config BOREN = OFF
#pragma config CPD = OFF
#pragma config CP = OFF

unsigned long clock = 1000000;//クロック周波数1MHz
unsigned char ps_val = 4;//TMR2プリスケーラ値
unsigned int f[8] ={262,294,330,349,392,440,494,523};//ドレミファソラシドの周波数

void tone(unsigned int freq){
    PR2 = clock / (freq * 4 * ps_val) - 1;//PR2の計算
    CCPR1L = PR2 / 2;//デューティ比50%
}

void main() {
    CMCON = 0x07;//comparator OFF
    TRISA = 0b00100000;
    TRISB = 0x00;
    PORTA = 0x00;
    PORTB = 0x00;
   
    /*PWM関係カウンタ・レジスタ初期化 */
    CCPR1L = 0;
    CCPR1H = 0;
    TMR2 = 0;
   
    /* PWM設定 */
    CCP1CON = 0b00001100;//PWM mode
    T2CON = 0b00000001;//prescaler 1:4
    TMR2ON = 1;//TMR2 START
   
    while(1){
        for(unsigned char i = 0; i <= 7;i++){
        tone(f[i]);
        __delay_ms(1000);
        }
    }
}

 耳で聞いた感じでは、違和感なくドレミファソラシドが聞こえます。

 周波数カウンタで、各周波数を実測してみました。(Hz)

音階(周波数) 実測値
ド262       262
レ294       294
ミ330       330
ファ349    348
ソ392       393
ラ440       440
シ494       496
ド523       525
けっこう良い精度で、音階を発生させることができました。

#define _C 262 //ド
#define _D 294 //レ
#define _E 330 //ミ
#define _F 349 //ファ
#define _G 392 //ソ
#define _A 440 //ラ
#define _B 494 //シ
#define __C 523 //_ド
と音階を定義しておけば、記号でメロディーがかけそうです。

PIC16F628A周波数カウンタ

$
0
0
 MPLABX XC8環境で、PIC16F628Aで周波数カウンタを作ってみます。

 PIC16F88/PIC16F648A周波数カウンタは、既にアセンブラで製作し、私のホームページ(JH7UBCホームページ)に記事を掲載しています。

 また、PIC16F628AとPIC15F883を使った7セグメントLED表示の周波数カウンタについてもホームページに記事を掲載しています。

 これらの周波数カウンタは、「電子工作etc」というサイトの「周波数カウンタV7」をアセンブラに移植したものです。

 今回は、周波数カウンタV7のCソースリストをXC8コンパイラに移植しましたので、簡単だったのですが、一部でひっかかり、思ったより時間がかかりました。

 PIC16F628A周波数カウンタの原理を簡単に説明します。

 周波数カウンタ本体は、TMR1(16bit)を使います。これだけでは、65,535Hzまでしかカウントできません。

 そこで、TMR1がオーバーフローしたらカウントアップするのカウンタをソフトでつければ、測定周波数を大きくすることができます。

 周波数測定時間1秒を発生するタイムベースは、TMR2(8bit)を使います。クロックは、20MHzのクリスタルオシレータを外部クロックとして使い、プリスケーラを1:16にすれば、(1/20000000)*4*16*256=819.2usごとに割り込みがTMR2発生します。(1秒間に1220.703回の割り込み) そこで、カウンタに12270をセットし、TMR2に残りの時間に対応する数をセットしてスタートさせることにより、1秒を実現しています。

TMR1.TMR2ともプログラムでON/OFFできますので、この2つのタイマを利用して周波数カウンタを作ることができるわけです。

今回は、XC8の演習として製作しましたので、LCD表示ではなく、シリアル通信で周波数データをパソコンに送り、TeraTermで受信して表示します。

回路図です。
イメージ 1

ブレッドボードと自作SGから10MHzの信号を与えているところです。
イメージ 2

TeraTermの表示です。1秒ごとに周波数が表示されます。

イメージ 3


プログラムです。(ちょっと長いです)
/*
 * File:   PIC16F628A_counter.c
 * Author: JH7UBC Keiji Hata
 * Frequency Counter V7
 * Created on 2018/11/17
 */
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
// CONFIG
#pragma config FOSC = ECIO      // Oscillator Selection bits (EC: I/O function on RA6/OSC2/CLKOUT pin, CLKIN on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF      // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)
/* PIC16F628A pin assign
 * pin-01(RA2)
 * pin-02(RA3)
 * pin-03(RA4) LED
 * pin-04(RA5)
 * pin-05(VSS)
 * pin-06(RB0)
 * pin-07(RB1/RX)
 * pin-08(RB2/TX)
 * pin-09(RB3)
 * pin-10(RB4)
 * pin-11(RB5)
 * pin-12(RB6/T1CLK) Signal input
 * pin-13(RB7)
 * pin-14(VDD)
 * pin-15(RA6)
 * pin-16(RA7/CLKIN) Clock input(20MHz)
 * pin-17(RA0)
 * pin-18(RA1)
 *
 * 信号をカウントするためにTMR1(16bit)を使用する。TMR1=TMR1H(8bit)+TMR1L(8bit)
 * ゲートタイム(1sec)を得るためにTMR2(8bit)を使用する。
 */
#define LED RA4
#define _XTAL_FREQ 20000000
static unsigned int MeassuremmentCnt;
/*TMR2のオーバーフロー割り込み*/
void interrupt isr(){
    TMR2IF = 0;//TMR2割り込みフラッグクリア
    MeassuremmentCnt --;
    if (MeassuremmentCnt == 0){
        TMR1ON = 0;//ゲートを閉める。
        TMR2ON = 0;//TMR2を停止する。
    }
}
/*周波数測定*/
unsigned long FreqMeassurement(){
    static unsigned long freq;
    /*TIMERの初期化*/
    TMR1IF = 0;
    TMR1L = 0;
    TMR1H = 0;
    /*TMR2の初期化*/
    TMR2IF = 0;
    MeassuremmentCnt = 1221;
    TMR2 = 0x4C;
    /*counter 初期化*/
    freq = 0;
    /*割り込み許可*/
    PEIE = 1;
    GIE = 1;
    //count start
    TMR2ON = 1;
    /*gate time調整 NOP 25*/
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    /*ゲートを開ける*/
    TMR1ON = 1;
    while(TMR2ON){
        if(TMR1IF == 1){
            TMR1IF = 0;
            freq ++;
        }
    }
    if(TMR1IF == 1){
        TMR1IF = 0;
        freq ++;
    }
    /*換算*/
    freq = freq * 65536;
    freq = freq + ((unsigned)TMR1H * 256) + (unsigned)TMR1L;
   
    return(freq);
}

/*1文字送信 */
void putch(unsigned char byte){
    while(!TXIF);
    TXREG = byte;
}
void main() {
    CMCON = 0x07;//comparator OFF
    TRISA = 0b11100000;//RA5-7 input 他はoutput
    TRISB = 0b01000110;//RB1,RB2,RB6 input 他はoutput
    /*TMR2の設定*/
    TMR2IE = 1;
    TMR2IF = 0;
    TOUTPS0 = 0;
    TOUTPS1 = 0;
    TOUTPS2 = 0;
    TOUTPS3 = 0;
    TMR2ON = 0;
    T2CKPS0 = 1;
    T2CKPS1 = 1;
    TMR2 = 0;
    /*TMR1の設定*/
    TMR1IE = 0;
    TMR1IF = 0;
    T1CKPS0 = 0;
    T1CKPS1 = 0;
    T1OSCEN = 0;
    TMR1CS = 1;
    nT1SYNC = 1;
    TMR1ON = 0;
    TMR1L = 0;
    TMR1H = 0;
   
    /*USART設定*/
    TXSTA = 0x24;       //送信設定 非同期 高速
    RCSTA = 0x90;       //受信設定 非同期 継続受信
    SPBRG = 129;         //クロック20MHz レート9600bps
   
    while(1){
    /*周波数の測定*/
    LED = 0;//RA4 LED ON
    unsigned long frequency = FreqMeassurement();
    LED = 1;//RA4 LED OFF
    printf("%ld\r\n",frequency);
    __delay_ms(500);
    }
}

今回のトラブルシューティング

T1CONのT1SYNCの書き方が分からず、このままだとエラーになります。
Web上を探しましたが、書き方が分からず。
しかし、T1CONbits.まで入力したら、小さなウインドウが開き、書き方がわかりました。
負論理なのでnT1SYNCと書けばよいのでした。

プログラムがうまく動き、周波数を表示したのですが、32768Hzまでしかう表示できず、それを超えると負表示になります。表示printf("%d\r\n",frequency); では、int(16bit)までしか変換できないのです。
そこで、%dを%ldとしました。これで倍長整数(long)まで変換できるようになりました。

家庭菜園 今年のまとめ

$
0
0
 我が家の家庭菜園では、相変わらずオータムポエムが数日おきに収穫できます。

 今年の家庭菜園の結果をまとめておきます。(収穫数はその都度記録しています。暇ですねー)

 ハツカダイコン 973本
 ルッコラ     647
 コマツナ     422
 ホウレンソウ   95
 ナス       2084
 トマト       1297
 ピーマン     575
 ケール      306
 オクラ       235
 トウモロコシ    50

 ハツカダイコン、ルッコラ、コマツナ、ナスの収穫は、例年並み
 トマトは、若干不作
 ピーマンは豊作
 トウモロコシは、出来が悪かったです。

 今年はじめてケールを作ってみました。来年も作ってみようと思います。

 台風の大きな被害もなく、家庭菜園の季節を終えることができましたが、7月から8がつにかけての高温と雨不足、9月の長雨と、天候は順調とは言えませんでした。

 これから来年の4月まで冬ごもりです。来年は何を作ろうかな・・・・

PIC16F88 Blink

$
0
0
 今回からPICの品種をPIC16F88に変更して、MPLABX XC8の勉強をしていきます。

 PIC16F88は、発売されてからずいぶん経ちますが、DAコンバータ以外の機能をほとんど持っていて、今でも十分通用する強力なPICです。

 まずは、Lチカをやってみます。

 回路図です。

イメージ 1

 内部クロックを使うことにします。この設定は、まずCONFIGで FOSC = INTOSCIO とします。

 PIC16F88は、内部クロックの周波数を31.25KHz~8MHzの間で設定することができます。

 これを設定するのは、OSCCONレジスタで、bit6-bit4で、111にすると内部クロック8MHzになります。

 それから、PIC16F88は、ディフォルトでRA0~RA4,RB6,RB7はアナログポートに設定されています。

 今回、すべてデジタルポートとしますので、ANSEL = 0x00とします。

 ブレッドボードは、簡単なので省略します。

 プログラムです。CONFIGの項目が多くなります。PWRTE = ONにして他はOFFにしました。
 0.5sec(500ms)ごとにLEDが点滅します。

#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
// 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 = RB0
#pragma config CP = OFF
// CONFIG2
#pragma config FCMEN = OFF
#pragma config IESO = OFF

#define _XTAL_FREQ 8000000

void main() {
    ANSEL = 0x00;           //アナログポートを利用しない
    OSCCON = 0b01110000;    // 内部クロック8MHz
    TRISA = 0b01000000;     // RA5 input 他はoutput
    TRISB = 0b00000000;     // RB0-RB7 output
    PORTA = 0x00;           // PORTA初期化
    PORTB = 0x00;           // PORTB初期化
    while(1){               // 無限ループ
        PORTB = 1;          // LED点灯
        __delay_ms(500);    // 500ms wait
        PORTB = 0;          //LED消灯
        __delay_ms(500);    // 500ms wait
    }
}

PIC16F88 シリアル通信とファイルの分割

$
0
0
 MPLABX XC( 環境で、PIC16F88のシリアル通信テストとファイルの分割をやってみます。

 PICでのシリアル通信は、PIC16F628Aシリアル通信テストですでに行いました。PIC16F88のシリアル通信ピンは、PIC16F628Aと若干異なります。パソコンとの接続は、USBシリアル変換モジュールFT234Xを使います。

 回路図(接続図)は次のとおりです。電源もFT234Xから供給します。

イメージ 1

 ブレッドボードです。

イメージ 2

 プログラムです。今回は、シリアル通信の部分を「serial,c」というソースファイルに書きます。プログラム本体は、「main.c」に書きます。この2つのファイルを繋ぐのが、ヘッダーファイルで「serial.h」です。

 ソースファイル、メインファイル、ヘッダーファイルへの分割方法やそれらのファイルの関係は、「一週間で身につくC言語の基本」というサイトに分かりやすく書いてあります。参考になりました。

 また、プログラムは、このところお世話になっている渓鉄さんのサイトを参考にさせていただきました。

メインファイル main.cです。
通信関係をserial.cに書きましたので、見通しがよくなりました。
-----------------------------------------------------------
/*
 * File:   main.c
 * Author: JH7UBC Keiji Hata
 * PIC16F88 Serial communication
 * Created on 2018/11/19
 */
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include "serial.h"
// 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 = RB0
#pragma config CP = OFF
// CONFIG2
#pragma config FCMEN = OFF
#pragma config IESO = OFF
#define _XTAL_FREQ 8000000
void main() {
    ANSEL = 0x00;       //アナログポートを使わない
    OSCCON = 0b01110000;//内部クロック8MHz
    TRISA = 0b00100000; //RA5はinput 他はoutput
    TRISB = 0b00000100; //RB2はinput 他はoutput
    PORTA = 0x00;       //PORTA初期化
    PORTB = 0x00;       //PORTB初期化
    serial_init();      //AUSART設定
   
    while(1){           //無限ループ
    printf("JH7UBC\r\n");
    __delay_ms(500);
    }
}
----------------------------------------------------------

ヘッダファイル serial.h です。
今回は、プロトタイプ宣言だけにしました。
----------------------------------------------------------
/*
 * File:   serial.h
 * Author: JH7UBC Keiji Hata
 * Created on 2018/11/19
 */
#ifndef SERIAL_H
#define SERIAL_H
/*プロトタイプ宣言*/
void serial_init();
void putch(unsigned char);
unsigned char getch();
unsigned char getche();
#endif /* SERIAL_H */
----------------------------------------------------------

ソースファイル serial.c です。
----------------------------------------------------------
/*
 * serial.c
 * シリアル通信関係関数
 */

#include <xc.h>
#include <stdio.h>
#include "serial.h"
/*シリアル通信設定*/

void serial_init(){
    TXSTA = 0x24;   //非同期 高速
    RCSTA = 0x90;   //非同期 継続受信可
    SPBRG = 51;     //クロック8MHz 9600bps
}
/*1文字送信*/
void putch(unsigned char byte){
    while(!TXIF);   //TXIFが1になるまで待つ
    TXREG = byte;   //1文字送信
}
/*1文字受信*/
unsigned char getch(){
    while(!RCIF);   //RCIFが1になるまで待つ
    return RCREG;   //1文字受信
}
/*エコーバック付き入力*/
unsigned char getche(){
    unsigned char c;
    c = getch();
    putch(c);
    return c;
}

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

 それぞれのファイルは、MPLABXでは、つぎのように配置して、コンパイルしました。

イメージ 3

 しかし、それぞれのファイルをmain.cと同じ場所においてもコンパイルしてもOKでした。この辺がまだよくわかっていません。

 TeraTermには、0.5秒ごとにJH7UBCが表示されます。
 

イメージ 4

PIC16F88 ロータリーエンコーダその1

$
0
0
 MPLABX XC8でロータリーエンコーダを使うプログラムをテストします。

 ロータリーエンコーダの使い方としては、割り込みを使う方法と使わない方法があります。

 今回は、割り込みを使わない方法をテストします。wsnakのブログに掲載されているArduino用のプログラムをXC8用に移植し、若干の改良を行いました。

 回路図です。電源と通信関係は、前の記事と同じです。ロータリーエンコーダは、RB0とRB1に接続します。

 PORTBはPIC側でウィークプルアップして、プルアップ抵抗を省略しています。回転の結果は数字のカウントアップ・カウントダウンをTeraTermで表示します。

イメージ 1

 ブレッドボードです。ロータリーエンコーダは、秋月電子で販売しているものです。

イメージ 2

 プログラムです。シリアル通信関係のヘッダファイル 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
 */
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include "serial.h"
/* RB0 ロータリーエンコーダA
 * 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
// CONFIG2
#pragma config FCMEN = OFF
#pragma config IESO = OFF
#define _XTAL_FREQ 8000000
#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;

/*エンコーダの回転方向判定*/
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;
        }
       
    }
      
}

 TeraTermには、次のようにロータリーエンコーダを時計回りに回すと数字がカウントアップされ、反時計回りに回すと数字がカウントダウンされて表示されます。

イメージ 3



 次は、割り込みを利用してロータリーエンコーダを使う方法をやってみます。

PIC16F88 ロータリーエンコーダその2

$
0
0
 MPLABX  XC8環境においてPIC16F88で、ロータリーエンコーダを使うプログラムのテストその2です。

 今回は、PIC16F88のピン状態変化割り込みを利用してロータリーエンコーダの回転方向を判定するプログラムを考えてみます。

 まず、PIC16F88でピン状態変化割り込みを利用できるのは、RB4~RB7で、このうち1つでもピンの状態が変化すれば、RBIFが1になります。

 そこで、RB6とRB7を使うことにし、次のようにロータリーエンコーダを配線します。結果はシリアル通信でTeraTermで表示することにします。nRBPU = 0;として、PORTBはプルアップしておきます。

イメージ 1

 ロータリーエンコーダを回転させるとAとBの状態が変化し割り込みがかかります。チャタリングがありますので、実際にはどのように割り込みが発生するか簡単なプログラムで見てみました。

イメージ 2

 ロータリーエンコーダの1つの変化に対して2~3回の割り込みが発生しています。チャタリングによるものと思われます。チャタリング対策が必要です。

 次に、回転方向を判別する方法です。今回は、ロータリーエンコーダを購入したときについてくる秋月電子の説明書にある方法でやってみます。

 BAの値の前の値を左に1ビットシフトし、現在のBAの値と排他的OR(XOR)をとり、下位2ビットだけを見ると

 時計回りの時は、0か1の値(すなわち<2)をとり、反時計回りの時は2か3の値(すなわち>=2)をとります。
 これを利用して、回転方向を判定することができます。


イメージ 3

 ブレッドボードです。

イメージ 4

 プログラムです。シリアル通信の部分は省略します。前の記事を参照してください。

#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include "serial.h"
// 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 = RB0
#pragma config CP = OFF
// CONFIG2
#pragma config FCMEN = OFF
#pragma config IESO = OFF
#define ECA RB6 //エンコーダB
#define ECB RB7 //エンコーダA
#define _XTAL_FREQ  8000000
unsigned char curDat = 0;
unsigned char befDat = 0;
unsigned char EA;
unsigned char EB;
signed char count= 0;

void interrupt isr(){
    RBIE = 0;   //RB割り込み禁止
    RBIF = 0;   //RB割り込みフラッグクリア
    __delay_ms(2);
    EA = ECA;
    EB = ECB;
    curDat = EA + (EB<<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;
    }
     RBIE = 1;   //RB割り込み許可
}

void main() {
    ANSEL = 0x00;       //アナログポートを使わない
    OSCCON = 0b01110000;//内部クロック8MHz
    TRISA = 0b00100000; //RA5はinput 他はoutput
    TRISB = 0b11000100; //RB3.RB6,RB7はinput 他はoutput
    PORTA = 0x00;       //PORTA初期化
    PORTB = 0x00;       //PORTB初期化
    OPTION_REGbits.nRBPU = 0;//PORTB Pull up
    serial_init();      //AUSART設定
    EA = ECA;
    EB = ECB;
    befDat = EA + (EB<<1);
    RBIE = 1;           //RBピン割り込み許可
    GIE = 1;            //全割り込み許可
   
    while(1){           //無限ループ
    }
}

時計回りに回転すると「RIGHT」と反時計回りに回転すると「LEFT」とTeraTermで表示されます。

イメージ 5




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

$
0
0
 MPLABX XC8でPIC16F88のアナログポート(A/Dコンバータ)を使うテストをします。

 使い方は、「PICで遊ぶ電子工作」というサイトのこちらのページがたいへん参考になりました。

 PIC16F88では、RA0~RA4とRB6,RB7をアナログポートとして使うことができます。

 A/Dコンバータは、10bitで、アナログ値(電圧)を0~1023の数値で読み込むことができます。

 今回は、RA0(AN0)を使い、ボリュームからの電圧を数値で読み取ってみます。数値はシリアル通信でTeraTermで表示します。

 回路図です。
イメージ 1

 ブレッドボードです。

イメージ 2

 プログラムです。

 まず、A/Dコンバータを使うための次のように初期設定をします。

 TRISAレジスタで、RA0(AN0)を入力に設定します。
 ANSELレジスタで、RA0(AN0)をアナログポートに設定します。
 ADCON0,ADCON1,ADCON2レジスタで、A/D変換クロックの設定、基準電圧の設定、A/D変換結果の格納方法、A/D変換有効の設定をします。

 今回は、内臓クロック8MHzを使いますので、A/D変換クロック1.6us~6.4usの範囲で計算すると16Toscで 2.0us
になります。

 基準電圧はVdd=5Vとします。 

 格納方法は、右詰めとします。

 A/D変換の方法はつぎのようにします。

 A/D変換対象ポートの設定
 A/D変換の準備完了を待ちます。(50us待ちます)
 A/D変換開始
 A/D変換完了を待ちます。
 A/D変換結果を読み取ります。

 シリアル通信のヘッダファイルserial.hとソースファイルserial.cは、シリアル通信テストと同じものを使います。

#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include "serial.h"
/* PIC16F88におけるA/D変換テスト
 * pin17 AN0を使う
 * Vrefは、Vdd=5V
 */
// 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 = RB0
#pragma config CP = OFF
// CONFIG2
#pragma config FCMEN = OFF
#pragma config IESO = OFF
#define _XTAL_FREQ 8000000
void main() {
    OSCCON = 0b01110000;//内部クロック8MHz
   
    /* A/D変換設定 */
    TRISA = 0b00100001; //RA0,RA5はinput 他はoutput
    ANSEL = 0b00000001; //AN0を使う。他はデジタルポート
    TRISB = 0b11001000; //RB3はinput 他はoutput
    /* A/D変換クロックの設定 クロック8MHz 16Tosc 2.0us */
    ADCON1bits.ADCS2 = 1;
    ADCON0bits.ADCS1 = 0;
    ADCON0bits.ADCS0 = 1;
    /* 基準電圧の設定 Vref+ = Vdd,Vref- = Vss */
    ADCON1bits.VCFG1 = 0;
    ADCON1bits.VCFG0 = 0;
    /* A/D変換結果の格納方法の設定 */
    ADCON1bits.ADFM = 1;//右詰め
    /* A/D変換を有効にする */
    ADCON0bits.ADON = 1;
    /* A/D変換対称ボートの選択 AN0を選択*/
    ADCON0bits.CHS2 = 0;
    ADCON0bits.CHS1 = 0;
    ADCON0bits.CHS0 = 0;
   
    PORTA = 0x00;       //PORTA初期化
    PORTB = 0x00;       //PORTB初期化
    serial_init();      //シリアル通信初期化
   
    while(1){
        /* A/D変換 */
        __delay_us(50);             //50us待つ
        ADCON0bits.GO_nDONE = 1;    //A/D変換開始
        while(ADCON0bits.GO_nDONE); // A/D変換終了まで待つ
       
        /* A/D変換結果を読み取る */
        unsigned int val = ADRESH;
        val = (val<<8)+ADRESL;
        printf("%d\r\n",val);
        __delay_ms(500);
    }
}

 ボリュームを回して、0~5Vの電圧を数値で読みとっています。0~1023の数値で読み取れます。

イメージ 3

 ボリュームの位置を一定にして、A/D変換結果を見ます。

イメージ 4

 数値に±1ビット程度のゆらぎがあります。外来ノイズによるものと思われます。

明日は、第10回FQP

$
0
0
 第10回FCWA CW QSO パーティが、いよいよ明日に迫りました。

 時間は、9:00~21:00で、バンドは1.9MHz~28MHzです。

 交換ナンバーは、RST+オペレータ名(例 599KAZU)です。

 各バンドで、CQ FQPのトンツーが聞こえると思います。

 それぞれのペースで、CW交信を楽しんでいただきたいと思います。

 詳しい規約はFCWAのホームページかCQ誌12月号、JARLニュース秋号に掲載されています。

 皆様のご参加をお待ちしております。

 では、お空でお会いしましょう。

 FQPマネージャー JH7UBC 畠 恵治

FQP終了

$
0
0
 昨日、第10回FCWA CW QSOパーティ(FQP)が、盛会のうちに終了しました。

 参加いただいた各局、ありがとうございました。ログを提出された局は、FCWAのホームページに掲載しております。
 参加されて、まだログを提出されていない局は、ぜひログの提出をお願いします。締め切りは12月31日です。

 結果の発表は来年1月頃、表彰と記念品の発送は、来年5月のFCWA総会後になります。

 今回の第10回記念大会は、コンディション的にはまずまずだったと思います。ハイバンドは季節と太陽活動不活発期のため開けませんでしたが、10MHz以下のバンドで活発に交信が行われました。

 当局JH7UBCは、午前1回、午後1回、夜1回の3回オペレーションを各2時間ほど行い、ほぼ昨年並みの交信数となりました。交信いただいた各局、ありがとうございました。

PIC16F88 温度計その1

$
0
0
 PIC16F88のA/Dコンバータを利用して温度計を作ってみます。

 温度センサーは、まず定番のLM35DZを使ってみます。

 LM35DZは、0mV+10.0mV/℃の出力を持つ3端子センサーです。

 20℃で、0.200Vを出力します。

 回路図です。Vref+(RA3)に加える電圧は、10kΩのボリュームで調整できるようにします。

 センサーの出力は、今回AN4(RA4)に加えていますが、アナログポートのどこでもかまいません。

 温度は、USBシリアル変換モジュールFT234Xを通してTeraTermに表示します。通信速度は9600bpsです。

 これは、TeraTermのデフォルトの通信速度が9600bpsだからです。当然もっと早くもできます。

 7セグメントLEDやLCD表示にしてみるのも良いと思います。

イメージ 1

 プログラムです。mainプログラムのみ記載します。serial.hとserial.cは、シリアル通信のテストのものと同じです。

 1秒ごとにセンサーの電圧を読み、過去4個のデータとの移動平均をとっています。

  A/D変換結果ADresとVrefとVinの関係は、ADres = Vin / (Vref / 1024) ですから

  Vref = 4.096 V とすると ADres = Vin / (4.096 / 1024) = Vin * 250 となります。

  20℃の時、LM35DZの出力は、0.200Vですから ADres = 0.200 * 250 = 50 となります。

  これを℃の数値と一致させるために、ADresの値を4倍して、10で割っています。

  小数点以下の数値を得るには、10の剰余をとっています。


 unsigned int temp[5];
void main() {
    OSCCON = 0b01110000;//内部クロック8MHz
   
    /* A/D変換設定 */
    TRISA = 0b00111000; //RA3,RA4,RA5はinput 他はoutput
    ANSEL = 0b00010000; //AN4を使う。他はデジタルポート
    TRISB = 0b00001000; //RB3はinput 他はoutput
    /* A/D変換クロックの設定 クロック8MHz 16Tosc 2.0us */
    ADCON1bits.ADCS2 = 1;
    ADCON0bits.ADCS1 = 0;
    ADCON0bits.ADCS0 = 1;
    /* 基準電圧の設定 Vref+ = Vref+(RA3),Vref- = Vss */
    ADCON1bits.VCFG1 = 1;
    ADCON1bits.VCFG0 = 0;
    /* A/D変換結果の格納方法の設定 */
    ADCON1bits.ADFM = 1;//右詰め
    /* A/D変換を有効にする */
    ADCON0bits.ADON = 1;
    /* A/D変換対称ボートの選択 AN4を選択*/
    ADCON0bits.CHS2 = 1;
    ADCON0bits.CHS1 = 0;
    ADCON0bits.CHS0 = 0;
   
    PORTA = 0x00;       //PORTA初期化
    PORTB = 0x00;       //PORTB初期化
    serial_init();      //シリアル通信初期化
   
    while(1){
           /* A/D変換 */
        __delay_us(50);             //50us待つ
        ADCON0bits.GO_nDONE = 1;    //A/D変換開始
        while(ADCON0bits.GO_nDONE); // A/D変換終了まで待つ
        temp[4] = temp[3];
        temp[3] = temp[2];
        temp[2] = temp[1];
        temp[1] = temp[0];
    
        /* A/D変換結果を読み取る */
        unsigned int val = ADRESH;
        temp[0] = (val<<8)+ADRESL;
        int avetemp =(temp[0]+temp[1]+temp[2]+temp[3]+temp[4])/5;
        avetemp = avetemp * 4;
        int tempH = avetemp / 10;
        int tempL = avetemp % 10;
        printf("%d",tempH);
        printf(".%d C\r\n",tempL);
        __delay_ms(1000);
    }
}

ブレッドボードと左から温度センサーの電圧、実際の温度、TeraTermの画面です。

イメージ 2

 温度センサーの出力0.23Vに対して、測定値は22.4℃です。

 やはり、0.1℃の値は、ふらつきます。A/D変換まわりのノイズ対策をもっと徹底する必要がありそうです。

 または、精度を落として1℃単位で表示すると良いかもしれません。

PIC16F88 温度計その2

$
0
0
 だいぶ以前に購入した温度センサーS-8100Bがパーツボックスにありました。

 この温度センサーは、-8.0mV/℃の負特性を持ち、その出力は、

 -20℃で1.908V
   +30℃で1.508V
   +80℃で1.095Vです。

 リニアリティは、±1.0% (-20℃~+80℃)とマイナスの温度も計測可能です。

 上の特性からこの温度センサーの出力Eと温度(℃)との関係は次の式で表すことができます。

 E = 1.748 - 0.008 t

 PIC16F88のVref+=4.096VとするとA/Dコンバータの変換結果ADresは

 -20℃の時  ADres = 1.908/4.096 * 1024 = 477
 +30℃の時  ADres = 1.508/4.096 * 1024 = 377
 +80℃の時  ADres = 1.095/4.096 * 1024 = 273

 となり、温度の50℃の変化に対して100デジット変化します。

 また、0℃の時は、ADres = 1.748/4.096 * 1024 = 437 ですから

 求める温度t(℃)は

 t = (437 - ADres) / 2  で計算できます。

 プログラムは、前の記事とほぼ同じですので、省略します。

 回路図です。S-8100Bの出力は、1MΩでプルアップしています。

イメージ 1


 ブレッドボードとTeraTermの表示です。

イメージ 2

 この温度計の精度は1℃です。
Viewing all 440 articles
Browse latest View live