UMEHOSHI ITA TOP PAGE    COMPUTER SHIEN LAB

UMEHOSHI IT (my_uart.cと.h)

UART (Universal Asynchronous Receiver/Transmitter, ユーアート) は、調歩同期方式によるシリアル信号で p32mx270f256bのUART1と通信する際に使う関数群です。

この初期化は、my_sys.cのハンドル初期関数init_interrupt()内で、handlers[_IDX_INIT_UART1]()の呼び出しで行われます。
(このhandlers[_IDX_INIT_UART1]には、デフォルトでinit_uart関数が登録されています。)

そして、send_uart1(uint8_t)で、uart1へ出力するデータをバッファuartSndBufに登録します。
この出力は、_def_polls_uart()マクロのポーリングで行われています。
_def_polls_uart()マクロは、handlers[_IDX_DEF_POLLS_UART] に登録される関数です。
my_sys.c 内で通常は、recive_and_send_uart_with_polling関数が登録されます。 この関数内で、スルーモードでなければ、recive_and_send_uart_no_through(); が呼び出されています。

このrecive_and_send_uart_no_through(); 内で、uartSndBufにデータがあればUARTに出力しますが、 受信のポーリングが優先されて、UART受信があれば、 _recv_uart1(data); マクロが実行されます。
_recv_uart1(data); マクロは、handlers[_IDX_RECV_UART1] に登録された関数実行です。
よって、「ウメ・エディットプログラム(ユーザー用のプログラム)」で処理の置き換えが可能です。
(デフォルトでは _recv_uart1マクロに、recv_uart1関数が登録されて、受信dataをUSBへ出力します。)


上記で説明のように、UARTのポーリングは_def_polls_uart()マクロで行われ、 その実態は、handlers[_IDX_DEF_POLLS_UART] に登録される関数です。
そして、「UART1コマンドモード」場合は、UARTで「UME専用Hexコマンド」を受け付けるモードでは、
handlers[_IDX_DEF_POLLS_UART] = handlers[_IDX_CMD_POLLS_UART]; // これで「UART1コマンドモード」へ切り替え
の初期化により、_def_polls_uart()マクロの登録関数を「UART1コマンドモード」用に置き換えることで実現しています。
このhandlers[_IDX_CMD_POLLS_UART]には、uart_cmd_mode_polling()関数が登録されています。
これにより、「UART1コマンドモード」ではuart_cmd_mode_polling()関数によるポーリングが行われます。


以上のポーリング処理により、UART送受信の割り込みが使われていません。
ですが、将来に割り込みを使う変更を考慮して割り込み関数の定義が存在しています。

my_uart.hのソース

#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のソース

// 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; // 受信データ取得
        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() {
    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;

    // 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++);
}

// スルーモードでない場合の UART受信と送信処理用 
// UARTハード受信バッファに受信データがあれば、_recv_uart1( data )マクロで処理し、
// UART送信バッファにデータがあれば、UART送信ハードにセットする.
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;
            }
        }
    }
}

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