内容 - TI E2E Community

MSP430 複数ペリフェラルを並行動作させる方式
[ マルチ IO システム ]
Version3.0
2014.11.26
PIC 山内 一男
この方式では、複数の IO 処理を並行して実行できますので、MSP430 を幅広く利用できます。
OS を使用せずに、複数ペリフェラルを並行動作させて、IO 完了やソフトイベントなどを複数管理して通知することができま
す。 これにより、イベントドリブン型の並行処理システムを構築できます。
この方式のライブラリ自体は、デバイスシリーズに依存していません。 ドライバーと ISR に特別なマクロコールを組み込んで
ライブラリと連携して実現します。 ドライバーと ISR はデバイスシリーズに依存します。
現在は、MSP430G2553 と MSP430FR5969 に実装してあります。 別途マルチ IO システムとして example を提供
いたします。
内容
1 機能概要 .................................................................................................................. 2
1.1 従来方式との違い、改善点 .......................................................................................... 2
2 方式概要 .................................................................................................................. 2
2.1 イベント定義テーブル ( the table of events ) .............................................................. 3
2.2 IO 制御テーブル (The common table for IO control) .................................................. 3
2.2.1 IO 制御テーブルにアクセスするマクロ............................................................................ 3
2.2.2 LPM mode control ........................................................................................... 4
2.2.3 IO status and IO completed ............................................................................. 4
2.3 Event Manager の動作 ............................................................................................ 4
2.4
Event Manager の関数例........................................................................................ 4
3 ドライバーと ISR へのインプリメント ........................................................................................ 5
3.1 ドライバー .............................................................................................................. 5
3.2 ISR .................................................................................................................... 5
4 アプリケーションでのイベント処理例 ........................................................................................ 5
4.1 イベント ハンドリング ................................................................................................... 5
4.2 リトライ例 ............................................................................................................... 7
1
1 機能概要

複数ペリフェラル(デバイスと呼びます)を並行動作させて、その IO 完了などのイベントを1カ所で待ち合わせて、イベ
ントドリブン処理を可能にします。 イベントは16個を扱えます。

シーケンス処理を組めるように、特定のイベントだけ選別して待ち合わせる機能があります。

待ち合わせるときに、適切な LPM0、LPM3、LPM4 を選んで wait します。

delay イベントを起こすことで、タイムアウト監視やリトライが可能です。
1.1 従来方式との違い、改善点
複数のデバイスを動作させたとき、従来の __bis_SR_register(LPMx_bits+GIE); で待ち合わせる方法は、

wait が解除されたのはどのデバイスが完了したのか判断ができません。

LPM0/LPM3/LPM4 どれで待つかはアプリケーションが適切に判断する必要があります。

ISR が先に wait 解除し、アプリケーションが後から wait するクロス状態が起きると、永遠に wait することになります。
今回提案する方式は、ドライバーと ISR に所定の手続きを追加することで、

複数のペリフェラル IO の待ち合わせを可能にします。

複数同時の IO 完了も IOctl 領域の bit にプールされますので、取りこぼしがありません。

ISR の wait 解除とアプリケーションの IO 完了のタイミングが、クロスしても正常に判定できます。

特定のペリフェラル IO 完了だけ選別することができますので、A->B->C のように IO 完了をシーケンスに組むこともで
きます。

ドライバーが必要な LPMx を設定しますので、アプリケーションは LPM 制御の意識が不要になります。
2 方式概要

下記の構成要素が連携して動作します。
2

driver(デバイスに必要な初期設定をして ISR を呼ぶもの)と ISR は、IO 情報を IOctl に書き込んで、wait と
event を管理する event manager と相互連携して動きます。

AP は、IO 要求(サイズ、バッファアドレス)を IO_req にセットして、driver を呼びます。

driver は device に必要な設定をして IO を開始します。 ISR は device からの割り込みを処理して、IO 完了した
ら AP を active にします。

driver と ISR は、IO 情報を IOctl に書き込んで、event manager に通知します。 event manager は IO 中
の LPMx wait の制御と、AP へ IO 完了の device_ID を通知します。
2.1 イベント定義テーブル ( the table of events )

IO 要求や完了などのイベントは device ID で識別します。 これは、ユーザが再定義可能です。

