====== 簡易シリアルバッファ ====== ===== 概要 ===== 市販測定器や自作測定器の中には、測定したデータをRS232C経由でパソコンに送信する機能を持ったものがあります。 しかし、測定現場によっては、湿度が高い、粉塵が多い、設置場所が狭いなどのために、パソコンを持ち込むことが困難な場合があります。 そこで今回は、 * 測定時には、測定器から送信されてくるデータを一旦バッファリングし、→パソコン不要 * 測定完了後には、バッファリングしたデータを、パソコン側にデータ転送する。→測定器不要 ことが可能なコンパクトな「簡易シリアルバッファ」を製作しました。 {{:imgpaste:202004:htmikan-20200430-162225.png?500}} <仕様> * バッファリング容量→256kバイト(正確には、262,144バイト) * 通信速度→9600bps * シリアル信号→TTLレベル(対PIC)、RS232Cレベル(対パソコン) ===== 動作原理 ===== ◎受信データのバッファリング 受信したシリアルデータを、ダブルバッファに交互に書き込み、満杯になったバッファの内容を、随時EEPROMに書き込みます。 ◎バッファリングデータの送信 EEPROMに書き込んだデータを、シリアル出力します。 {{:imgpaste:202004:htmikan-20200430-162323.png}} ===== 動作原理(ハードウェア) ===== ◎USARTモジュール(受信) * シリアルデータを受信し、割り込みを発生させます。 ◎ダブルバッファ(メモリ) * 受信したデータは、64バイト×2個のダブルバッファに書き込まれます。 ◎外付EEPROM * 満杯になったダブルバッファの内容は、2個の外付EEPROM(24LC1025)に書き込まれます。(I2C通信使用) ◎内蔵EEPROM * 外付EEPROMに書き込んだデータ長は、内蔵EEPROMに書き込まれます。 ◎USARTモジュール(送信) * シリアルデータを送信します。 ◎トランジスタ(2SC1815) * シリアル信号のTTLレベル(対PIC)をRS232Cレベル(対パソコン)に変換(位相反転含む)します。 ===== 動作原理(ソフトウェア) ===== ◎シリアルデータの受信とバッファリング(メモリ)処理→interrupt関数 * シリアルデータを受信すると、割り込みが発生します。 * 受信したデータをメモリ(ダブルバッファ<64バイト×2個>)に書き込みます。 ◎バッファリング(外付EEPROM)処理→recv関数 * 満杯になったダブルバッファの内容を外付EEPROMに書き込みます。 * 外付EEPROMに書き込んだデータ長を、内蔵EEPROMに書き込みます。 ◎受信データ送信処理→send関数 * 外付EEPROMに書き込まれた受信データを、内蔵EEPROMに書き込まれたデータ長分だけ送信します。 ◎受信モードと送信モードの切り替え処理 * 受信モードの開始\\ モードスイッチ(SW_MODE)をオフの状態にして、開始スイッチ(SW_START)を押下します。\\ LED_RECV_MODEが点灯します。\\ データを受信する毎に、LED_RECV_DATAが反転します。 * 受信モードの停止\\ 停止スイッチ(SW_STOP)を押下します。\\ LED_RECV_MODEが消灯します。 * 送信モードの開始\\ モードスイッチ(SW_MODE)をオンの状態にして、開始スイッチ(SW_START)を押下します。\\ LED_SEND_MODEが点灯します。 * 送信モードの停止\\ 停止スイッチ(SW_STOP)を押下します。\\ LED_SEND_MODEが消灯します。 ===== 回路図 ===== {{:imgpaste:202004:htmikan-20200430-162502.png}} ===== ソースコード ===== //********************************************************************** /*    「簡易シリアルバッファ」  ・通信速度 → 9600bps  ・受信データ最大長 → 256kバイト(131072 * 2)  24LC1025*2個使用 */ //********************************************************************** //■■■関数宣言■■■ extern void main(); extern void recv(); extern void send(); extern void init_port(); extern void init_eeprom_ex(); extern void eeprom_write_ex(long addr, short *buf, short len); extern void eeprom_read_ex(long addr, short *buf, short len); extern void init_usart(); extern void interrupt(); //********************************************************************** //■■■マクロ定義■■■ //USART sbit TX_Direction at TRISB.B5; sbit RX_Direction at TRISB.B2; //EEPROM sbit Soft_I2C_Scl at RA0_bit; sbit Soft_I2C_Sda at RA1_bit; sbit Soft_I2C_Scl_Direction at TRISA0_bit; sbit Soft_I2C_Sda_Direction at TRISA1_bit; #define ACK 1 #define NO_ACK 0 //LED sbit LED_RECV_MODE at PORTA.B2; sbit LED_RECV_MODE_Direction at TRISA.B2; sbit LED_SEND_MODE at PORTA.B3; sbit LED_SEND_MODE_Direction at TRISA.B3; sbit LED_RECV_DATA at PORTA.B4; sbit LED_RECV_DATA_Direction at TRISA.B4; //SW sbit SW_START at PORTB.B0; sbit SW_START_Direction at TRISB.B0; sbit SW_STOP at PORTB.B1; sbit SW_STOP_Direction at TRISB.B1; sbit SW_MODE at PORTB.B3; sbit SW_MODE_Direction at TRISB.B3; //other #define INPUT_MODE 1 #define OUTPUT_MODE 0 // #define CR 0x0D #define LF 0x0A //********************************************************************** char buf1[64], buf2[64]; short len1 = 0, len2 = 0; short flag = 0; //********************************************************************** //■■■メイン関数■■■ void main() { short cnt; // OSCCON = 0b01110000; //クロックを8MHzに設定します。 ANSEL = 0b00000000; //A/D変換モジュールは使用しません。 // init_port(); init_eeprom_ex(); init_usart(); // for (cnt = 0; cnt < 5; cnt++) { LED_RECV_MODE = 1; LED_SEND_MODE = 1; LED_RECV_DATA = 1; Delay_ms(100); LED_RECV_MODE = 0; LED_SEND_MODE = 0; LED_RECV_DATA = 0; Delay_ms(100); } // while (1) { if ((SW_MODE == 1) && (SW_START == 0)) { while (SW_START == 0) { Delay_ms(100); } LED_RECV_MODE = 1; recv(); LED_RECV_MODE = 0; } if ((SW_MODE == 0) && (SW_START == 0)) { while (SW_START == 0) { Delay_ms(100); } LED_SEND_MODE = 1; send(); LED_SEND_MODE = 0; } } } //********************************************************************** //■■■シリアルデータ受信関数■■■ void recv() { long addr = 0; // len1 = 0; len2 = 0; flag = 0; LED_RECV_DATA = 0; // 割り込みを許可します。 INTCON.PEIE = 1; INTCON.GIE = 1; // while ((SW_STOP != 0) && (addr < (131072 * 2))) { if (len1 == 64) { len1 = 0; eeprom_write_ex(addr, buf1, 64); addr += 64; // EEPROM_Write(0, addr & 0xFF); EEPROM_Write(1, (addr >> 8) & 0xFF); EEPROM_Write(2, (addr >> 16) & 0xFF); EEPROM_Write(3, (addr >> 24) & 0xFF); } if (len2 == 64) { len2 = 0; eeprom_write_ex(addr, buf2, 64); addr += 64; // EEPROM_Write(0, addr & 0xFF); EEPROM_Write(1, (addr >> 8) & 0xFF); EEPROM_Write(2, (addr >> 16) & 0xFF); EEPROM_Write(3, (addr >> 24) & 0xFF); } } // 割り込みを禁止します。 INTCON.PEIE = 0; INTCON.GIE = 0; // if (len1 > 0) { eeprom_write_ex(addr, buf1, len1); addr += len1; } if (len2 > 0) { eeprom_write_ex(addr, buf2, len2); addr += len2; } // EEPROM_Write(0, addr & 0xFF); EEPROM_Write(1, (addr >> 8) & 0xFF); EEPROM_Write(2, (addr >> 16) & 0xFF); EEPROM_Write(3, (addr >> 24) & 0xFF); } //********************************************************************** //■■■シリアルデータ送信関数■■■ void send() { short cnt; long addr = 0, data_size; // data_size = EEPROM_Read(3); data_size = (data_size << 8) + EEPROM_Read(2); data_size = (data_size << 8) + EEPROM_Read(1); data_size = (data_size << 8) + EEPROM_Read(0); // while ((SW_STOP != 0) && (data_size > 0)) { if (data_size > 64) { eeprom_read_ex(addr, buf1, 64); for (cnt = 0; cnt < 64; cnt++) { UART1_Write(buf1[cnt]); } addr += 64; data_size -= 64; } else { eeprom_read_ex(addr, buf1, data_size); for (cnt = 0; cnt < data_size; cnt++) { UART1_Write(buf1[cnt]); } data_size = 0; } } } //********************************************************************** //■■■入出力ポート初期化関数■■■ void init_port() { SW_START_Direction = INPUT_MODE; SW_STOP_Direction = INPUT_MODE; SW_MODE_Direction = INPUT_MODE; LED_RECV_MODE_Direction = OUTPUT_MODE; LED_SEND_MODE_Direction = OUTPUT_MODE; LED_RECV_DATA_Direction = OUTPUT_MODE; LED_RECV_MODE = 0; LED_SEND_MODE = 0; LED_RECV_DATA = 0; } //********************************************************************** //■■■EEPROM初期化関数■■■ void init_eeprom_ex() { Soft_I2C_Init(); } //********************************************************************** //■■■EEPROM書き込み関数■■■ void eeprom_write_ex(long addr, short *buf, short len) { unsigned short cnt; // Soft_I2C_Start(); switch (addr & 0x30000) { case 0x00000: Soft_I2C_Write(0xA0); break; case 0x10000: Soft_I2C_Write(0xA8); break; case 0x20000: Soft_I2C_Write(0xA2); break; case 0x30000: Soft_I2C_Write(0xAA); break; } /* if ((addr & 0x10000) == 0) Soft_I2C_Write(0xA0); else Soft_I2C_Write(0xA8); */ Soft_I2C_Write((addr >> 8) & 0xFF); Soft_I2C_Write(addr & 0xFF); for (cnt = 0; cnt < len; cnt++) { Soft_I2C_Write(buf[cnt]); } Soft_I2C_Stop(); Delay_ms(1); } //********************************************************************** //■■■EEPROM読み込み関数■■■ void eeprom_read_ex(long addr, short *buf, short len) { unsigned short cnt; // Soft_I2C_Start(); switch (addr & 0x30000) { case 0x00000: Soft_I2C_Write(0xA0); break; case 0x10000: Soft_I2C_Write(0xA8); break; case 0x20000: Soft_I2C_Write(0xA2); break; case 0x30000: Soft_I2C_Write(0xAA); break; } /* if ((addr & 0x10000) == 0) Soft_I2C_Write(0xA0); else Soft_I2C_Write(0xA8); */ Soft_I2C_Write((addr >> 8) & 0xFF); Soft_I2C_Write(addr & 0xFF); Soft_I2C_Start(); switch (addr & 0x30000) { case 0x00000: Soft_I2C_Write(0xA1); break; case 0x10000: Soft_I2C_Write(0xA9); break; case 0x20000: Soft_I2C_Write(0xA3); break; case 0x30000: Soft_I2C_Write(0xAB); break; } /* if ((addr & 0x10000) == 0) Soft_I2C_Write(0xA1); else Soft_I2C_Write(0xA9); */ for (cnt = 0; cnt < (len - 1); cnt++) { buf[cnt] = Soft_I2C_Read(ACK); } buf[cnt] = Soft_I2C_Read(NO_ACK); Soft_I2C_Stop(); Delay_ms(1); } //********************************************************************** //■■■USART初期化関数■■■ void init_usart() { TX_Direction = OUTPUT_MODE; RX_Direction = INPUT_MODE; UART1_Init(9600); PIE1.RCIE = 1; PIR1.RCIF = 0; } //********************************************************************** //■■■割り込み関数■■■ void interrupt() { char rd; // if (PIR1.RCIF == 1) { PIR1.RCIF = 0; // rd = UART1_Read(); LED_RECV_DATA = ~LED_RECV_DATA; switch (flag) { case 0: buf1[len1] = rd; len1++; if (len1 == 64) { flag = 1; } break; case 1: buf2[len2] = rd; len2++; if (len2 == 64) { flag = 0; } break; } } } //********************************************************************** ===== 動作確認 ===== 左側:24LC1025×2個、PIC16F88、スイッチ関連、LED関連です。 右側:シリアル信号変換用のトランジスタ(2SC1815×2個)です。 {{:imgpaste:202004:htmikan-20200430-162703.png}}{{:imgpaste:202004:htmikan-20200430-162707.png}} {{:imgpaste:202004:htmikan-20200430-162712.png}}{{:imgpaste:202004:htmikan-20200430-162716.png}} 前回製作した、「簡易パルスカウンタ」からのデータを受信し、更に、その受信したデータをパソコンに送信し、 ハイパーターミナルで受信してみました。 {{:imgpaste:202004:htmikan-20200430-162720.png}} このページは稲崎様の閉鎖したHPのコピーで、著作権は稲崎様にあります。[[elechobby:picdic:picdic|詳細]] This page is a copy of Mr. Inasaki's closed website, and the copyright is held by him.[[elechobby:picdic:picdic|Details]]