RL-78は64ピン版を対象とし、ユニットとチャンネルの関係は以下の通りです
UART0のTxD:ユニット0(m=0)、チャンネル0(n=0)
UART0のRxD:ユニット0(m=0)、チャンネル1(n=1)
UART1のTxD:ユニット0(m=0)、チャンネル2(n=2)
UART1のRxD:ユニット0(m=0)、チャンネル3(n=3)
UART2のTxD:ユニット1(m=1)、チャンネル0(n=0)
UART2のRxD:ユニット1(m=1)、チャンネル1(n=1)
UART0(TxD)のシリアル・モード・レジスタは、SMRmn のm=0、n=0 なのでSMR00 と表記され、UART0(RxD)のシリアル・モード・レジスタは、SMRmn のm=0、n=1 なのでSMR01 と表記されます
また、UART2(TxD)のシリアル・モード・レジスタは、SMRmn のm=1、n=0 なのでSMR10 と表記され、UART2(RxD)のシリアル・モード・レジスタは、SMRmn のm=1、n=1 なのでSMR11 と表記されます
ユニット0のCK00とCK10は8MHz(SMR動作クロックで使用)でセットしてます
SAU0EN = 1U; // 1 = シリアルアレイユニット0 クロック供給開始
SAU1EN = 1U; // 1 = シリアルアレイユニット1 クロック供給開始
NOP();
NOP();
NOP();
NOP();
SPS0 = (uint16_t)( 0x0050U | 0x0002U ); // シリアルクロック分周 CK00 - fCLK/4 8MHz
// CK01 - fCLK/32 1MHz
SPS1 = (uint16_t)( 0x0050U | 0x0002U ); // シリアルクロック分周 CK10 - fCLK/4 8MHz
// CK11 - fCLK/32 1MHz
ST0 |= (uint16_t)( 0x0002U | 0x0001U ); // UART0 送受信停止
ST0 |= (uint16_t)( 0x0008U | 0x0004U ); // UAR10 送受信停止
ST1 |= (uint16_t)( 0x0002U | 0x0001U ); // UART2 送受信停止
UART0の設定例
・ボーレート 115200
・データ長 8ビット
・ストップ 1ビット
・パリティ なし
①シリアル・モード・レジスタ(SMR00)の設定
b15 0 動作クロックはCK00 8Mhz
b14 0 転送クロックは分周
b13-9 0000 リザーブ
b8 0 ソフトトリガのみ有効
b7 0 リザーブ
b6 0 立ち下がりがスタートビット
b5-3 100 リザーブ
b2-1 01 UARTモード
b0 1 バッファ空き割込み
②シリアル通信動作設定レジスタ(SCR00)の設定
b15-14 10 送信のみ
b13-11 000 未使用&リザーブ
b10 0 エラー割込みINTSREx禁止
b9-8 00 NONEパリティ
b7 1 LSBファースト
B6 0 リザーブ
B5-4 01 ストップビット長=1ビット
b3ー2 01 リザーブ
b1-0 11 データ長=8ビット
③転送クロック(SDR00)の設定
ボーレート115200とする
上位8ビットを68を設定値とすると、b15-b9が分周値となる
動作クロック8MHz/(2*(68/2+1))=114285
④シリアル・モード・レジスタ(SMR01)の設定
b15 0 動作クロックはCK00 8Mhz
b14 0 転送クロックは分周
b13-9 0000 リザーブ
b8 1 UART受信時
b7 0 リザーブ
b6 0 立ち下がりがスタートビット
b5-3 100 リザーブ
b2-1 01 UARTモード
b0 0 未使用
⑤シリアル通信動作設定レジスタ(SCR01)の設定
b15-14 01 受信のみ
b13-11 000 未使用&リザーブ
b10 1 エラー割込みINTSREx許可
b9-8 00 NONEパリティ
b7 1 LSBファースト
B6 0 リザーブ
B5-4 01 ストップビット長=1ビット
b3ー2 01 リザーブ
b1-0 11 データ長=8ビット
/* ---< UART0(割込無効) >--- */
STMK0 = 1U; // UART0 送信完了割込みマスク(INTST0)
STIF0 = 0U; // UART0 送信完了割込み要求フラグクリア
SRMK0 = 1U; // UART0 受信完了割込みマスク(INTSR0)
SRIF0 = 0U; // UART0 受信完了割込み要求フラグクリア
SREMK0 = 1U; // UART0 受信エラー割込みマスク(INTSRE0)
SREIF0 = 0U; // UART0 受信エラー割込み要求フラグクリア
/* ---< UART0(割込レベル) >--- */
STPR10 = 0U; // UART0 送信割込み優先1
STPR00 = 1U;
SRPR10 = 0U; // UART0 受信割込み優先1
SRPR00 = 1U;
SREPR10 = 0U; // UART0 受信エラー割込み優先1
SREPR00 = 1U;
/* ---< シリアル通信(TxD0) >--- */
SMR00 = (uint16_t)(0x0020U|0x0002U|0x0001U); // シリアルモード
SCR00 = (uint16_t)(0x8000U|0x0080U|0x0010U|0x0004U|0x0003U);// シリアル通信動作設定
SDR00 = 0x4400U; // b15-b8 転送クロック
SOE0 |= BIT0; // 出力Hi
/* ---< シリアル通信(RxD0) >--- */
SIR01 = (uint16_t)( 0x0004U|0x0002U|0x0001U); // エラーフラグクリア
SMR01 = (uint16_t)( 0x0100U|0x0020U|0x0002U); // シリアルモード
SCR01 = (uint16_t)(0x4000U|0x0400U|0x0080U|0x0010U|0x0007U);// シリアル通信動作設定
SDR01 = 0x4400U; // b15-b8 転送クロック
NFEN0 |= BIT0; // RxD0 ノイズフィルタ有効
送信処理
基本的なデータ送信は、Send関数を利用して割込み内送信バッファ(_gSndBuf)へ送信データをセットすることで送信できます。実際の送信は、IntSnd_0関数の割込み処理で送信しています
・Send関数はSetByte関数を使い1バイト毎、割込み内送信バッファ(_gSndBuf)へセットします
・割込み内送信バッファ(_gSndBuf)はリングバッファとして利用します
・Send関数は全ての指定バイトをセットするとSendStart関数で送信を開始します(実際には割込み関数IntSnd_0で送信)
・SendStart関数で、送信割込みの状態が送信中(データなし)でなければ送信開始を行います。送信開始は送信中フラグ (_gSending)のセットと割込み内送信バッファデータ(_gSndBuf)を送信レジスタ(TXD0)へ書込むだけです。送信中(データあり)なら何もしないで戻ります(割込み処理内で割込み内書込みインデックスまで勝手に送信されます)
#define C_SIZE_COM_BUF 40 // 割込内送受信バッファサイズ
#define M_RING_INC(a,b) (((b)<=(a+1)) ? (a=0):(a+=1)) // マクロ定義
//************************
// UART
// 割込内送受信バッファ
//************************
typedef struct
{
uint16_t WriteIdx; // 書込みインデックス
uint16_t ReadIdx; // 読込みインデックス
uint8_t Buf[ C_SIZE_COM_BUF ]; // バッファ
} T_COM_BUFFER;
static volatile T_COM_BUFFER _gSndBuf = { 0 }; // 割込内送信バッファ
//************************************************************************************************
// 機能概要 :送信バッファへ指定バイトセットして送信する
// 引数 :Data 送信データ
// :Count データ数
// 戻り値 :TRUE(成功)、FALSE(エラー)
// 備考 :
//************************************************************************************************
static BOOL Send( uint8_t* Data, uint16_t Count )
{
uint16_t index;
for( index = 0; index < Count; index++ ) // メッセージ・データセット
{
if( FALSE == SetByte( Data[ index ] ))
{
return FALSE;
}
}
return SendStart(); // 書込みインデックス更新と送信
}
//************************************************************************************************
// 機能概要 :送信バッファへ1バイトセットする
// 引数 :Data セットするデータ
// 戻り値 :TRUE(成功)、FALSE(エラー)
// 備考 :
//************************************************************************************************
static BOOL SetByte( uint8_t Data )
{
STMK0 = 1U; // 送信割込みマスク
/* ---< 送信バッファへセット >--- */
_gSndBuf.Buf[ _gSndBuf.WriteIdx ] = Data; // データ格納
M_RING_INC(_gSndBuf.WriteIdx,C_SIZE_COM_BUF); // 書込みインデックス更新
if( _gSndBuf.ReadIdx == _gSndBuf.WriteIdx ) // 読込みインデックスに追いついた
{
return FALSE;
}
STMK0 = 0U; // 送信割込み許可
return TRUE;
}
//************************************************************************************************
// 機能概要 :送信開始
// 引数 :なし
// 戻り値 :TRUE(成功)、FALSE(エラー)
// 備考 :送信割込動作中は自動で送信されるので起動の必要なし
//************************************************************************************************
static BOOL SendStart( void )
{
STMK0 = 1U; // 送信割込みマスク
/* ---< 送信データなしの場合 >--- */
if( _gSending == FALSE )
{
_gSending = TRUE;
SMR00 |= 0x0001U; // 転送前のバッファ空きでの割込み
TXD0 = _gSndBuf.Buf[_gSndBuf.ReadIdx]; // データをセット
M_RING_INC(_gSndBuf.ReadIdx,C_SIZE_COM_BUF); // 読込みインデックス更新
}
/* ---< 送信データありの場合 >--- */
else
{
;/* Do Nothing 割り込み処理内で書き込みインデックスまで勝手に送信する */
}
STMK0 = 0U; // 送信割込み許可
return TRUE;
}
受信処理
ここでの例は、かなりデフォルメして省略しています
基本的なデータ受信は、main関数内からRcvFrame関数を受信完了するまでコールします。ここでの受信完了は受信カウントが C_SIZE_COM_BUF に達したら完了としています。タイマー割込みでRcvFrame関数をコールするようなこともできますが、main関数内でコールするのが複雑にならず、私はお勧めします。ただし、main関数内とかで別の処理に例えば87us以上の時間を要する処理を入れると受信をスキップしてしまい受信できなくなります(ボーレート115200は1バイト87usで送信される)
設計方針にもよりますが、基本main関数のwhileループ内で時間のかかる処理はさせない方がいいです。例えばセンサ読込みでセンサがオンになるまでセンサ監視関数内で待つなどの処理を行うなど、CPU時間を無駄使いしていることや、ウォッチドックなどの処理ができなくなってしまいます。
また、main関数でない関数でもwhileループを入れるのは避けるのが基本です。ともかく弊害が多いです
・割込み内受信バッファ(_gRcvBuf)はリングバッファとして利用します
・割込み関数内で受信されると書込みインデックス(_gRcvBuf.WriteIdx)が更新されます
・RcvFrame関数で常に書込みインデックス(_gRcvBuf.WriteIdx)を監視してデータありなら、割込み内受信バッファ(_gRcvBuf)に受信データを格納します
・指定カウント分受信したらRcvFrame関数はTRUEを返します
#define C_SIZE_COM_FRAME 22 // 送受信フレームの最大長
//*************************
// UART
// 割込外送受信フレーム
//*************************
typedef struct
{
uint16_t Count; // 送受信バイト数
uint8_t Data[ C_SIZE_COM_FRAME ]; // 送受信データ
} T_COM_FRAME;
static T_COM_FRAME _gRcvFrame = { 0 }; // 割込外受信フレーム
void main( void )
{
_gRcvFrame.Count = 0; // 割込外受信フレームの受信クリア
while( 1 )
{
if( TRUE == RcvFrame())
{
// C_SIZE_COM_FRAME 分のデータを受信完了
// ここに受信処理を入れる
_gRcvFrame.Count = 0; // 割込外受信フレームの受信クリア
}
}
}
BOOL RcvFrame( void )
{
uint8_t rcvData; // 1バイト受信データ
/* ---< 受信データなし >--- */
if( _gRcvBuf.ReadIdx == _gRcvBuf.WriteIdx )
{
return FALSE;
}
/* ---< 受信データあり >--- */
rcvData = _gRcvBuf.Buf[ _gRcvBuf.ReadIdx ]; // データ取得
M_RING_INC( _gRcvBuf.ReadIdx, C_SIZE_COM_BUF ); // 読込インデックス更新
_gRcvFrame.Data[ _gRcvFrame.Count++ ] = rcvData; // データセット
if( _gRcvFrame.Count >= C_SIZE_COM_FRAME )
{
return TRUE;
}
return FALSE;
}
送受信割込み
受信割込みは、1バイトしたら割込みが発生します。受信レジスタ(RXD0)を割込み内受信バッファ(_gRcvBuf)に受信データを格納します
送信割込みは、割込み内送信バッファの読込みインデックス(_gSndBuf.ReadIdx)を監視し、送信データありなら割込み内送信バッファのデータをTXD0に書込みます
データなしで割込みがかかった時に、「転送完了での割込み」でかかったなら_gSendingをFALSEにして転送完了として問題ないですが、「転送前のバッファ空きでの割込み」でかかったなら「転送完了での割込み」に変更させて「転送完了での割込み」の割込みを待ちます(これは念のための処理です)
#pragma interrupt IntRcv_0( vect = INTSR0 ) // ベクターテブルへの関数セット
#pragma interrupt IntSnd_0( vect = INTST0 )
#pragma interrupt IntErr_0( vect = INTSRE0 )
//*************************
// ビット
//*************************
#define BIT0 (0x00000001U)
#define BIT1 (0x00000002U)
#define BIT2 (0x00000004U)
#define BIT3 (0x00000008U)
#define BIT4 (0x00000010U)
#define BIT5 (0x00000020U)
#define BIT6 (0x00000040U)
#define BIT7 (0x00000080U)
//************************************************************************************************
// 機能概要:UART動作・割込み許可
// 引数 :なし
// 戻り値 :なし
// 備考 :
//************************************************************************************************
static void Start( void )
{
SO0 |= BIT0; // 出力Hiセット
SOE0 |= BIT0; // 出力許可
SS0 |= ( BIT1 | BIT0 ); // 送受信開始
STIF0 = 0U; // 送信割込み要求フラグクリア
SRIF0 = 0U; // 受信割込み要求フラグクリア
SREIF0 = 0U; // 受信エラー割込み要求フラグクリア
STMK0 = 0U; // 送信割込み許可
SRMK0 = 0U; // 受信割込み許可
SREMK0 = 0U; // 受信エラー割込み許可
}
//************************************************************************************************
// 機能概要:UART停止・割込み禁止
// 引数 :なし
// 戻り値 :なし
// 備考 :
//************************************************************************************************
static void Stop( void )
{
STMK0 = 1U; // 送信割込みマスク
SRMK0 = 1U; // 受信割込みマスク
SREMK0 = 1U; // 受信エラー割込みマスク
ST0 |= ( BIT1 | BIT0 ); // 送受信停止
SOE0 &= ~(BIT0); // 出力禁止
STIF0 = 0U; // 送信割込み要求フラグクリア
SRIF0 = 0U; // 受信割込み要求フラグクリア
SREIF0 = 0U; // 受信エラー割込み要求フラグクリア
}
//************************************************************************************************
// 機能概要:受信割込みハンドラ
// 引数 :なし
// 戻り値 :なし
// 備考 :
//************************************************************************************************/
static void __near IntRcv_0( void )
{
volatile uint8_t rx_data = RXD0; // レジスタ読込み
_gRcvBuf.Buf[ _gRcvBuf.WriteIdx ] = rx_data; // データ格納
M_RING_INC(_gRcvBuf.WriteIdx,C_SIZE_COM_BUF); // 書込インデックス更新
if( _gRcvBuf.WriteIdx == _gRcvBuf.ReadIdx ) // 書込が読込に追いついた
{
return;
}
}
//************************************************************************************************
// 機能概要:送信割込みハンドラ
// 引数 :なし
// 戻り値 :なし
// 備考 :転送完了で_gSendingをFALSEにセット。転送完了でなけれな送信バッファ内データを送信し続ける
//************************************************************************************************
static void __near IntSnd_0( void )
{
if( _gSndBuf.ReadIdx == _gSndBuf.WriteIdx ) // 読込みが書込みに追いついた場合
{
/* ---< 転送完了での割込み >--- */
if(( SMR00 & 0x0001U ) == 0U )
{
_gSending = FALSE; // 転送完了
}
/* ---< 転送前のバッファ空きでの割込み >--- */
else
{
SMR00 &= ~(0x0001U); // 転送完了割込みセット
}
}
else
{
SMR00 |= 0x0001U; // 転送前のバッファ空きでの割込み
TXD0 = _gSndBuf.Buf[ _gSndBuf.ReadIdx ]; // レジスタ書込み
M_RING_INC(_gSndBuf.ReadIdx,C_SIZE_COM_BUF); // 読込みインデックス更新
}
}
//************************************************************************************************
// 機能概要:受信エラー割込み
// 引数 :なし
// 戻り値 :なし
// 備考 :
//************************************************************************************************
static void __near IntErr_0( void )
{
volatile uint8_t err;
volatile uint8_t rx = 0;
rx |= RXD0; // ダミーリード
err = (uint8_t)( SSR01 & 0x0007U ); // エラー取得(フレーミング、パリティ、オーバーラン)
SIR01 = (uint16_t)err; // 同エラークリア
// ここでエラー処理追加
}