LCDの制御方法 dsPIC入門講座01
開発環境
基盤
- SPPBoard(dsPIC30F3014使用)
コンパイラ
- C30
はじめに
組み込みプログラムの入門として
1 ~ 9までの数字を0.1秒毎にカウントアップしてLCDの1マス目に表示するプログラムを作成しました。
LCDはSD1602HUOBを使用します。
それでは、仕様書の読み方から解説していきたいと思います。
LCDの仕様書から必要な情報を読み取ろう
基礎知識
R/S端子
- Low : コマンドと認識
- High: 文字データと認識
R/W端子
- Low : Writeモード
- High: Readモード
E端子
- Low -> Highに変化で命令を回収
今回はR/WはGNDに落としているため常にWriteモードです。
E端子は意図的な命令であることと信号を送るタイミングを知らせるために使います。
用意されたコマンド
Clear Display 0000 0001
Cursor At Home 0000 0010
Entry Mode Set 0000 0100
Display ON/OFF 0000 1000
Cursor/DisplayShift 0001 0000
Function Set 0010 0000
CGRAW AddressSet 0100 0000
DDRAW AddressSet 1000 0000
どの位置に1を立てるかでどのコマンドかが判別されます。
LCDの初期化手順
電源ON
15ms以上待つ
8ビットモードに設定
4.1ms以上待つ
8ビットモードに設定
100us以上待つ
8ビットモードに設定
4ビットモードに設定
ファンクション設定
ディスプレイOFF
ディスプレイON
エントリーモード
LCDの制御関数を作ろう
まず、ピンの名前をわかりやすく定義しましょう。
#define LCD_RS LATBbits.LATB5
#define LCD_E LATBbits.LATB4
#define LCD_D4 LATBbits.LATB9
#define LCD_D5 LATBbits.LATB10
#define LCD_D6 LATBbits.LATB11
#define LCD_D7 LATBbits.LATB12
8ビットモード用指令関数
void lcd_out8(unsigned char dat){
LCD_RS = 0;
LCD_E = 1;
LCD_D4 = (dat & 0x01);
dat = dat >> 1;
LCD_D5 = (dat & 0x01);
dat = dat >> 1;
LCD_D6 = (dat & 0x01);
dat = dat >> 1;
LCD_D7 = (dat & 0x01);
wait_usec(50);
LCD_E = 0;
wait_usec(50);
}
- LCD_RS = 0
LCDにコマンドを送信 LCD_E = 1
コマンドを回収(450nsecの時間が必要)LCD_D[4-7] = (dat & 0x01)
LCDの上位4本に指令を送信wait_usec(50)
上記の命令を回収LCD_E = 0
E端子をLowへ
上位ピンに一つずつ送る理由
LCDの上位4ピンは9, 10, 11, 12に接続されています。
char型は8ビットなのでまとめてLATBで出力に変更できないからです。
4ビットモード用指令関数
void lcd_out4(int rs, unsigned char dat){
unsigned char bk = dat;
LCD_RS = rs;
LCD_E = 1;
bk = bk >> 4;
LCD_D4 = (bk & 0x01);
bk = bk >> 1;
LCD_D5 = (bk & 0x01);
bk = bk >> 1;
LCD_D6 = (bk & 0x01);
bk = bk >> 1;
LCD_D7 = (bk & 0x01);
wait_usec(50);
LCD_E = 0;
wait_usec(50);
LCD_E = 1;
LCD_D4 = (dat & 0x01);
dat = dat >> 1;
LCD_D5 = (dat & 0x01);
dat = dat >> 1;
LCD_D6 = (dat & 0x01);
dat = dat >> 1;
LCD_D7 = (dat & 0x01);
wait_usec(50);
LCD_E = 0;
wait_usec(50);
LCD_RS = 0;
}
引数rsが0の時コマンド、1の時文字データとして扱います。
LCDへは上位4ビット、下位4ビットの順に送信します。
例として引数datに0010 1000が渡されたとします。
順番だけ見ればLCDは0010 0001と認識しそうですがLCD_D7が12ビット目なのを思い出すと
それぞれ反転して0010 1000となることがわかるでしょう。
LCD初期化関数
void lcd_format(void){
wait_msec(20);
lcd_out8(0x23);
wait_msec(10);
lcd_out8(0x23);
lcd_out8(0x22);
lcd_out4(0, 0x28);
lcd_out4(0, 0x0E);
lcd_out4(0, 0x06);
lcd_out4(0, 0x02);
}
仕様書よりも長めにwaitを入れています。
lcd_out8(0x23)
Function Set コマンド
0010 0011 -> (認識)0011 0000lcd_out8(0x22)
Function Set コマンド
0010 0010 -> (認識)0010 0000lcd_out4(0, 0x28)
Function Set コマンド
0010 1000lcd_out4(0, 0x0E)
Display ON/OFF コマンド
0000 1110lcd_out4(0, 0x06)
Entry ModeSet コマンド
0000 0110lcd_out4(0, 0x02)
Cursor At Home コマンド
0000 0010
1文字表示関数
void lcd_data(unsigned char asci){
lcd_out4(1, asci);
wait_usec(50);
}
文字列表示関数
void lcd_puts(unsigned char *str){
while(*str != 0x00){
lcd_out4(1, *str);
str++;
}
}
数字表示関数
void lcd_convert(unsigned int number){
lcd_data('0' + number);
}
画面消去関数
void lcd_clear(void){
lcd_out4(0, 0x01);
wait_usec(1650);
}
LCD制御関数を使ってみよう
まずはコンフィギュレーション設定をしましょう。
_FOSC(CSW_FSCM_OFF & XT_PLL8);
_FWDT(WDT_OFF);
_FBORPOR(PBOR_ON & BORV_20 & PWRT_64 & MCLR_EN);
_FGS(CODE_PROT_OFF);
_FOSC(CSW_FSCM_OFF & XT_PLL8)
クロック停止の際の処理を監視せず、内部の別クロック源への切り替えもしないので
CSW(クロック切り替え)とFSCM(クロックのエラー検出)をOFFに設定
8x10MHz(外部発振器) = 80MHz(クロック周波数)_FWDT(WDT_OFF)
特にシステムの監視を行わないためウォッチドックタイマを無効_FBORPOR(PBOR_ON & BORV_20 & PWRT_64 & MCLR_EN)
電源ON直後に動作をリセット
電源OFF直後に動作停止
電源OFFを検出する電圧 -> 2.0V
電源ON直後のリセットパルス幅 -> 64msec
MCLRピンを有効_FGS(CODE_PROT_OFF)
コードプロテクト -> 読み出し、書き込みともにOFF
main文の中
unsigned char msgA[] = "start";
LATB = 0x0;
TRISB = 0x00;
lcd_format();
lcd_puts(msgA);
wait_msec(1000);
ConfigIntTimer1(T1_INT_PRIOR_3 & T1_INT_ON);
OpenTimer1(T1_ON & T1_GATE_OFF & T1_PS_1_64 &
T1_SYNC_EXT_OFF & T1_SOURCE_INT, 31250-1);
while(1);
ConfigIntTimer1(T1_INT_PRIOR_3 & T1_INT_ON)
優先度3 & 割り込み許可OpenTimer1(...)
タイマ周期=(4/クロック周波数)x(プリスケーラ分周比)x(PR1設定-1) 100msec = 4/80MHz x 64 x 31250
T1_ON : タイマ1を有効
T1_GATE_OFF : ゲートタイマモードOFF(外部信号の時間幅の計測をしない)
T1_PS_1_64 : プリスケーラ分周比を64に設定
T1_SYNC_EXT_OFF : タイマ非同期
T1_SOURCE_INT : 内部クロックを使用
31250-1 : PR1設定
タイマ1の定義
void _ISR _T1Interrupt(void){
static unsigned int TimeCount1 = 0;
IFS0bits.T1IF = 0;
TimeCount1++;
if(TimeCount1 <= 9){
lcd_clear();
lcd_convert(TimeCount1);
}else{
TimeCount1 = 0;
}
}
- IFS0bits.T1IF = 0
割り込みサブルーチンの構文では割り込みフラグをクリアできないため
以上で100msec毎に1~9までの範囲でカウントアップするプログラムは完成です。
blog comments powered by Disqus
Published
Category
SPPBoardTags
Profile
千葉工大産のロボットナビゲーションエンジニア
ros-jpの勉強会の主催やロボカップ世界大会優勝チームのリーダをやってました。
badge_description about badge's
総訪問者数
ツイート