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テストチュウ」という表示を繰り返すという処理をしています。 実行・デバッグ † |