UMEHOSHI ITA TOP PAGE COMPUTER SHIEN LAB
UART (Universal Asynchronous Receiver/Transmitter, ユーアート) は、調歩同期方式によるシリアル信号で p32mx270f256bのUART1と通信する際に使う関数群です。
#ifndef _MY_UART_H #define _MY_UART_H #define RINGBUFFSIZE 256 // 送受信リングバッファ構造体と操作関数------------------------------------------- typedef struct RingBuffer_t { uint8_t buff[RINGBUFFSIZE]; int in_Idx; int outIdx; int numSndBytes; // 上記のバッファに記憶されるバイト数 } RingBuffer; extern RingBuffer uartSndBuf; // USRT送信用リングバッファ extern RingBuffer uartRecBuf; // USRT受信用リングバッファ //void ringbuf_reset(RingBuffer *p); bool ringbuf_is_read(RingBuffer *p); //bool ringbuf_is_full(RingBuffer *p); //void ringbuf_set_data(RingBuffer *p, uint8_t c ); //uint8_t ringbuf_get_data(RingBuffer *p); int ringbuf_copy_to(RingBuffer *p,uint8_t *pBuff); // 以上が送受信リングバッファ構造体と操作関数-------------------------------ここまで // デフォルトのuartのポーリング関数と、関連メソッド------------------------------ void recive_and_send_uart_with_polling(); void init_uart(); int recv_uart1(uint8_t); int send_uart1(uint8_t); // デフォルトのuartのポーリング関数と、関連メソッド-------------------------ここまで // UARTコマンドモードや割り込み用ポーリング関数と、関連メソッド--------------------- bool request_acc_uartbuff(uint16_t id); uint16_t release_acc_uartbuff(uint16_t id); void uart_cmd_mode_polling();//UARTコマンドモード時のポーリング関数 void uart_isr_buff_polling();//uartの割り込みリ利用時のポーリング処理 int get_size_rec_uart();//uartRecBufに記憶されたUART送信データ数を返す int get_data_rec_uart();//uartRecBufからの受信データ取り出し int get_size_snd_uart();// uartSndBufに記憶されたUART送信データ数を返す void set_data_snd_uart(uint8_t c);// uartSndBufへの送信データをセット int get_uart1_capacity();//uartSndBufにセット可能なバイト数を返す。 void send_string_to_uart1(char *s);// 文字列をuart1に送信します. // UARTコマンドモードや割り込み用ポーリング関数と、関連メソッド--------------ここまで #define MY_UART_ERR_PARITY 0x0001 // UARTパリティエラー判定 #define MY_UART_ERR_FRAMING 0x0002 // UARTフレーミングエラー(STOPbit異常) #define MY_UART_ERR_OVERRUN 0x0004 // USRT受信バッファオーバーラン エラー #define MY_UART_ERR_SND_ISR 0x0008 // 送信必要なしで、送信割り込みが起きた矛盾 #define MY_UART_ERR_REC_SET 0x0010 // UART受信処理でUSB出力バッファフルの設定失敗 #define MY_UART_ERR_SND_SET 0x0020 // UART送信処理でUART送信バッファフルの設定失敗 #endif /* _UART_H */
// my_uart.c // (p32mx270f256bのUART1と調歩同期方式によるシリアル通信する際に使う定義群です。) #include <stdbool.h> // Defines true #include <stdlib.h> // Defines EXIT_FAILURE #include <xc.h> #include <sys/attribs.h>//割込み関係の定義 #include <proc/p32mx270f256b.h> #include "my_sys.h" #include "my_app.h" #include "my_usb_cmd.h" #include "my_uart.h" #include "my_beep.h" #include "common.h" // RingBuffer構造体の操作関数群-------------------------------------------------- void ringbuf_reset(RingBuffer *p) { p->in_Idx = p->outIdx = -1; p->numSndBytes = 0; } bool ringbuf_is_read(RingBuffer *p) { return p->numSndBytes > 0; } bool ringbuf_is_full(RingBuffer *p) { return p->numSndBytes == RINGBUFFSIZE; } void ringbuf_set_data(RingBuffer *p, uint8_t c) { p->numSndBytes++; if (p->in_Idx + 1 >= RINGBUFFSIZE) p->in_Idx = -1; p->buff[++p->in_Idx] = c; } uint8_t ringbuf_get_data(RingBuffer *p) { p->numSndBytes--; if (p->outIdx + 1 >= RINGBUFFSIZE) p->outIdx = -1; return p->buff[++p->outIdx]; } int ringbuf_copy_to(RingBuffer *p, uint8_t *pBuff) { int recSize = 0; while (ringbuf_is_read(p)) pBuff[recSize++] = ringbuf_get_data(p); return recSize; } // USRT送信用リングバッファ--と関連操作関数定義==================================== RingBuffer uartSndBuf = { {0}, -1, -1, 0 }; RingBuffer uartRecBuf = { {0}, -1, -1, 0 }; // USRT受信用リングバッファ //割り込み時に上記ringbuffを操作の排他制御用 volatile uint16_t accessing_uartbuffer __attribute__((section("sfrs"))); uint16_t my_uart_err = 0; // my_uart.c でのエラー情報 // 上記エラー情報への設定関数 int my_uart_set_err(uint16_t d) { my_uart_err |= d; } // UART関連通信エラーチェック. void check_uart_error() { // パリティエラー判定 if (core_once_count3_val == 0 && U1STAbits.PERR == 1) { my_uart_set_err(MY_UART_ERR_PARITY); } // フレーミングエラー判定(あるべき位置にストップビットがなかった) if (core_once_count3_val == 0 && U1STAbits.FERR == 1) { my_uart_set_err(MY_UART_ERR_FRAMING); } // 受信バッファオーバーラン エラー 判定(チップから取り出す前に次のデータが来た) if (core_once_count3_val == 0 && U1STAbits.OERR == 1) { my_uart_set_err(MY_UART_ERR_OVERRUN); } } // 割り込み時に上記ringbuffを操作する使う排他制御用の権限要求関数 bool request_acc_uartbuff(uint16_t id) { if (accessing_uartbuffer == id) { if (id == _ID_ACCESS_UARTASK) IEC1bits.U1RXIE = 1; // UART1受信割込みを許可 else IEC1bits.U1RXIE = 0; // UART1受信割込みを不許可 return true; } if (accessing_uartbuffer != 0) { if (id != _ID_ACCESS_UARTASK) IEC1bits.U1RXIE = 1; // UART1受信割込みを許可 else IEC1bits.U1RXIE = 0; // UART1受信割込みを不許可 return false; } accessing_uartbuffer |= id; if (accessing_uartbuffer == id) { if (id == _ID_ACCESS_UARTASK) IEC1bits.U1RXIE = 1; // UART1受信割込みを許可 else IEC1bits.U1RXIE = 0; // UART1受信割込みを不許可 return true; // outBuffersへのアクセス権を得た } accessing_uartbuffer &= ~id; if (id != _ID_ACCESS_UARTASK) IEC1bits.U1RXIE = 1; // UART1受信割込みを許可 else IEC1bits.U1RXIE = 0; // UART1受信割込みを不許可 return false; } // 上記割込み取得した権限を返す関数(ウメ・エディット・側だけで使う) uint16_t release_acc_uartbuff(uint16_t id) { uint16_t rtnval = accessing_uartbuffer &= ~id; //出力バッフ利用終了 IEC1bits.U1RXIE = 1; // UART1受信割込みを許可 return rtnval; } //UARTコマンドモード時のポーリング関数 // 受信データがあれば、 void uart_cmd_mode_polling() { uint8_t data; if (U1STAbits.URXDA == 1) {//UART受信バッファ内にデータが存在する。 data = U1RXREG; // 受信データ取得 if (_reply_boot_message() == 0) return;// 202503:追加 char rtn = select_command(data); // 逐次に受け取る引数がコマンド判定実行 if (rtn == 0) { set_commad_char(data); // 文字をコマンド解析バッファにセット } } if (ringbuf_is_read(& uartSndBuf) // UARTへの送信データがある? && U1STAbits.UTXBF == 0 // 送信バッファはフルではない && U1STAbits.TRMT == 1 //送信バッファが空? ) { data = ringbuf_get_data(& uartSndBuf); // UART送信バッファから取り出し U1TXREG = data; // チップ内UARTの送信バッファに入れる。 } } //uartRecBufに記憶されたUART送信データ数を返す int get_size_rec_uart() { return uartRecBuf.numSndBytes; } //uartRecBufからの受信データ取り出し int get_data_rec_uart() { return ringbuf_get_data(& uartRecBuf); } // uartSndBufに記憶されたUART送信データ数を返す int get_size_snd_uart() { return uartSndBuf.numSndBytes; } // uartSndBufへの送信データをセット void set_data_snd_uart(uint8_t c) { ringbuf_set_data(& uartSndBuf, c); } // UART1の初期設定 void init_uart() { // UART用の入出力ビットを指定 TRISASET = 0x00000010; // RX (RA4)入力指定 TRISBSET = 0x00000100; // CTS (RB8)入力指定 TRISBCLR = 0x00000210; // RTS (RB9) , TX(RB4) 出力 U1CTSR = 0x4; //RPB8(TABLE 11-6: PERIPHERAL PIN SELECT INPUT REGISTER MAP ) U1RXR = 0x2; //RPA4を、UART1のRX入力に使う指定 RPB4R = 0x1; //U1TX(出力) RPB9R = 0x1; //U1RTS(出力) U1MODE = 0x0; // UARTx モードレジスタ設定-------------------------------------- U1MODEbits.ON = 1; //UART1を有効 (USRT 関連端子がUSRT仕様指定で働く). //(無効時はPORTx、TRISx、LATxレジスタでビット制御). U1MODEbits.SIDL = 0; //アイドル中も動作を継続する. U1MODEbits.IREN = 0; //IrDAを無効にする . U1MODEbits.RTSMD = 1; // UxRTS ピンモード選択を片方向モード(フロー制御しない). //U1MODEbits.UEN = 2;//UxTX、UxRX、UxCTS、UxRTS ピンを有効にして使う U1MODEbits.UEN = 0; //UxTX、UxRXをピンを有効, UxCTS、UxRTSはPORTxレジスタで指定 // (RTSはRB9で制御). U1MODEbits.WAKE = 0; //スリープ中スタートビット検出時復帰を無効. U1MODEbits.LPBACK = 0; //ループバックモードを有無効にする. // UART ボーレート(baudrate)設定関連指定. U1MODEbits.ABAUD = 0; //baud レート自動検出イネーブルビット無効. U1MODEbits.RXINV = 0; // 受信極性反転ビット UxRXのアイドル状態は「1」. U1MODEbits.BRGH = 1; //高baudレート指定(高速モード:4x baudクロックを有効にする). U1BRG = 87; // 115200 bps Set Baud rate( BRGH が1の時). //上記の意味: 87=(40000000/4/88-115200)/115200 U1MODEbits.PDSEL = 0; // 8 ビットデータ、パリティなし. U1MODEbits.STSEL = 0; // 1 個のストップビット. U1STA = 0; // UARTのステータスレジスタ設定 ( 割り込みを含む ). U1STAbits.ADM_EN = 0; //自動アドレス検出モードを無効にする. U1STAbits.ADDR = 0; //自動アドレス検出に使うアドレスキャラクタ(上が0で意味なし). U1STAbits.UTXISEL = 0; //送信バッファに空きが1つでもあればで割込みする指定. U1STAbits.UTXINV = 0; //UxTXの送信極性反転ビット:アイドル状態は「1」. U1STAbits.URXEN = 1; // レシーバイネーブルビット:有効. U1STAbits.UTXBRK = 0; // 送信ブレークビット 無効または完了. U1STAbits.UTXEN = 1; // 送信イネーブルビット:有効. U1STAbits.URXISEL = 0; //受信バッファが空きが1つでもあればで割込みする指定. U1STAbits.ADDEN = 0; //アドレス検出モードを無効にする. U1STAbits.OERR = 0; //受信バッファオーバーランエラーステータスビットクリア . // ハードウェアでセットされ、この代入のソフトウェアでのみでクリア. // 送受信共通のUART1用割込み優先度、サブ優先度の設定. IPC8bits.U1IP = 3; //Interrupt Priorty (IPC8 レジスタ設定). IPC8bits.U1IS = 1; //Interrupt sub-priority level = 1 //割込み用フラグIFS1,IEC1の設定 [セクション 8. 割り込み:8.3 動作]参照 //以下は受信関連 IFS1bits.U1RXIF = 0; // Clear the interrupt status flag (1になって割込む) //IEC1bits.U1RXIE = 1; // UART1受信 Enable(割込み許可) IEC1bits.U1RXIE = 0; // UART1受信 割込み不許可 (ポーリング利用) //以下は送信関連 IFS1bits.U1TXIF = 0; // Clear the interrupt status flag (1になって割込む) //IEC1bits.U1EIE = 1;// 送受信エラー用割込み IEC1bits.U1TXIE = 0; // UART1送信 Enable(割込み不許可) // 以下は、別途 USB シリアル変換ボードなどを利用してESP32に書き込む場合に使うコード-- // U1MODE=0x0;// UART の無効設定 // U1STA=0; // _RB4=1; // UART TX でプルアップされる出力端子をHiでワイヤードORを可能にする。 } // ブートメッセージに返信する // ESP32-WROOM-32Dの起動時のメッセージ出力を止める細工として作成 int reply_boot_message_status = 0; //一定時間後、受信に対して1回だけ改行で応答する制御用 :202503追加 int reply_boot_message() { if (reply_boot_message_status == 0) { if (core_once_count1_val <= 0) {// 起動1秒後 //起動後の一定期間を無視し、その後、改行を1回だけ出して無視モードを終える reply_boot_message_status = 1; U1TXREG = '\r'; //UART送信バッファに入れる。 U1TXREG = '\n'; //UART送信バッファに入れる。 } // 上記改行の送出で、Boot Logが止まらない場合を想定した細工 // として、(core_once_count2_val > 0)の間はまでの受信は無効にしている } if (core_once_count2_val > 440) {// ダウンカウンター起動後約1.5秒後まで return 0; // 無視 } else if (core_once_count2_val > 0) {// 約1.5 ? 2秒で受信があれば debug_hex16(19,0x2001,1);// ESP32が 正しく起動していない警告音 return 0; // 2秒まで、無視 } return 1; // 応答終了のタイミング } // :202503 下記を上記のように変更 //int reply_boot_message() { // static int flag = 1; //一定時間後、受信に対して1回だけ改行で応答する制御用 // if (core_once_count2_val > 0) { // if (flag == 1) { // if (core_once_count1_val == 0) {// 起動1秒後 // //起動後の一定期間を無視し、その後、改行を1回だけ出して無視モードを終える // flag = 0; // U1TXREG = '\r'; //UART送信バッファに入れる。 // U1TXREG = '\n'; //UART送信バッファに入れる。 // } // 上記改行の送出で、Boot Kogが止まらない場合を想定した細工 // // よって、(core_once_count2_val > 0)の間はまでの受信は無効にしている // } // return 0; // 2秒まで、無視 // } // return 1; // 応答終了のタイミング //} // UART1からの1byte受信すると呼び出される。処理した場合は1、処理しない場合は0を返す。 int recv_uart1(uint8_t data) { // 起動時の受信を無視して、"\r\n"のメッセージを1回だけ送出する. if (_reply_boot_message() == 0) return 0; // :202503 マクロに変更 // UARTから受信したデータを、USB出力バッファの余裕があって出力可能ならUSBに出力 if (_get_capacity() < 20) { my_uart_set_err(MY_UART_ERR_REC_SET); // 容量が一杯に近い! . return 0; } send_char(data); // UARTで受信した1文字を USBへ出力する。 return 1; } // UART1で送信するデータをセットする。セットできた時1、出来ない時0を返す. // ユーザープログラムからのマクロ呼び出し可 // スルーモード時は、USBからの受信(my_usb.c:set_recive_data関数)で、呼び出される。. int send_uart1(uint8_t data) { if (ringbuf_is_full(& uartSndBuf)) { my_uart_set_err(MY_UART_ERR_SND_SET); return 0; } ringbuf_set_data(& uartSndBuf, data); //UART送信用リングバッファにセット. return 1; } // UART1のバッファで、格納可能は残りの容量(バイト数)を返す. int get_uart1_capacity() { return RINGBUFFSIZE - uartSndBuf.numSndBytes; } // 文字列をuart1に送信します. void send_string_to_uart1(char *s) { while (*s != NULL) send_uart1(*s++); } /* ========================================== :202503 以下をコメントで削除して、簡潔に書き直す。 int recive_and_send_uart_no_through() { static uint8_t data; if (U1STAbits.URXDA == 1) {//UART受信バッファ内にデータが存在する。 data = U1RXREG; // 受信データ取得 _recv_uart1(data); // 処理の置き換え可能な 1byte受信処理のマクロ } if (ringbuf_is_read(& uartSndBuf) // UARTへの送信データがある? && U1STAbits.UTXBF == 0 // 送信バッファはフルではない && U1STAbits.TRMT == 1 //送信バッファが空? ) { data = ringbuf_get_data(& uartSndBuf); // UART送信バッファから取り出し U1TXREG = data; // チップ内UARTの送信バッファに入れる。 } } // USB処理の中で呼び出されるUARTの送受信の処理(初期のデフォルトポーリング関数) void recive_and_send_uart_with_polling()//uartのポーリング処理 { static uint8_t data; static int wait_count = 0; static recive_by_uart_mode = 0; //UARTからの受信モード check_uart_error(); //UART関連通信エラーチェック // if (flagThrough == 0) {// T.スルーモードでない? recive_and_send_uart_no_through(); // スルーモードでない場合のUART送受信 // return; // } // // 以下は、T.スルーモード処理時の入出力------- // if (recive_by_uart_mode == 0) {//UARTへ送信モード(USB受信) // // UARTへの送信する登録データがあれば、取り出してUARTへ出力 // // (登録データは、スルーモードであれば、USB受信で行われる) // if (ringbuf_is_read(& uartSndBuf) // UARTへの送信データがある? // && U1STAbits.UTXBF == 0 // 送信バッファはフルではない // && U1STAbits.TRMT == 1 //送信バッファが空? // ) { // wait_count = 0; // data = ringbuf_get_data(& uartSndBuf); // UART送信バッファから取り出し // U1TXREG = data; // チップ内UARTの送信バッファに入れる。 // // return; // } // } // // UARTからの受信(USB送信バッファへ登録)があれば、 // // しばらく、このUARTからの受信モードを続ける。 // if (U1STAbits.URXDA == 1 || recive_by_uart_mode == 1) { // recive_by_uart_mode = 1; // if (U1STAbits.URXDA == 1) {//チップのUART受信バッファ内にデータが存在する. // data = U1RXREG; // 受信データ取得 // recv_uart1(data); // UARTで受信した1byteを USBへ出力する登録をする。 // wait_count = 1; // } else { // wait_count++; // if (wait_count > 100) { // // UARTからしばらくデータがこなければ、UARTからの受信モードを終える // recive_by_uart_mode = 0; // } // } // } //} :202503 上記範囲は、通常モードだけで使う処理で以下に作り直した ================================ */ // 通常モードだけで使う_def_polls_uart()マクロのUAR1受信処理 // (USB処理の中で呼び出されるUARTの送受信の処理で、デフォルトポーリング関数) void recive_and_send_uart_with_polling()//uartのポーリング処理 { static uint8_t data; check_uart_error(); //UART関連通信エラーチェック if (U1STAbits.URXDA == 1) {//UART受信バッファ内にデータが存在する。 data = U1RXREG; // 受信データ取得 _recv_uart1(data); // 処理の置き換え可能な 1byte受信処理のマクロ // デフォルトは、recv_uart1関数が登録 } if (ringbuf_is_read(& uartSndBuf) // UARTへの送信データがある? && U1STAbits.UTXBF == 0 // 送信バッファはフルではない && U1STAbits.TRMT == 1 //送信バッファが空? ) { data = ringbuf_get_data(& uartSndBuf); // UART送信バッファから取り出し U1TXREG = data; // チップ内UARTの送信バッファに入れる。 } } // USB処理の中で呼び出されるUARTの送受信の処理 void uart_isr_buff_polling()//uartの割り込みリ利用時のポーリング処理 { //IEC1bits.U1RXIE = 0; // UART1受信割込み許可変更 if (request_acc_uartbuff(_ID_ACCESS_USBTASK) == false) { //IEC1bits.U1RXIE = 1; // UART1受信割込み許可変更 return; } if (get_size_rec_uart() > 0) { uint8_t data = ringbuf_get_data(& uartRecBuf); _recv_uart1(data); // 処理の置き換え可能な 1byte受信処理のマクロ } if (get_size_snd_uart() > 0) { release_acc_uartbuff(_ID_ACCESS_USBTASK); IEC1bits.U1TXIE = 1; // UART1送信割込み 割込みEnable } else { IEC1bits.U1TXIE = 0; // UART1送信 割込みEnable OFF } release_acc_uartbuff(_ID_ACCESS_USBTASK); //IEC1bits.U1RXIE = 1; // UART1受信割込み許可変更 } // 割り込み 割り込みグループ優先度: 3にセット // 優先度は1(最低優先度)から7(最優先)の間で選択できる。 // より高い優先度を持つ割り込みベクタから先に処理されます。 // (下記のIPL4SOFTが優先度で、このソースでは、「IPC8bits.U1IP」設定と連動が必要) void __ISR(_UART_1_VECTOR, IPL3SOFT)Uart1Handler(void) { extern void send_string(char *s); char s[128]; if (IFS1bits.U1RXIF == 1) {// 受信に対するインタラプト判定 if (request_acc_uartbuff(_ID_ACCESS_UARTASK) == false) { //IFS1bits.U1RXIF = 0; //mU1TXClearIntFlag() return; } uint8_t c = U1RXREG; // UART受信データ取得 ringbuf_set_data(& uartRecBuf, c); //受信バッファに入れる release_acc_uartbuff(_ID_ACCESS_UARTASK); IFS1bits.U1RXIF = 0; //mU1RXClearIntFlag() } else if (IFS1bits.U1TXIF == 1) {// 送信バッファが空?インタラプト判定 if (request_acc_uartbuff(_ID_ACCESS_UARTASK) == false) { IFS1bits.U1TXIF = 0; //mU1TXClearIntFlag() return; } if (ringbuf_is_read(& uartSndBuf) == false) {// 送信の必要がない? // 送信必要なしで、送信割り込みが起きた矛盾時のエラー my_app_set_err(MY_UART_ERR_SND_ISR); IEC1bits.U1TXIE = 0; // UART1送信 割込みEnable OFF } else { uint8_t data = ringbuf_get_data(& uartSndBuf); U1TXREG = data; //送信バッファに入れる。 if (ringbuf_is_read(& uartSndBuf) > 0) {// まだ送信の必要がある? IEC1bits.U1TXIE = 1; // UART1送信割込み 割込みEnable } else { IEC1bits.U1TXIE = 0; // UART1送信 割込みEnable OFF } } release_acc_uartbuff(_ID_ACCESS_UARTASK); IFS1bits.U1TXIF = 0; //mU1TXClearIntFlag() } else if (IFS1bits.U1EIF == 1) {// エラーか? check_uart_error(); } }