#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パス設定) これでヘッダファイルを参照出来るようになったので、FreeRTOSConfig.hの先頭で、LPC17xx.hをincludeするようにしておきましょう。~ #include "LPC17xx.h" //Code Read Protectについては、コードを読み出されないようにするためのセキュリティ設定ですので、ここは外しておいて構いません。~ // //#ref(ledproject.png,left,nowrap,LEDプロジェクト設定) // //次にLibrariesタブの設定ですが、ここもすでに、CMSISv2p00_LPC17xxの設定が入っていると思います。~ //同様に、DriverLibを追加します。~ //ここは、「DriverLib」と記載するだけにしておいて下さい。~ // //#ref(libraries.png,left,nowrap,ライブラリ設定) // //最後に、Libraries Pathsの設定も同様にします。~ // //#ref(libpaths.png,left,nowrap,ライブラリパス設定) // //ここまでで、プロジェクトの設定は終わりですので、反映させて一旦ビルドをかけてみましょう。~ //QuickStartPanelからBuildを実行して、ledtest01.axfが出来上がればOKです。 // //「Project References」で指定した、CMSISv2p00_LPC17xxと、DriverLibのそれぞれのインクリメンタルビルドが行われてから、ledtest01のビルドが行われます。~ // // //** ソースコード //以下、ソースコード。~ //まず、最初から作られているmain.cをベースに、以下のincludeを追加します。~ // // // TODO: insert other include files here // #include "lpc17xx_pinsel.h" // #include "lpc17xx_gpio.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) // //** クロックの設定 //デバッガで停止中に、SysTick_Configの引数に指定している、SystemCoreClockにカーソルをあててみて下さい。~ //ポップアップが表示されて、100000000という数字が値として設定されているのがわかると思います。~ //(このために、SYSTICK_InternalInitじゃなくてSysTick_Configを使用していました) // //#ref(scc.png,left,nowrap,SystemCoreClock) // //実はこのLPC1769、このままでは100MHzでしか動作していません。~ //LPC1769より前のLPC1768ではよかったのですが、LPC1769は120MHzでの動作が可能ですので、これを修正しておきましょう。~ // //この設定は、CMSISv2p00_LPC17xxライブラリの方で行われています。~ //CMSISv2p00_LPC17xxプロジェクトのsystem_LPC17xx.cを開いて、PLL0CFG_Valの値を変更します。~ // // #define CLOCK_SETUP 1 // #define SCS_Val 0x00000020 // #define CLKSRCSEL_Val 0x00000001 // #define PLL0_SETUP 1 // #define PLL0CFG_Val 0x00050077 // //#define PLL0CFG_Val 0x00050063 // #define PLL1_SETUP 1 // #define PLL1CFG_Val 0x00000023 // #define CCLKCFG_Val 0x00000003 // #define USBCLKCFG_Val 0x00000000 // #define PCLKSEL0_Val 0x00000000 // #define PCLKSEL1_Val 0x00000000 // #define PCONP_Val 0x042887DE // #define CLKOUTCFG_Val 0x00000000 // //これで、120MHzで動作するようになりました。~ //こちらの詳細につきましては、以下のblogがとてもわかりやすいと思います。~ //参考にさせていただきました。 // //[[PS3とLinux、電子工作も-LPCXpresso LPC1769の120MHzクロック設定:http://todotani.cocolog-nifty.com/blog/2011/07/lpcxpresso-lp-1.html]] // //以上で、非RTOS版のLEDチカチカは終了です。~