freertosというオープンソースなOSがあって、前にMSP430で使ってみたところ非常に有用だった。

psoc5用のdemoもあるみたいだし、ちょっと試してみよう。

Download the RTOS source code from here.  からフルソースをDLし、適当なところに解凍。
(この記事を書いている時点でのバージョンは7.0.2)

PSoC CreatorからFreeRTOSv7.0.2\Demo\CORTEX_CY8C5588_PSoC_Creator_GCC\FreeRTOS_Demo Workspace.cywrkを開くと、コンポーネントをアップデートしろと言ってくるので適当にアップデート。

Project : FreeRTOS_Demo
Schematic : TopDesign/TopDesign.cysch

UART_1 v 1.50 v 2.0
High_Frequency_PWM_0 v 1.50 v 2.0
High_Frequency_PWM_1 v 1.50 v 2.0
timer_clock v 1.0 v 1.50
timer_clock_1 v 1.0 v 1.50
Timer_20KHz v 1.50 v 2.0
Timer_48MHz v 1.50 v 2.0

Project : FreeRTOS_Demo
Design Wide APIs

cy_boot v 2.0 v 2.21


ビルドで何かに引っかかるんじゃないかと思ったがすんなり通った。warningすらゼロ。

  • P0[0] connects to Rx for UART input or P0[1] for loopback.
  • P0[1] connects to Tx for UART output or P0[0] for loopback.
  • P0[4] connects to LED1.
  • P0[5] connects to LED2.
  • P0[6] connects to LED3.
  • P0[7] connects to LED4.
  • P1[7] connects to SW1.
ここに書いてあるように結線した。

P20111115_1000000745

 今まで2~3本しか使わなかったジャンパ線がいきなり7本。
さすがにサンプルとは違うぜ。

それじゃ電源ONしてProgram。
P20111116_1000000748

おおー動いてるやん。
ここまでアッサリ動くとは。

 PCのほうは接続したCOMポートをTeraTermで57600bpsで開く。
WS000032[1]
おおーきてるきてる。

お、LCDの文字がかわった。
P20111116_1000000749


Fail?
UARTのループバックテストかな?

P20111116_1000000747
一度電源を切ってループバックの結線に変えてもう一度電源ON。
 
P20111116_1000000750
お、Pass1になった。
ジッタを計測してるのかな。
240nsのジッタ。
いいのか悪いのかわからないけど多分いいんだろう。

P20111116_1000000752
Passがインクリメントされてジッターは240nsで安定してる。


TopDesign.cyschを見ると
WS000033[1]
 ひゃーこれは細かい割り込みをいっぱい使ってそうだな。
でもそれだけか。
意外にシンプルかな。

ソースmain.cを見るとたくさんのテスト用タスクを起動しているっぽい。

/* Start the standard demo tasks.  These are just here to exercise the
kernel port and provide examples of how the FreeRTOS API can be used. */
vStartBlockingQueueTasks( mainBLOCK_Q_PRIORITY );
vCreateBlockTimeTasks();
vStartCountingSemaphoreTasks();
vStartDynamicPriorityTasks();
vStartMathTasks( mainINTEGER_TASK_PRIORITY );
vStartGenericQueueTasks( mainGEN_QUEUE_TASK_PRIORITY );
vStartIntegerMathTasks( mainINTEGER_TASK_PRIORITY );
vStartPolledQueueTasks( mainQUEUE_POLL_PRIORITY );
vStartQueuePeekTasks();
vStartSemaphoreTasks( mainSEM_TEST_PRIORITY );
vStartLEDFlashTasks( mainFLASH_TEST_TASK_PRIORITY );
vAltStartComTestTasks( mainCOM_TEST_TASK_PRIORITY, 57600, mainCOM_LED );
vStartInterruptQueueTasks();

/* Start the error checking task. */
  ( void ) xTaskCreate( vCheckTask, ( signed portCHAR * ) "Check", configMINIMAL_STACK_SIZE, NULL, mainCHECK_TASK_PRIORITY, NULL );


それでも、これをビルドしたときに出てくるメモリ使用状況は、
Flash used: 40456 of 262144 bytes (15.4 %).
SRAM used: 34264 of 65536 bytes (52.3 %).
とまだまだ入りそう。 
 
rptファイルに残るTechnology mapping summaryは
WS000034[1]
 となっている。
まだまだ余裕ということか。



新しくプロジェクトを作ってfreertosでhello worldをつくってみよう。

WS000035[1]
File→New→Project...

WS000037[1]
Empty PSoC 5 Designを選択して、名前や場所は適当に。
freertostest01とd:\test\psoc5とした。


WS000036[1]
TopDesign.cyschではとりあえずLCDとLEDを追加。


WS000039[1]
ピンアサインはとりあえずfreertosのデモに合わせた。


Workspace Explorerにfreertosのソースを追加。
WS000041[1]
こんなかんじ。
global.hは自分で作ったヘッダ。
中身は
#include <device.h>
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
 これだけ。
