前回からの続きです。
F/Vコンバータのプログラム
プログラムの主な内容は、以下の2つです。
- 入力パルスの間隔を測定(→周波数を読み取り)
- 周波数に対応した値をD/AコンバータにSPI通信で送信
使用したマイコンは、PIC16F88です。
パルス間隔の測定
パルス間隔の測定は、CCP1モジュールの「キャプチャ機能」を使いました。
キャプチャは、CCP1ピンの信号(今回は立ち下がりエッジ)をトリガとして、その瞬間のタイマ1カウンタの値をキャプチャレジスタに保存する、という機能です。こういう用途にぴったりです。
マイコンのクロックは20MHz、プリスケーラ1としたとき、タイマ1の1カウントは
です。タイマ1のカウンタは16bitなので、約13.1msでカウンタがオーバーフローします。周波数にすると76.33Hzとなり、この値以下の周波数を検知できないことになります。
プリスケーラを8とした場合でも、104.8msでカウンタがオーバーフローするため、9.54Hz以下は検知することができません。
そこで、タイマ1がオーバーフローしたことを検知し、オーバーフローした回数をカウントする変数を用意します。このオーバーフローカウント変数とタイマ1を合わせて32bitのカウンタとする ことで、低い周波数の入力に対応することにしました。(プリスケーラ設定は1としました。)
また、パルス間隔の測定を行う際に、キャプチャによってカウンタ値を保存した後にタイマ1カウンタを停止し、カウンタクリア、再スタートさせています。そのため、キャプチャ時点からカウンタが再スタートするまでの時間が誤差となります。
この誤差を補正するために、キャプチャした値に補正値を加えています。
D/Aコンバータ送信用にデータを加工
今回使用したD/Aコンバータ「MCP4922」は12bitなので、デジタル値は0~4096です。また、D/Aコンバータの出力電圧は、Vref=2.495Vとしたので、0~4.99Vとなります(D/AにてVrefのゲイン2倍に設定)。出力電圧と入力周波数の関係は、1V/100Hzです。
パルス間隔の計測値と、D/Aへ送信する値の対応式は、このようになります。
D/Aコンバータに値を送信
SPI通信は、SSPモジュールによるSPIモード(マスタ側)を使いました。
使用したD/Aコンバータ「MCP4922」は、SPIモード0と3に対応しているので、「モード0」としました。
(SPI通信のモードについてはこちら→SPI通信は「モード」に注意! SPI通信は4種類ある!)
フローチャート
プログラムのフローチャートはこのようになってます。
ソースコード
#include <xc.h> #include <pic16f88.h> #include <stdio.h> // CONFIG1 #pragma config FOSC = HS // Oscillator Selection bits (HS oscillator) #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 I/O, MCLR internally tied to VDD) #pragma config BOREN = ON // Brown-out Reset Enable bit (BOR enabled) #pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming) #pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off) #pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off) #pragma config CCPMX = RB0 // CCP1 Pin Selection bit (CCP1 function on RB0) #pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off) // CONFIG2 #pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled) #pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode disabled) #define LDAC PORTBbits.RB6 #define CS PORTBbits.RB5 #define Hosei 22 #define debug PORTAbits.RA0 #define FS 499UL //Full Scale=499Hz #define Clock 20000UL //kHz #define Edge 0 //1:Risig edge , 0:Falling edge #define _XTAL_FREQ 20000000 //CLK:20MHz /*グローバル変数*****************/ unsigned short overflow_cnt; // unsigned short overflow_buff; // unsigned char update_flag; // unsigned char overflow_lim; unsigned long t_value; unsigned short DAC_dat_buff; unsigned long Tfs; //Full Scale time unsigned long t_value_old; /*関数のプロトタイプ宣言**********/ void DAC_update(unsigned short); void update_check(void); void SPI_Write(unsigned char dat); /*割込み処理*********************/ void __interrupt() INT(void){ if(PIR1bits.TMR1IF){ // Timer1 overflow interrupt if(overflow_cnt < 0xFFFF){ overflow_cnt++; } else{ overflow_lim = 1; } PIR1bits.TMR1IF = 0; //TMR1IF flag clear } if(PIR1bits.CCP1IF){ // input capture interrupt T1CONbits.TMR1ON = 0; // Timer1 stop TMR1H = 0; // Counter Clear TMR1L = 0; T1CONbits.TMR1ON = 1; // Timer1 start overflow_buff = overflow_cnt; overflow_cnt = 0; update_flag = 1; PIR1bits.CCP1IF = 0; //CCP1IF flag clear } } /*******************************************************/ void main(void){ unsigned long t_value_now; unsigned short val_buff; //各種設定 OPTION_REG = 0b11000000; //Pull-up:D INTCON = 0b01000000; //Perioheral Interrupt Enable PIE1 = 0x05; //CCP1, Timer1 Interrupt Enable PIR1 = 0x00; PIE2 = 0x00; PIR2 = 0x00; PCON = 0x03; TRISA = 0b00000000; //all output TRISB = 0b00000011; //SDI, RB0(CCP1) input T1CON = 0x08; // if(Edge){ CCP1CON = 0x05; //Capture mode, every rising edge } else{ CCP1CON = 0x04; //Capture mode, every falling edge } SSPSTAT = 0b01000000; //data sample Middle, CKE:1, (SPI_mode0) SSPCON = 0b00100001; //SSP_Enable, idle_L, OSC/16 RCSTA = 0x00; ANSEL = 0b00000000; //all digital CMCON = 0b00000111; //Comparator disable Tfs = 250 * Clock / FS; PORTA = 0x00; //clear PORTB = 0x00; update_flag = 0; overflow_lim = 0; overflow_cnt = 0; overflow_buff = 0; TMR1H = 0; TMR1L = 0; t_value_old = 0xFFFFFFFF; DAC_update(0); INTCONbits.GIE = 1; // T1CONbits.TMR1ON = 1; //Timer1 Run //Mainloop while(1){ update_check(); t_value_now = (((unsigned long)overflow_cnt)<<16) + (((unsigned long)TMR1H) << 8) + TMR1L + Hosei; update_check(); if((t_value_now > t_value_old ) && !overflow_lim){ val_buff = ((Tfs << 12) / t_value_now); if(val_buff > 4095){ val_buff = 4095; } DAC_update(val_buff); } if(overflow_lim){ DAC_update(0); overflow_lim = 0; } } } /*****************************************************/ //DAC_update // //DACの値を更新する /*****************************************************/ void DAC_update(unsigned short Data){ unsigned char dataH, dataL; dataH = (unsigned char)((Data>>8) | 0x0050); dataL = (unsigned char)(Data & 0x00FF); CS = 0; LDAC = 1; SPI_Write(dataH); SPI_Write(dataL); LDAC = 0; CS = 1; } /*****************************************************/ // update_check // // update_flagが1であればDACを更新 /*****************************************************/ void update_check(void){ if(update_flag){ t_value = (((unsigned long)overflow_buff)<<16) + (((unsigned long)CCPR1H)<<8) + CCPR1L + Hosei; DAC_dat_buff = ((Tfs << 12) / t_value); if(DAC_dat_buff > 4095){ DAC_dat_buff = 4095; } t_value_old = t_value; DAC_update(DAC_dat_buff); update_flag = 0; } } /*****************************************************/ // SPI_Write // // SPIで1byte送信 /*****************************************************/ void SPI_Write(unsigned char dat){ SSPBUF = dat; //送信開始 while(!SSPIF); //送信完了までwait SSPIF = 0; }
(次回へ続く)
0 件のコメント:
コメントを投稿