「UART2の設定例」と「UART2の送受信割込み」についての説明はしていません。ソースだけ記載していますので、UART0の説明と合わせて読んでください。レジスタ名などの設定が異なるだけで全く同じです
フレーム構造
送受信データをSTXコードとETXコードで囲うことで通信エラーを発見できます
だた、DATAやチェックサムCHKSUMがSTXやETXコードと同じになると通信できなくなります。そのため可変長コード対応としてDLE(Data Link Escape)コードをエスケープシーケンスのコードとして使ったりします
例えば、DATA1がSTXと同じだったりDLEと同じだったりする場合、DLEコードを挿入してDLEコードの次が、データであることを示します
可変長の構成だったとしても、受信データを扱うときに結局データ長のチェックがあった方がよく、可変長のメリットがあまり感じられなくてフレーム囲いはするが固定長での送受信をよく使っています
その方法ですがTYPEを設けて、このTYPEとデータ数をリスト化して持っておけばフレームでの送受信が可能となります(まあ一般的な通信だとは思います)
・受信する場合は、STXを受信してフレーム受信開始とします
・TYPEを受信して、例えば3バイト受信するとなった場合にDATA0~2を受信します
・チェックサムを受信し、TYPEからDATA2までのチェックサムを計算して、受信したチェックサムと比較します(不一致ならエラー処理)
・最後にETXを受信して受信完了となります
ACKとNACKの考え方
データ通信する時に、正常受信でACK、受信エラーでNACKを通知する仕様がそこそこあります。実際にはどういう仕様とするかはかなり面倒です。送受信が非同期だと送信側からどんどんデータを送り、受信側がエラーとした場合、送信側は何番目のなんのデータでエラーとなったか判断する必要があります
エラーが分かったとしてどういったリカバーをさせるかといのも、まず正解はないと思います
ACK、NACKよりは、送信側からTYPE0のデータを渡したら受信側がTYPE0を応答する。エラーならTYPE0でなくエラーコードを返せばよいことになります。まあなにがいいかは各企業での設計思想がありますので正解はないのですが、結構昔の仕様に従っているようなしがらみもあり、個人的にはACK、NACKは好きではないです(嫌いです)
フレーム送信処理
SetByte関数とSendStart関数はUART0の説明と同じなので省略します。フレーム送信処理はSendFrame関数で行います。TYPEからDATAnの送信データをフレームで囲んで送信します
・SendFrame関数はTYPEからDATAnまでの配列を受取ります
・割込み内送信バッファにSTXをセット
・割込み内送信バッファにTYPEからDATAnのデータをセットしながら、チェックサムを計算します
・割込み内送信バッファにチェックサムをセットします
・割込み内送信バッファにETXをセット
・SendStart関数をコールして送信します
#define C_SIZE_COM_BUF 40 // 割込内送受信バッファサイズ
#define C_SIZE_COM_FRAME 22 // 送受信フレームの最大長
#define C_DAT_STX 0x02 // コントロールコードSTX
#define C_DAT_ETX 0x03 // コントロールコードETX
#define M_RING_INC(a,b) (((b)<=(a+1)) ? (a=0):(a+=1)) // マクロ定義
//*************************
// UART
// 割込外送受信フレーム
//*************************
typedef struct
{
uint16_t Count; // 送受信バイト数
uint8_t Data[ C_SIZE_COM_FRAME ]; // 送受信データ
} T_COM_FRAME;
static volatile T_COM_BUFFER _gSndBuf = { 0 }; // 割込内送信バッファ
//************************************************************************************************
// 機能概要:汎用フレーム送信、STX,ETXを付加し固定長フレームでバッファにセット
// 引数 :pSndFrame 送信フレーム
// 戻り値 :TRUE(成功)、FALSE(エラー)
// 備考 :
//************************************************************************************************
BOOL SendFrame( T_COM_FRAME* pSndFrame )
{
uint16_t index = 0; // 送信フレームインデックス
uint8_t chkSum = 0; // チェックサム計算
//*************************
// STX処理
//*************************
if( FALSE == SetByte( C_DAT_STX )) // STXセット
{
return FALSE;
}
//*************************
// データ処理
//*************************
for( ; index < pSndFrame->Count; index++ ) // メッセージ・データセット
{
chkSum += pSndFrame->Data[ index ];
if( FALSE == SetByte( pSndFrame->Data[ index ] ))
{
return FALSE;
}
}
//*************************
// チェックサム処理
//*************************
if( FALSE == SetByte( chkSum ))
{
return FALSE;
}
//*************************
// ETX処理
//*************************
if( FALSE == SetByte( C_DAT_ETX )) // ETXセット
{
return FALSE;
}
return SendStart(); // 書込みインデックス更新と送信
}
//************************************************************************************************
// 機能概要:送信バッファへ1バイトセットする
// 引数 :Data セットするデータ
// 戻り値 :TRUE(成功)、FALSE(エラー)
// 備考 :
//************************************************************************************************
static BOOL SetByte( uint8_t Data )
{
STMK2 = 1U; // 送信割込みマスク
/* ---< 送信バッファへセット >--- */
_gSndBuf.Buf[ _gSndBuf.WriteIdx ] = Data; // データ格納
M_RING_INC( _gSndBuf.WriteIdx, C_SIZE_COM_BUF ); // 書込みインデックス更新
if( _gSndBuf.ReadIdx == _gSndBuf.WriteIdx ) // 読込みインデックスに追いついた
{
return FALSE;
}
STMK2 = 0U; // 送信割込み許可
return TRUE;
}
//************************************************************************************************
// 機能概要:送信開始
// 引数 :なし
// 戻り値 :TRUE(成功)、FALSE(エラー)
// 備考 :送信割込動作中は自動で送信されるので起動の必要なし
//************************************************************************************************
static BOOL SendStart( void )
{
STMK2 = 1U; // 送信割込みマスク
/* ---< 送信データなしの場合 >--- */
if( _gSending == FALSE )
{
_gSending = TRUE;
SMR10 |= 0x0001U; // 転送前のバッファ空きでの割込み
TXD2 = _gSndBuf.Buf[ _gSndBuf.ReadIdx ]; // データをセット
M_RING_INC( _gSndBuf.ReadIdx,C_SIZE_COM_BUF); // 読込みインデックス更新
}
/* ---< 送信データありの場合 >--- */
else
{
;/* Do Nothing 割り込み処理内で書き込みインデックスまで勝手に送信する */
}
STMK2 = 0U; // 送信割込み許可
return TRUE;
}
フレーム受信処理
RcvFrame関数は、main関数のwhile文内からコールします。「RL-78UART0操作例の受信処理」を参照してください。受信完了は、RcvFrame関数からの戻り値が「enComStatus_RcvEtx」の場合となります
RcvFrame関数による受信処理
・受信データがあればデータをrcvDataに受信します。そのデータがどんなデータなのかRcvByte関数でチェックします
・受信データチェックはSTX、データ、ETXなどの受信状態をチェックし、受信状態毎の処理に分岐します
・enComStatus_RcvStxなら、割込外受信フレームgRcvFrameをクリアします
・enComStatus_RcvDatなら、割込外受信フレームgRcvFrameにデータを格納します
・enComStatus_RcvEtxなら、チェックサムを計算し異常ならエラーを戻します
・ETX受信でフレーム受信完了となります
RcvByte関数による受信データチェック
・先頭の処理でSTX受信チェックを行い、STXならステートを2番目の受信チェックに変更します。STXでなければエラーを戻します
・2番目の処理では、TYPEによるデータ長(チェックサム含む)をGetRcvLen関数(未記載)で取得し、ステートを3番目の受信チェックに変更します
・3番目の処理でデータ長分のデータを受信したら、次のデータがETXであることをチェックします。ETXでなければエラーを戻します
//*************************
// UART
// 受信ステータス
//*************************
typedef enum
{
enComStatus_RcvNon = 0, // 受信データなし
enComStatus_RcvStx, // STX受信
enComStatus_RcvDat, // データ受信中
enComStatus_RcvEtx, // フレーム受信完了
enComStatus_RcvNotStx, // 先頭がSTXでない無効データ受信
enComStatus_RcvNotEtx, // 最後がETXでない無効データ受信
enComStatus_RcvErr, // エラー発生
enComStatus_RcvNotSupported // 未対応プロトコル
} COM_RCV_STATUS;
T_COM_FRAME gRcvFrame = { 0 }; // 割込外受信フレーム
void main( void )
{
_gRcvState = enComState_RcvStandby;
while( 1 )
{
if( enComStatus_RcvEtx== RcvFrame())
{
// ここに受信完了処理を入れる
_gRcvState = enComState_RcvStandby;
}
}
}
//************************************************************************************************
// 機能概要 :受信バッファからフレームデータを取得、成功の場合読込みインデックスを更新
// 引数 :なし
// 戻り値 :enComStatus_RcvNon 受信データなし
// :enComStatus_RcvStx STX受信
// :enComStatus_RcvDat データ受信中
// :enComStatus_RcvEtx フレーム受信完了
// :enComStatus_RcvErr エラー発生
// 備考 :
//************************************************************************************************
COM_RCV_STATUS RcvFrame( void )
{
uint16_t index = 0;
uint8_t checkSum = 0;
COM_RCV_STATUS rcvStatus; // 1バイト受信結果
uint8_t rcvData; // 1バイト受信データ
/* ---< 受信データなし >--- */
if( _gRcvBuf.ReadIdx == _gRcvBuf.WriteIdx )
{
return enComStatus_RcvNon;
}
//*************************
// 受信データ読み込み
//*************************
rcvData = _gRcvBuf.Buf[ _gRcvBuf.ReadIdx ]; // データ取得
M_RING_INC( _gRcvBuf.ReadIdx, C_SIZE_COM_BUF ); // 読込インデックス更新
rcvStatus = RcvByte( rcvData ); // 受信データ1バイト解析
//*************************
// 受信データ解析
//*************************
switch( rcvStatus )
{
/* ---< 読飛ばし >--- */
case enComStatus_RcvStx: // STX受信
gRcvFrame.Count = 0; // STX受信でクリア
return rcvStatus;
/* ---< データ受信 >--- */
case enComStatus_RcvDat:
if( gRcvFrame.Count >= C_SIZE_COM_FRAME )
{
return enComStatus_RcvErr;
}
gRcvFrame.Data[ gRcvFrame.Count++ ] = rcvData;
return rcvStatus;
/* ---< フレーム受信完了 >--- */
case enComStatus_RcvEtx:
/* ---< データ長のチェックとチェックサムのチェックを行い、チェックサムをデータから除き戻す >--- */
for( ; index < gRcvFrame.Count - 1; index++ )
{
checkSum += gRcvFrame.Data[ index ];
}
if( checkSum != gRcvFrame.Data[ gRcvFrame.Count - 1 ] )
{
return enComStatus_RcvErr;
}
gRcvFrame.Count--; // チェックサムデータ排除
return rcvStatus;
/* ---< 無効データ受信 >--- */
case enComStatus_RcvNotStx:
return enComStatus_RcvErr;
case enComStatus_RcvNotEtx:
return enComStatus_RcvErr;
/* ---< 未対応プロトコル >--- */
case enComStatus_RcvNotSupported:
return enComStatus_RcvErr;
default:
return enComStatus_RcvErr;
}
}
//************************************************************************************************
// 機能概要 :固定長フレームのバイト単位での解析
// :指定データに対し以下の処理を行う
// :SXT受信でenComStatus_RcvStxを戻す
// :ETX以外は受信データとしenComStatus_RcvDatを戻す
// :EXT受信でenComStatus_RcvEtxを戻す
// :先頭がSTXでない無効データ受信でenComStatus_RcvNotStxを戻す
// :最後がETXでない無効データ受信でenComStatus_RcvNotEtxを戻す
// :未対応プロトコルでenComStatus_RcvNotSupportedを戻す
// 引数 :Data 受信データ
// 戻り値 :処理内容参照
// 備考 :
//************************************************************************************************/
static COM_RCV_STATUS RcvByte( uint8_t Data )
{
static int16_t length; // 受信メッセージ長
const T_MES_TBL* table;
switch( _gRcvState )
{
/* ---< 先頭の処理 >--- */
case enComState_RcvStandby: // 待機中
if( Data == C_DAT_STX ) // 先頭がSTX
{
_gRcvState = enComState_RcvStx;
return enComStatus_RcvStx;
}
return enComStatus_RcvNotStx; // 先頭がSTXでない無効データ
/* ---< 2番目の処理 >--- */
case enComState_RcvStx: // STX受信済
length = GetRcvLen( Data );
_gRcvState = enComState_Rcv;
return enComStatus_RcvDat; // データ受信
/* ---< 3番目以降の処理 >--- */
case enComState_Rcv: // データ受信中
/* ---< 最後のバイト受信 >--- */
if( length == 0 )
{
if( Data == C_DAT_ETX ) // ETX受信
{
_gRcvState = enComState_RcvStandby;
return enComStatus_RcvEtx;
}
return enComStatus_RcvNotEtx; // 最後がETXでない無効データ
}
length--;
return enComStatus_RcvDat; // データ受信
default:
return enComStatus_RcvNotSupported;
}
}
UART2の設定例
・ボーレート 115200
・データ長 8ビット
・ストップ 1ビット
・パリティ なし
/* ---< UART2(割込無効) >--- */
STMK2 = 1U; // UART2 送信完了割込みマスク(INTST2)
STIF2 = 0U; // UART2 送信完了割込み要求フラグクリア
SRMK2 = 1U; // UART2 受信完了割込みマスク(INTSR2)
SRIF2 = 0U; // UART2 受信完了割込み要求フラグクリア
SREMK2 = 1U; // UART2 受信エラー割込みマスク(INTSRE2)
SREIF2 = 0U; // UART2 受信エラー割込み要求フラグクリア
/* ---< UART2(割込レベル) >--- */
STPR12 = 0U; // UART2 送信割込み優先1
STPR02 = 1U;
SRPR12 = 0U; // UART2 受信割込み優先1
SRPR02 = 1U;
SREPR12 = 0U; // UART2 受信エラー割込み優先1
SREPR02 = 1U;
/* ---< シリアル通信(TxD2) >--- */
SMR10 = (uint16_t)(0x0020U|0x0002U|0x0001U); // シリアルモード
SCR10 = (uint16_t)(0x8000U|0x0080U|0x0010U|0x0004U|0x0003U);// シリアル通信動作設定
SDR10 = 0x4400U; // b15-b8 転送クロック fMCK/( 2*(設定値+1) )
SOE1 |= BIT0; // 出力Hi
/* ---< シリアル通信(RxD2) >--- */
SIR11 = (uint16_t)(0x0004U|0x0002U|0x0001U); // エラーフラグクリア
SMR11 = (uint16_t)(0x0100U|0x0020U|0x0002U); // シリアルモード
SCR11 = (uint16_t)(0x4000U|0x0400U|0x0080U|0x0010U|0x0007U);// シリアル通信動作設定
SDR11 = 0x4400U; // b15-b8 転送クロック
NFEN0 |= BIT4; // RxD2 ノイズフィルタ有効
送受信割込み
#pragma interrupt IntRcv_2( vect = INTSR0 ) // ベクターテブルへの関数セット
#pragma interrupt IntSnd_2( vect = INTST0 )
#pragma interrupt IntErr_2( 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 )
{
SO1 |= BIT0; // 出力Hiセット
SOE1 |= BIT0; // 出力許可
SS1 |= ( BIT1 | BIT0 ); // 送受信開始
STIF2 = 0U; // 送信割込み要求フラグクリア
SRIF2 = 0U; // 受信割込み要求フラグクリア
SREIF2 = 0U; // 受信エラー割込み要求フラグクリア
STMK2 = 0U; // 送信割込み許可
SRMK2 = 0U; // 受信割込み許可
SREMK2 = 0U; // 受信エラー割込み許可
}
//************************************************************************************************
// 機能概要:UART停止・割込み禁止
// 引数 :なし
// 戻り値 :なし
// 備考 :
//************************************************************************************************
static void Stop( void )
{
STMK2 = 1U; // 送信割込みマスク
SRMK2 = 1U; // 受信割込みマスク
SREMK2 = 1U; // 受信エラー割込みマスク
ST1 |= ( BIT1 | BIT0 ); // 送受信停止
SOE1 &= ~(BIT0); // 出力禁止
STIF2 = 0U; // 送信割込み要求フラグクリア
SRIF2 = 0U; // 受信割込み要求フラグクリア
SREIF2 = 0U; // 受信エラー割込み要求フラグクリア
}
//************************************************************************************************
// 機能概要:受信割込みハンドラ
// 引数 :なし
// 戻り値 :なし
// 備考 :
//************************************************************************************************
static void __near IntRcv_2( void )
{
volatile uint8_t rx_data = RXD2; // レジスタ読込み
_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_2( void )
{
if( _gSndBuf.ReadIdx == _gSndBuf.WriteIdx ) // 読込みが書込みに追いついた場合
{
/* ---< 転送完了での割込み >--- */
if(( SMR10 & 0x0001U ) == 0U )
{
_gSending = FALSE; // 転送完了
}
/* ---< 転送前のバッファ空きでの割込み >--- */
else
{
SMR10 &= ~(0x0001U); // 転送完了割込みセット
}
}
else
{
SMR10 |= 0x0001U; // 転送前のバッファ空きでの割込み
TXD2 = _gSndBuf.Buf[ _gSndBuf.ReadIdx ]; // レジスタ書込み
M_RING_INC(_gSndBuf.ReadIdx,C_SIZE_COM_BUF); // 読込みインデックス更新
}
}
//************************************************************************************************
// 機能概要:受信エラー割込み
// 引数 :なし
// 戻り値 :なし
// 備考 :
//************************************************************************************************
static void __near IntErr_2( void )
{
volatile uint8_t err;
volatile uint8_t rx = 0;
rx |= RXD2; // ダミーリード
err = (uint8_t)( SSR11 & 0x0007U ); // エラー取得(フレーミング、パリティ、オーバーラン)
SIR11 = (uint16_t)err; // 同エラークリア
// ここでエラー処理追加
}