ソースファイルが増えた時いちいちインクルードするのが 面倒なので先に作っておく。
もちろんmain.cでもincludeしておく。

main.cはfreertosのデモから
prvHardwareSetup関数の
/* Port layer functions that need to be copied into the vector table. */
extern void xPortPendSVHandler( void );
extern void xPortSysTickHandler( void );
extern void vPortSVCHandler( void );
extern cyisraddress CyRamVectors[];

/* Install the OS Interrupt Handlers. */
CyRamVectors[ 11 ] = ( cyisraddress ) vPortSVCHandler;
CyRamVectors[ 14 ] = ( cyisraddress ) xPortPendSVHandler;
CyRamVectors[ 15 ] = ( cyisraddress ) xPortSysTickHandler;

LCD_Character_Display_Start(); 
これだけコピー。(これがなんだかはよくわからん)
 
vApplicationStackOverflowHook
vApplicationMallocFailedHook
は元々ほぼ空関数なのでそのままコピー。 

main関数はこんなかんじ。
#define TASK_PRIORITY ( tskIDLE_PRIORITY + 3 )
void main()
{
    /* Place your initialization/startup code here (e.g. MyInst_Start()) */

prvHardwareSetup();
    /* CYGlobalIntEnable; */ /* Uncomment this line to enable global interrupts. */

  xTaskCreate( vTestTask, ( signed portCHAR * ) "test", configMINIMAL_STACK_SIZE, NULL, TASK_PRIORITY, NULL );

vTaskStartScheduler();
for(;;)
    {
        /* Place your application code here. */
    }
}
CYGlobalIntEnableはいらないのかな?
とりあえずdemoのソースを軽く見ても見当たらないので、コメントのまま試そう。

vTestTask関数はこんな感じ。

void vTestTask( void *pvParameters )
{
LCD_Character_Display_ClearDisplay();
LCD_Character_Display_Position( 0, 0 );
LCD_Character_Display_PrintString( "HELLO" );
LCD_Character_Display_Position( 1, 0 );
LCD_Character_Display_PrintString("  WORLD");
for(;;){
}
}

ビルドして実行。

P20111116_1000000753

おおー動いた。

この後LEDを点滅させるようにしたり、tickとidleをフックさせるようにしてみて次のことがわかった。

freeRTOSConfig.hのconfigTICK_RATE_HZに指定した周期で勝手にタイマ割り込みが設定される。
tickをhookさせるとその割り込み時に呼ばれる。

idleフックはOSの仕様通り暇な時呼ばれる。

俺の好みとしては、SleepTimerをTICKにしたい。
そうするとidleフックでSleepにしてもSleepTimerで起きてTICKが通常どおり処理できるはず。
 port.cを改造してSleepTimerを使うようにしてみるか。


よく調べてみると、Cortex-MシリーズにはNVICという割り込み制御機構の中に、SYSTICKというTICKを扱う機能があったのだ。
さっき出てきたpendSVやSVCっていうのもNVICの機能らしい。

demoのport.cでは、この機能を使って割り込みをかけていたのだ。

PSoC3と同じように考えがちだがPSoC5はCortex-M3ということでかなり違いがあるな。


port.cを改造してSleepTimerをTICKに使うようにした。

FreeRTOSConfig.hにconfigUSE_TICK_SLEEPTIMERを追加

#define configUSE_TICK_SLEEPTIMER 1

port.cを改造

void xPortSysTickHandler( void )
{
unsigned long ulDummy;

#if configUSE_TICK_SLEEPTIMER
SleepTimer_1_GetStatus();
#endif

/* If using preemption, also force a context switch. */
#if configUSE_PREEMPTION == 1
*(portNVIC_INT_CTRL) = portNVIC_PENDSVSET;
#endif

ulDummy = portSET_INTERRUPT_MASK_FROM_ISR();
{
vTaskIncrementTick();
}
portCLEAR_INTERRUPT_MASK_FROM_ISR( ulDummy );
}
/*-----------------------------------------------------------*/

/*
 * Setup the systick timer to generate the tick interrupts at the required
 * frequency.
 */
void prvSetupTimerInterrupt( void )
{
#if configUSE_TICK_SLEEPTIMER
isr_SleepTimer_StartEx(xPortSysTickHandler);
SleepTimer_1_Start();
#else
/* Configure SysTick to interrupt at the requested rate. */
*(portNVIC_SYSTICK_LOAD) = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
*(portNVIC_SYSTICK_CTRL) = portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE;
#endif
SleepTimerを追加
WS000042[1]
とりあえず64msにした。

そして、1000/64=15.625なので、FreeRTOSConfig.hで
 #define configTICK_RATE_HZ ( ( portTickType ) 16 ) 

にした
 
ビルドして実行。
動いた。

次にIdleフック関数に
CyPmSaveClocks();
CyPmSleep(PM_SLEEP_TIME_NONE, PM_SLEEP_SRC_CTW);
CyPmRestoreClocks();
 
を入れてみた。
同じように動いた。
こうすることで、次のSleepTimerが入るまでSleepするということになるらしい。
今度消費電力も測ってみよう。