- 追加された行はこの色です。
- 削除された行はこの色です。
#analog
#norelated
#contents
* LCD文字表示(RTOS) [#bb712a69]
** MAPLE board [#ba36d854]
頑張ってハンダ付けしましょうw~
そんなに大変な作業ではないです。~
まぁ、私はフリー拡張スロットのコネクタのハンダ付け、一部失敗して斜めについてますけどね……。~
** LCDモジュール [#k233bed7]
MAPLE boardについてきたのは、[[TC1602E-25A:http://www.linkman.jp/user/shohin.php?p=61364]]という型番のLCDでした。~
このLCDは、4または8ビットモード(HD44780互換)ということですから、HD44780のデータシートを探してくることにします。~
というわけで、[[alldatasheet:http://www.alldatasheet.jp/datasheet-pdf/pdf/63673/HITACHI/HD44780.html]]から拾って来ました。~
基本的には、AVRの時にやった[[LCDサンプル]]と変わりません。~
** 構成 [#y147a72c]
MAPLE board附属の回路図を見れば分かる通り、DB0〜DB3は接続されていませんので、4ビットモードでの動作となります。~
また、R/WがGNDに接続されていますので、Writeモード固定ということになります。~
Writeモード固定なので、Busyフラグチェックは出来ませんから、コマンド実行後は実行時間分待つ必要があります。~
LPC1769は、LPC1768と同じなので、MAPLE boardの取扱説明書「8.3 LPCXPresso LPC1768 使用時」というところのピン配置を見ると、どのピンが何に割当たっているのかわかります。~
以下のようになっているみたいですね。~
|ポート番号|ピン番号|LCD|h
|P0|15|D5|
|P0|16|D6|
|P0|17|D4|
|P2|1|RS|
|P2|2|E|
|P2|3|D7|
** プロジェクト [#o8640e7e]
まずは、ledtest02と同じように、FreeRTOSの新規プロジェクトを作って、ひと通り設定をしておきます。~
プロジェクト名は、「lcdtest」としました。~
元々のLEDと今回のLCD、それに全体的な管理をするアプリケーションとしてのタスクを作って動かすようにしてみましょう。~
それぞれをモジュールとして管理し、モジュールを管理するためのデータをハンドル的に扱えるようにします。~
データをハンドル化し、ロジックと切り離すことによって、LEDが2つとか、LCDが2つとかになった場合でも、それぞれを独立して動かせるようになるはずです。~
今回の最終的なsrcフォルダ以下の構成は、以下のようになります。~
アプリケーションモジュール、LCDモジュール、LEDモジュールをフォルダごとに分け、それぞれに外部公開ヘッダ、内部用ヘッダ、ソースファイルがあります。~
#ref(folder.png,left,nowrap,プロジェクトフォルダ構成)
** ソースコード [#r84ebfbc]
*** 共通定義等 [#k9e1191b]
LEDのソースにもありましたが、ピンをGPIOとして使うのでも、ペリフェラルの機能を使うのでも、ポート番号とピン番号の指定が必要になります。~
なので、セットで扱えるような構造体を用意しておきます。~
共通で使うので、src直下に置いておきましょう。~
ファイル名はapldevice.hとしました。~
#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_ */
----
次に、各タスクのプライオリティ定義を用意しておきましょう。~
タスク間の関係は、アプリケーション全体で相互に意識しなければならないので、これもsrc直下に置いて共通で使います。~
ファイル名はapltask.hとし、これをincludeすればFreeRTOSのタスク機能が使えるように、FreeRTOS.hとtask.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という関数を定義しておきます。~
TaskDelayの実体は、apltask.cファイルをsrc直下に作って、以下のように作っておきます。~
#include "apltask.h"
void TaskDelay( portTickType time )
{
vTaskDelay( time / portTICK_RATE_MS );
}
vTaskDelayは、引数がtick数であり、msではありません。~
前回書きましたが、FreeRTOSConfig.h内のconfigTICK_RATE_HZで、毎秒1000tickという指定をしているので、たまたま1tickが1msと同義になっているだけにすぎません。~
configTICK_RATE_HZの値を書き換えてしまうと、ms単位の動作を期待しているのに、実際にsleepする時間が変化してしまいます。~
このため、msを指定できるラッパー関数を用意して、それに対応します。~
参考までに、portTICK_RATE_MSは、FreeRTOS内で以下のように定義されています。~
#define portTICK_RATE_MS ( ( portTickType ) 1000 / configTICK_RATE_HZ )
----
タスクの次は、イベントキューを使うための定義を用意します。~
イベントキューは、LCDに表示するためのデータをLCDタスクに送信するために使います。~
ファイル名はaplqueue.hとし、これをincludeすればFreeRTOSのキュー機能が使えるように、FreeRTOS.hとqueue.hを参照します。~
こちらも、キュー全体のリソース管理という意味から、src直下に置いて共通で使います。~
#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個分までためられるものとします。~
LCDの表示領域は、16文字*2行の最大32文字なので、一つのQUE_DATAに1文字をぶら下げたら最大サイズを食うことになりますが、まとめて書きたい時は文字列として一つのQUE_DATAで送ればいいですし、LCDタスクが処理しきる前にこのキューを食いつぶすような送り方をすることは、よほどないでしょう。~
----
*** メイン [#cdae8a84]
まず先に、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モジュール [#ve9bd177]
次に、LEDモジュールです。~
まずは、外部ヘッダのled.h。~
#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モジュール [#tfde4bc2]
次に、LCDモジュールです。~
まずは、外部ヘッダのlcd.h。~
#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モジュール [#bce77d3d]
最後に、APLモジュールです。~
まずは、外部ヘッダのapl.h。~
#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);
}
}
** ソースコード解説 [#t6a7ed0f]
//まず、メイン関数ですが、LEDがつながっているポート0の22番ピンの設定とON初期化はledtest01と同じです。~
//その後、LEDタスクを生成して、タスク動作をスタートさせるという流れになっています。~
//
//xTaskCreateはタスクの生成を行うマクロですが、第1引数にタスク関数の指定、第2引数にタスク名、第3引数にタスクのスタックサイズ、第4引数にタスクに渡すパラメータ、第5引数にタスクの優先度、第6引数にタスクハンドルを受け取る変数の参照を指定します。~
//
//第1引数のタスク関数は、戻り値なし(void)でvoid*型の関数を指定する必要があります。~
//また、この関数はreturnしてはいけません。~
//無限ループ等で関数から抜けないようにしておく必要があります。~
//LedTask関数も、そのように作っています。~
//
//第2引数のタスク名は、FreeRTOSConfig.h内のconfigMAX_TASK_NAME_LEN定義の文字数以下で、タスク名を指定します。~
//デフォルトでは12文字までですね。~
//終端文字を入れて12文字なので、実質11文字になります。~
//
// #define configMAX_TASK_NAME_LEN ( 12 )
//
//第3引数のスタックサイズには、そのタスクで使用するスタックの割り当てサイズを指定します。~
//スタックとは、ローカル変数や関数コール時の引数等を一時的に保存するための領域で、基本的には大きいサイズのローカル変数を用意したり、関数コールを繰り返して深いネストまで行くと、消費量が多くなります。~
//
//本当に必要なサイズが、実際にどれくらいかというのを厳密に調べるのは結構大変なのですが、FreeRTOSにはスタック領域の残量が最小でどれくらいだったかというのがわかる機能もありますので、それを指標に決めることも出来ます。~
//
//今回はひとまず、64を指定します。~
//実際のメモリサイズとしては、ここで指定した値に4をかけたものが確保されることになります。~
//つまり、64*4で256バイトのスタックが確保されます。~
//
//第4引数のパラメータですが、今回はタスクに何も渡すものがないので、NULLを指定しておきます。~
//
//第5引数のタスク優先度ですが、0からFreeRTOSConfig.h内のconfigMAX_PRIORITIES-1までの値が指定出来ます。~
//デフォルトでは5なので、0(低優先度)〜4(高優先度)の優先度が指定出来ます。~
//
// #define configMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 5 )
//
//ただし、CPUが何も実行するタスクがない場合、FreeRTOSが自動的に生成するアイドルタスクが実行されますが、この優先度は最低優先度の0になっています。~
//このため、優先度0はアイドルタスク専用としておき、1〜4までを使うのがいいと思います。~
//
//今回、LEDタスクとしての優先度は、アイドルタスクの次に低い優先度としておきます。~
//今後、優先度の高い他のタスクを動作させるようになり、そのタスクが大幅に処理時間を食うような事があった場合、優先度の低いLEDタスクには処理が回らず、LEDの点滅が行われなくなり、目で状況が確認できるという設計も出来るかもしれません。~
//
//第6引数のタスクハンドルですが、タスクハンドルを後で使用することがない場合には、受け取る必要もないので、NULLを指定します。~
//例えば、別のタスクからLEDタスクを削除したり、サスペンドしたりレジュームしたりする場合は、タスクハンドルが必要になるので、ここで受け取っておいたハンドルを使用します。~
//
//vTaskStartSchedulerは、アイドルタスクを作成して、各タスクの実行を開始する命令です。~
//アイドルタスクとLEDタスクが開始され、優先度の高いLEDタスクから動き始めます。~
//
//----
//
//最後に、タスク関数の中身ですが、基本的にはledtest01の無限ループの内容そのものです。~
//一つ違うのは、500ms待つための関数が、タスクを待ち状態にさせるものに変更になっているという点です。~
//
//vTaskDelayは、指定されたtick数分、タスクを待ち状態にするという関数ですが、FreeRTOSConfig.h内のconfigTICK_RATE_HZで、毎秒1000tickという指定をしているので、1tickは1msと同義になります。~
//
// #define configTICK_RATE_HZ ( ( portTickType ) 1000 )
//
//今回は、LEDタスク以外にはアイドルタスクしかないので、vTaskDelayでタスクが500msの待ち状態になっている間は、アイドルタスクが動いていることになります。~
//
//** 実行・デバッグ
** 実行・デバッグ [#u6ecacd5]
//ビルドして実行してみます。~
//当然ながら、見た目はledtest01の時と変わりません。~
//
//ただ、タスクという概念で動作するようになったので、今後色々なことを並行してやるのに、便利な作りになりました。~
//タスクのデバッグも、普通にブレークポイントをはるだけで可能です。~
//
//** スタックサイズ
//今回、スタックサイズに64(*4=128バイト)を指定しましたが、これが実際にどれくらい使われているかを調べてみます。~
//
//FreeRTOSは、xTaskCreateでタスクを生成する時に、指定された分のスタック領域を確保し、内容が0xa5で埋められます。~
//そして、スタックという特性上、確保されたメモリの後ろ側から使用します。~
//スタック領域が使用された場合、基本的にはそこのメモリアドレスの内容を書き換えるわけですから、値が変わっていれば、そこは使用された領域だというのがわかります。
//
//タスク処理中に任意のタイミングで、uxTaskGetStackHighWaterMarkという関数を呼ぶと、確保されているメモリの先頭から、0xa5でなくなる箇所まで遡り、遡った数/4の値を返します。~
//
//LedTask関数を、以下のように書き換えてみましょう。~
//
// void LedTask(void *param) {
// uint32_t state = 0;
// unsigned portBASE_TYPE freesize = 0;
// while (1) {
// vTaskDelay(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));
// freesize = uxTaskGetStackHighWaterMark(NULL);
// }
// }
//
//uxTaskGetStackHighWaterMarkをコールする箇所にブレークポイントを仕掛けておき、止まったらステップ実行して、freesizeに入る値を確認してみます。~
//今回は、35という値が帰って来ました。~
//
//ということは、35(140バイト)/64(256バイト)で、140バイトが空いているということになります。~
//
//もちろん、これは実際に動作した結果そうなっているというだけのことなので、この値が絶対にスタックの最大値だというわけではありません。~
//今回はたまたま、深い関数コールのネストが行われずにスタックがあまり使われなかっただけかもしれませんが、サイズとしてはこれの2倍近い値を確保していますので、問題ないでしょう。~
//
//自分でタスクのプログラムを書く場合、どこが一番深いネストで、最大のスタックサイズを取りうるところはどこなのかというのは、常に意識しておく必要があります。~
//メモリの容量制限の厳しいマイコンでは、特に意識しないとなりませんね。~
//
//また、これ以外にも、スタックオーバーフローを検知してフック関数を呼んでもらう方法等があります。~
//使う機会があれば、そのうち書いてみたいと思います。~