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();
}
}