RX72T で CAN の検証で気がついた・・
RX72T で CAN の動作確認をした際、思ったように動作しない・・
※以前に CAN のサンプルを作成する際、RX64M 1 台で行っており、複数台で互いに通信する確認はしていなかった・・
- データ送信しても、データが送られない。
- データの受信もしない。
この感じは、割り込みぽぃなぁーと思い、
割り込み関係を確認したら、単純に CAN 関係割り込みが設定されていないだけだった・・
※本来、割り込みが正しく設定されない場合、初期化時にエラーを返す必要がある・・
- 標準割り込みは、ちゃんと実装していない事を思い出す。
- 新規にペリフェラルのマネージャーを実装した時に、追加していた。
- なので、デバイスによりバラバラで、統一性が無い・・・
- ここらで、ちゃんと実装しておこうと思う。
RX64M では、CAN 関係は、選択型割り込みBなので、問題無かった。
RX66T/RX72T では、CAN の割り込みは通常ベクターなので、通常ベクターの登録関数に CAN 関係の割り込みを追加する必要がある。
通常割り込みで、厄介なのは、割り込みベクター番号から、IER、IPR などの割り込み設定関係へのアクセスでは規則性が無い場合がある。
その為、対応する通常ベクターを全て実装しておく必要がある・・・
※単純には、割り込みベクター番号から、IER、IPR レジスターを推定出来ない・・
また、IPR は、シェアされて共通になっている場合もある。
割り込み関係の整理
初期の実装では、割り込み設定は、ペリフェラル別に行っていた。
※初期の実装では、ペリフェラルの定義に、割り込みベクターを「定数」として含めていなかったので、個別に対応する必要があった。
現在の実装では、割り込みベクター型や番号は、ペリフェラルの定義を参照する事で得られるようになっている。
↓現在のSCIクラステンプレートの実装:
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
@brief SCI 定義基底クラス
@param[in] base ベース・アドレス
@param[in] per ペリフェラル型
@param[in] txv 送信割り込みベクター
@param[in] rxv 受信割り込みベクター
@param[in] INT 送信終了割り込みベクター型
@param[in] tev 送信終了割り込みベクター
@param[in] pclk PCLK 周波数
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
template <uint32_t base, peripheral per, ICU::VECTOR txv, ICU::VECTOR rxv,
typename INT, INT tev, uint32_t pclk>
struct sci_t {
static const auto PERIPHERAL = per; ///< ペリフェラル型
static const auto TX_VEC = txv; ///< 受信割り込みベクター
static const auto RX_VEC = rxv; ///< 送信割り込みベクター
static const auto TE_VEC = tev; ///< 送信終了割り込みベクター
static const uint32_t PCLK = pclk; ///< PCLK 周波数
SCI などでは、割り込みを使う場合、受信と送信、同時に使う仕様なので、それでも良かった。
しかし、割り込みベクター別に設定を行うべきなので、その仕様を改めた。
//-----------------------------------------------------------------//
/*!
@brief 割り込みレベルを設定する
@param[in] per 周辺機器タイプ
@param[in] lvl 割り込みレベル(0の場合、割り込み禁止)
@return 成功なら「true」
*/
//-----------------------------------------------------------------//
static bool set_level(peripheral per, uint8_t lvl) noexcept
{
bool ena = lvl != 0 ? true : false;
switch(per) {
case peripheral::SCI1:
ICU::IPR.RXI1 = lvl;
ICU::IER.RXI1 = ena;
ICU::IPR.TXI1 = lvl;
ICU::IER.TXI1 = ena;
break;
case peripheral::SCI5:
ICU::IPR.RXI5 = lvl;
ICU::IER.RXI5 = ena;
ICU::IPR.TXI5 = lvl;
ICU::IER.TXI5 = ena;
break;
↑以前の実装では、ペリフェラル毎に割り込みを設定していた・・(今後、廃止する予定)
↓今後、割り込みベクター毎に登録する・・
//-----------------------------------------------------------------//
/*!
@brief 割り込みレベルを設定する
@param[in] vec 割り込み要因
@param[in] lvl 割り込みレベル(0の場合、割り込み禁止)
@return 成功なら「true」
*/
//-----------------------------------------------------------------//
static bool set_level(ICU::VECTOR vec, uint8_t lvl) noexcept
{
bool ena = lvl != 0 ? true : false;
switch(vec) {
case ICU::VECTOR::RXI1:
ICU::IER.RXI1 = 0;
ICU::IPR.RXI1 = lvl;
ICU::IER.RXI1 = ena;
break;
case ICU::VECTOR::TXI1:
ICU::IER.TXI1 = 0;
ICU::IPR.TXI1 = lvl;
ICU::IER.TXI1 = ena;
break;
かなり、大掛かりな修正なので、時間がかかりそう・・・
作業実績
RX24T | RX64M | RX71M | RX65N | RX72N | RX66T | RX72T | |
---|---|---|---|---|---|---|---|
icu.hpp | O | O | O | O | O | O | O |
icu_mgr.hpp | O | O | O | O | O | O | O |
all_compile (Debug)
Pass.
all_compile (Release)
Pass.
追記 (2021-05-11 08:12:55 Tuesday)
- ハードウェアーマニュアルを良く読むと、IR レジスタ、IER レジスタは、割り込み要因の番号と一致するようだ。
- IPR レジスタに関しては、割り込み要因番号が32以下の場合と、特定のレジスタ(RX24T)の場合に、例外的なアクセスを行えば良いらしい。
そこで、IPR クラスの [] オペレーターによるアクセスを少々工夫する事で、余分なコードを削減する事が出来た。
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
@brief IPR レジスタ @n
全て、下位4ビットが有効
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
template <uint32_t base>
struct ipr_t {
...
//-------------------------------------------------------------//
/*!
@brief []オペレータ
@param[in] vec 標準割り込みベクター型
@return IPR レジスターの参照
*/
//-------------------------------------------------------------//
volatile uint8_t& operator [] (VECTOR vec) noexcept {
uint32_t idx = 0;
switch(vec) {
case VECTOR::BUSERR: idx = 0; break;
case VECTOR::RAMERR: idx = 0; break;
case VECTOR::FIFERR: idx = 1; break;
case VECTOR::FRDYI: idx = 2; break;
case VECTOR::SWINT2: idx = 3; break;
case VECTOR::SWINT: idx = 3; break;
case VECTOR::CMI0: idx = 4; break;
case VECTOR::CMI1: idx = 5; break;
case VECTOR::CMWI0: idx = 6; break;
case VECTOR::CMWI1: idx = 7; break;
default: idx = static_cast<uint32_t>(vec); break;
}
return *reinterpret_cast<volatile uint8_t*>(base + idx);
}
};
typedef ipr_t<0x00087300> IPR_;
static IPR_ IPR;
icu_mgr クラスの「set_level()」関数では、以下のようにシンプルとなった。
//-----------------------------------------------------------------//
/*!
@brief 割り込みレベルを設定する
@param[in] vec 通常割り込みベクター型
@param[in] lvl 割り込みレベル(0の場合、割り込み禁止)
*/
//-----------------------------------------------------------------//
static void set_level(ICU::VECTOR vec, uint8_t lvl) noexcept
{
bool ena = lvl != 0 ? true : false;
ICU::IER.enable(vec, 0);
ICU::IPR[vec] = lvl;
ICU::IER.enable(vec, ena);
}