●PIC で A/D変換を行う

PICにA/D変換回路が入っています、それを使ってアナログ値の電圧をデジタルの数字に変換したいと思います。
ただ、単に変換してもわかりにくいので、 PIC で RS232c通信を行う で作ったシリアル通信の仕組みで、 パソコンに変換データを送りたいと思います。
AD変換を行う電圧の作成は電源のプラスとマイナスの間に100KΩの可変抵抗を接続し、可変抵抗の真ん中の端子をRA0に接続しました。


可変抵抗を回すと抵抗値が変化して、抵抗分圧により色々な電圧が作成できます。


RS232cで通信できる状態のブレッドボードに可変抵抗を取り付けた所です


■ PIC16F1827でのAN0のアナログ値を送信するプログラム

#include <stdio.h>
#include <stdlib.h>
// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable (PWRT enabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF       // Internal/External Switchover (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)
// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = HI        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), high trip point selected.)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

#include <xc.h>
#define _XTAL_FREQ 16000000

void send(unsigned char c) {
    while (!TXIF);
    TXREG = c;
}

void putch(char c) {
    send(c);
}

int receive(unsigned char*c) {
    if (RCIF) {
        *c = RCREG;
        return 0;
    }
    return -1;
}

void main() {
    OSCCON = 0b01111010; //クロック周波数16MHz
    ANSELA = 0b00000000; //ポートAをデジタル入力にする
    ANSELB = 0b00000000; // ポートBをデジタル入力にする
    PORTA = 0b00000000;
    PORTB = 0b00000000;
    TRISA = 0b00000000;
    TRISB = 0b00000010; //RB1は入力にしないと受信できない

    TXCKSEL = 0; // TX:RB2
    RXDTSEL = 0; // RX:RB1

    TXSTA = 0b00100100;
    BRG16 = 1;
    RCSTA = 0b10010000;

    //ボーレート= FOSC /(4([SPBRGHx:SPBRGx] + 1))
    // SPBRGHx:SPBRGx:=((FOSC / ボーレート)/ 4)-1

    const uint16_t DBR = ((uint32_t) ((uint32_t) _XTAL_FREQ / 9600) / 4) - 1;

    SPBRGH = (uint8_t) (DBR >> 8);
    SPBRGL = (uint8_t) (DBR);

    TRISA =  0b00000001; // RA0を入力にする
    ANSELA = 0b00000001; // ポートAをデジタル入力、RA0をアナログ入力
    WPUA =   0b11111110; // RA0の弱プルアップを無効
    nWPUEN = 0;          // WPUx ラッチの値に応じて弱プルアップを有効にする

  //ADCON1 = 0b11100000; // 変換結果右詰め FOSC/64 VREF+をAVDD
    ADCON1 = 0b11100011; // 変換結果右詰め FOSC/64 VREF+をFVR
    FVRCON = 0b10000011; // FVR有効 出力4倍 (4.096 V)
    while(!FVRRDY);       // FVRが安定するまで待つ
    ADCON0 = 0b00000001; // アナログチャンネルAN0 ADC有効

    while (1) {
        GO = 1;     // 変換の開始
        while (GO); // 変換の完了
        printf("%d\r\n", ADRES); // 変換結果を出力
    }
}
赤文字部分がAD変換をしている箇所で、他は、PIC で RS232c通信を行う で作成したプログラムです。
固定参照電圧 FVR を使っていますが、電源電圧を使う場合には、設定部分を次のように変更します。

    ADCON1 = 0b11100000; // 変換結果右詰め FOSC/64 VREF+をAVDD
  //ADCON1 = 0b11100011; // 変換結果右詰め FOSC/64 VREF+をFVR
  //FVRCON = 0b10000011; // FVR有効 出力4倍 (4.096 V)
  //while(!FVRRDY);      // FVRが安定するまで待つ
    ADCON0 = 0b00000001; // アナログチャンネルAN0 ADC有効

▼ 入力ピンを RA4/AN4 に変更する

上記プログラムではAN0を使用していましたが、AN4のピンに変更します。
    TRISA =  0b00010000; //RA4を入力にする
    ANSELA = 0b00010000; //ポートAをデジタル入力、RA4をアナログ入力
    WPUA =   0b11101111; //RA4の弱プルアップを無効
    nWPUEN = 0;          //WPUx ラッチの値に応じて弱プルアップを有効にする

  //ADCON1 = 0b11100000; //  変換結果右詰め FOSC/64 VREF+をAVDD
    ADCON1 = 0b11100011; //  変換結果右詰め FOSC/64 VREF+をFVR
    FVRCON = 0b10000011; // FVR有効 出力4倍 (4.096 V)
    while(!FVRRDY);      //FVRが安定するまで待つ
    ADCON0 = 0b00010001; // アナログチャンネルAN4 ADC有効

    while (1) {
        GO = 1; // 変換の開始
        while (GO); // 変換の完了
        printf("%d\r\n", ADRES); // 変換結果を出力
    }
赤文字部分を変更することによりAN0からAN4に変更できました。

▼ 入力ピンを RB4/AN8 に変更する

RB側のピンはRS232cの送受信に使っていますので、このままでは送信がうまく動作しませんが、イメージはつかめると思います。
    TRISB =  0b00010000; // RB4は入力にする
    ANSELB = 0b00010000; // ポートBをデジタル入力、RB4をアナログ入力
    WPUB =   0b11101111; // RB4の弱プルアップを無効
    nWPUEN = 0;          // WPUx ラッチの値に応じて弱プルアップを有効にする

  //ADCON1 = 0b11100000; // 変換結果右詰め FOSC/64 VREF+をAVDD
    ADCON1 = 0b11100011; // 変換結果右詰め FOSC/64 VREF+をFVR
    FVRCON = 0b10000011; // FVR有効 出力4倍 (4.096 V)
    while(!FVRRDY);      // FVRが安定するまで待つ
    ADCON0 = 0b00100001; // アナログチャンネルAN8 ADC有効
