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
© Copyright 2024