#analog #norelated #contents * LEDチカチカ(RTOS) [#i310dd56] ** RTOS [#u1ca3865] RTOSとはなんぞやというのは、なかなか説明が難しいものではあるのですが、基本的には複数のタスクで動作し、タスク間でのリソースを共有出来る仕組みを持ったOSといったところでしょうか。~ [[Wikipedia:http://ja.wikipedia.org/wiki/%E3%83%AA%E3%82%A2%E3%83%AB%E3%82%BF%E3%82%A4%E3%83%A0%E3%82%AA%E3%83%9A%E3%83%AC%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0]]や、[[このあたり:http://www.kumikomi.net/archives/2008/12/32rtos.php?page=6]]が参考になるでしょうか。~ 書籍では、[[リアルタイムOSと組み込み技術の基礎:http://www.amazon.co.jp/exec/obidos/ASIN/4789833283/shella0x19-22]]がとても詳しく解説されていると思いますので、オススメです。~ μITRONなんかは、非常に有名ですね。~ 今回使用するFreeRTOSは、サイズが小さくシンプルで、色々なマイコンに対応しているので、覚えておくと移植も楽なのではないかと思います。~ FreeRTOSのリファレンスマニュアルやチュートリアルは、[[公式サイト:http://www.freertos.org/]]で有償で販売されています。~ が、APIリファレンスくらいであれば、[[公式サイト:http://www.freertos.org/]]の左側のメニューから、FreeRTOS->API Referenceで見ることができますので、まずはこれで感じを掴んでから、さらに深く知りたい場合には購入してみてもいいかもしれません。~ 私はまだ買ってませんけどw~ ** 構成 [#gb2699d7] 構成は、[[LEDチカチカ(RTOS)]]の方と変わりません。~ 基板上のLEDを、FreeRTOSを使用して点滅させます。~ ** プロジェクト [#oee75559] まずは、新規プロジェクトを作りますが、前回とは選ぶプロジェクトが違います。~ QuickStartPanelのNew projectから、LPC176Xの「FreeRTOS Project」を選択して、適当なプロジェクト名をつけます。~ ここでは、「ledtest02」としました。~ MCUはLPC1769を選び、途中Advanced OptionでFreeRTOSのrootを選べますが、とりあえずはこのままで。~ すると、以下の様な構成で、プロジェクトが出来上がります。~ #ref(ledtest02.png,left,nowrap,プロジェクト構成) FreeRTOS_なんとかというフォルダは、FreeRTOS用のファイルであり、ここは自分でいじることはありません。~ 自分でいじるのは、srcフォルダにある、cr_startup_lpc17.c、main.cと、FreeRTOSConfig.hになります。~ ざっと、デフォルトで作られたmain.cを見てみると、2つのタスクが作られて、vUserTask1は一定時間ごとに状態を反転させ、vUserTask2は一定時間ごとにカウンタを増やしているのがわかります。~ それぞれに関連性はないので協調して動くものではないですが、これらが見かけ上同時に、並行して動くんだなということは、なんとなくわかると思います。~ main関数は、これらのタスクを生成して、スタートさせているだけです。~ RTOSのイメージが、なんとなく掴めるでしょうか。~ 次にFreeRTOSConfig.hを見てみましょう。~ この中身をいじることにより、FreeRTOSの機能をカスタマイズすることが可能です。~ とりあえず直しておく必要があるのは、configCPU_CLOCK_HZです。~ これは、CPUクロックを設定する必要があるので、ledtest01で使ったのと同じ、SystemCoreClockを設定しておきます。~ #define configCPU_CLOCK_HZ ( ( unsigned long )SystemCoreClock ) ただし、実はこれではビルドが通りません。~ SystemCoreClockというのは、元々CMSISライブラリのsystem_LPC17xx.cに定義されているものであり、CMSISライブラリのincフォルダにあるsystem_LPC17xx.hでexternされています。~ このため、CMSISライブラリを参照する必要がありますが、ledtest01の時には、CMSIS Library to link project toを設定するところがあり、CMSISv2p00_LPC17XXを選択しましたが、今回はその指定がありませんでした。~ FreeRTOSを選んだ時には、FreeRTOSのソースが組み入れられるだけで、デフォルトではCMSISライブラリを使えるようには出来ていないようです。~ ledtest01の時と同じように、CMSISv2p00_LPC17xxと、DriverLibを使用してLEDの点滅をさせたいので、このプロジェクトに対して、これらを使えるように、設定を変更します。~ ledtest02のプロジェクトを、右クリックからプロパティを開きます。~ まず、「Project References」で、CMSISv2p00_LPC17xxと、DriverLibの両方にチェックを入れておきます。~ #ref(prjref.png,left,nowrap,プロジェクト参照) 次に、「C/C++ General」の「Paths and Symbols」を開いて、includeとライブラリの設定をします。~ IncludesタブのLanguagesがGNU Cのところを選択し、/CMSISv2p00_LPC17xx/incと、/DriverLib/incを追加します。~ #ref(includes.png,left,nowrap,Includeパス設定) そして、Librariesタブに、CMSISv2p00_LPC17xxと、DriverLibを追加します。~ #ref(libraries.png,left,nowrap,ライブラリ設定) 最後に、Library Pathsタブに、/CMSISv2p00_LPC17xx/Debugと、/DriverLib/Debugを追加します。~ #ref(librarypaths.png,left,nowrap,ライブラリパス設定) これでヘッダファイルを参照出来るようになったので、FreeRTOSConfig.hの先頭で、LPC17xx.hをincludeするようにしておきましょう。~ #include "LPC17xx.h" ビルドを行い、ledtest02.axfが出来上がればOKです。~ ビルドは通りましたが、実はまだ、ledtest01とはプロジェクトの設定が違うところがあります。~ プロジェクトのプロパティに、Symbolsというタブがあり、色々なシンボルとそれに対する値が定義されていると思います。~ Show built-in valuesというチェックを外すと、このプロジェクトに定義されているもののみが表示されます。~ ledtest01とledtest02の定義を比べてみましょう。~ |ledtest01のみ|ledtest02のみ|h |__REDLIB__|__NEWLIB__| |__USE_CMSIS|| ||GCC_ARMCM3| ||PACK_STRUCT_END| まず、__REDLIB__と__NEWLIB__の定義ですが、これはどのCライブラリを使用するか、という選択になります。~ [[CodeRedのサポートページ:http://support.code-red-tech.com/CodeRedWiki/RedlibAndNewlib]]に書いてある通り、NewLibは標準のライブラリ、RedLibはCodeRed社独自のライブラリということになります。~ RedLibの特徴としては、C90準拠でサイズが小さいため、メモリの制約が厳しい時に有効ですが、C++プロジェクトでは使用できないということになるようです。~ C++を使わない限りは、RedLibを使用するのがいいと思いますので、ここはledtest01にあわせて変えておきましょう。~ 使用ライブラリの切り替えは、QuickStartPanelのQuick Settingsから、Set library typeで、Redlib(none)を選びます。~ すると、シンボル定義や、リンクするライブラリの切り替えを全部自動でやってくれます。~ nohostやSemihostについては、デバッグ用だったりするので、とりあえずここでは選びません。~ #ref(sellib.png,left,nowrap,ライブラリ選択) 次にledtest01のみで定義されていた__USE_CMSISですが、これは文字通り、CMSISライブラリを使うためのものです。~ この定義があると、cr_startup_lpc17.cの中で、CMSISライブラリのSystemInitという初期化関数を呼び、主にクロック関係の初期化をしてくれます。~ やっておいた方がいいと思うので、ledtest02にもこの定義を追加しておきましょう。~ SymbolsタブでAddボタンを押し、以下のように入れておきます。~ #ref(usecmsis.png,left,nowrap,定義追加) ledtest02で定義されているGCC_ARMCM3は、FreeRTOSが適切な設定をincludeするために使用していますので、これはこのままにしておきます。~ もう一つのPACK_STRUCT_ENDですが、これは現状、どこでも使用されていません。~ ただ、FreeRTOSのサンプルプログラムで、構造体定義のパディングをなくすために使用しているようです。~ 現状あってもなくても影響はないはずですが、とりあえずはそのままにしておきます。~ FreeRTOSConfig.hの他の設定については、今のところはいじりません。~ 基本的なプロジェクトの設定はこんなところかと思いますが、最後にledtest01とledtest02のcr_startup_lpc17.cの違いを見ておきましょう。~ 内容としては、ledtest01の方が新しいようなのですが、ledtest02では、FreeRTOS用に書き換えられている箇所があります。~ 以下の3関数が、g_pfnVectorsという割り込み関数テーブルの中に「The SysTick handler」、「The PendSV handler」、「SVCall handler」として設定されています。~ extern void xPortSysTickHandler(void); extern void xPortPendSVHandler(void); extern void vPortSVCHandler( void ); これは、FreeRTOSがタスクスケジューリングをするために使うものなので、これらの割り込みは、自前では使うことができません。~ SysTick handlerはledtest01で使用しましたが、FreeRTOSで使用するので、同じような時間管理はできません。~ ただし、FreeRTOSにタスクを一定時間休止するような機能や、タイマーが用意されていますので、そちらを使うことになります。~ ** ソースコード [#d48a41c4] まずはざっくり全部消してしまって、ledtest01の最初の部分だけ持ってきましょう。~ #ifdef __USE_CMSIS #include "LPC17xx.h" #endif #include <cr_section_macros.h> #include <NXP/crp.h> // Variable to store CRP value in. Will be placed automatically // by the linker when "Enable Code Read Protect" selected. // See crp.h header for more information __CRP const unsigned int CRP_WORD = CRP_NO_CRP; // TODO: insert other include files here #include "lpc17xx_pinsel.h" #include "lpc17xx_gpio.h" 次に、FreeRTOSを使うためのヘッダをincludeします。~ 基本のヘッダと、今回はタスクを使うので、タスクのヘッダを指定します。~ #include "FreeRTOS.h" #include "task.h" //次に、指定時間waitするためのカウンタをグローバル変数として用意し、そのカウンタをインクリメントする割り込み関数、実際のwaitを行う関数をそれぞれ用意します。~ // // // TODO: insert other definitions and declarations here // volatile uint32_t msTicks = 0; // void SysTick_Handler(void) { // msTicks++; // } // void systick_delay (uint32_t delayTicks) { // uint32_t currentTicks = msTicks; // while ((msTicks - currentTicks) < delayTicks); // } // //そして、メイン関数は以下のようになります。~ // // int main(void) { // // TODO: insert code here // PINSEL_CFG_Type pincfg; // ピン設定構造体 // uint32_t state = 0; // // // LED初期化 // pincfg.Funcnum = PINSEL_FUNC_0; // pincfg.OpenDrain = PINSEL_PINMODE_NORMAL; // pincfg.Pinmode = PINSEL_PINMODE_PULLUP; // pincfg.Portnum = PINSEL_PORT_0; // pincfg.Pinnum = PINSEL_PIN_22; // PINSEL_ConfigPin(&pincfg); // GPIO_SetDir(PINSEL_PORT_0, (1 << PINSEL_PIN_22), 1 ); // GPIO_SetValue(PINSEL_PORT_0, (1 << PINSEL_PIN_22)); // // SysTick_Config(SystemCoreClock / 1000); // // while (1) { // systick_delay(500); // state = GPIO_ReadValue(PINSEL_PORT_0); // GPIO_ClearValue(PINSEL_PORT_0, state & (1 << PINSEL_PIN_22)); // GPIO_SetValue(PINSEL_PORT_0, ~state & (1 << PINSEL_PIN_22)); // } // return 0; // } // //** ソースコード解説 //まず、メイン関数の先頭からですが、LEDを使用するために、LEDがつながっているポート0の22番ピンの設定をします。~ //ピンの設定用構造体PINSEL_CFG_Typeは以下のようになっており、これに必要な値を設定してPINSEL_ConfigPinをコールすることにより、そのピンが使えるようになります。~ //(デフォルト値のままであればそのまま使えますが、一応……)~ // // typedef struct // { // uint8_t Portnum; /**< Port Number, should be PINSEL_PORT_x, // where x should be in range from 0 to 4 */ // uint8_t Pinnum; /**< Pin Number, should be PINSEL_PIN_x, // where x should be in range from 0 to 31 */ // uint8_t Funcnum; /**< Function Number, should be PINSEL_FUNC_x, // where x should be in range from 0 to 3 */ // uint8_t Pinmode; /**< Pin Mode, should be: // - PINSEL_PINMODE_PULLUP: Internal pull-up resistor // - PINSEL_PINMODE_TRISTATE: Tri-state // - PINSEL_PINMODE_PULLDOWN: Internal pull-down resistor */ // uint8_t OpenDrain; /**< OpenDrain mode, should be: // - PINSEL_PINMODE_NORMAL: Pin is in the normal (not open drain) mode // - PINSEL_PINMODE_OPENDRAIN: Pin is in the open drain mode */ // } PINSEL_CFG_Type; // //PINSELやGPIOの定義は、DriverLibの中にあるヘッダの定義です。~ //これらは、解凍したペリフェラルドライバライブラリの中にある、「LPC1700 Peripheral Driver Library Manual.chm」に、どのような命令があってどのような値が設定できるのか、詳しく載っていますので、DriverLibの中身を使う際にはこれを見て下さい。~ // //どのような設定をすればいいかは、ユーザーマニュアルを見て確認します。~ // //ユーザーマニュアルは[[NXP社のHPから検索して:http://www.nxp.com/technical-support-portal/#/tid=50809,sid=56890,bt=,tab=usermanuals,p=1,rpp=,sc=,so=]]ダウンロードできます。~ //[[LPC17xx User manual:http://www.nxp.com/documents/user_manual/UM10360.pdf]]を落として下さい。~ // //落としたら、「Chapter 8: LPC17xx Pin connect block」を開きます。~ //「Table 74」を見ると、今回使用するP0[22]は「Table 80」に記載があるようなので、そこを見ます。~ // //#ref(table74.png,left,nowrap,Table 74) // //「Table 80」を見ると、P0[22]は、Function00(0)の時はGPIO(汎用的なI/Oピン)として、Function01(1)の時はRTS1、Function11(3)の時はTD1として動作するように書かれています。~ // //#ref(table80.png,left,nowrap,Table 80) // //この、RTS1やTD1というのは何かというのは、「Table 73. Pin description」に記載があり、RTS1はUART1という機能のTD1はCAN1という機能の出力ピンとして使えるというのがわかります。~ // //#ref(table73.png,left,nowrap,Table 73) // //今回は、GPIOとしてこのピンを使いますので、機能は0番を指定する必要があり、PINSEL_CFG_TypeのFuncnumにはPINSEL_FUNC_0(0)を設定します。~ //PINSEL_CFG_TypeのOpenDrainは、オープンドレインにはしないので、PINSEL_PINMODE_NORMAL(0)を設定します。~ //PINSEL_CFG_TypeのPinmodeは、内蔵プルアップのままにするので、PINSEL_PINMODE_PULLUP(0)を設定します。~ //さらに、PortnumはPINSEL_PORT_0で、PinnumがPINSEL_PIN_22なのはいいですね。~ //詳細については、ユーザーマニュアルの同じ所に載っていますので、確認してみて下さい。~ // //最後に、この構造体を指定してPINSEL_ConfigPinを呼べば、これでP0[22]のピンを使用する準備は整いました。~ //---- //次に、そのピンに対して、実際に操作を行います。~ // //GPIO_SetDirは、指定したポートの、立てたビットのピンの入出力を設定します。~ //PINSEL_PORT_0の、PINSEL_PIN_22ビットのピンを、出力(1)用に設定します。~ // //これでH/Lを出力できるようになったので、まずはLEDをON状態に初期化します。~ //GPIOの命令は、GPIO_SetValueで指定したピンをHighに、GPIO_ClearValueで指定したピンをLowにします。~ //一つの命令でH/Lを設定するわけではないので、注意が必要です。~ // //ユーザーマニュアルの「Chapter 9: LPC17xx General Purpose Input/Output (GPIO)」を読むと理解が深まると思いますが、GPIOのレジスタには、SETレジスタとCLRレジスタがあり、これをラッパーしているのが前述の命令ということになります。~ // //#ref(setclr.png,left,nowrap,SETとCLR) // //まずはON状態で初期化するので、GPIO_SetValueにPINSEL_PORT_0の、PINSEL_PIN_22ビットのピンを指定すれば、この時点でLEDが点灯します。~ //---- //次に、SysTick_Configで、1msの割り込みを発生させます。~ //これは、DriverLibのSYSTICK_InternalInitを使用してもいいはずですが、これはCMSISライブラリの方の命令です。~ // //SysTick_Configは、引数に指定したクロックで割り込みを発生させます。~ //ここでは、システムコアクロックを1000で割った値を入れているので、1ms毎に割り込みが発生することになります。~ // //引数にmsを指定すればいいSYSTICK_InternalInitよりわかりにくいですが、こちらを選んだのには理由がありますので、後述します。~ // //SysTick_Configは、System Tick Timerの割り込みを発生させますが、この割り込み時に実行される関数は、今回のledtest01プロジェクトを作成した時に自動的に生成されている、cr_startup_lpc176x.c内に書かれています。~ //この中に、g_pfnVectorsという割り込み関数テーブルがあり、割り込み発生時にはこのテーブルを参照してSysTick_Handlerが呼ばれますが、実体はWEAKつきで宣言されており、無限ループで何もしないようになっています。~ // //今回、同名のSysTick_Handlerという関数を作った事により、WEAK付きの元々の関数は無視され、新たに作成した関数がSystem Tick Timerの割り込みでコールされるようになります。~ //---- //さて、後は、無限ループの中身ですが、最初は1msの割り込みカウンタが引数の500回を超えるまでwaitするサブ関数です。~ //500ms待って、LEDをトグルさせ、1秒周期の点滅にします。~ // //waitが終わったら、その時のポート0の状態を読み出し、ローカル変数に保存しています。~ // //次に、22番ピンがHighであればLowにするため、22番ピンの元の状態を指定してGPIO_ClearValueを呼びます。~ //元の状態がLowであれば、GPIO_ClearValueには0が指定されるので、状態としては何も変わらないことになります。~ // //さらに、22番ピンがLowであればHighにするため、22番ピンの元の状態を反転させたものを指定してGPIO_SetValueを呼びます。~ //元の状態がHighであれば、GPIO_SetValueには0が指定されるので、状態としては何も変わらないことになります。~ // //** 実行・デバッグ //ソースが出来て、ビルドが通ったら、いよいよ実行です。~ //LPC1769と一体になっている、LPC-LinkとPCをUSBで繋いで、QuickStartPanelのDebugを押します。~ // //#ref(debug.png,left,nowrap,デバッグ) // //初回は、ドライバのインストールなどがあり、時間がかかるかもしれません。~ //待つかキャンセルかを聞かれるダイアログが表示されることもあるようです。~ //また、USBを接続しなおしたりした場合にも、エミュレータ選択ダイアログが表示されることがありますので、その場合は接続しなおして再検索をするといいようです。~ // //無事に実行できると、自動的にmain関数の先頭でbreakがかかります。~ //Step Over(F6)でステップ実行を行い、最初のGPIO_SetValueでLEDが点灯することが確認できると思います。~ // //Resume(F8)を押せば、LEDが点滅します。~ //Suspendで、その時実行している箇所で停止します。~ //Terminate(Ctrl+F2)でデバッガは停止し、マイコン自体はそのまま動作します。~ // //お手軽にIDEでデバッグ出来るのは楽でいいですね。~ // //#u2b(v1xaoRxPuSY,w=640,h=360) //