// -------------------------------------------------- // Global Versatile Controler http://www.gvc-on.net/ // -------------------------------------------------- // -------------------------------------------------- // Revision Memo (Y.M.D Editor/Memo) // -------------------------------------------------- // // 2013.06.06 T.Kabu // マスターコントローラー用 // // マスターコントローラーは、言ってみればシリアルとI2Cのゲートウェイの役目だけ // ・初期起動時 // ・I2Cを全スキャンして接続モジュールとそのタイプを取得 // ・起動メッセージをシリアル出力 // ・接続モジュールとそのタイプの一覧を出力 // ・デリミタフレーム送信 // ・ループ時の処理 // ・シリアルからのデータの有無を確認 // →データがあれば // ・データを受信してためていく // ・コマンドとして認められればコマンド処理 // (→データがなければ、もしくはコマンド処理が終わったら、特に何もしないで以下の処理に続く) // ・取得可能なモジュールからは順次データを取得していく // 以上の繰り返し // // 2013.05.08 T.Kabu // GVC Rev.2としてのもろもろを定義 // //--------------------------------------------------- // Include Header //--------------------------------------------------- // ---------------------------------------- // Standard Header // ---------------------------------------- #include <xc.h> #include <plib.h> #include <htc.h> #include <stdio.h> #include <stdlib.h> #include <string.h> // ---------------------------------------- // User Header // ---------------------------------------- // Pragma Header #include "pragma.h" // PIC Parameter define and initialize #include "pic_init.h" // GVC Parameter define and initialize #include "gvc_init.h" // -------------------------------------------------- // Const Define // -------------------------------------------------- #define VERSION "=== GVC MASTER CONTROLLER PROGRAM for 18F26K22 ===" // 受信バッファサイズ #define serial_rcvbuffsize 256 #define serial_rcvbuffring 255 // serial_rcvbuffsizeから1減らした値を設定 // GVCで扱うメッセージの最大サイズ(もろもろ込み込みで…TBD) #define GVC_MSG_MAX 2500 #define GVC_MSG_MIN 256 #define GVC_MIN_MESSAGE_LENGTH 7 // シリアルメッセージの最小サイズ // PICのI2Cアドレス、適時変更すること #define I2C_ADDR 0x01 // ------------------------------ // Define MPL115A2 (Atmospheric pressure Sensor) // ------------------------------ #define GVC_PIR_ADDRESS 0x11 // I2C Device ID : PIR MODULE #define GVC_TEMP_HUMI_ADDRESS 0x20 // I2C Device ID : TEMP & HUMI MODULE #define GVC_DISTANCE_ADDRESS 0x50 // I2C Device ID : DISTANCE MODULE #define MPL115A2_ADDRESS 0x60 // I2C Device ID : MPL115A2 (Atmospheric pressure Sensor) // -------------------------------------------------- // Variable Param // -------------------------------------------------- // 割り込み処理内でのレジスタの値を格納する変数はここで宣言すること、割り込み処理内で宣言してはいけない!! T.Kabu 2013.05.14 unsigned char reg_RCSTA1; // 受信ステータスレジスタ unsigned char reg_SSP1STAT; // SSP1ステータスレジスタ static unsigned char serial_rcvptr; // 受信バッファポインタ static unsigned char serial_readptr; // 受信読み出しポインタ static unsigned char serial_rcvbuff[serial_rcvbuffsize]; // 受信リングバッファ(RCREG1) unsigned char mainbuffer[GVC_MAINBUFF_SIZE]; // メインバッファ unsigned char subbuffer[GVC_SUBBUFF_SIZE]; // サブバッファ // 割り込み処理とは違うけど、ここでバッファ位置などを宣言したらはまったのでここで宣言するな!! XC8のバグにはまるぞ!! T.Kabu 2013.05.14 unsigned char i2c_slavetbl[0x80]; // I2Cスレーブ状態格納テーブル(作業用) GVC_SERIAL_MESSAGE_t * gvc_serial_message; // GVC シリアルメッセージ用ポインタ(mainbufferにかぶせる) /// GVC_I2C_MESSAGE_t * gvc_i2c_message; // GVC I2Cメッセージ用ポインタ(gvc_serial_messageにかぶせる) // ------------------------------ // Global Parameter MPL115A2 (Atmospheric pressure Sensor) // ------------------------------ short int sia0; // MPL115A2のa0係数 short int sib1; // MPL115A2のb1係数 short int sib2; // MPL115A2のb2係数 short int sic12; // MPL115A2のc12係数 short int sic11; // MPL115A2のc11係数 short int sic22; // MPL115A2のc22係数 long int sia0l; // MPL115A2のa0係数(long値) long int sib1l; // MPL115A2のb1係数(long値) long int sib2l; // MPL115A2のb2係数(long値) long int sic12l; // MPL115A2のc12係数(long値) long int sic11l; // MPL115A2のc11係数(long値) long int sic22l; // MPL115A2のc22係数(long値) // ------------------------------ // GVC Message : Delimiter // ------------------------------ // デリミタメッセージ … 最後の0x00はmain()内でCRCに置換される unsigned char GVC_DELIMITER_MSG[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xab, 0xaa, 0x00 }; // -------------------------------------------------- // Function prototype // -------------------------------------------------- // -------------------------------------------------- // Sub Routine // -------------------------------------------------- // ------------------------------ // Send Delimiter Frame // ------------------------------ void send_delimiterframe() { // デリミタフレームを送信 send_strdata(&GVC_DELIMITER_MSG); // デリミタフレームを送信 send_strdata(&GVC_DELIMITER_MSG); // デリミタフレームを送信 send_strdata(&GVC_DELIMITER_MSG); // デリミタフレームを送信 send_strdata(&GVC_DELIMITER_MSG); // デリミタフレームを送信 send_strdata(&GVC_DELIMITER_MSG); } // ------------------------------ // Send GVC Other Message // ------------------------------ void send_gvc_other_message(int dev_num, int data_len, char * data) { // リザルトLEDを点灯 PORT_RESULT_LED = LED_ON; // メインバッファにGVC_SERIAL_MESSAGE_tをかぶせる gvc_serial_message = (GVC_SERIAL_MESSAGE_t *)mainbuffer; // メッセージを設定 gvc_serial_message->msg_type = GVC_MSG_OTHER; // メッセージタイプを設定 gvc_serial_message->dev_num = dev_num; // デバイス番号を設定 gvc_serial_message->format = 0x01; // フォーマットを設定(0x01=通常テキスト) gvc_serial_message->cmd = 0x01; // 結果(情報種別)を設定(0x01=初期情報、システム情報) gvc_serial_message->data_len = data_len; // データ長を設定 memcpy(gvc_serial_message->data, data, data_len); // データを設定 // CRCを計算して、メッセージの最後に設定 gvc_serial_message->data[ gvc_serial_message->data_len ] = GetCRC8((void *)gvc_serial_message, GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len); // ↑データを送信する側は、データのCRCを計算したあとで // そのCRCをデータの最後に連結して相手に送ればいい // 処理結果を送信 send_serial((char *)gvc_serial_message, (GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len + 1)); // リザルトLEDを消灯 PORT_RESULT_LED = LED_OFF; } // ------------------------------ // Recieve serialdata // ------------------------------ char rcv_serialdata(void) { unsigned char data; // 返り値用データに受信データコピー data = serial_rcvbuff[serial_readptr]; // 受信読み出しポインタを加算 serial_readptr ++; // 受信読み出しポインタをリングる serial_readptr &= serial_rcvbuffring; // 受信データを返す return data; } // ------------------------------ // Setup MSSP1 18F26K22 // ------------------------------ void init_mssp1_18F26K22(void) { // ------------------------------ // MSSP1制御データ設定 マスターとして設定する場合 // ------------------------------ // bit 7 : SMP 1 = Slew rate control disabled for standard speed mode (100 kHz and 1 MHz) // : 0 = Slew rate control enabled for high speed mode (400 kHz) // bit 6 : CKE 1 = Enable input logic so that thresholds are compliant with SMbus specification // : 0 = Disable SMbus specific inputs // bit 5 : D/A: Data/Address bit (I2C mode only) // : 1 = Indicates that the last byte received or transmitted was data // : 0 = Indicates that the last byte received or transmitted was address // bit 4 : P: Stop bit // : (I2C mode only. This bit is cleared when the MSSPx module is disabled, SSPxEN is cleared.) // : 1 = Indicates that a Stop bit has been detected last (this bit is ‘0’ on Reset) // : 0 = Stop bit was not detected last // bit 3 : S: Start bit // : (I2C mode only. This bit is cleared when the MSSPx module is disabled, SSPxEN is cleared.) // : 1 = Indicates that a Start bit has been detected last (this bit is ‘0’ on Reset) // : 0 = Start bit was not detected last // bit 2 : R/W: Read/Write bit information (I2C mode only) // : In I2 C Master mode: // : 1 = Transmit is in progress // : 0 = Transmit is not in progress // : OR-ing this bit with SEN, RSEN, PEN, RCEN or ACKEN will indicate if the MSSPx is in Idle mode. // : (このビットと、SEN、RSEN、PEN、RCEN、またはACKEN との論理和を取ると、マスタモードがアクティブかどうかを判断できる) // bit 1 : UA: Update Address bit (10-bit I2C mode only) // bit 0 : BF: Buffer Full Status bit // : Receive (SPI and I2 C modes): // : 1 = Receive complete, SSPxBUF is full // : 0 = Receive not complete, SSPxBUF is empty // : Transmit (I2 C mode only): // : 1 = Data transmit in progress (does not include the ACK and Stop bits), SSPxBUF is full // : 0 = Data transmit complete (does not include the ACK and Stop bits), SSPxBUF is empty SSP1STAT= 0b00000000; // 400kHz Slew rate // bit7 : WCOL master(1 = Collision, 0 = No Collision), slave(1 = must be cleard, 0 = No Collision) // bit6 : SSP1OV SPI(pass), I2C(1 = overflow, 0 = not Overflow) // bit5 : SSP1EN SPI(pass), I2C(1 = enable SDA/SCL w/input mode, 0 = disable) // bit4 : CKP SPI(pass), I2C(master(1 = enable clock, 0 = hold clock low), slave (unused)) // bit3-0 : SSP1M 0110 I2C Slave mode, 7bit address // : SSP1M 0111 I2C Slave mode, 10bit address // : SSP1M 1000 I2C Master mode, clock = Fosc/(4 * (SSP1ADD + 1)) // : SSP1M 1011 I2C F/W controled Master mode(Slave Idle) // : SSP1M 1110 I2C Slave mode, 7bit address w/Start/Stop bit INT // : SSP1M 1111 I2C Slave mode, 10bit address w/Start/Stop bit INT SSP1CON1= 0b00101000; // No Col, Not Overflow, enable SDA/SCL w/input mode, hold clock low, Master mode, clock = Fosc/(4 * (SSP1ADD + 1)) // SSP1CON2bits.SEN = 1 は、実際にマスターから何かを送信するときに設定するので初期設定時には不要 // SSP1ADD はマニュアルを見て設定すること、FOSC=16MHzで100kHzなら27h、400kHzなら09h。ただし400kHzはこのPICでは厳密には準拠していないらしい…Σ(゚Д゚;エーッ! SSP1ADD = 0x27; // クロック=FOSC/((SSPADD + 1)*4) 8MHz/((0x13+1)*4)=0.1(100KHz) // MSSP1割り込みフラグ初期化 PIR1bits.SSP1IF = 0; // MSSP1バス衝突割り込みフラグを初期化 PIR2bits.BCL1IF = 0; // 100ms待つ Delay_10ms(10); } // ---------------------------------------- // Setup 18F26K22 for Analog Voltage // ---------------------------------------- void init_pic_for_analogvoltage(void) { // ------------------------------ // ポートA設定 // ------------------------------ // bit7 : RA7 1 = input, 0 = output // bit6 : RA6 1 = input, 0 = output // bit5 : RA5 1 = input, 0 = output // bit4 : RA4 1 = input, 0 = output // bit3 : RA3 1 = input, 0 = output // bit2 : RA2 1 = input, 0 = output // bit1 : RA1 1 = input, 0 = output // bit0 : RA0 1 = input, 0 = output TRISA = 0b00001111; // RA0-RA3をを電圧測定用にinputモード // ANSELA: PORTA ANALOG SELECT REGISTER ポートのI/Oモードの設定。 // bit7-6 : none (0) // bit5 : ANSA4: Analog Select between Analog or Digital Function on pins RA5, respectively // 0 = Digital I/O. Pin is assigned to port or digital special function. // 1 = Analog input. Pin is assigned as analog input(1). Digital input buffer disabled. // bit4 : none (0) // bit3 : ANSA3: Analog Select between Analog or Digital Function on pins RA3, respectively // bit2 : ANSA2: Analog Select between Analog or Digital Function on pins RA2, respectively // bit1 : ANSA1: Analog Select between Analog or Digital Function on pins RA1, respectively // bit0 : ANSA0: Analog Select between Analog or Digital Function on pins RA0, respectively ANSELA = 0b00001111; // AN0(=RA0),AN1(=RA1),AN2(=RA2),AN3(=RA3) Analog input // ADCON0 は、実際に電圧を読むときに、そのポートを指定しないといけないのでここではいじらない // bit 7 TRIGSEL: Special Trigger Select bit // 1 = Selects the special trigger from CTMU // 0 = Selects the special trigger from CCP5 // bit 6-4 Unimplemented: Read as ‘0’ // bit 3-2 PVCFG<1:0>: Positive Voltage Reference Configuration bits // 00 = A/D VREF+ connected to internal signal, AVDD // 01 = A/D VREF+ connected to external pin, VREF+ // 10 = A/D VREF+ connected to internal signal, FVR BUF2 // 11 = Reserved (by default, A/D VREF+ connected to internal signal, AVDD) // bit 1-0 NVCFG0<1:0>: Negative Voltage Reference Configuration bits // 00 = A/D VREF- connected to internal signal, AVSS // 01 = A/D VREF- connected to external pin, VREF- // 10 = Reserved (by default, A/D VREF+ connected to internal signal, AVSS) // 11 = Reserved (by default, A/D VREF+ connected to internal signal, AVSS) ADCON1 = 0b00001000; // VREF+はFVRから、VREF-はGNDに対しての値となる // AD変換制御レジスタ2設定 A/D CONTROL REGISTER 2 // bit 7 ADFM: A/D Conversion Result Format Select bit // 1 = Right justified // 0 = Left justified // bit 6 Unimplemented: Read as ‘0’ // bit 5-3 ACQT<2:0>: A/D Acquisition time select bits. Acquisition time is the duration that the A/D charge holding // capacitor remains connected to A/D channel from the instant the GO/DONE bit is set until conversions // begins. // 000 = 0(1) // 001 = 2 TAD // 010 = 4 TAD // 011 = 6 TAD // 100 = 8 TAD // 101 = 12 TAD // 110 = 16 TAD // 111 = 20 TAD // bit 2-0 ADCS<2:0>: A/D Conversion Clock Select bits // 000 = FOSC/2 // 001 = FOSC/8 // 010 = FOSC/32 // 011 = FRC(1) (clock derived from a dedicated internal oscillator = 600 kHz nominal) // 100 = FOSC/4 // 101 = FOSC/16 // 110 = FOSC/64 // 111 = FRC(1) (clock derived from a dedicated internal oscillator = 600 kHz nominal) ADCON2 = 0b10110101; // 右寄せ、16TAD、FOSC/16 // 電圧リファレンス制御レジスタ0 FIXED VOLTAGE REFERENCE CONTROL REGISTER // bit 7 FVREN: Fixed Voltage Reference Enable bit // 0 = Fixed Voltage Reference is disabled // 1 = Fixed Voltage Reference is enabled // bit 6 FVRST: Fixed Voltage Reference Ready Flag bit // 0 = Fixed Voltage Reference output is not ready or not enabled // 1 = Fixed Voltage Reference output is ready for use // bit 5-4 FVRS<1:0>: Fixed Voltage Reference Selection bits // 00 = Fixed Voltage Reference Peripheral output is off // 01 = Fixed Voltage Reference Peripheral output is 1x (1.024V) // 10 = Fixed Voltage Reference Peripheral output is 2x (2.048V)(1) // 11 = Fixed Voltage Reference Peripheral output is 4x (4.096V)(1) // bit 3-2 Reserved: Read as ‘0’. Maintain these bits clear. // bit 1-0 Unimplemented: Read as ‘0’. VREFCON0 = 0b10110000; // FVR有効、x4の4.096Vとする } // ------------------------------ // Interrupt Routine // ------------------------------ // I2Cのマスターとスレーブとのやり取りは、データシートの説明が言葉足らずなために // 非常に判りづらい。ただし、判ってしまうと何だそれだけか、となる。 // // 特にスレーブ側であれこれ設定したり判定や処理に必要なのは次のビット // ・D/A…1=SSP1BUFの中はデータ、0=SSP1BUFの中はアドレス(空っぽ…空の読み出し要求時) // ・R/W…1=マスターがスレーブから受信、0=マスターがスレーブへ送信 // ・BF…1=バッファに何か入っている(空の読み出し要求時も)、0=バッファは空っぽ // ・CKP…マスターにデータ送信を許可するとき、スレーブから送信するときに1にしてSCLをリリースする // ・SSP1IF…割り込みフラグ、なんかしたらクリアする // ・SEN…マスターからデータを受信する時に、ソフト側でCKPを制御するために1にする。0だとうまく動かないよ!! //(・S…スタートビット、特に使わなくてもいい気がする) // // で、判定に使うビットが多いけど、基本的にはSSP1STATなので、マスクして一括判定すればOK。 // -------------------------------------------------- // 18F26K22 Interrupt Routine // -------------------------------------------------- // 割り込みはすべてinterrupt宣言されたこの関数が呼ばれる static void interrupt interrupt_18F26K22() { // 全割り込みを禁止(=0) (Global Interrupt Enable bit ... INTCON) INTCONbits.GIE = 0; // ---------------------------------------- // MSSP1割り込み処理 (Rev.1の18F26K22_I2C.cを参照すること) // ---------------------------------------- // マスターコントローラーは、MSSP割り込みはエラーか複数マスターコントローラーでもなければ発生しない // MSSP割り込み(=1)なら (Synchronous Serial Port (MSSP) Interrupt Flag bit ... PIR1) if (PIR1bits.SSP1IF == 1) { // ステータスLEDを点灯 PORT_STATUS_LED = LED_ON; // MSSP割り込みクリア(=0) (Synchronous Serial Port (MSSP) Interrupt Flag bit ... PIR1) PIR1bits.SSP1IF = 0; // SSP1ステータスを取得、D/A、R/W、BFビットをマスク reg_SSP1STAT = SSP1STAT & 0b00100101; // SSP1ステータスが、アドレス(D/A=0)で、かつマスターがスレーブへ送信(R/W=0)、かつバッファに何かある(BF=1)なら if (reg_SSP1STAT == 0b00000001) { } // SSP1ステータスが、データ(D/A=1)で、かつマスターがスレーブへ送信(R/W=0)、かつバッファに何かある(BF=1)なら else if (reg_SSP1STAT == 0b00100001) { } // アドレス(D/A=0)で、かつマスターがスレーブから受信(R/W=1)、かつバッファに何かある(BF=1)な else if (reg_SSP1STAT == 0b00000101) { } // データ(D/A=1)で、かつマスターがスレーブから受信(R/W=1)、かつバッファが空(BF=0)なら else if (reg_SSP1STAT == 0b00100100) { } // これら以外は else { } // ステータスLEDを消灯 PORT_STATUS_LED = LED_OFF; } // ---------------------------------------- // シリアル1受信割り込み処理 // ---------------------------------------- // シリアル1受信割り込み(=1)なら (RC1IF: EUSART1 Receive Interrupt Flag bit ... INTCON) // 1 = The EUSART1 receive buffer, RCREG1, is full (cleared when RCREG1 is read) // 0 = The EUSART1 receive buffer is empty if (PIR1bits.RC1IF == 1) { // 受信ステータスを取得 reg_RCSTA1 = RCSTA1; // 受信ステータスを確認してフレーミングエラー、オーバーランエラーがなければ if ((reg_RCSTA1 & 0b00000110) == 0b00000000) { // 受信バッファ(RCREG1)から1バイト取得 serial_rcvbuff[serial_rcvptr] = RCREG1; // 受信バッファポインタを加算(+1) serial_rcvptr ++; // 受信バッファポインタをリングる serial_rcvptr &= serial_rcvbuffring; } // 受信ステータスを確認してオーバーランエラーがあるなら else if (reg_RCSTA1 & 0b00000010) { // CREN レシーバイネーブルビットをクリアして再設定 RCSTA1bits.CREN = 0; RCSTA1bits.CREN = 1; } else { // 受信バッファ(RCREG1)から1バイト取得 serial_rcvbuff[serial_rcvptr] = RCREG1; } // シリアル1受信割り込みクリア(=0)…は1バイト受信すれば自動的にクリアされるので必要ない //PIR1bits.RC1IF = 0; // ほんとはここで、受信バッファポインタserial_rcvptrがリングバッファを上書きしないように // するとか、あわせてフロー制御するとか、いろいろあるけど、今回はとりあえずほっとく } // 全割り込みを許可(=1) (Global Interrupt Enable bit ... INTCON) INTCONbits.GIE = 1; } // -------------------------------------------------- // Module Routine // -------------------------------------------------- // ------------------------------ // Get GVC TEMPERATURE DATA // ------------------------------ int get_gvc_temp_data(char i2c_addr, GVC_I2C_MESSAGE_t * gvc_i2c_message) { // 現状はマスターコントローラーが自動的にモジュールデータを要求するので、ここでformat, cmd等を設定する // gvcdから要求される場合には、set_gvc_switch_onoff()のようにそのまま流用すればよい // 温度を要求するメッセージを生成 gvc_i2c_message->format = 0x01; // 現在の温度(摂氏)と湿度を、文字列で小数点表記の実数16文字で、その他Vref、分解能、もTAB区切りで gvc_i2c_message->cmd = 0x11; // データ要求(モジュールに搭載している1番目のデバイスのデータを取得、温度と湿度は同じモジュールなので、温度は0x11、湿度は0x12で取得) gvc_i2c_message->data_len = 0; // データはないので0を設定 // CRCを計算して、メッセージの最後に設定 gvc_i2c_message->data[gvc_i2c_message->data_len] = GetCRC8((void *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len); // ↑データを送信する側は、データのCRCを計算したあとで // そのCRCをデータの最後に連結して相手に送ればいい // I2C でメッセージ送信 i2c_write(i2c_addr, (char *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len + 1); // スレーブ側が上のメッセージを受けた時点で返すべきデータを生成するようにしたなら、ここで待たなくていい // 逆に、スレーブ側が返すべきデータの生成に時間を要するようなら、少し待ったり、できたかどうかポーリングするなどの処理が必要 // I2Cからメッセージ受信(実際のデータ長はdata_lenに入っているが、I2Cの場合には受信バイト数を指定しないといけないのでGVC_MSG_MINだけ受信する) i2c_read(i2c_addr, (char *)gvc_i2c_message, GVC_MSG_MIN); // ↓で、受けた側はCRCまで含めてCRC計算してみると // 正しければCRCの結果が0になる。 // CRCの確認(format + cmd + data_len + data[] + checksum、をCRCする)でOKなら if (GetCRC8((void *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len + 1) == 0) { // 戻る return; } // CRCエラーなら else { // エラーメッセージを生成 sprintf(gvc_i2c_message->data, "SLAVE MESSAGE CRC ERROR!? data_len=%d", gvc_i2c_message->data_len); // メッセージ長を設定 gvc_i2c_message->data_len = strlen(gvc_i2c_message->data); } // 戻る return; } // ------------------------------ // Get GVC HUMIDITY DATA // ------------------------------ int get_gvc_humi_data(char i2c_addr, GVC_I2C_MESSAGE_t * gvc_i2c_message) { // 現状はマスターコントローラーが自動的にモジュールデータを要求するので、ここでformat, cmd等を設定する // gvcdから要求される場合には、set_gvc_switch_onoff()のようにそのまま流用すればよい // 温度を要求するメッセージを生成 gvc_i2c_message->format = 0x11; // 現在の温度(摂氏)と湿度を、文字列で小数点表記の実数16文字で、その他Vref、分解能、もTAB区切りで gvc_i2c_message->cmd = 0x12; // データ要求(モジュールに搭載している2番目のデバイスのデータを取得、温度と湿度は同じモジュールなので、温度は0x11、湿度は0x12で取得) gvc_i2c_message->data_len = 0; // データはないので0を設定 // CRCを計算して、メッセージの最後に設定 gvc_i2c_message->data[gvc_i2c_message->data_len] = GetCRC8((void *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len); // ↑データを送信する側は、データのCRCを計算したあとで // そのCRCをデータの最後に連結して相手に送ればいい // I2C でメッセージ送信 i2c_write(i2c_addr, (char *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len + 1); // スレーブ側が上のメッセージを受けた時点で返すべきデータを生成するようにしたなら、ここで待たなくていい // 逆に、スレーブ側が返すべきデータの生成に時間を要するようなら、少し待ったり、できたかどうかポーリングするなどの処理が必要 // I2Cからメッセージ受信(実際のデータ長はdata_lenに入っているが、I2Cの場合には受信バイト数を指定しないといけないのでGVC_MSG_MINだけ受信する) i2c_read(i2c_addr, (char *)gvc_i2c_message, GVC_MSG_MIN); // ↓で、受けた側はCRCまで含めてCRC計算してみると // 正しければCRCの結果が0になる。 // CRCの確認(format + cmd + data_len + data[] + checksum、をCRCする)でOKなら if (GetCRC8((void *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len + 1) == 0) { // 戻る return; } // CRCエラーなら else { // エラーメッセージを生成 sprintf(gvc_i2c_message->data, "SLAVE MESSAGE CRC ERROR!? data_len=%d", gvc_i2c_message->data_len); // メッセージ長を設定 gvc_i2c_message->data_len = strlen(gvc_i2c_message->data); } // 戻る return; } // ------------------------------ // Get GVC DISTANCE DATA // ------------------------------ int get_gvc_dist_data(char i2c_addr, GVC_I2C_MESSAGE_t * gvc_i2c_message) { // 現状はマスターコントローラーが自動的にモジュールデータを要求するので、ここでformat, cmd等を設定する // gvcdから要求される場合には、set_gvc_switch_onoff()のようにそのまま流用すればよい // 温度を要求するメッセージを生成 gvc_i2c_message->format = 0x06; // とりあえず(TBD) gvc_i2c_message->cmd = 0x11; // データ要求コマンド(モジュールに搭載している1番目のデバイスのデータを取得) gvc_i2c_message->data_len = 0; // データはないので0を設定 // CRCを計算して、メッセージの最後に設定 gvc_i2c_message->data[gvc_i2c_message->data_len] = GetCRC8((void *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len); // ↑データを送信する側は、データのCRCを計算したあとで // そのCRCをデータの最後に連結して相手に送ればいい // I2C でメッセージ送信 i2c_write(i2c_addr, (char *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len + 1); // スレーブ側が上のメッセージを受けた時点で返すべきデータを生成するようにしたなら、ここで待たなくていい // 逆に、スレーブ側が返すべきデータの生成に時間を要するようなら、少し待ったり、できたかどうかポーリングするなどの処理が必要 // I2Cからメッセージ受信(実際のデータ長はdata_lenに入っているが、I2Cの場合には受信バイト数を指定しないといけないのでGVC_MSG_MINだけ受信する) i2c_read(i2c_addr, (char *)gvc_i2c_message, GVC_MSG_MIN); // ↓で、受けた側はCRCまで含めてCRC計算してみると // 正しければCRCの結果が0になる。 // CRCの確認(format + cmd + data_len + data長 + checksum、をCRCする)でOKなら if (GetCRC8((void *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len + 1) == 0) { // 戻る return; } // CRCエラーなら else { // エラーメッセージを生成 sprintf(gvc_i2c_message->data, "SLAVE MESSAGE CRC ERROR!? data_len=%d", gvc_i2c_message->data_len); // メッセージ長を設定 gvc_i2c_message->data_len = strlen(gvc_i2c_message->data); } // 戻る return; } // ------------------------------ // Get GVC PIR DATA // ------------------------------ int get_gvc_pir_data(char i2c_addr, GVC_I2C_MESSAGE_t * gvc_i2c_message) { // 現状はマスターコントローラーが自動的にモジュールデータを要求するので、ここでformat, cmd等を設定する // gvcdから要求される場合には、set_gvc_switch_onoff()のようにそのまま流用すればよい // 温度を要求するメッセージを生成 gvc_i2c_message->format = 0x02; // 現在の焦電センサーの経過(サンプリング間隔と検知カウント数を要求) gvc_i2c_message->cmd = 0x11; // データ要求コマンド(モジュールに搭載している1番目のデバイスのデータを取得) gvc_i2c_message->data_len = 0; // データはないので0を設定 // CRCを計算して、メッセージの最後に設定 gvc_i2c_message->data[gvc_i2c_message->data_len] = GetCRC8((void *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len); // ↑データを送信する側は、データのCRCを計算したあとで // そのCRCをデータの最後に連結して相手に送ればいい // I2C でメッセージ送信 i2c_write(i2c_addr, (char *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len + 1); // スレーブ側が上のメッセージを受けた時点で返すべきデータを生成するようにしたなら、ここで待たなくていい // 逆に、スレーブ側が返すべきデータの生成に時間を要するようなら、少し待ったり、できたかどうかポーリングするなどの処理が必要 // I2Cからメッセージ受信(実際のデータ長はdata_lenに入っているが、I2Cの場合には受信バイト数を指定しないといけないのでGVC_MSG_MINだけ受信する) i2c_read(i2c_addr, (char *)gvc_i2c_message, GVC_MSG_MIN); // ↓で、受けた側はCRCまで含めてCRC計算してみると // 正しければCRCの結果が0になる。 // CRCの確認(format + cmd + data_len + data長 + checksum、をCRCする)でOKなら if (GetCRC8((void *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len + 1) == 0) { // 戻る return; } // CRCエラーなら else { // エラーメッセージを生成 sprintf(gvc_i2c_message->data, "SLAVE MESSAGE CRC ERROR!? data_len=%d", gvc_i2c_message->data_len); // メッセージ長を設定 gvc_i2c_message->data_len = strlen(gvc_i2c_message->data); } // 戻る return; } // ------------------------------ // Set GVC SWITCH ON/OFF // ------------------------------ int set_gvc_switch_onoff(char i2c_addr, GVC_I2C_MESSAGE_t * gvc_i2c_message) { // スイッチのON/OFFを要求するメッセージを生成…というよりはそのまま使う // これは、もともとの要求メッセージがgvcdからくるのでそれを流用すればいいから // 他の温度とか湿度とか、マスターコントローラー発のメッセージの場合は流用するものが無いのでマスターコントローラー内で生成する必要がある /// gvc_i2c_message->format = 0x00; // NOT USE /// gvc_i2c_message->cmd = cmd; // ON/OFF COMMAND /// gvc_i2c_message->data_len = 0; // データはないので0を設定 // CRCを計算して、メッセージの最後に設定 gvc_i2c_message->data[gvc_i2c_message->data_len] = GetCRC8((void *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len); // ↑データを送信する側は、データのCRCを計算したあとで // そのCRCをデータの最後に連結して相手に送ればいい // I2C でメッセージ送信 i2c_write(i2c_addr, (char *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len + 1); // 戻る return; } // ------------------------------ // Get GVC SWITCH STATUS // ------------------------------ int get_gvc_switch_status(char i2c_addr, GVC_I2C_MESSAGE_t * gvc_i2c_message) { // スイッチのON/OFF状態を要求するメッセージを生成…というよりは、指定されたi2c_addrのモジュールの状態をそのまま取得するだけ // I2Cからメッセージ受信(実際のデータ長はdata_lenに入っているが、I2Cの場合には受信バイト数を指定しないといけないのでGVC_MSG_MINだけ受信する) i2c_read(i2c_addr, (char *)gvc_i2c_message, GVC_MSG_MIN); // ↓で、受けた側はCRCまで含めてCRC計算してみると // 正しければCRCの結果が0になる。 // CRCの確認(format + cmd + data_len + data長 + checksum、をCRCする)でOKなら if (GetCRC8((void *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len + 1) == 0) { // 戻る return; } // CRCエラーなら else { // エラーメッセージを生成 sprintf(gvc_i2c_message->data, "SLAVE MESSAGE CRC ERROR!? data_len=%d", gvc_i2c_message->data_len); // メッセージ長を設定 gvc_i2c_message->data_len = strlen(gvc_i2c_message->data); } // 戻る return; } // ------------------------------ // Set GVC IR TXRX // ------------------------------ int set_gvc_ir_txrx(char i2c_addr, GVC_I2C_MESSAGE_t * gvc_i2c_message) { // IRの送信/受信を要求するメッセージを生成…というよりはそのまま使う // これは、もともとの要求メッセージがgvcdからくるのでそれを流用すればいいから // 他の温度とか湿度とか、マスターコントローラー発のメッセージの場合は流用するものが無いのでマスターコントローラー内で生成する必要がある /// gvc_i2c_message->format = 0x00; // NOT USE /// gvc_i2c_message->cmd = cmd; // ON/OFF COMMAND /// gvc_i2c_message->data_len = 0; // データはないので0を設定 // CRCを計算して、メッセージの最後に設定 gvc_i2c_message->data[gvc_i2c_message->data_len] = GetCRC8((void *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len); // ↑データを送信する側は、データのCRCを計算したあとで // そのCRCをデータの最後に連結して相手に送ればいい // I2C でメッセージ送信 i2c_write(i2c_addr, (char *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len + 1); // 戻る return; } // ------------------------------ // Get GVC IR STATUS // ------------------------------ int get_gvc_ir_status(char i2c_addr, GVC_I2C_MESSAGE_t * gvc_i2c_message) { // 赤外線データを受信する場合には、もともとのgvc_i2c_messageがmain_buffになっていてGVC_MSG_MAXだけ受信しないといけないョ!! 今はとりあえずGVC_MSG_MINにしとく 2013.07.16 T.Kabu // I2Cからメッセージ受信(実際のデータ長はdata_lenに入っているが、I2Cの場合には受信バイト数を指定しないといけないのでGVC_MSG_MINだけ受信する) // i2c_read(i2c_addr, (char *)gvc_i2c_message, GVC_MSG_MIN); // GVC_MSG_MAXだけ受信するようにしてみた i2c_read(i2c_addr, (char *)gvc_i2c_message, GVC_MSG_MAX); // ↓で、受けた側はCRCまで含めてCRC計算してみると // 正しければCRCの結果が0になる。 // CRCの確認(format + cmd + data_len + data[] + checksum、をCRCする)でOKなら if (GetCRC8((void *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len + 1) == 0) { // 戻る return; } // CRCエラーなら else { // データフォーマットを、モジュール情報(通常テキストフォーマット)に変更 gvc_i2c_message->format = 0x81; // エラーメッセージを生成 sprintf(gvc_i2c_message->data, "SLAVE MESSAGE CRC ERROR!? data_len=%d", gvc_i2c_message->data_len); // メッセージ長を設定 gvc_i2c_message->data_len = strlen(gvc_i2c_message->data); } // 戻る return; } // ------------------------------ // Set GVC IR DATA // ------------------------------ int set_gvc_ir_data(char i2c_addr, GVC_I2C_MESSAGE_t * gvc_i2c_message) { // IRの送信/受信を要求するメッセージを生成…というよりはそのまま使う // これは、もともとの要求メッセージがgvcdからくるのでそれを流用すればいいから // 他の温度とか湿度とか、マスターコントローラー発のメッセージの場合は流用するものが無いのでマスターコントローラー内で生成する必要がある /// gvc_i2c_message->format = 0x00; // NOT USE /// gvc_i2c_message->cmd = cmd; // ON/OFF COMMAND /// gvc_i2c_message->data_len = 0; // データはないので0を設定 // CRCを計算して、メッセージの最後に設定 gvc_i2c_message->data[gvc_i2c_message->data_len] = GetCRC8((void *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len); // ↑データを送信する側は、データのCRCを計算したあとで // そのCRCをデータの最後に連結して相手に送ればいい // I2C でメッセージ送信 i2c_write(i2c_addr, (char *)gvc_i2c_message, GVC_I2C_MESSAGE_HEADER_SIZE + gvc_i2c_message->data_len + 1); // 戻る return; } // ------------------------------ // Setup MPL115A2 (Atmospheric pressure Sensor) // ------------------------------ void setup_MPL115A2(void) { // MPL115A2は、それ自体がI2Cのスレーブデバイスとして動作するので、他のGVCスレーブモジュールとは // 扱い方が異なる。 // http://www.ne.jp/asahi/shared/o-family/ElecRoom/AVRMCOM/MPL115A2cnt/MPL115A2cnt.html // http://nontomos.web.fc2.com/arduino03/arduino03.html // http://plaza.rakuten.co.jp/odorie/diary/201012080001/ short int paramMSB; short int paramLSB; // MPL115A2係数を要求するメッセージを生成 subbuffer[0] = 0x04; // I2C でメッセージ送信 i2c_write(MPL115A2_ADDRESS, subbuffer, 1); // MPL115A2から係数12バイトを受信する i2c_read(MPL115A2_ADDRESS, (char *)subbuffer, 12); // 係数a0の上位、および下位バイト取得&係数a0(およびlong値)に設定 paramMSB = subbuffer[0]; // 上位1バイト受信 paramLSB = subbuffer[1]; // 下位1バイト受信 sia0 = paramMSB << 8; // 上位バイトなので8ビットシフトして設定 sia0 |= paramLSB & 0x00FF; // 下位バイトをマージ sia0l = sia0; // 係数a0(long値)にコピー // 係数b1の上位、および下位バイト取得&係数b1(およびlong値)に設定 paramMSB = subbuffer[2]; // 上位1バイト受信 paramLSB = subbuffer[3]; // 下位1バイト受信 sib1 = paramMSB << 8; // 上位バイトなので8ビットシフトして設定 sib1 |= paramLSB & 0x00FF; // 下位バイトをマージ sib1l = sib1; // 係数b1(long値)にコピー // 係数b2の上位、および下位バイト取得&係数b2(およびlong値)に設定 paramMSB = subbuffer[4]; // 上位1バイト受信 paramLSB = subbuffer[5]; // 下位1バイト受信 sib2 = paramMSB << 8; // 上位バイトなので8ビットシフトして設定 sib2 |= paramLSB & 0x00FF; // 下位バイトをマージ sib2 &= 0xFFFF; // マスク(なんでだろ?) sib2l = sib2; // 係数b2(long値)にコピー // 係数c12の上位、および下位バイト取得&係数c12(およびlong値)に設定 paramMSB = subbuffer[6]; // 上位1バイト受信 paramLSB = subbuffer[7]; // 下位1バイト受信 sic12 = paramMSB << 8; // 上位バイトなので8ビットシフトして設定 sic12 |= paramLSB & 0x00FF; // 下位バイトをマージ sic12l = sic12; // 係数c12(long値)にコピー // 係数c11の上位、および下位バイト取得&係数c11(およびlong値)に設定 paramMSB = subbuffer[8]; // 上位1バイト受信 paramLSB = subbuffer[9]; // 下位1バイト受信 sic11 = paramMSB << 8; // 上位バイトなので8ビットシフトして設定 sic11 |= paramLSB & 0x00FF; // 下位バイトをマージ sic11l = sic11; // 係数c11(long値)にコピー // 係数c22の上位、および下位バイト取得&係数c22(およびlong値)に設定 paramMSB = subbuffer[10]; // 上位1バイト受信 paramLSB = subbuffer[11]; // 下位1バイト受信 sic22 = paramMSB << 8; // 上位バイトなので8ビットシフトして設定 sic22 |= paramLSB & 0x00FF; // 下位バイトをマージ sic22l = sic22; // 係数c22(long値)にコピー } // ------------------------------ // Get MPL115A2 (Atmospheric pressure Sensor) // ------------------------------ float get_MPL115A2(void) { short int paramMSB; short int paramLSB; long int uiPadc; long int uiTadc; float a1; float a2; float pcomp; float hPa; // MPL115A2は、それ自体がI2Cのスレーブデバイスとして動作するので、他のGVCスレーブモジュールとは // 扱い方が異なる。 // http://www.ne.jp/asahi/shared/o-family/ElecRoom/AVRMCOM/MPL115A2cnt/MPL115A2cnt.html // http://nontomos.web.fc2.com/arduino03/arduino03.html // http://plaza.rakuten.co.jp/odorie/diary/201012080001/ // MPL115A2に大気圧、温度の計算開始命令を送信 subbuffer[0] = 0x12; subbuffer[1] = 0x01; // I2C でメッセージ送信 i2c_write(MPL115A2_ADDRESS, subbuffer, 2); // 大気圧の計算を待つ Delay_10ms(5); // MPL115A2に大気圧の要求命令(0x00)を送信 subbuffer[0] = 0x00; // I2C でメッセージ送信 i2c_write(MPL115A2_ADDRESS, subbuffer, 1); // MPL115A2から大気圧と温度の4バイトを受信する i2c_read(MPL115A2_ADDRESS, (char *)subbuffer, 4); // 大気圧の上位、および下位バイト取得&大気圧(uiPadc)に設定 paramMSB = subbuffer[0]; // 上位1バイト受信 paramLSB = subbuffer[1]; // 下位1バイト受信 uiPadc = paramMSB << 8; // 上位バイトなので8ビットシフト uiPadc |= paramLSB; // 下位バイトをマージ uiPadc &= 0xFFFF; // long値なので、必要な16ビット分だけマスクして uiPadc >>= 6; // 本来の値が入っている10ビットだけを得るために6ビットシフトして捨てる // 温度の上位、および下位バイト取得&温度(uiTadc)に設定 paramMSB = subbuffer[2]; // 上位1バイト受信 paramLSB = subbuffer[3]; // 下位1バイト受信 uiTadc = paramMSB << 8; // 上位バイトなので8ビットシフト uiTadc |= paramLSB; // 下位バイトをマージ uiTadc &= 0xFFFF; // long値なので、必要な16ビット分だけマスクして uiTadc >>= 6; // 本来の値が入っている10ビットだけを得るために6ビットシフトして捨てる // 大気圧計算 a1 = sib1l; a1 += (sic11l * uiPadc * 0.00006103515); a1 += (sic12l * uiTadc * 0.00048828125); a2 = (sib2l * 0.5); a2 += (sic22l * uiTadc * 0.00003051757); pcomp = sia0l * 0.125; pcomp += a1 * uiPadc * 0.00012207031; pcomp += a2 * uiTadc * 0.00012207031; hPa = 0.63538611925 * pcomp + 500.0; // 大気圧(hPa)を算出 return hPa; // 大気圧を返して戻る } // ------------------------------ // Get I2C Slave Module Info // ------------------------------ void get_module_info(void) { char slave_addr; // I2Cスレーブアドレス // ステータスLEDを点灯 PORT_STATUS_LED = LED_ON; // バッファを初期化 memset((void *)i2c_slavetbl, 0x00, sizeof(i2c_slavetbl)); // テーブル0番はマスターとして設定 i2c_slavetbl[0] = 'M'; // I2C スレーブデバイスをスキャン for (slave_addr = 1; slave_addr < 0x7f; slave_addr ++) { // リザルトLEDを点灯 PORT_RESULT_LED = LED_ON; // スレーブ接続のGVCモジュールのモジュールタイプを一覧テーブルに設定 i2c_slavetbl[slave_addr] = i2c_slave_check(slave_addr); // リザルトLEDを消灯 PORT_RESULT_LED = LED_OFF; } // ステータスLEDを消灯 PORT_STATUS_LED = LED_OFF; } // -------------------------------------------------- // Main loop // -------------------------------------------------- void main(void) { char work_mode = 1; // 動作モード(1:通常、0:停止状態) char wait_count; // コマンド待ちカウンタ int rx_pos = 0; // 受信データの位置 int rx_data_len = 0; // 受信データの長さ int check_char; // 受信バッファのチェック用ポインタ int rx_remainder; // 受信バッファの残りの先頭位置 int rx_remainder_len = 0; // 受信バッファの残りの長さ int ftoa_status; // ftoa()用のステータス変数 // ---------------------------------------- // リセットする場合にここに飛ぶ // ---------------------------------------- RESET_JOB: // Setup 18F26K22 init_pic_18F26K22(); // Setup MSSP1 18F26K22 init_mssp1_18F26K22(); // Setup EUSART 18F26K22 init_eusart_18F26K22(); // LED Brink led_brink(1); // ---------------------------------------- // これより下に、個別の設定を記述 // ---------------------------------------- // デリミタデータにCRCをつける GVC_DELIMITER_MSG[ strlen(GVC_DELIMITER_MSG) ] = GetCRC8((void *)GVC_DELIMITER_MSG, strlen(GVC_DELIMITER_MSG)); // Setup 18F26K22 for Analog Voltage init_pic_for_analogvoltage(); // Get Vdd Voltage vdd_volt = get_vdd(); // もしVddが4.0Vより大きいとか、1.0V未満なら if (vdd_volt > 4.0 || vdd_volt < 1.0) { // たぶんちゃんと測定できていないってことで、5.00に固定 vdd_volt = 5.0; } TRISA = 0b00000111; // RA3をoutputモードにして切り離す // 動作モードを通常に設定 work_mode = 1; // ---------------------------------------- // 個別設定終了 // ---------------------------------------- // LED Brink led_brink(2); // 受信バッファポインタを初期化 serial_rcvptr = 0; // 受信読み出しポインタを初期化 serial_readptr = 0; // メインバッファ/サブバッファを初期化 memset((void *)mainbuffer, 0x00, sizeof(mainbuffer)); memset((void *)i2c_slavetbl, 0x00, sizeof(i2c_slavetbl)); // EUSART1 RX 割り込み許可(=1) (EUSART1 Receive Interrupt Enable bit ... PIE1) PIE1bits.RC1IE = 1; // MSSP割り込み許可(=1) (Synchronous Serial Port (MSSP) Interrupt Enable bit ... PIE1) PIE1bits.SSP1IE = 1; // 周辺割り込み許可(=1) (Peripheral Interrupt Enable bit ... INTCON) INTCONbits.PEIE = 1; // 全割り込みを許可(=1) (Global Interrupt Enable bit ... INTCON) INTCONbits.GIE = 1; // ------------------------------ // I2C モジュールスキャン // ------------------------------ get_module_info(); // もし大気圧センサー(MPL115A2)がないなら if (i2c_slavetbl[MPL115A2_ADDRESS] == 'x') { // 何もしない } // あるなら else { // Setup MPL115A2 (Atmospheric pressure Sensor) setup_MPL115A2(); } // デリミタフレーム送信 send_delimiterframe(); // サブバッファにバージョン情報を設定 sprintf(subbuffer, "%s, work_mode=%d, wait_count=%d, Vdd=%s", VERSION, work_mode, wait_count, ftoa(vdd_volt, &ftoa_status)); // デーモンに対してメッセージ送信 send_gvc_other_message(gvc_serial_message->dev_num, strlen(subbuffer), subbuffer); // LED Brink led_brink(3); // ------------------------------ // メインループ // ------------------------------ while(1) { // 受信データポインタと受信データの長さを初期化 rx_pos = 0; rx_data_len = 0; // デリミタフレーム送信 send_delimiterframe(); // デリミタフレーム送信後、デーモンからのコマンドを約1秒待つ for (wait_count = 0; wait_count < 10; wait_count ++) { // -------------------------------- // データ受信ループ // -------------------------------- while (1) { // シリアルバッファに未読データがないなら if (serial_rcvptr == serial_readptr) { // ループを抜ける break; } // データがあるなら else { // 受信バッファから一文字取得 mainbuffer[rx_pos] = rcv_serialdata(); // 受信データポインタと受信データの長さを加算 rx_pos ++; rx_data_len ++; } } // -------------------------------- // デーモンからのメッセージ確認 // -------------------------------- // rx_data_lenが最小メッセージサイズ以上の長さがあるなら if (rx_data_len >= GVC_MIN_MESSAGE_LENGTH) { // 0からrx_len-GVC_MIN_MESSAGE_LENGTHの中に有効なメッセージタイプがあるかどうかスキャン for (check_char = 0; check_char <= (rx_data_len - GVC_MIN_MESSAGE_LENGTH); check_char ++) { // マスターコントローラーへのコマンド送信があったら、 if (mainbuffer[check_char] == 0x05) { // メインバッファにGVC_SERIAL_MESSAGE_tをかぶせる gvc_serial_message = (GVC_SERIAL_MESSAGE_t *)(mainbuffer + check_char); // // 受信バッファ内にCRCまで含めたメッセージフレームが収まっていて、かつCRCチェックしてOKなら if ( ( (rx_data_len - check_char) >= (GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len + 1) ) && GetCRC8((void *)gvc_serial_message, (GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len + 1)) == 0 ) { // あまったデータの先頭位置を設定 rx_remainder = check_char + (GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len + 1); // あまったデータを計算 rx_remainder_len = rx_data_len - rx_remainder; // コマンドに基づいて処理 … この辺、もっとコマンドが増えてきたら関数の配列をコールする形にしたいが、そこまでのメモリはないか :-) switch (gvc_serial_message->cmd) { case 0x01 : // 0x01:バージョン要求 // サブバッファにバージョン情報を設定 sprintf(subbuffer, "%s, work_mode=%d, wait_count=%d, Vdd=%s", VERSION, work_mode, wait_count, ftoa(vdd_volt, &ftoa_status)); // デーモンに対してメッセージ送信 send_gvc_other_message(gvc_serial_message->dev_num, strlen(subbuffer), subbuffer); break; case 0x02 : // 0x02:モジュール一覧要求 // I2C モジュールスキャン(再スキャンするだけでリセット動作ではない) get_module_info(); // デーモンに対してメッセージ送信 send_gvc_other_message(gvc_serial_message->dev_num, sizeof(i2c_slavetbl), i2c_slavetbl); break; case 0x03 : // 0x03:モジュールデータ要求 /* case 0x10 : // 0x10:モジュールデータ要求(モジュールに搭載している1番目のデバイスのデータを取得) case 0x11 : // 0x10:モジュールデータ要求(モジュールに搭載している1番目のデバイスのデータを取得) case 0x12 : // 0x10:モジュールデータ要求(モジュールに搭載している2番目のデバイスのデータを取得) case 0x13 : // 0x10:モジュールデータ要求(モジュールに搭載している3番目のデバイスのデータを取得) case 0x14 : // 0x10:モジュールデータ要求(モジュールに搭載している4番目のデバイスのデータを取得) case 0x15 : // 0x10:モジュールデータ要求(モジュールに搭載している5番目のデバイスのデータを取得) case 0x16 : // 0x10:モジュールデータ要求(モジュールに搭載している6番目のデバイスのデータを取得) case 0x17 : // 0x10:モジュールデータ要求(モジュールに搭載している7番目のデバイスのデータを取得) case 0x18 : // 0x10:モジュールデータ要求(モジュールに搭載している8番目のデバイスのデータを取得) case 0x19 : // 0x10:モジュールデータ要求(モジュールに搭載している9番目のデバイスのデータを取得) case 0x1A : // 0x10:モジュールデータ要求(モジュールに搭載している10番目のデバイスのデータを取得) case 0x1B : // 0x10:モジュールデータ要求(モジュールに搭載している11番目のデバイスのデータを取得) case 0x1C : // 0x10:モジュールデータ要求(モジュールに搭載している12番目のデバイスのデータを取得) case 0x1D : // 0x10:モジュールデータ要求(モジュールに搭載している13番目のデバイスのデータを取得) case 0x1E : // 0x10:モジュールデータ要求(モジュールに搭載している14番目のデバイスのデータを取得) case 0x1F : // 0x10:モジュールデータ要求(モジュールに搭載している全てのデバイスのデータを取得) // この処理は現状マスターコントローラーが自動的にやっているので実装しない T.Kabu // サブバッファにバージョン情報を設定 sprintf(subbuffer, "Sorry, now doing autopilot. :-)"); // デーモンに対してメッセージ送信 send_gvc_other_message(gvc_serial_message->dev_num, strlen(subbuffer), subbuffer); */ break; case 0x20 : // 0x20:スイッチOFF要求 case 0x21 : // 0x21:スイッチON要求 // 対象スレーブモジュールに対してスイッチON/OFF要求 set_gvc_switch_onoff(gvc_serial_message->dev_num, (GVC_I2C_MESSAGE_t *)&(gvc_serial_message->format)); // break; しないで、続けて現在のスイッチ状態を返す case 0x2f : // 0x2f:スイッチ状態要求 // サブバッファにコマンドをいったんコピー memcpy(subbuffer, (void *)gvc_serial_message, (GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len)); // サブバッファにGVC_SERIAL_MESSAGE_tをかぶせる gvc_serial_message = (GVC_SERIAL_MESSAGE_t *)subbuffer; // メッセージタイプをとりあえずマスターコントローラーからの情報に変更 gvc_serial_message->msg_type = GVC_MSG_OTHER; // 対象スレーブモジュールのスイッチのON/OFF状態を要求 get_gvc_switch_status(gvc_serial_message->dev_num, (GVC_I2C_MESSAGE_t *)&(gvc_serial_message->format)); // CRCを計算して、メッセージの最後に設定 gvc_serial_message->data[ gvc_serial_message->data_len ] = GetCRC8((void *)gvc_serial_message, GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len); // ↑データを送信する側は、データのCRCを計算したあとで // そのCRCをデータの最後に連結して相手に送ればいい // デーモンに対してメッセージ送信 send_serial((char *)gvc_serial_message, (GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len + 1)); break; case 0x91 : // 0x91:IR送信要求 case 0x92 : // 0x92:IR受信要求 case 0x95 : // 0x95:IR削除要求 // 対象スレーブモジュールに対して要求 set_gvc_ir_txrx(gvc_serial_message->dev_num, (GVC_I2C_MESSAGE_t *)&(gvc_serial_message->format)); // ↓このあと、サブバッファでなくてメインバッファでね? // サブバッファにコマンドをいったんコピー memcpy(subbuffer, (void *)gvc_serial_message, (GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len)); // サブバッファにGVC_SERIAL_MESSAGE_tをかぶせる gvc_serial_message = (GVC_SERIAL_MESSAGE_t *)subbuffer; // メッセージタイプをとりあえずマスターコントローラーからの情報に変更 gvc_serial_message->msg_type = GVC_MSG_OTHER; // 対象スレーブモジュールのステータスを要求 get_gvc_ir_status(gvc_serial_message->dev_num, (GVC_I2C_MESSAGE_t *)&(gvc_serial_message->format)); // CRCを計算して、メッセージの最後に設定 gvc_serial_message->data[ gvc_serial_message->data_len ] = GetCRC8((void *)gvc_serial_message, GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len); // ↑データを送信する側は、データのCRCを計算したあとで // そのCRCをデータの最後に連結して相手に送ればいい // デーモンに対してメッセージ送信 send_serial((char *)gvc_serial_message, (GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len + 1)); break; case 0x93 : // 0x93:IR設定要求 // 赤外線リモコンで設定or取得をする場合、後ろのデータは無視しないといけない(main_bufferを使ってしまうため) // 対象スレーブモジュールに対して要求 set_gvc_ir_data(gvc_serial_message->dev_num, (GVC_I2C_MESSAGE_t *)&(gvc_serial_message->format)); // ↓このあと、サブバッファにコマンドをコピーしてしまうと、メモリリークするのでしない // サブバッファにコマンドをいったんコピー /// memcpy(subbuffer, (void *)gvc_serial_message, (GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len)); // サブバッファにGVC_SERIAL_MESSAGE_tをかぶせる /// gvc_serial_message = (GVC_SERIAL_MESSAGE_t *)subbuffer; // メッセージタイプを赤外線情報に変更…でなくて、ここは単なる設定だからOTHERにしている gvc_serial_message->msg_type = GVC_MSG_OTHER; // ↓ここでデータを受信してしまうと、subbufferの128bytesを大きくメモリリークしてしまうよねぇ? // 対象スレーブモジュールからデータを取得 get_gvc_ir_status(gvc_serial_message->dev_num, (GVC_I2C_MESSAGE_t *)&(gvc_serial_message->format)); // CRCを計算して、メッセージの最後に設定 gvc_serial_message->data[ gvc_serial_message->data_len ] = GetCRC8((void *)gvc_serial_message, GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len); // ↑データを送信する側は、データのCRCを計算したあとで // そのCRCをデータの最後に連結して相手に送ればいい // デーモンに対してメッセージ送信 send_serial((char *)gvc_serial_message, (GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len + 1)); break; case 0x94 : // 0x94:IR読出要求 // 赤外線リモコンで設定or取得をする場合、後ろのデータは無視しないといけない(main_bufferを使ってしまうため) // 対象スレーブモジュールに対してIRデータ取得要求 set_gvc_ir_txrx(gvc_serial_message->dev_num, (GVC_I2C_MESSAGE_t *)&(gvc_serial_message->format)); // ↓このあと、サブバッファに一旦コマンドヘッダを待避させて、メインバッファを再初期化してからデータを受信する // なので、赤外線読出要求に続けて何かコマンドを送らないようにしないといけない(ドキュメント明記すること) // サブバッファにコマンドをいったんコピー memcpy(subbuffer, (void *)gvc_serial_message, (GVC_SERIAL_MESSAGE_HEADER_SIZE)); // サブバッファにGVC_SERIAL_MESSAGE_tをかぶせる gvc_serial_message = (GVC_SERIAL_MESSAGE_t *)subbuffer; // 受信データポインタと受信データの長さを初期化して、デーモンからの残りのデータを破棄 rx_pos = 0; rx_data_len = 0; rx_remainder = 0; rx_remainder_len = 0; // メインバッファを初期化 memset((void *)mainbuffer, 0x00, sizeof(mainbuffer)); // メインバッファにメッセージのヘッダ部分をコピー memcpy(mainbuffer, (void *)gvc_serial_message, GVC_SERIAL_MESSAGE_HEADER_SIZE); // メインバッファにGVC_SERIAL_MESSAGE_tをかぶせる gvc_serial_message = (GVC_SERIAL_MESSAGE_t *)mainbuffer; // メッセージタイプを赤外線情報に変更 gvc_serial_message->msg_type = GVC_MSG_IR; // 対象スレーブモジュールからデータを取得 get_gvc_ir_status(gvc_serial_message->dev_num, (GVC_I2C_MESSAGE_t *)&(gvc_serial_message->format)); // CRCを計算して、メッセージの最後に設定 gvc_serial_message->data[ gvc_serial_message->data_len ] = GetCRC8((void *)gvc_serial_message, GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len); // ↑データを送信する側は、データのCRCを計算したあとで // そのCRCをデータの最後に連結して相手に送ればいい // デーモンに対してメッセージ送信 send_serial((char *)gvc_serial_message, (GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len + 1)); break; case 0x7e : // 0x7e:マスターコントローラーリセット要求 // リセットする goto RESET_JOB; /// break; // breakに意味があるのかな? :-) case 0x7f : // 0x7f:マスターコントローラーストップ要求 // 動作モードを停止状態に設定 work_mode = 0; break; default : // サブバッファに未実装メッセージである旨を設定 sprintf(subbuffer, "%02x, Not implemented", gvc_serial_message->cmd); // デーモンに対してメッセージ送信 send_gvc_other_message(gvc_serial_message->dev_num, strlen(subbuffer), subbuffer); break; } // デリミタメッセージの次から残りのデータをメインバッファの先頭にコピー memmove(mainbuffer, mainbuffer + rx_remainder, rx_remainder_len); // 次の受信バッファポインタを設定 rx_pos = rx_remainder_len; // 受信バッファ長を設定 rx_data_len = rx_remainder_len; // スキャンループから抜ける break; } } else { } } } // 100ms待つ Delay_10ms(10); } // 動作モードが動作状態なら if (work_mode == 1) { // 何もしない } // 動作モードが停止状態なら、コマンドをひたすら待つ else { // リザルトLEDを点灯 PORT_RESULT_LED = LED_ON; continue; } // ------------------------------ // お天気センサーモジュールに対して温度を要求 // ------------------------------ // モジュールがないなら if (i2c_slavetbl[GVC_TEMP_HUMI_ADDRESS] == 'x') { // 何もしない } // モジュールがあるなら else { // リザルトLEDを点灯 PORT_RESULT_LED = LED_ON; // メインバッファを初期化 memset((void *)mainbuffer, 0x00, sizeof(mainbuffer)); // メインバッファにGVC_SERIAL_MESSAGE_tをかぶせる gvc_serial_message = (GVC_SERIAL_MESSAGE_t *)mainbuffer; gvc_serial_message->msg_type = GVC_MSG_TEMP; gvc_serial_message->dev_num = GVC_TEMP_HUMI_ADDRESS; // 温度データを取得(I2C_ADDR=GVC_TEMP_HUMI_ADDRESS) get_gvc_temp_data(GVC_TEMP_HUMI_ADDRESS, (GVC_I2C_MESSAGE_t *)&(gvc_serial_message->format)); // CRCを計算して、メッセージの最後に設定 gvc_serial_message->data[ gvc_serial_message->data_len ] = GetCRC8((void *)gvc_serial_message, GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len); // ↑データを送信する側は、データのCRCを計算したあとで // そのCRCをデータの最後に連結して相手に送ればいい // 処理結果を送信 send_serial((char *)gvc_serial_message, (GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len + 1)); // リザルトLEDを消灯 PORT_RESULT_LED = LED_OFF; } // ------------------------------ // お天気センサーモジュールに対して相対湿度を要求 // ------------------------------ // モジュールがないなら if (i2c_slavetbl[GVC_TEMP_HUMI_ADDRESS] == 'x') { // 何もしない } // モジュールがあるなら else { // リザルトLEDを点灯 PORT_RESULT_LED = LED_ON; // メインバッファを初期化 memset((void *)mainbuffer, 0x00, sizeof(mainbuffer)); // メインバッファにGVC_SERIAL_MESSAGE_tをかぶせる gvc_serial_message = (GVC_SERIAL_MESSAGE_t *)mainbuffer; gvc_serial_message->msg_type = GVC_MSG_HUMI; gvc_serial_message->dev_num = GVC_TEMP_HUMI_ADDRESS; // 湿度データを取得(I2C_ADDR=GVC_TEMP_HUMI_ADDRESS) get_gvc_humi_data(GVC_TEMP_HUMI_ADDRESS, (GVC_I2C_MESSAGE_t *)&(gvc_serial_message->format)); // CRCを計算して、メッセージの最後に設定 gvc_serial_message->data[ gvc_serial_message->data_len ] = GetCRC8((void *)gvc_serial_message, GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len); // ↑データを送信する側は、データのCRCを計算したあとで // そのCRCをデータの最後に連結して相手に送ればいい // 処理結果を送信 send_serial((char *)gvc_serial_message, (GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len + 1)); // リザルトLEDを消灯 PORT_RESULT_LED = LED_OFF; } // ------------------------------ // MPL115A2(Atmospheric pressure) // ------------------------------ // モジュールが無いなら if (i2c_slavetbl[MPL115A2_ADDRESS] == 'x') { // 何もしない } // モジュールがあるなら else { // リザルトLEDを点灯 PORT_RESULT_LED = LED_ON; // メインバッファを初期化 memset((void *)mainbuffer, 0x00, sizeof(mainbuffer)); // メインバッファにGVC_SERIAL_MESSAGE_tをかぶせる gvc_serial_message = (GVC_SERIAL_MESSAGE_t *)mainbuffer; gvc_serial_message->msg_type = GVC_MSG_PRESS; gvc_serial_message->dev_num = MPL115A2_ADDRESS; // 大気圧を取得(MPL115A2)して、データに設定 sprintf(gvc_serial_message->data, "%s", ftoa(get_MPL115A2(), &ftoa_status)); // データ長を設定 gvc_serial_message->data_len = strlen(gvc_serial_message->data); // CRCを計算して、メッセージの最後に設定 gvc_serial_message->data[ gvc_serial_message->data_len ] = GetCRC8((void *)gvc_serial_message, GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len); // ↑データを送信する側は、データのCRCを計算したあとで // そのCRCをデータの最後に連結して相手に送ればいい // 処理結果を送信 send_serial((char *)gvc_serial_message, (GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len + 1)); // リザルトLEDを消灯 PORT_RESULT_LED = LED_OFF; } // ------------------------------ // 測距センサーモジュールに対して距離を要求 // ------------------------------ // モジュールが無いなら if (i2c_slavetbl[GVC_DISTANCE_ADDRESS] == 'x') { // 何もしない } // モジュールがあるなら else { // リザルトLEDを点灯 PORT_RESULT_LED = LED_ON; // メインバッファを初期化 memset((void *)mainbuffer, 0x00, sizeof(mainbuffer)); // メインバッファにGVC_SERIAL_MESSAGE_tをかぶせる gvc_serial_message = (GVC_SERIAL_MESSAGE_t *)mainbuffer; gvc_serial_message->msg_type = GVC_MSG_DIST; gvc_serial_message->dev_num = GVC_DISTANCE_ADDRESS; // 距離データを取得(I2C_ADDR=GVC_DISTANCE_ADDRESS) get_gvc_dist_data(GVC_DISTANCE_ADDRESS, (GVC_I2C_MESSAGE_t *)&(gvc_serial_message->format)); // CRCを計算して、メッセージの最後に設定 gvc_serial_message->data[ gvc_serial_message->data_len ] = GetCRC8((void *)gvc_serial_message, GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len); // ↑データを送信する側は、データのCRCを計算したあとで // そのCRCをデータの最後に連結して相手に送ればいい // 処理結果を送信 send_serial((char *)gvc_serial_message, (GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len + 1)); // リザルトLEDを消灯 PORT_RESULT_LED = LED_OFF; } // ------------------------------ // 赤外線センサーモジュールに対して距離を要求 // ------------------------------ // モジュールが無いなら if (i2c_slavetbl[GVC_PIR_ADDRESS] == 'x') { // 何もしない } // モジュールがあるなら else { // リザルトLEDを点灯 PORT_RESULT_LED = LED_ON; // メインバッファを初期化 memset((void *)mainbuffer, 0x00, sizeof(mainbuffer)); // メインバッファにGVC_SERIAL_MESSAGE_tをかぶせる gvc_serial_message = (GVC_SERIAL_MESSAGE_t *)mainbuffer; gvc_serial_message->msg_type = GVC_MSG_IR; gvc_serial_message->dev_num = GVC_PIR_ADDRESS; // 赤外線センサーの状態を取得(I2C_ADDR=GVC_PIR_ADDRESS) get_gvc_pir_data(GVC_PIR_ADDRESS, (GVC_I2C_MESSAGE_t *)&(gvc_serial_message->format)); // CRCを計算して、メッセージの最後に設定 gvc_serial_message->data[ gvc_serial_message->data_len ] = GetCRC8((void *)gvc_serial_message, GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len); // ↑データを送信する側は、データのCRCを計算したあとで // そのCRCをデータの最後に連結して相手に送ればいい // 処理結果を送信 send_serial((char *)gvc_serial_message, (GVC_SERIAL_MESSAGE_HEADER_SIZE + gvc_serial_message->data_len + 1)); // リザルトLEDを消灯 PORT_RESULT_LED = LED_OFF; } } }