LCD文字表示(RTOS) †MAPLE board †頑張ってハンダ付けしましょうw LCDモジュール †MAPLE boardについてきたのは、TC1602E-25Aという型番のLCDでした。 というわけで、alldatasheetから拾って来ました。 基本的には、AVRの時にやったLCDサンプルと変わりません。 構成 †MAPLE board附属の回路図を見れば分かる通り、DB0〜DB3は接続されていませんので、4ビットモードでの動作となります。 LPC1769は、LPC1768と同じなので、MAPLE boardの取扱説明書「8.3 LPCXPresso LPC1768 使用時」というところのピン配置を見ると、どのピンが何に割当たっているのかわかります。
プロジェクト †まずは、ledtest02と同じように、FreeRTOSの新規プロジェクトを作って、ひと通り設定をしておきます。 元々のLEDと今回のLCD、それに全体的な管理をするアプリケーションとしてのタスクを作って動かすようにしてみましょう。 今回の最終的なsrcフォルダ以下の構成は、以下のようになります。 ソースコード †共通定義等 †LEDのソースにもありましたが、ピンをGPIOとして使うのでも、ペリフェラルの機能を使うのでも、ポート番号とピン番号の指定が必要になります。 #ifndef APLDEVICE_H_ #define APLDEVICE_H_ // ポートピンセット typedef struct _tagPORTPIN_SET{ uint8_t port_num; // ポート番号 uint8_t pin_num; // ピン番号 } PORTPIN_SET, *LPPORTPIN_SET; #endif /* APLDEVICE_H_ */ 次に、各タスクのプライオリティ定義を用意しておきましょう。 #ifndef APLTASK_H_ #define APLTASK_H_ #include "FreeRTOS.h" #include "task.h" #define APL_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 ) #define LED_TASK_PRIORITY ( tskIDLE_PRIORITY + 2 ) #define LCD_TASK_PRIORITY ( tskIDLE_PRIORITY + 2 ) #define APL_STACK_SIZE 64 #define LED_STACK_SIZE 64 #define LCD_STACK_SIZE 64 void TaskDelay( portTickType time ); #endif /* APLTASK_H_ */ タスクプライオリティの関係は、アプリケーションとしての動作をアイドルの一つ上に、表示系をその一つ上としました。 また、LEDサンプルの時に使ったvTaskDelayのラッパーとして、TaskDelayという関数を定義しておきます。 #include "apltask.h" void TaskDelay( portTickType time ) { vTaskDelay( time / portTICK_RATE_MS ); } vTaskDelayは、引数がtick数であり、msではありません。 参考までに、portTICK_RATE_MSは、FreeRTOS内で以下のように定義されています。 #define portTICK_RATE_MS ( ( portTickType ) 1000 / configTICK_RATE_HZ ) タスクの次は、イベントキューを使うための定義を用意します。 #ifndef APLQUEUE_H_ #define APLQUEUE_H_ #include "FreeRTOS.h" #include "queue.h" // イベントキューデータ typedef struct _tagQUE_DATA{ uint8_t evcode; // イベント番号 int32_t param; // パラメータ void *data; // イベントデータ } QUE_DATA, *LPQUE_DATA; #define LCD_QUE_LENGTH 32 #endif /* APLQUEUE_H_ */ キューのデータは汎用的にQUE_DATAとして定義し、これを送受信します。 そして、LCDのキューとしては、QUE_DATAを32個分までためられるものとします。 メイン †まず先に、main.cの中身を書いてしまいます。 #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 "led/led.h" #include "lcd/lcd.h" #include "apl/apl.h" static LED_DATA led_data; static LCD_DATA lcd_data; static APL_DATA apl_data; int main(void) { // TODO: insert code here PORTPIN_SET portpinset; // 管理データ初期化 LED_InitHandle( &led_data ); LCD_InitHandle( &lcd_data ); APL_InitHandle( &apl_data ); // LEDポートピン設定 portpinset.port_num = PINSEL_PORT_0; portpinset.pin_num = PINSEL_PIN_22; LED_Setting( &led_data, &portpinset ); // LCDポートピン設定 portpinset.port_num = PINSEL_PORT_0; portpinset.pin_num = PINSEL_PIN_17; LCD_Setting( &lcd_data, LCD_PORTPIN_TYPE_DATA4, &portpinset ); portpinset.pin_num = PINSEL_PIN_15; LCD_Setting( &lcd_data, LCD_PORTPIN_TYPE_DATA5, &portpinset ); portpinset.pin_num = PINSEL_PIN_16; LCD_Setting( &lcd_data, LCD_PORTPIN_TYPE_DATA6, &portpinset ); portpinset.port_num = PINSEL_PORT_2; portpinset.pin_num = PINSEL_PIN_1; LCD_Setting( &lcd_data, LCD_PORTPIN_TYPE_RS, &portpinset ); portpinset.pin_num = PINSEL_PIN_2; LCD_Setting( &lcd_data, LCD_PORTPIN_TYPE_E, &portpinset ); portpinset.pin_num = PINSEL_PIN_3; LCD_Setting( &lcd_data, LCD_PORTPIN_TYPE_DATA7, &portpinset ); // モジュール初期化 LED_Init( &led_data, LED_ONOFF_ON ); LCD_Init( &lcd_data ); APL_Init( &apl_data ); // ハンドル設定 APL_SetLCDHandle( &apl_data, &lcd_data ); // タスク生成 LED_Start( &led_data, ( int8_t* )"LED" ); LCD_Start( &lcd_data, ( int8_t* )"LCD" ); APL_Start( &apl_data, ( int8_t* )"APL" ); // スケジューラ実行 vTaskStartScheduler(); return 0; } LEDモジュール †次に、LEDモジュールです。 #ifndef LED_H_ #define LED_H_ // include定義 #include "lpc17xx_pinsel.h" #include "apldevice.h" #include "apltask.h" // LEDモジュールデータ typedef struct _tagLED_DATA{ xTaskHandle htask; // タスクハンドル PORTPIN_SET portpin; // ポートピンセット } LED_DATA, *LPLED_DATA; typedef enum _tagLED_ONOFF_MODE{ LED_ONOFF_OFF, LED_ONOFF_ON } LED_ONOFF_MODE; // LEDハンドル初期化 int LED_InitHandle( LPLED_DATA handle ); // LEDハード設定 int LED_Setting( LPLED_DATA handle, LPPORTPIN_SET lpportpin ); // LEDモジュール初期化 int LED_Init( LPLED_DATA handle, LED_ONOFF_MODE onoff ); // LEDタスクスタート int LED_Start( LPLED_DATA handle, const int8_t* const name ); #endif /* LED_H_ */ 次に、内部用ヘッダのledin.h。 #ifndef LEDIN_H_ #define LEDIN_H_ // LEDタスク void led_task( void *param ); #endif /* LEDIN_H_ */ 最後に、LEDモジュール本体の、led.cです。 // include定義 #include "lpc17xx_pinsel.h" #include "lpc17xx_gpio.h" #include "led.h" #include "ledin.h" // LEDハンドル初期化 int LED_InitHandle( LPLED_DATA handle ) { if( NULL == handle ){ return -1; } handle->portpin.port_num = PINSEL_PORT_0; handle->portpin.pin_num = PINSEL_PIN_0; handle->htask = NULL; return 0; } // LEDハード設定 int LED_Setting( LPLED_DATA handle, LPPORTPIN_SET lpportpin ) { if( NULL == handle || NULL == lpportpin ){ return -1; } handle->portpin = *lpportpin; return 0; } // LEDモジュール初期化 int LED_Init( LPLED_DATA handle, LED_ONOFF_MODE onoff ) { PINSEL_CFG_Type pincfg; // ピン設定構造体 if( NULL == handle ){ return -1; } if( NULL != handle->htask ){ return -1; } // LED初期化 pincfg.Funcnum = PINSEL_FUNC_0; pincfg.OpenDrain = PINSEL_PINMODE_NORMAL; pincfg.Pinmode = PINSEL_PINMODE_PULLUP; pincfg.Portnum = handle->portpin.port_num; pincfg.Pinnum = handle->portpin.pin_num; PINSEL_ConfigPin( &pincfg ); GPIO_SetDir( handle->portpin.port_num, ( 1 << handle->portpin.pin_num ), 1 ); if( LED_ONOFF_OFF == onoff ){ // OFFで初期化 GPIO_ClearValue( handle->portpin.port_num, ( 1 << handle->portpin.pin_num )); } else { // ONで初期化 GPIO_SetValue( handle->portpin.port_num, ( 1 << handle->portpin.pin_num )); } return 0; } // LEDタスクスタート int LED_Start( LPLED_DATA handle, const int8_t* const name ) { if( NULL == handle ){ return -1; } if( NULL != handle->htask ){ return -1; } // タスク生成 if( pdTRUE != xTaskCreate( led_task, name, LED_STACK_SIZE, handle, LED_TASK_PRIORITY, &handle->htask )){ return -1; } return 0; } // LEDタスク void led_task( void *param ){ LPLED_DATA handle = ( LPLED_DATA )param; uint32_t state = 0; // unsigned portBASE_TYPE freesize = 0; while (1) { vTaskDelay(500); state = GPIO_ReadValue( handle->portpin.port_num ); GPIO_ClearValue( handle->portpin.port_num, state & (1 << handle->portpin.pin_num)); GPIO_SetValue( handle->portpin.port_num, ~state & (1 << handle->portpin.pin_num)); // freesize = uxTaskGetStackHighWaterMark(NULL); } } LCDモジュール †次に、LCDモジュールです。 #ifndef LCD_H_ #define LCD_H_ // include定義 #include "lpc17xx_pinsel.h" #include "apldevice.h" #include "apltask.h" #include "aplqueue.h" typedef enum _tagLCD_PORTPIN_TYPE{ LCD_PORTPIN_TYPE_RS, LCD_PORTPIN_TYPE_E, LCD_PORTPIN_TYPE_DATA4, LCD_PORTPIN_TYPE_DATA5, LCD_PORTPIN_TYPE_DATA6, LCD_PORTPIN_TYPE_DATA7, LCD_PORTPIN_TYPE_MAX } LCD_PORTPIN_TYPE; // LCDモジュールデータ typedef struct _tagLCD_DATA{ xTaskHandle htask; // タスクハンドル xQueueHandle hque; // キューハンドル PORTPIN_SET portpin[LCD_PORTPIN_TYPE_MAX]; // ポートピンセット uint8_t ready; // 準備状態 } LCD_DATA, *LPLCD_DATA; // LCDハンドル初期化 int LCD_InitHandle( LPLCD_DATA handle ); // LCDハード設定 int LCD_Setting( LPLCD_DATA handle, LCD_PORTPIN_TYPE type, LPPORTPIN_SET lpportpin ); // LCDモジュール初期化 int LCD_Init( LPLCD_DATA handle ); // LCDタスクスタート int LCD_Start( LPLCD_DATA handle, const int8_t* const name ); // LCD準備状態取得 int LCD_GetReady( LPLCD_DATA handle ); // LCD文字送信 int LCD_SendChar( LPLCD_DATA handle, char ch ); // LCD文字列送信 int LCD_SendString( LPLCD_DATA handle, char *str ); // LCD数字送信 int LCD_SendNumber( LPLCD_DATA handle, int32_t num ); // LCD消去 int LCD_Clear( LPLCD_DATA handle ); // LCD位置設定 int LCD_SetPos( LPLCD_DATA handle, uint8_t x, uint8_t y ); #endif /* LCD_H_ */ 次に、内部用ヘッダのlcdin.h。 #ifndef LCDIN_H_ #define LCDIN_H_ // 送信モード typedef enum _tagLCDIN_SENDMODE{ LCDIN_SENDMODE_COMMAND, LCDIN_SENDMODE_DATA } LCDIN_SENDMODE; // イベントコード typedef enum _tagLCDIN_EVENTCODE{ LCDIN_EVENTCODE_SENDCHAR, // 文字表示 LCDIN_EVENTCODE_SENDSTR, // 文字列表示 LCDIN_EVENTCODE_SENDNUMBER, // 数字表示 LCDIN_EVENTCODE_SENDCOMMAND // コマンド送信 } LCDIN_EVENTCODE; // LCDタスク void lcdin_task( void *param ); // LCDデバイス初期化 void lcdin_devinit( LPLCD_DATA handle ); // LCDデバイス4bit送信 void lcdin_snd4bit( LPLCD_DATA handle, LCDIN_SENDMODE mode, uint8_t cmd ); // LCDデバイスコマンド送信 void lcdin_sendcmd( LPLCD_DATA handle, uint32_t cmd ); // LCDデバイス文字送信 void lcdin_sendchar( LPLCD_DATA handle, char data ); // LCDデバイス数字送信 void lcdin_sendnumber( LPLCD_DATA handle, int32_t data ); // LCDイベント送信 int lcdin_sendevent( LPLCD_DATA handle, LPQUE_DATA lpque ); #endif /* LCDIN_H_ */ 最後に、LCDモジュール本体の、lcd.cです。 // include定義 #include "lpc17xx_pinsel.h" #include "lpc17xx_gpio.h" #include "lcd.h" #include "lcdin.h" // LCDコマンド typedef enum _tagLCDIN_CMD_MODE{ LCDIN_CMD_CLEAR, // クリア LCDIN_CMD_SETPOS, // 位置設定 } LCDIN_CMD_MODE; static const uint8_t LCDCmd[] = { 0x01, // クリア 0x80, // 位置設定 }; static const uint8_t LCDCmdWait[] = { 0x01, // クリア 0x00, // 位置設定 }; // LCDハンドル初期化 int LCD_InitHandle( LPLCD_DATA handle ) { int i = 0; // ループ変数 if( NULL == handle ){ return -1; } for( i = 0; i < LCD_PORTPIN_TYPE_MAX; i++ ){ handle->portpin[i].port_num = PINSEL_PORT_0; handle->portpin[i].pin_num = PINSEL_PIN_0; } handle->htask = NULL; handle->hque = NULL; handle->ready = 0; return 0; } // LCDハード設定 int LCD_Setting( LPLCD_DATA handle, LCD_PORTPIN_TYPE type, LPPORTPIN_SET lpportpin ) { if( NULL == handle || 0 > type || LCD_PORTPIN_TYPE_MAX <= type || NULL == lpportpin ){ return -1; } handle->portpin[type] = *lpportpin; return 0; } // LCDモジュール初期化 int LCD_Init( LPLCD_DATA handle ) { PINSEL_CFG_Type pincfg; // ピン設定構造体 int i = 0; // ループ変数 if( NULL == handle ){ return -1; } if( NULL != handle->htask ){ return -1; } // LCD初期化 pincfg.Funcnum = PINSEL_FUNC_0; pincfg.OpenDrain = PINSEL_PINMODE_NORMAL; pincfg.Pinmode = PINSEL_PINMODE_PULLUP; for( i = 0; i < LCD_PORTPIN_TYPE_MAX; i++ ){ pincfg.Portnum = handle->portpin[i].port_num; pincfg.Pinnum = handle->portpin[i].pin_num; PINSEL_ConfigPin( &pincfg ); GPIO_SetDir( handle->portpin[i].port_num, ( 1 << handle->portpin[i].pin_num ), 1 ); GPIO_ClearValue( handle->portpin[i].port_num, ( 1 << handle->portpin[i].pin_num )); } return 0; } // LCDタスクスタート int LCD_Start( LPLCD_DATA handle, const int8_t* const name ) { if( NULL == handle ){ return -1; } if( NULL != handle->htask || NULL != handle->hque ){ return -1; } // キュー生成 handle->hque = xQueueCreate( LCD_QUE_LENGTH, sizeof( QUE_DATA )); if( NULL == handle->hque ){ return -1; } // タスク生成 if( pdTRUE != xTaskCreate( lcdin_task, name, LCD_STACK_SIZE, handle, LCD_TASK_PRIORITY, &handle->htask )){ vQueueDelete( handle->hque ); handle->hque = NULL; return -1; } return 0; } // LCD準備状態取得 int LCD_GetReady( LPLCD_DATA handle ) { if( NULL == handle ){ return -1; } return handle->ready; } // LCDタスク void lcdin_task( void *param ){ LPLCD_DATA handle = ( LPLCD_DATA )param; char *str = NULL; // 文字列表示用 QUE_DATA que_data; // イベントキューデータ unsigned portBASE_TYPE freesize = 0; lcdin_devinit( handle ); while (1) { if( pdTRUE != xQueueReceive( handle->hque, &que_data, portMAX_DELAY )){ continue; } switch( que_data.evcode ){ case LCDIN_EVENTCODE_SENDCHAR : // 文字表示 lcdin_sendchar( handle, ( char )que_data.param ); break; case LCDIN_EVENTCODE_SENDSTR : // 文字列表示 str = que_data.data; if( NULL != str ){ while( *str ){ lcdin_sendchar( handle, *str ); str++; } vPortFree( que_data.data ); } break; case LCDIN_EVENTCODE_SENDNUMBER : // 数字表示 lcdin_sendnumber( handle, que_data.param ); break; case LCDIN_EVENTCODE_SENDCOMMAND : // コマンド送信 lcdin_sendcmd( handle, ( uint32_t )que_data.param ); break; default : break; } freesize = uxTaskGetStackHighWaterMark(NULL); } } // LCDデバイス初期化 void lcdin_devinit( LPLCD_DATA handle ) { // 初期化コマンド送信 TaskDelay( 50 ); lcdin_snd4bit( handle, LCDIN_SENDMODE_COMMAND, 0b0011 ); TaskDelay( 5 ); lcdin_snd4bit( handle, LCDIN_SENDMODE_COMMAND, 0b0011 ); lcdin_snd4bit( handle, LCDIN_SENDMODE_COMMAND, 0b0011 ); lcdin_snd4bit( handle, LCDIN_SENDMODE_COMMAND, 0b0010 ); // Function Set lcdin_snd4bit( handle, LCDIN_SENDMODE_COMMAND, 0b0010 ); lcdin_snd4bit( handle, LCDIN_SENDMODE_COMMAND, 0b1000 ); // Display on/off lcdin_snd4bit( handle, LCDIN_SENDMODE_COMMAND, 0b0000 ); lcdin_snd4bit( handle, LCDIN_SENDMODE_COMMAND, 0b1111 ); // Clear Display lcdin_sendcmd( handle, ( LCDCmdWait[LCDIN_CMD_CLEAR] << 8 ) | LCDCmd[LCDIN_CMD_CLEAR] ); // Entry Mode lcdin_snd4bit( handle, LCDIN_SENDMODE_COMMAND, 0b0000 ); lcdin_snd4bit( handle, LCDIN_SENDMODE_COMMAND, 0b0110 ); handle->ready = 1; } // LCDデバイス4bit送信 void lcdin_snd4bit( LPLCD_DATA handle, LCDIN_SENDMODE mode, uint8_t cmd ) { uint8_t i = 0; // ループ変数 if( LCDIN_SENDMODE_COMMAND == mode ){ GPIO_ClearValue( handle->portpin[LCD_PORTPIN_TYPE_RS].port_num, ( 1 << handle->portpin[LCD_PORTPIN_TYPE_RS].pin_num )); // コマンドモード } else { GPIO_SetValue( handle->portpin[LCD_PORTPIN_TYPE_RS].port_num, ( 1 << handle->portpin[LCD_PORTPIN_TYPE_RS].pin_num )); // データモード } for( i = LCD_PORTPIN_TYPE_DATA4; i < LCD_PORTPIN_TYPE_MAX; i++ ){ GPIO_ClearValue( handle->portpin[i].port_num, ( 1 << ( handle->portpin[i].pin_num ))); GPIO_SetValue( handle->portpin[i].port_num, ((( cmd >> ( i - LCD_PORTPIN_TYPE_DATA4 )) & 1 ) << ( handle->portpin[i].pin_num ))); } GPIO_SetValue( handle->portpin[LCD_PORTPIN_TYPE_E].port_num, ( 1 << handle->portpin[LCD_PORTPIN_TYPE_E].pin_num )); // Enable TaskDelay( 1 ); GPIO_ClearValue( handle->portpin[LCD_PORTPIN_TYPE_E].port_num, ( 1 << handle->portpin[LCD_PORTPIN_TYPE_E].pin_num )); // NotEnable TaskDelay( 1 ); } // LCDデバイスコマンド送信 void lcdin_sendcmd( LPLCD_DATA handle, uint32_t cmd ) { lcdin_snd4bit( handle, LCDIN_SENDMODE_COMMAND, ( cmd >> 4 ) & 0x0f ); lcdin_snd4bit( handle, LCDIN_SENDMODE_COMMAND, cmd & 0x0f ); if(( cmd >> 8 ) & 0xff ){ TaskDelay(( cmd >> 8 ) & 0xff ); } } // LCDデバイス文字送信 void lcdin_sendchar( LPLCD_DATA handle, char data ) { lcdin_snd4bit( handle, LCDIN_SENDMODE_DATA, data >> 4 ); lcdin_snd4bit( handle, LCDIN_SENDMODE_DATA, data ); } // LCDデバイス数字送信 void lcdin_sendnumber( LPLCD_DATA handle, int32_t data ) { int32_t tmpnum = data; // 数字変換用 int8_t keta = 0; // 数字変換用 int8_t i= 0, j = 0; // ループ変数 if( 0 > data ){ lcdin_sendchar( handle, '-' ); data = -data; } if( 0 == data ){ tmpnum++; // 0の桁を1とするため } while( 0 != tmpnum ){ tmpnum /= 10; keta++; } for( i = keta - 1; i >= 0; i-- ){ tmpnum = 1; for( j = 0; j < i; j++ ){ tmpnum *= 10; } lcdin_sendchar( handle, 48 + data / tmpnum ); data %= tmpnum; } } // LCDイベント送信 int lcdin_sendevent( LPLCD_DATA handle, LPQUE_DATA lpque ) { if( NULL == handle->hque ){ return -1; } if( pdTRUE != xQueueSend( handle->hque, lpque, 0 )){ return -1; } return 0; } // LCD文字送信 int LCD_SendChar( LPLCD_DATA handle, char ch ) { QUE_DATA que_data; if( NULL == handle ){ return -1; } que_data.evcode = LCDIN_EVENTCODE_SENDCHAR; que_data.param = ( int32_t )ch; que_data.data = NULL; return lcdin_sendevent( handle, &que_data ); } // LCD文字列送信 int LCD_SendString( LPLCD_DATA handle, char *str ) { QUE_DATA que_data; size_t size = 0; // 確保サイズ int ret = -1; // 結果格納用 char *ptr = NULL; // コピー用 if( NULL == handle || NULL == str ){ return -1; } ptr = str; while( *ptr ){ size++; ptr++; } if( 0 == size ){ return -1; } que_data.evcode = LCDIN_EVENTCODE_SENDSTR; que_data.param = 0; que_data.data = pvPortMalloc( size ); if( NULL == que_data.data ){ return -1; } ptr = ( char* )que_data.data; while( *str ){ *ptr = *str; str++; ptr++; } ret = lcdin_sendevent( handle, &que_data ); if( 0 > ret ){ vPortFree( que_data.data ); } return ret; } // LCD数字送信 int LCD_SendNumber( LPLCD_DATA handle, int32_t num ) { QUE_DATA que_data; if( NULL == handle ){ return -1; } que_data.evcode = LCDIN_EVENTCODE_SENDNUMBER; que_data.param = num; que_data.data = NULL; return lcdin_sendevent( handle, &que_data ); } // LCD消去 int LCD_Clear( LPLCD_DATA handle ) { QUE_DATA que_data; uint32_t cmd = ( LCDCmdWait[LCDIN_CMD_CLEAR] << 8 ) | LCDCmd[LCDIN_CMD_CLEAR]; if( NULL == handle ){ return -1; } que_data.evcode = LCDIN_EVENTCODE_SENDCOMMAND; que_data.param = ( int32_t )cmd; que_data.data = NULL; return lcdin_sendevent( handle, &que_data ); } // LCD位置設定 int LCD_SetPos( LPLCD_DATA handle, uint8_t x, uint8_t y ) { QUE_DATA que_data; uint32_t cmd = ( LCDCmdWait[LCDIN_CMD_SETPOS] << 8 ) | LCDCmd[LCDIN_CMD_SETPOS]; if( NULL == handle ){ return -1; } if( 0 != y ){ cmd += 0x40; } cmd += x; que_data.evcode = LCDIN_EVENTCODE_SENDCOMMAND; que_data.param = ( int32_t )cmd; que_data.data = NULL; return lcdin_sendevent( handle, &que_data ); } APLモジュール †最後に、APLモジュールです。 #ifndef APL_H_ #define APL_H_ // include定義 #include "apldevice.h" #include "apltask.h" #include "lcd/lcd.h" // APLモジュールデータ typedef struct _tagAPL_DATA{ xTaskHandle htask; // タスクハンドル LPLCD_DATA hlcd; // LCDハンドル } APL_DATA, *LPAPL_DATA; // APLハンドル初期化 int APL_InitHandle( LPAPL_DATA handle ); // LCDハンドル設定 int APL_SetLCDHandle( LPAPL_DATA hapl, LPLCD_DATA hlcd ); // APLモジュール初期化 int APL_Init( LPAPL_DATA handle ); // APLタスクスタート int APL_Start( LPAPL_DATA handle, const int8_t* const name ); #endif /* APL_H_ */ 次に、内部用ヘッダのaplin.h。 #ifndef APLIN_H_ #define APLIN_H_ // APLタスク void apl_task( void *param ); #endif /* APLIN_H_ */ 最後に、APLモジュール本体の、apl.cです。 // include定義 #include "apl.h" #include "aplin.h" // APLハンドル初期化 int APL_InitHandle( LPAPL_DATA handle ) { if( NULL == handle ){ return -1; } handle->htask = NULL; handle->hlcd = NULL; return 0; } // LCDハンドル設定 int APL_SetLCDHandle( LPAPL_DATA hapl, LPLCD_DATA hlcd ) { if( NULL == hapl || NULL == hlcd ){ return -1; } hapl->hlcd = hlcd; return 0; } // APLモジュール初期化 int APL_Init( LPAPL_DATA handle ) { if( NULL == handle ){ return -1; } if( NULL != handle->htask ){ return -1; } return 0; } // APLタスクスタート int APL_Start( LPAPL_DATA handle, const int8_t* const name ) { if( NULL == handle ){ return -1; } if( NULL != handle->htask ){ return -1; } // タスク生成 if( pdTRUE != xTaskCreate( apl_task, name, APL_STACK_SIZE, handle, APL_TASK_PRIORITY, &handle->htask )){ return -1; } return 0; } void apl_task( void *param ){ LPAPL_DATA handle = ( LPAPL_DATA )param; unsigned portBASE_TYPE freesize = 0; // LCD準備待機 while( 1 != LCD_GetReady( handle->hlcd )){ TaskDelay(100); } while (1) { LCD_SendChar( handle->hlcd, 'A' ); LCD_SendNumber( handle->hlcd, 0 ); LCD_SendChar( handle->hlcd, 'B' ); LCD_SendNumber( handle->hlcd, 1234567890 ); LCD_SetPos( handle->hlcd, 0, 1 ); LCD_SendChar( handle->hlcd, 'C' ); LCD_SendNumber( handle->hlcd, 2 ); LCD_SendChar( handle->hlcd, 'D' ); LCD_SendNumber( handle->hlcd, -987654321 ); TaskDelay(5000); LCD_Clear( handle->hlcd ); LCD_SendString( handle->hlcd, "LCD\xc3\xbd\xc4\xc1\xad\xb3" ); TaskDelay(5000); LCD_Clear( handle->hlcd ); freesize = uxTaskGetStackHighWaterMark(NULL); } } ソースコード解説 †共通定義のところはある程度書いたので、メインから。 メイン †先頭はお決まりのincludeとCRPです。 まずは、LED、LCD、APLと、各モジュールのヘッダをincludeし、ハンドルとして使うためのモジュールを管理するデータの実体を宣言しています。 次に、LEDとLCDで使うポートとピンの設定をしています。 次に、各モジュールを使えるように初期化します。 そして、APLモジュールでLCDを操作するために、LCDのハンドルをAPLモジュールに設定しておきます。 最後に、各モジュールのタスクやキューを生成して、スケジューラ実行で実際にタスクを走らせています。 ハンドル的な考え方以外は、そんなに大したことしてないですね。 LEDモジュール †次に、LEDモジュールです。 外部ヘッダのled.hには、まずモジュールデータとして、自分自身のタスクハンドルと、自分が使うポートとピンを記憶しておく構造体を持ちます。 内部用ヘッダのledin.hは、外部には公開しないタスク関数のみの定義になります。 LEDモジュール本体の、led.cについては、基本的にはLEDチカチカ(RTOS)でやったものをモジュール化しただけです。 LCDモジュール †次に、今回のメイン、LCDモジュールです。 外部ヘッダのlcd.hには、まず、ポートピンのenumを定義しています。 モジュールデータとして、自分自身のタスクハンドルとコマンドを受け取るためのキューハンドル、ポートピンセットの構造体を持ちます。 そしてもう一つ、LCDは初期化に多少の時間を必要とするため、タスクの先頭で初期化処理を行います。 あとは、各種機能のプロトタイプですね。 内部用ヘッダのlcdin.hは、LCDの送信モード、キューで受け取った処理を切り分けるためのイベントコード、内部関数のプロトタイプになります。 最後に、LCDモジュール本体の、lcd.cですが、基本的な流れとしては、LCD各種機能用の命令が外部から呼ばれたら、必要なデータをキューへと詰めて、LCDタスク側でそれを受け取り処理します。 lcdin_taskでは、まずlcdin_devinitを呼んでLCDの初期化を行い、初期化が終わったらモジュールデータの準備フラグを立てます。 本来、複数のタスクから同時に参照・変更するような変数は、ミューテックスやセマフォで保護する必要がありますが、今回はタスク側からしか変更せず、参照タイミングもシビアなものではないので、特別なことはしていません。 lcdin_devinitでの初期化は、HD44780のデータシート通りです。 1ms以上の待ちが必要なものは、TaskDelayで待ちを入れています。 コマンドは2つくらいテーブルに持っていますが、他に使うものが増えたら、初期化部分も含めて、順次追加していきたいと思います。 LCD_SendCharやLCD_SendNumber、コマンド系については、LCDタスクへQUE_DATAを送信する時、内容をそのままQUE_DATAに詰めて送信していますが、LCD_SendStringだけは可変長の文字列なので、FreeRTOSが管理しているHeap領域を使用して文字列をコピーし、コピーした領域のポインタを送信するようにしています。 APLモジュール †最後のAPLモジュールは、タスク起動時にLCDが使用できるまで待って、5秒おきに文字、数字、文字列の表示と、「LCDテストチュウ」という表示を繰り返すという処理をしています。 実行・デバッグ † |