イベントは下記のように bit 対応で定義します。 max16 個です。 “Driver.h”で定義します。
/* ========
device ID(uint16_t), user need to define
============== */
// the fixed ID
#define
Interval
0x01
// interval timer, timerA1
#define
DelayMs
0x02
// for delay_ms, ACLK(LPM3)-> WDT
// user need to define following IDs
#define
Timer0
0x04
// ACLK,LPM3
//#define
Timer1
0x08
// SMCLK,LPM0
#define
SPI4
0x20
// SMCLK,LPM0
#define
ADC
0x40
// ADC
#define
UartA0
0x80
// SMCLK,LPM0
#define
PIO1
0x0100
// Port 1,LPM4
#define
PIO2
0x0200
//
2
#define
PIO3
0x0400
//
3
#define
PIO4
0x0800
//
4
rev2.0
2.2 IO 制御テーブル (The common table for IO control)

下記の構造体が Driver.h で定義されており、実体は IOctl_driver.c にあります。

driver と ISR がこのテーブルの device ID に該当する bit を on/off して制御します。
// common control structure for drivers and check_IOwait()
struct IO_ctl {
uint16_t
Rq_LPM0;
// requested LPM0
uint16_t
Rq_LPM3;
//
uint16_t
St_busy;
// under IO executing
uint16_t
St_endMK;
// IO end mark
LPM3
};
extern struct IO_ctl IOctl;
// defined in IOctl_driver.c
2.2.1 IO 制御テーブルにアクセスするマクロ
アクセスの間違い防止と拡張性のため、下記のようなマクロを DriverG.h に用意してあります。 ドライバーと ISR はこのマ
クロを使用して、IOctl table にアクセスします。 dev は device ID を表します。
例 IOctl_start(UartA0);
/* -----
Macro define
----------------------------------------------- */
#define IOctl_LPM0on(dev) IOctl.Rq_LPM0 |= dev
3
#define IOctl_LPM0off(dev) IOctl.Rq_LPM0 &= ~dev
#define IOctl_LPM3on(dev) IOctl.Rq_LPM3 |= dev
#define IOctl_LPM3off(dev) IOctl.Rq_LPM3 &= ~dev
#define IOctl_start(dev) IOctl.St_busy |=dev; IOctl.St_endMK &= ~dev;
#define IOctl_stop(dev) IOctl.St_busy &=~dev; IOctl.St_endMK |= dev;
2.2.2 LPM mode control

LPM0 の wait を必要なデバイスは、Rq_LPM0 の対応 bit を on にします。 IO 完了で off します。

LPM3 の wait を必要なデバイスは、Rq_LPM3 の対応 bit を on にします。 IO 完了で off します。

Event Manager は、wait するとき Rq_LPM0 と Rq_LPM3 を見て LPMx
を判断して wait します。
2.2.3 IO status and IO completed

driver は、IO 開始するとき St_busy の device ID bit を on します。 IO 中か否かの識別に使います。

ISR は IO 完了(IO_completed)のとき、St_endMK の該当 bit を on にして、Active mode に復帰します。

この後、event manager が wake-up して、St_endMK: on の device ID を event として AP に返します。
2.3 Event Manager の動作

アプリケーションは、 __bis_SR_register(LPM0_bits+GIE); の代わりに Check_IOwait(); で IO
完了を待ちます。

event manager は、IO 完了デバイスを IOctl の St_endMK(LSB first)の on bit を探して見つけだし、その
device ID(uint16_t)を返します。 その時 St_endMK の device ID の bit は off します。

複数 IO 完了しているときは、この IOctl:St_endMK にプールされていますので、Check_IOwait();を呼ぶこと
で順次処理することができます。

もし IO 完了しているものがなければ(St_endMK=0)、Rq_LPM0、Rq_LPM3 の bit が on の device がある
場合は、LPM0(優先)か LPM3 で、無ければ LPM4 で wait します。
2.4
Event Manager の関数例
1) uint16_t
Check_IOwait(uint8_t wait_type)
戻り値
dv_id
IO 完了したデバイス識別子(Device ID)が戻ります。
wait_type:
IOnowait
wait しません、IO 完了なしは dv_id=0 で戻ります。
IOwait
wait します、タイムアウトなしです。
2) uint16_t
Check_onlyDEV(uint16_t dv_id, uint8_t wait_type)
指定の dv-id イベントが起きるのを待ちます
4
3 ドライバーと ISR へのインプリメント
この方式では、ドライバーと ISR は IOctl を制御する所定の手続きの追加が必要です。
3.1 ドライバー

ドライバーは、wait するときに必要な LPM0/LPM3 を設定し、IO の開始を設定します。 下記2行追加します。
IOctl_LPM3on(UartA0);
IOctl_start(UartA0);
3.2 ISR

ISR は、IO 完了時には LPMx 解除と IO 終了を設定して、active に復帰します。
IOctl_LPM3off(UartA0);
IOctl_stop(UartA0);
__bic_SR_register_on_exit(LPM_all);