赤文字部分を変更することによりRB4を使用する事ができました。


■ PIC12F1822 でのアナログ値を送信するプログラム

8ピンPICである、PIC12F1822で動作させたいと思います。
RA0とRA1はシリアル通信で使用しており、残りでA/D変換のできるピンはRA2:AN2と、RA4:AN3の2本になります。
今回はRA2:AN2を使いたいと思います。
プログラム自体のAD変換部分やポートの設定はPIC16F1827と全く同じです。
ただ、メモリが少なく printf で値を文字に変換するとメモリーが足りなくなりコンパイルできないため、
自作 print 関数により数字を文字に変換して送信するようにしています。

#include <stdio.h>
#include <stdlib.h>
// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable (PWRT enabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF       // Internal/External Switchover (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)
// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = HI        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), high trip point selected.)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

#include <xc.h>
#define _XTAL_FREQ 16000000

void send(unsigned char c) {
    while (!TXIF);
    TXREG = c;
}

//数字を送信する
void print(unsigned int n) {
    unsigned int p = 10000;
    unsigned char i;
    while (p) {
        i = 0;
        while (n >= p) {
            i++;
            n = n - p;
        }
        send('0' + i);
        p = p / 10;
    }
    send('\r');
    send('\n');
}

int receive(unsigned char*c) {
    if (RCIF) {
        *c = RCREG;
        return 0;
    }
    return -1;
}

void main() {
    OSCCON = 0b01111010; // クロック周波数16MHz
    ANSELA = 0b00000100; // ポートAをデジタル入力にする RA2はアナログ入力
    PORTA = 0b00000000;
    TRISA = 0b00000110; // RA1は入力にしないと受信できない RA2は入力にする
    WPUA = 0b11111011; // RA2の弱プルアップを無効
    nWPUEN = 0; // WPUx ラッチの値に応じて弱プルアップを有効にする

    TXCKSEL = 0; // TX:RA0
    RXDTSEL = 0; // RX:RA1

    TXSTA = 0b00100100;
    BRG16 = 1;
    RCSTA = 0b10010000;

    //ボーレート= FOSC /(4([SPBRGHx:SPBRGx] + 1))
    // SPBRGHx:SPBRGx:=((FOSC / ボーレート)/ 4)-1

    const uint16_t DBR = ((uint32_t) ((uint32_t) _XTAL_FREQ / 9600) / 4) - 1;

    SPBRGH = (uint8_t) (DBR >> 8);
    SPBRGL = (uint8_t) (DBR);

    //ADCON1 = 0b11100000; // 変換結果右詰め FOSC/64 VREF+をAVDD
    ADCON1 = 0b11100011; // 変換結果右詰め FOSC/64 VREF+をFVR
    FVRCON = 0b10000011; // FVR有効 出力4倍 (4.096 V)
    while (!FVRRDY); // FVRが安定するまで待つ
    ADCON0 = 0b00001001; // アナログチャンネルAN2 ADC有効

    while (1) {
        GO = 1; // 変換の開始
        while (GO); // 変換の完了
        // printf("%d\r\n", ADRES); // 変換結果を出力 メモリーが足りなくなるため使用できない
        print(ADRES);//数字をシリアル通信で送信する
    }
}
赤文字部分が自作 print 関数です。
この状態でメモリーは データ13% プログラム10% 使用しているそうです。
printf("%d\r\n", ADRES);とすると、この行一つでメモリーが足りなくなります。
いやーメモリが少ないのはキツイですね、他の部分はPIC16F1827とほぼ同じです。


■ PIC10F222 でのアナログ変換をするプログラム

PIC10F222にはUART回路が入ってないためシリアル通信がそのままではできません。
仕方がないのでアナログ値をLEDの点滅により表現したいと思います。

今回作成した回路です


ブレッドボードで実際に作成した所


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

#pragma config IOSCFS = 8MHZ    // Internal Oscillator Frequency Select bit (8 MHz)
#pragma config MCPU = OFF        // Master Clear Pull-up Enable bit (Pull-up enabled)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config CP = OFF         // Code protection bit (Code protection off)
#pragma config MCLRE = OFF      // GP3/MCLR Pin Function Select bit (GP3/MCLR pin function is digital I/O, MCLR internally tied to VDD)

#include <xc.h>
#define _XTAL_FREQ 8000000 //クロック周波数 __delay_ms()に必要

int main(int argc, char** argv) {
    OSCCAL = 0b00000000;
    OPTION = 0B11000000;
    ADCON0 = 0B00100101;
    TRISGPIO = 0b00000010;
    int i;
    while (1) {
        GO = 1; // 変換の開始
        while (GO); // 変換の完了
        GPIO = 0b00000000; //GPIOに出力
        for (i = 0; i < ADRES; i++) __delay_ms(1);
        GPIO = 0b00000111;
        for (i = 0; i < ADRES; i++) __delay_ms(1);
    }
    return (EXIT_SUCCESS);
}
プログラムを書き込んでボリュームを回すと点滅スピードが変化します。
しかし、このプログラム、メチャクチャはまりました。
赤文字部分の ADCON0 の設定部分、英語版データシートを開くと次のように書いてあります。

bit 7 ANS1: ADC Analog Input Pin Select bit
  1 = GP1/AN1 configured for analog input
  0 = GP1/AN1 configured as digital I/O
bit 6 ANS0: ADC Analog Input Pin Select bit
  1 = GP0/AN0 configured as an analog input
  0 = GP0/AN0 configured as digital I/O

このとうりに設定するとちゃんと動作しました。


▲トップページ > マイコンなど