/**************************************************************************** * メイン・プログラム:簡易グラフ表示温度計テスト * * GLCD:ノキア液晶5110 * 温度センサ:103AT-2(サーミスタ) * * * 原典参考先: * @ トラ技2006年3月号 山本さんの記事 * A 「太田さんのホームページ」の記事 * * Condition: * 8MHz Internal RC oscillator * Fcy=8MHz/2=4MHz, Tcy=250ns (電池駆動を想定し、最高速:16M/4で使用) * * CPU: PIC24FJ64GA002 * * 2016.11.23 N.Ishii *****************************************************************************/ #include "p24FJ64GA002.h" #include "timer.h" #include "nokiaGlcdlib.h" /// コンフィギュレーション ビットの設定 _CONFIG1( JTAGEN_OFF // JTAGの使用:無効 & GCP_OFF // コードプロテクト:しない & GWRP_OFF // 書き込みプロテクト:しない & BKBUG_OFF // バックグラウンドデバッグ:無効 & COE_OFF // クリップオンエミュレーション:無効 & ICS_PGx1 // EMUC/EMUDをPCG1/PGD1と共用 & FWDTEN_OFF // WDT:無効 ) /// Fosc = FRC (8MHz) _CONFIG2( IESO_OFF // 2速度スタートアップ:無効 & FNOSC_FRC // 発振器の選択:FRCを選択する & FCKSM_CSDCMD // クロック切り替え・クロックモニタ:供に無効 // & OSCIOFNC_OFF // OSCOピン機能:OSCOまたはFosc/2の出力する & OSCIOFNC_ON // OSCOピン機能:RA3ポートとする 161118 & IOL1WAY_OFF // RPレジスタプロテクト:プロテクトしない & I2C1SEL_PRI // I2C1のピン選択:主ピンを使用する & POSCMOD_NONE // 主発振器:使用しない ) /// 漢字ドット・パターン・テーブル // 「温度」の上半分 const unsigned char font_ondoH[] = { 0b00000000, 0b00010000, 0b01100000, 0b00000001, 0b00000110, 0b00000000, 0b00000000, 0b01111110, 0b01001010, 0b01001010, 0b01001010, 0b01001010, 0b01001010, 0b01111110, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b11111100, 0b00100100, 0b00100100, 0b00100100, 0b11111100, 0b10100100, 0b10100111, 0b10100100, 0b11111100, 0b00100100, 0b00100100, 0b00100100, 0b00000000, }; // 「温度」の下半分 const unsigned char font_ondoL[] = { 0b00000000, 0b11000000, 0b00110000, 0b00001100, 0b01000011, 0b01000000, 0b01111110, 0b01000010, 0b01000010, 0b01111110, 0b01000010, 0b01111110, 0b01000010, 0b01000010, 0b01111110, 0b01000000, 0b00000000, 0b10000000, 0b01100000, 0b10011111, 0b10000000, 0b10000010, 0b01000010, 0b01000110, 0b00101010, 0b00010010, 0b00110010, 0b01001010, 0b01000110, 0b10000000, 0b10000000, 0b00000000, }; char MsgData[] = "xx゚C"; // 温度値メッセージ・テーブル unsigned char MsgAD[] = "AD=xxx"; // AD変換値表示用 #define TEMP_HIST_MAX 45 #define TEMP_LOOP_MAX 100 /// サーミスタ関連定数(元の値は、これの1/100の値) #define TEMP_OFFSET 18185 #define TEMP_SLOPE 215 #define GRAPH_LOOP_MAX 60 #define TEMP_GET_NONEXT (-1) #define TEMP_GET_EMPTY (-2) /// リングバッファ用 char temp_hist[TEMP_HIST_MAX]; // temp_hist[45]:温度値データ履歴バッファで、グラフX軸間のドット数(45dot)分の要素数を持つ。 int temp_hist_head = 0; int temp_hist_full = 1; int ad_sum = 0; int intr_count = 0; int graph_count = GRAPH_LOOP_MAX; /// プロトタイプ宣言 void print_temp(char Temperature, char temp_sign); void ltostring(char digit, unsigned long data, char *buffer); void print_Yaxis(void); void print_graph(void); void add_temp(char); int get_temp(int, char *); int main(void) { //// I/O設定 // AD1PCFG = 0xFFFF; // A/D Digi 選択:全デジタルI/Oに設定 AD1PCFG = 0xFFFD; // AD Channel-1(AN1/RA1) Aanalog Input、他は全てデジタルI/Oに設定 CLKDIV = 0x0000; // System Clock devider 1:1 /// Setup PORT In/Out TRISB = 0x0000; // all output TRISAbits.TRISA3= 0; // 赤LED:デバッグ用 TRISAbits.TRISA4= 0; // 緑LED:デバッグ用 LATAbits.LATA3 = 1; // 赤LED消灯 LATAbits.LATA4 = 1; // 緑LED消灯 /// 液晶表示器の初期化と開始メッセージ表示 LCD_init(); LCD_locate(0,0); // 原点(左上)に設定 LCD_clear(LCD_WIDTH * LCD_BANKS); // 全画面クリア LCD_ROMstr("Start Test !!"); // 開始メッセージ表示 delay_ms(1000); //// 簡易グラフィック温度計の、Y座標描画 LCD_clear(LCD_WIDTH * LCD_BANKS); // 全画面クリア // ADC Module OFF AD1CON1bits.ADON=0; /// Inittalize Timer3:T3= Tcy*PS*n= 0.00025mS*64*625 = 10mS PR3 = 624; // n = 625 (PR3=n-1) T3CON = 0b1000000000100000; // T3_ON, T3_GATE_OFF, T3_PS_1_64, T3_SOURCE_INT /// Initialize ADC: Mod Initialize SQ AD1CHS = 0x0001; // AD Channel-1 Select AD1CSSL = 0x0000; // Scan_None AD1CON3 = 0x1F05; // Acquisition Time=31Tad, 1Tad=5*Tcy= 1.25u AD1CON2 = 0x0000; // Vref=AVdd-AVss, Scan Off, Interrupt Timming=EOC AD1CON1 = 0x0040; // Module Off, Format Integer, T3 Triger, Set SAMP Bits-> Start Sampling // ADC Module ON AD1CON1bits.ADON = 1; // Start Auto Sampling AD1CON1bits.ASAM = 1; // Enable ADC intterrupt IEC0bits.AD1IE = 1; while(1){ ; // 割込み待ち } } /**************************************** * ADC1 interrupt routine (t = 10mS) *****************************************/ void __attribute__((__interrupt__, auto_psv)) _ADC1Interrupt(void) { int ResultData; // ADC生データ int temp_val; int Temperature; char temp_sign; /* 整数演算にするため、TEMP_SLOPE、TEMP_OFFSETともに100倍になっている。 それに合わせて、A/D変換値を100回加算する */ while(!IFS0bits.AD1IF); // End of convertion ? ResultData = ADC1BUF0; // Read 10bit ADC Data From AN1 BUF ResultData >>= 2; // 生データを、8dit精度に落とす。 ad_sum += ResultData; // 割込み毎に、ADC生データを加算 ++intr_count; if (intr_count >= TEMP_LOOP_MAX) { // 100回加算したら以下を実行 // 10mSごとに割込み発生するので、10mS * 100 = 1秒ごとに実行 LCD_locate(0, 0); LCD_clear(LCD_WIDTH * LCD_BANKS); // 全画面クリア /// 温度を計算する if (ad_sum <= TEMP_OFFSET) { // プラスの場合(例:ad_sum= 128*100=12800(ad値:128=25℃)の場合) temp_val = TEMP_OFFSET - ad_sum; // 例:temp_val= 18185-12800= 5385 temp_sign = 1; } else { // マイナスの場合 temp_val = ad_sum - TEMP_OFFSET; temp_sign = 0; } Temperature = (temp_val + (TEMP_SLOPE/2)) / TEMP_SLOPE; // 実表示の温度値:例:5385+(215/2)/215= 25.54℃で、だいたい合う。 //// 液晶に表示する /// A/D変換値(100回の平均値)を表示 ltostring(3, ad_sum/TEMP_LOOP_MAX, MsgAD+3); // 数値をASCIIに変換しバッファへ格納 LCD_locate(0, 5); LCD_ROMstr(MsgAD); // AD変換値表示 print_temp(Temperature, temp_sign); // 温度値表示 //// 1分ごとの温度を保存する(リングバッファによる温度履歴の管理処理) ++graph_count; if (graph_count >= GRAPH_LOOP_MAX) { graph_count = 0; /// 温度データをリングバッファに格納 /// temp_sign == 1だったら、Temperature、そうでなければ、-Temperatureを格納 /// 下の記述は、引数のカッコの中に、if文を記述し、条件分岐された、それぞれの値を引数として、add_temp()を実行している。 /// 追記:N.Ishii 161123 add_temp((temp_sign == 1) ? (char)Temperature : (char)(-Temperature)); } print_Yaxis(); // グラフのY軸を表示 print_graph(); // 温度値履歴グラフを表示 ad_sum = 0; intr_count = 0; } IFS0bits.AD1IF = 0; // Clear Intterrupt ADC_Flag } /******************************* * 温度値を表示 ********************************/ void print_temp(char Temperature, char temp_sign) { unsigned char *pc; /// タイトル:"温度"を表示 LCD_locate(0, 0); pc = (unsigned char *)font_ondoH; LCD_set_data(pc, 32); LCD_locate(0, 1); pc = (unsigned char *)font_ondoL; LCD_set_data(pc, 32); /// 符号を表示 LCD_locate(0, 3); if (temp_sign == 1) { LCD_char('+'); } else { LCD_char('-'); } /// 温度値(バイナリ)を文字列に変換して、符号の後(x= 3キャラ目(dot指定で、x=12)から2桁整数で表示 ltostring(2, Temperature, MsgData);// 数値をASCIIに変換しバッファへ格納 LCD_locate(10, 3); LCD_ROMstr(MsgData); // 温度値表示 } /************************** * グラフのY軸表示 ***************************/ void print_Yaxis(void) { int x, y; unsigned char c; c = 0xff; x = LCD_MAX_X - TEMP_HIST_MAX - 2; /// 縦線を表示 for (y = 0; y < 6; y++) { LCD_locate(x, y); LCD_set_data(&c, 1); LCD_locate(x + TEMP_HIST_MAX + 1, y); LCD_set_data(&c, 1); } /// 10℃ごとの刻みを表示 for (y = 0; y <= LCD_MAX_Y; y += 10) { draw_point(x-1, y); draw_point(x + TEMP_HIST_MAX + 2, y); } } /*********************************** * 温度値履歴グラフを表示 ************************************/ void print_graph() { char ad_val; int temp, p, col; p = -1; col = 0; while ((p = get_temp(p, &ad_val)) != TEMP_GET_EMPTY) { temp = (int)ad_val; draw_point(LCD_MAX_X - col - 2, temp); if (p < 0) break; ++col; } } //// 以下は、リングバッファ用関数------------------------------------------------------------------------- /**************************************************** * 温度データをリングバッファに格納: コメント追記 N.Ishii 161123 *****************************************************/ void add_temp(char t) { temp_hist[temp_hist_head] = t; // 温度データ履歴バッファ(temp_hist[0]から順)に、温度値を格納 ++temp_hist_head; // バッファアドレスをインクリメント if (temp_hist_head >= TEMP_HIST_MAX) { // temp_hist[44]まで(つまり、TEMP_HIST_MAX=45)格納したら temp_hist_full = 1; // fullフラグをセットし、 temp_hist_head = 0; // headフラグをリセット } } /**************************************************** * リングバッファから温度データを取得 *****************************************************/ int get_temp(int p, char *t) { int i; if (!temp_hist_full && (temp_hist_head == 0)) { *t = 0; return TEMP_GET_EMPTY; } if (p < 0) { i = temp_hist_head; } else { i = p; } --i; if (i < 0) { i = TEMP_HIST_MAX - 1; } *t = temp_hist[i]; if (temp_hist_full) { if (i == temp_hist_head) return TEMP_GET_NONEXT; else return i; } else { if (i == 0) return TEMP_GET_NONEXT; else return i; } } ///--------------------------------------------------------------------------------------------------------- /************************************* * 数値からASCII文字列に変換する関数 **************************************/ void ltostring(char digit, unsigned long data, char *buffer) { char i; buffer += digit; // 文字列の最後へ for(i=digit; i>0; i--) { // 最下位桁から上位へ buffer--; // ポインター1 *buffer = (data % 10) + '0'; // その桁数値を文字にして格納 data = data / 10; // 桁-1 } /// ブランキング処理 i = 0; // buffer++; while((i < digit-1)&&(*buffer == '0')) // 上位桁が0の間 { *buffer = ' '; // ブランクに変換 buffer++; i++; } }