前回からの続きです。
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 件のコメント:
コメントを投稿