PIC+SPI+I2C 自記温湿度計+気圧計+8ch電圧計のソース - main()
参考になるかどうかわかりませんが.....
今回はmain()です。
装置の全体像とブロックダイヤグラムや関連記事(回路、ソース)へのリンクが
「PIC+SPI+I2C 自記温湿度計+気圧計+8ch電圧計+周波数カウンタ(技術要素一覧表)」
にあります。
MCP3208はRTCのバックアップ用電池の隣のソケットにいれました。
PICでSPI - 超高精度SPIバスRTC(リアルタイムクロック)DS3234Sの使い方(ソース付き)
PICでI2C - 大気圧・温度センサーLPS331APの使い方
I2C/SPI大気圧センサーLPS25Hの使い方 - 1 - 実装
意外に正確そうなI2C大気圧センサーMPL115A2の測定値
温度センサー3種の精度比較(摂氏0度~40度編)
PICで作るお手軽サーミスタ温度計 (2) - ソース付き
(趣味の)白金抵抗温度計の製作 - 準備編
PICでI2C - ADコンバーター・MCP3425の使い方
PIC18F26K22でSPI - 8ch/ADコンバータ MCP3208の使い方(ソース付き)
PICでSPI SDカードを読み書きする
趣味の電子工作
PIC
趣味の気象観測
趣味の実験
========
// xc8コンパイラー foat=24bit double=32bit でコンパイルすること
// 各種センサーのデータを取得し、それらをLCDに表示するとともにSDカードに記録します。
// どういうデータを表示するかとかどういうふうにデータを取得するかも指定できるようにしたいけれど未実装です。
// SDカードに記録する間隔だけは設定できるようになっています。
// タクトスイッチ2個で設定画面呼び出し、設定値保存、測定値手動記録、測定値保存終了などを制御します。
// 記録時間間隔は可変抵抗で選択します。
// 1,2,3,4,5,6,10,15,20,30,60,120,180,240,300,600秒から選べます。
// ただし1秒は特殊でこれが選択されると測定値の記録は行わずタクトスイッチが押されたときだけ記録されます。
// 1秒じゃなくて0秒の選択肢にするんでした (^^;;
// 測定とその記録は1秒では時間が足りません。またSDカードへの記録量が増えて行くと書き込みが遅くなるようです。
// つまり長時間記録をとるときは記録間隔は長めにしておいた方が無難です。
// 例えば2秒間隔で記録をとると、毎分0,2,4,6,8,...秒に記録されるはずが0,2,4,6,10,14,20,...秒に記録されるみたいになります。
// (これはわかりやすいようにデフォルメした表現です)
// 時刻合わせの機能もつくるつもりだったのですがRTC(DS3234S)があまりにも正確なので作成意欲を失いました。
// このRTCは秒を設定したらその0.5秒後から1PPSが出るらしいのでGPSの1PPSと同期するようにしたいものです。
// PIC18F26K22
// 01 RE3/VPP/MCLR .....(1) .... 10kでプルアップ
// 02 RA0/C12IN0-/AN0 ..... 可変抵抗の中点(?)へ、電源電圧(3.3V)の分圧を測定、設定値選択用
// 03 RA1/C12IN1-/AN1 /..... タクトスイッチ、メニューボタン
// 04 RA2/C2IN+/AN2/DACOUT/VREF .... タクトスイッチ、決定・終了ボタン
// 05 RA3/C1IN+/AN3/VREF+ .... 未使用
// 06 RA4/CCP5/C1OUT/SRQ/T0CKI ..... 未使用
// 07 RA5/C2OUT/SRNQ/SS1/HLVDIN/AN4 .... 未使用
// 08 VSS
// 09 RA7/CLKI/OSC1 .... RTC・DS3234Sの/CS
// 10 RA6/CLKO/OSC2 ..... ADコンバータのMCP3208の/CS
// 11 RC0/P2B/T3CKI/T3G/T1CKI/SOSCO ..... Reserved アナログ・スイッチの制御(I2Cバスラインの選択)
// 12 RC1/P2A/CCP2/SOSCI ..... Reserved アナログ・スイッチの制御(I2Cバスラインの選択)
// 13 RC2/CTPLS/P1A/CCP1/T5CKI/AN14 ..... アナログ・スイッチの制御(I2Cバスラインの選択)
// 14 RC3/SCK1/SCL1/AN15 ..... I2C/SCL ..... MCP3425、LPS331AP、LPS25H、AM2321、MLP115A2、AM3201
// 15 RC4/SDI1/SDA1/AN16 ..... I2C/SDA
// 16 RC5/SDO1/AN17 .... つかっちゃまずい?
// 17 RC6/P3A/CCP3/TX1/CK1/AN18 ..... Reserved TX1
// 18 RC7/P3B/RX1/DT1/AN19 ..... Reserved RX1
// 19 VSS .....(3)
// 20 VDD .....(2)
// 21 RB0/INT0/CCP4/FLT0/SRI/SS2/AN12 ..... INT0 ..... RTC/1PPS出力へ
// 22 RB1/INT1/P1C/SCK2/SCL2/C12IN3-/AN10 ...... SPI/SCK .... SDカード、DS3234S、MCP3208
// 23 RB2/INT2/CTED1/P1B/SDI2/SDA2/AN8 ..... SPI/SDI
// 24 RB3/CTED2/P2A/CCP2/SDO2/C12IN2-/AN9 ..... SPI/SDO
// 25 RB4/IOC0/P1D/T5G/AN11 .... SDカードの/CS
// 26 RB5/IOC1/P2B/P3A/CCP3/T3CKI/T1G/AN13 ..... 未使用
// 27 RB6/IOC2/TX2/CK2/PGC .....(5) ..... 未使用
// 28 RB7/IOC3/RX2/DT2/PGD .....(4) ..... 未使用
// SPI MODE 0 DS3234S(モード1/3とあるけれどこれで動いています)、MCP3208
// SSP2STAT &= 0xBF;
// SSP2CON1 &= 0xEF;
// SPI MODE 1
// SSP2STAT |= 0x40;
// SSP2CON1 &= 0xEF;
// SPI MODE 2 SDカード
// SSP2STAT &= 0xBF;
// SSP2CON1 |= 0x10;
// SPI MODE 3
// SSP2STAT |= 0x40;
// SSP2CON1 |= 0x10;
#include <xc.h>
#include <stdio.h>
#include <string.h>
#include "..\..\include\common\MyDef.h"
#include "..\..\include\common\I2CLib.h"
#include "..\..\include\common\ACM1602lib.h"
#include "..\..\include\common\lps331ap.h"
#include "..\..\include\common\MCP3425lib.h"
#include "..\..\include\common\AM2321lib.h"
#include "..\..\include\common\MPL115A2lib.h"
#include "..\..\include\common\SPILib.h"
#include "..\..\include\common\SDlib.h"
// C:\Program Files (x86)\Microchip\xc8\v1.32\docs\chips\18f26k22.html
#ifdef _18F26K22
#pragma config FOSC = INTIO67
#pragma config WDTEN = OFF
#pragma config MCLRE = INTMCLR
#endif
// 割込みがあったかどうか確認するためのフラグです。
static int Interrupt=0;
// センサーから読み込んだデータをLCDに16進表示するため
// sprintf()を使いたくないとき用
void putLCDhex(unsigned char ch)
{
char hex[]="0123456789ABCDEF";
LCD_Putc(hex[ch>>4]) ;
LCD_Putc(hex[ch & 0x0f]) ;
}
// 可変抵抗(アナログ端子の電圧)で記録時間間隔をコントロールします。
// エンコーダーやDIPスイッチを使うより簡単で応用がききます。
// RTCの時刻設定もこれにするつもりです。
int getValOfVR(void)
{
int val;
static int vtint[]={1,2,3,4,5,6,10,15,20,30,60,120,180,240,300,600};
ADCON0 = 0b00000011 ; // AN0のAD変換開始
while(ADCON0 & 0b00000010) ; // ADC変換終了待ち
val = ADRESH * 256 + ADRESL;
return vtint[val/64];
}
// 割込みがあったときはフラグを立てます。
// 割込みルーチンの中では何もしません。
void interrupt InterFunction( void )
{
// INT0割込み
if( INT0IF == 1 ) {
Interrupt = 1;
INT0IF = 0;
// I2C関連の割り込み処理
} else {
InterI2C() ;
}
}
void main()
{
unsigned char ct[8];
unsigned char d1,d2,d3;
char filename[16];
union {
char c[2] ;
int i ;
} ans ;
char dt_w[128] ="LPS331-T,LPS331-P,LPS25H-T,LPS25H-P,3425-V,3425-T\r\n";
char dt_m[128];
int j,k,sec,min;
int status=0;
unsigned char menu = 4;
int tint = 300,tint_t;
double val_LPS331[2];
double val_LPS25H[2];
// 白金薄膜抵抗の抵抗値を求めたり、それから温度を求めるための定数
// MCP3425 & Pt
unsigned char ip_MCP3425[4]={0,0,0,0};
float vlt_MCP3425;
float tmp_MCP3425;
float R1;
float V_cmp= 1.002965; // MCP3425の電圧補正値
float Offset = 0.00000;
float Vin=4.094; // 基準電圧
float Rin= 4980.0; // 直列抵抗(金属皮膜抵抗 0.5%)
float R0 = 100.00; // 白金薄膜抵抗の273.15Kでの抵抗値
float C_a= 0.0039083; // 白金薄膜抵抗の補間式係数
float C_b= -0.0000005775; // 白金薄膜抵抗の補間式係数
// MCP3208(ADコンバータ)で電圧を求めるための定数
float value[8];
float Vref=3.3;
float val_AM2321[2];
float prs_MPL115;
OSCCON = MyOSCCON ;
// 入力ピンは一般的に電源を含めどこに接続してもたいていヘーキですが
// 出力ピンが電源や他の出力ピンに接続されるは避けた方がいいと思います。
// だから事故防止のため出力として使わないピンは入力のままにしてあります。
// for I2C
ANSELCbits.ANSC3 = 0;
ANSELCbits.ANSC4 = 0;
// Select bus for I2C
// 同じアドレスのI2Cデバイスを使うのにSCLをアナログ・スイッチで切り替えるため
TRISC2 = 0;
PORTCbits.RC2 = 1;
// for SPI
ANSELBbits.ANSB2 = 0;
ANSELBbits.ANSB1 = 0;
TRISB1 = 0;
TRISB3 = 0;
// OUTPUT
// SDカードの/CS
TRISB4 = 0;
CS_SD = 1;
// RTC DS3234Sの/CS
TRISA7 = 0;
CS_RTC = 1;
// RTC DS3234Sの/CS
TRISA6 = 0;
CS_ADC = 1;
// INT0 RB0
ANSELBbits.ANSB0 = 0;
// INPUT RA1 メニューボタン
ANSELAbits.ANSA1 = 0;
// メニューボタンの機能
// 1. 測定値を記録する間隔を設定する画面に入ります
// 2. 設定画面を終了する
// ※ 起動した直後は設定画面になります。
// INPUT RA2 決定、終了ボタン
ANSELAbits.ANSA2 = 0;
// 決定・終了ボタンの機能
// 設定画面のとき
// 1. 画面に表示されている値を保存します。
// 設定画面でないとき
// 測点間隔が1秒以外に設定されているとき
// 測定値の記録を終了し、表示のみのモードに移行します。
// 測定間隔が1秒に設定されているとき
// (測定間隔1秒の場合は測定値の自動記録はしません)
// 測定値を記録します。
// ADC AN0 設定切替用可変抵抗
ADCON2 = 0b10101111 ;
ADCON1 = 0b00000000 ;
// SPIはひとまずmode 2として初期化します。
SPI_Init(SPI_MODE2,SPI_CLOCK_DIV16) ;
InitI2C_Master() ;
LCD_Init() ;
LCD_Clear();
LCD_Puts("Start!");
// これが重要みたいです。
for(j=0; j < 300; j++ ) __delay_ms(10);
// MCP3208のデータを読み込みます
// SDカードの初期化でエラーが起きることの対策として入れたがたぶん不要
for( k=0; k < 8; k++ ) {
// SPI MODE 0
SSP2STAT &= 0xBF;
SSP2CON1 &= 0xEF;
CS_ADC = 0;
d1=SPI_transfer(0x06 | ((k >> 2) & 0x01) ) ;
d2=SPI_transfer( k << 6 );
d3=SPI_transfer(0x00);
CS_ADC = 1;
value[k] = (float)(d2 & 0x0F)*256.0 + (float)d3;
value[k] = Vref * value[k]/4096.0;
}
// DS3234Sから時刻を取得します。
// この処理はどうして必要か不明
// なくても動きそうだが....
// SPI MODE 0
SSP2STAT &= 0xBF;
SSP2CON1 &= 0xEF;
CS_RTC = 0;
for( j=0; j < 25; j++ ) __delay_ms(20);
ct[0]=SPI_transfer(0x00) ;
for( j =0; j < 7; j++ ) {
ct[6-j]=SPI_transfer(0x00) ;
}
CS_RTC = 1;
// 時刻を取得します。
// SPI MODE 0
SSP2STAT &= 0xBF;
SSP2CON1 &= 0xEF;
CS_RTC = 0;
d1=SPI_transfer(0x00) ;
for( j =0; j < 7; j++ ) {
ct[6-j]=SPI_transfer(0x00) ;
}
CS_RTC = 1;
LCD_SetCursor(0,0);
for( j=0; j < 7; j++ ) {
putLCDhex(ct[j]);
}
LCD_SetCursor(0,1);
// ファイル名は8.3形式である必要があります。
sprintf(filename,"%02x%02x%02x%02x.TXT",ct[1],ct[2],ct[4],ct[5]);
// SDカードの初期化とファイルのオープン
// SPI MODE 2
SSP2STAT &= 0xBF;
SSP2CON1 |= 0x10;
ans.i = SD_Init() ;
if (ans.i != 0) {
LCD_Puts("Error-Init ") ; // 初期化エラー
putLCDhex(ans.c[1]) ;
putLCDhex(ans.c[0]) ;
while( PORTAbits.RA1 == 1 );
status = 0x01;
} else {
ans.i = SD_Open(filename,O_RDWR) ;
if (ans.i != 0) {
LCD_Puts("Error-Open ") ; // オープンエラー
while( PORTAbits.RA1 == 1 );
status = 0x02;
} else {
ans.i = SD_Write(dt_w,strlen(dt_w)) ;
if( ans.i == -1 ) {
LCD_Puts("Error-Write ") ; // オープンエラー
while( PORTAbits.RA1 == 1 );
status = 0x04;
}
}
}
// RTCの1PPSでINT0から割込みをかけます。
INT0IE = 1;
k = 0;
LCD_SetCursor(0,0);
while( status == 0 ) {
// いろいろ面倒くさいことをしていますがチャタリングやスイッチが押しっぱなしにされたときの対策です。
if( PORTAbits.RA1 == 0 ) {
if( menu == 4 ) menu = 5;
} else {
if( menu == 3 ) menu = 4;
else if( menu == 5 ) menu = 0;
}
if( menu < 3 ){
// 1PPSの割込みがなかったときは何もしない
if( Interrupt == 0 ) continue;
// データの取得、SDカード書き込み中は割込み禁止です。
INT0IE = 0;
if( PORTAbits.RA1 == 0 ) menu++;
else menu = 0;
// データ表示中にRA2がLowに落ちたら測定終了です。
// ただしデータ記録間隔が1秒のときは除きます
// RA2をHighにしたら再測定、ということにはなっていません。
if( PORTAbits.RA2 == 0 && tint != 1 ) break;
// DS3234Sから時刻を取得します。
// SPI MODE 0
SSP2STAT &= 0xBF;
SSP2CON1 &= 0xEF;
CS_RTC = 0;
d1 = SPI_transfer(0x00) ;
for( j =0; j < 7; j++ ) {
ct[6-j]=SPI_transfer(0x00) ;
}
CS_RTC = 1;
// 時刻をLCDに表示します。
LCD_SetCursor(0,0);
for( j=0; j < 7; j++ ) {
if( j == 3 || j == 4 ) LCD_Putc('-');
putLCDhex(ct[j]);
}
LCD_SetCursor(0,1);
// 白金薄膜抵抗の抵抗値から温度を求めます。
getDataMCP3425(0x07, ip_MCP3425);
LCD_SetCursor(0,0);
// for(j=0; j < 4; j++ ) putLCDhex(ip_MCP3425[j]);
vlt_MCP3425 = getVltMCP3425(0x07,V_cmp,Offset,ip_MCP3425);
R1 = getRegPt(Vin,Rin,0.0,vlt_MCP3425);
tmp_MCP3425 = getTmpPt(R0,C_a,C_b,R1);
// sprintf(dt_w,"%7.3f %7.3f",vlt_MCP3425,tmp_MCP3425);
// LCD_SetCursor(0,1);
// LCD_Puts(dt_w);
// LPS331APから気圧と温度を取得します。
lps331ap(LPS331AP, val_LPS331);
// LPS25Hから気圧と温度を取得します。
lps331ap(LPS25H, val_LPS25H);
// MCP3208のデータを読み込みます
for( k=0; k < 8; k++ ) {
// SPI MODE 0
SSP2STAT &= 0xBF;
SSP2CON1 &= 0xEF;
CS_ADC = 0;
d1=SPI_transfer(0x06 | ((k >> 2) & 0x01) ) ;
d2=SPI_transfer( k << 6 );
d3=SPI_transfer(0x00);
CS_ADC = 1;
value[k] = (float)(d2 & 0x0F)*256.0 + (float)d3;
value[k] = Vref * value[k]/4096.0;
}
// AM2321から温度と湿度を取得します。
AM2321(val_AM2321);
//
MPL115A2(&prs_MPL115);
// 温度と気圧をLCDに表示します。
// sprintf(dt_m,"%4.1f,%4.1f,%6.1f",
// tmp_MCP3425,val_LPS331[0],val_LPS331[1]);
// sprintf(dt_m,"%7.2f%7.2f",
// val_LPS331[1],val_LPS25H[1]);
sprintf(dt_m,"%6.3f%6.3f",
value[0],value[1]);
LCD_SetCursor(0,1);
LCD_Puts(dt_m) ;
// インターバル設定値ごとに測定結果をSDカードに書き込みます。
sec = ((ct[6] & 0xF0) >> 1) + ((ct[6] & 0xF0) >> 3) + (ct[6] & 0x0F);
min = ((ct[5] & 0xF0) >> 1) + ((ct[5] & 0xF0) >> 3) + (ct[5] & 0x0F);
sec = min * 60 + sec;
if( (sec % tint ) == 0 ) {
if( tint == 1 && PORTAbits.RA2 != 0 ) {
LCD_Puts(" ") ;
continue;
}
// 時刻をSDカードに記録します。
// SPI MODE 2
SSP2STAT &= 0xBF;
SSP2CON1 |= 0x10;
sprintf(dt_w,"%02x/%02x/%02x,%02x:%02x:%02x,",
ct[0],ct[1],ct[2],ct[4],ct[5],ct[6]);
ans.i = SD_Write(dt_w,strlen(dt_w)) ;
// 温度と気圧をSDカードに記録します。
sprintf(dt_m,"%6.2f,%7.2f,%6.2f,%7.2f,%9.6f,%7.2f,",
val_LPS331[0],val_LPS331[1],val_LPS25H[0],val_LPS25H[1],vlt_MCP3425,tmp_MCP3425);
ans.i = SD_Write(dt_m,strlen(dt_m)) ;
sprintf(dt_m,"%6.2f,%6.2f,", val_AM2321[0], val_AM2321[1]);
ans.i = SD_Write(dt_m,strlen(dt_m)) ;
sprintf(dt_m,"%8.2f,", prs_MPL115);
ans.i = SD_Write(dt_m,strlen(dt_m)) ;
for( k=0; k < 8; k++ ) {
sprintf(dt_m,"%6.3f,",value[k]);
ans.i = SD_Write(dt_m,strlen(dt_m)) ;
}
sprintf(dt_w,"\r\n");
ans.i = SD_Write(dt_w,strlen(dt_w)) ;
// 測定結果をSDカードに書き込んだことをLCDに表示します。
LCD_Puts(" R") ;
} else {
// SDカードへの書き込みをしないとき
LCD_Puts(" ") ;
}
LCD_SetCursor(0,0);
// 次の割込みを待ちます。
Interrupt = 0;
INT0IE = 1;
// メニューボタンが押されたときは記録間隔の設定します。
} else {
tint_t = getValOfVR();
sprintf(dt_w,"%4d ",tint_t);
LCD_SetCursor(0,0);
LCD_Puts(dt_w);
// 決定ボタンが押されている
if( PORTAbits.RA2 == 0 ) {
tint = tint_t;
LCD_SetCursor(6,0);
LCD_Puts("Set");
for(j=0; j < 50; j++ ) __delay_ms(10);
// 決定ボタンが押されていない
} else {
LCD_SetCursor(6,0);
LCD_Puts(" ");
}
for(j=0; j < 10; j++ ) __delay_ms(10);
}
}
// SDカードをクローズします。
if( status == 0 ){
INT0IE = 0;
// SPI MODE 2
SSP2STAT &= 0xBF;
SSP2CON1 |= 0x10;
SD_Close() ;
LCD_SetCursor(0,1);
LCD_Puts("End. ") ;
INT0IE = 1;
}
// 測定の記録が終わっても時刻の表示は続けます。
LCD_SetCursor(0,0);
while( 1 ) {
if( Interrupt == 0 ) continue;
// データの取得中は割込み禁止です。
INT0IE = 0;
// DS3234Sから時刻を取得します。
// SPI MODE 0
SSP2STAT &= 0xBF;
SSP2CON1 &= 0xEF;
CS_RTC = 0;
for( j=0; j < 25; j++ ) __delay_ms(20);
ct[0]=SPI_transfer(0x00) ;
for( j =0; j < 7; j++ ) {
ct[6-j]=SPI_transfer(0x00) ;
}
CS_RTC = 1;
for( j=0; j < 7; j++ ) {
if( j == 3 || j == 4 ) LCD_Putc(' ');
putLCDhex(ct[j]);
}
// LPS331APから気圧と温度を取得します。
// lps331ap(val_LPS331);
// 温度と気圧をLCDに表示します。
// sprintf(dt_m,"%5.1f %8.1f *",val_LPS331[0],val_LPS331[1]);
// LPS331APから気圧と温度を取得します。
lps331ap(LPS331AP, val_LPS331);
// LPS25Hから気圧と温度を取得します。
lps331ap(LPS25H, val_LPS25H);
AM2321(val_AM2321);
// 温度と気圧をLCDに表示します。
sprintf(dt_m,"%7.2f,%7.2f",
val_AM2321[0],val_AM2321[1]);
LCD_SetCursor(0,1);
LCD_Puts(dt_m) ;
LCD_SetCursor(0,0);
Interrupt = 0;
INT0IE = 1;
}
}
« 自記温度計、自記気圧計 - 複数のSPIデバイスがいっしょに使えなかった話 | トップページ | 交流電圧計(ミリバル)を作る - 高周波電圧計 - 1 »
「趣味の電子工作」カテゴリの記事
- PICで作る100MHz周波数カウンタ検証用XOR(エクスクルーシブオア)逓倍器(2016.03.08)
- 150MHz(~200MHz?)周波数カウンター用プリスケーラー(1/4分周器)(2016.03.06)
- 測温抵抗体(Pt100、白金薄膜温度センサー)の抵抗値を温度に変換する(平方根を使わない)計算式(2016.03.01)
- GPS/JJY(標準電波)を基準周波数源とするためのPLLの詳細(2016.02.27)
- GPS受信モジュール1PPS対決 - GE-612T vs GM-5157A(2016.02.21)
コメント
この記事へのコメントは終了しました。
« 自記温度計、自記気圧計 - 複数のSPIデバイスがいっしょに使えなかった話 | トップページ | 交流電圧計(ミリバル)を作る - 高周波電圧計 - 1 »
こんにちは。こんな話題の時にしか出て来れなくてすいません(天体写真は趣味で撮っているのですが)。
SDカードに記録するとの事ですが、あまり頻繁に記録を重ねるとすぐに寿命になってしまいます。私は1秒に1回データを書き込むような事をやっていて、数か月で中身が読めなくなってしまいました。そんな失敗がありましたので、お知らせに及んだ次第です。
投稿: min | 2015年2月19日 (木) 17時49分
あれ、そういうものなんですか。
データが消えるのが嫌なのでできるだけファイルを分割して1日に一度はPCに移すようにしているのですが、毎回SDカードを入れ替えるくらいしておいた方がよさそうですね。
ありがとうございます m(._.)m
投稿: セッピーナ | 2015年2月19日 (木) 20時02分