LPM_all = LPM4_bits+GIE
ISR は IO_req の U_status にエラー情報や IO 情報を返すことができます。 下記は timer が割り込み CCR 種
別を返す例です。 また、timer のように連続動作しながら、CCR 割り込みを通知する場合は、IOctl_stop(dev)
ではなく IOctl_Mark_end(dev)により割り込みイベントの通知だけをすることができます。
switch ( _even_in_range( TA1IV, 0x0A) ){
case 0x02:
// CCR1
P_tm1->U_status = 0x02;
// CCR1 interrupt
break;
case 0x0A:
// TAIFG
P_tm1->U_status = 0x01;
break;
default:
break;
}
IOctl_Mark_end(Timer1);
__bic_SR_register_on_exit(LPM_all);
// set active
4 アプリケーションでのイベント処理例
4.1 イベント ハンドリング

下記は、SPI スレーブのイベントドリブン処理の抜粋です。

前半 while までは、必要なデバイス(PIO、ADC、Timer0、SPI)の IO を開始しています。

while{}の中で各イベントの処理をしています。 switch(Dev_ID){}部分で、イベント(Dev_ID=PIO1、
ADC、SPI4)を case に区分して、個別の処理をしています。
// enable interrupt of P1.3, SW2
PIOx_IntEnable_HtoL( Pio1, &SW_req );
Interval_set( &Interval_req );
// start ADCtemp and timer0->TA0.0->ADC
5
// get my temperature TLV data
ADCtemp_getTLV( ADC_data );
ADCtemp_start( );
ADCtemp_IO_NoWait( &IO_ADC );
// enable ADC
timerA0_IO_NoWait( &IO_timer0 );
/*
-----
waiting event, endless loop
/*
// timer0 trigger ADC
---------------------------------- */
exit by SPI received start command, _cmd_Start
*/
while(1)
{
uint8_t text, status;
/*
-----
SPI transferring
----------------------------------- */
// sending RQSS if some messages leaved
SPI_receive();
Dev_ID = Check_IOwait( IOwait );
switch ( Dev_ID )
{
/*
-------- PIO1: SW2-> P1.3 interrupt ------------------------
case PIO1:
*/
// P1.3: SW2 interrupt
if( SW_req.U_status & SWbit )
{
LEDstatus ^= 0x00FF;
if( LEDstatus )
// toggle
{ P1OUT |= BIT0;
else
}
// LED on
{ P1OUT &= ~BIT0; }
// start interval to prevent from chattering of switchs
Interval_CCRx_start( _ccr1 );
Mess_set_cmdDATA( s_ID, Pio_ID, &PIO_event );
// send PIO event
}
break;
case Interval:
//
protecting from chattering
---------------
if( Interval_req.U_status == fg_CCR1 )
{
Interval_CCRx_stop( _ccr1 );
PIOx_ReEnable_HtoL ( Pio1, SWbit );
}
break;
/*
-------- ADC
---------------------------------------------- */
case ADC:
Mess_set_cmdDATA( s_ID, ADC_ID, &ADC_str );
// send ADC data
ADCtemp_IO_NoWait( &IO_ADC );
// restart ADC
break;
/*
-------
SPI4
-----------------------------------------------
*/
case SPI4:
status = Mess_get_CMD( &R_mess );
SPIs_stop();
if ( status != IO_completed )
{
// IO error
P1OUT |= BIT0;
// LED on
}
else
{
// IO completed
Mess_release_BUF();
// releasing the used buffer
switch ( R_mess.M_cmd )
{
case
_cmd_Start:
case
_cmd_PIO:
// restart
goto Start;
Mess_get_DATA( 2, &LEDstatus );
6
// LED on/off
if( LEDstatus )
P1OUT |= BIT0;
else
// LED on
P1OUT &= ~BIT0;
break;
case
_cmd_Test:
status = 0;
for(cnt=4; cnt< R_mess.size_DT; cnt++)
{
text = rvBuf[cnt] +1;
if ( text != rvBuf[cnt+1] )
{ status++; break; }
}
if ( status )
Mess_set_CMD( s_ID, sdBuf_ID, &NAK_str );
else
Mess_set_CMD( s_ID, sdBuf_ID, &ACK_str );
break;
default:
break;
}
//
end of SPI4 switch
}
break;
default:
break;
}
/* -----
switch end
----------------------------------- */
4.2 リトライ例

IO エラーが起きた時など、リトライしたいときは下記ように関数を呼ぶと、5ms 後に DelayMs イベントを起こすことがで
きます。 時間選択は Timer_driverG.h/Timer_driverFR.h に説明があります。 この関数は WDT をカウンタ
モードで利用しています。
msDelay_NoWait( _V5ms );
以 上
7