AT90USB162でLCD(キャラクタディスプレイ)に表示しました。Atmel Studio 6.2でプログラムを開発し、FlipでAT90USB162フラッシュにプログラムを書き込みました。LCDは、PC 4002-B (POWERTIP TECHNOLOGY,INC.)で1行40文字で2行表示され、LCDコントローラにST7066Uを使用しています。LCDの仕様を次に示します。
LCDユニット基板の配線
LCDユニットはAT90USB162と接続するために、上記の制御信号(RS,E)およびデータ信号(DB4,DB5,DB6,DB7)をLCDのコネクタからブレッドボード用のリード線を出します。また、電源(ピン番号:2)とGND(ピン番号:1)はAT90USB162に接続しているUSBから取ります。
LCDのアノード(A)、カソード(K)、R/W端子、コントラスト(Vo)についてはLCD基板の裏側で配線しました。配線したLCDユニットの裏側を次に示します。
LCD表示プログラム
AT90USB162は16MHzで動作させるので、F_CPUに16000000ULと定義します。この定義は、util/delay.hで参照されます。AT90USB162とLCD間のの信号接続が異なる場合は、それぞれ定義しているデータラインと制御ラインの各信号の定義を変更します。
LCDコントローラへのコマンドは、HD44780の互換LCDコントローラを使用しています。このため、HD44780と同じdelayで同じコマンドデータが使用できます。
各信号とポート間の定義とコマンドデータの設定値を次のようにコード化します。起動時に実行されるMain関数から、LCD表示に表示するためにの制御関数を呼び出します。Atmel Studioでは、各CPUの定義がavr/io.hにまとめられており(AT90USB162では最終的にiousb162.hを呼び出す)、この値を参照しながらプログラムを作成します。
AVRのCPUでは、各ポートはあらかじめ定義されている3つのレジスタでコントロールされます。レジスタDDRは、ピンがINPUTかOUTPUTかを決定します。PORTレジスタはピンのHIGH/LOWを制御し、PINレジスタでINPUTピンの状態を読み取ります。レジスタの各ビットは1本のピンに対応付けられ、たとえば、DDRB、PORTB、PINBの最下位ビットは、PB0(デジタルピン8)となります。
#define F_CPU 16000000UL #include <avr/io.h> #include <util/delay.h> #define lcd_D7_port PORTB // lcd D7 connection #define lcd_D7_bit PORTB7 #define lcd_D7_ddr DDRB #define lcd_D6_port PORTB // lcd D6 connection #define lcd_D6_bit PORTB6 #define lcd_D6_ddr DDRB #define lcd_D5_port PORTB // lcd D5 connection #define lcd_D5_bit PORTB5 #define lcd_D5_ddr DDRB #define lcd_D4_port PORTB // lcd D4 connection #define lcd_D4_bit PORTB4 #define lcd_D4_ddr DDRB #define lcd_E_port PORTB // lcd Enable pin #define lcd_E_bit PORTB3 #define lcd_E_ddr DDRB #define lcd_RS_port PORTB // lcd Register Select pin #define lcd_RS_bit PORTB2 #define lcd_RS_ddr DDRB // LCD module information #define lcd_LineOne 0x00 // start of line 1 #define lcd_LineTwo 0x40 // start of line 2 // LCD instructions #define lcd_Clear 0b00000001 // replace all characters with ASCII 'space' #define lcd_Home 0b00000010 // return cursor to first position on first line #define lcd_EntryMode 0b00000110 // shift cursor from left to right on read/write #define lcd_DisplayOff 0b00001000 // turn display off #define lcd_DisplayOn 0b00001100 // display on, cursor off, don't blink character #define lcd_FunctionReset 0b00110000 // reset the LCD #define lcd_FunctionSet4bit 0b00101000 // 4-bit data, 2-line display, 5 x 7 font #define lcd_SetCursor 0b10000000 // set cursor position // Program ID uint8_t program_author[] = "Tomosoft"; uint8_t program_version[] = "Atmel Studio 6.2 + Flip 3.4.7"; // Function Prototypes void lcd_write_4(uint8_t); void lcd_write_instruction_4d(uint8_t); void lcd_write_character_4d(uint8_t); void lcd_write_string_4d(uint8_t *); void lcd_init_4d(void); int main(void) { lcd_D7_ddr |= (1<<lcd_D7_bit); // 4 data lines - output lcd_D6_ddr |= (1<<lcd_D6_bit); lcd_D5_ddr |= (1<<lcd_D5_bit); lcd_D4_ddr |= (1<<lcd_D4_bit); lcd_E_ddr |= (1<<lcd_E_bit); // E line - output lcd_RS_ddr |= (1<<lcd_RS_bit); // RS line - output lcd_init_4d(); // initialize the LCD display for a 4-bit interface lcd_write_string_4d(program_author); // display the first line of information lcd_write_instruction_4d(lcd_SetCursor | lcd_LineTwo);// set cursor to start of second line _delay_us(80); // 40 uS delay (min) lcd_write_string_4d(program_version); // display the second line of information while(1){}; // endless loop return 0; }
4ビットLCDの初期化およびLCDへの1文字出力を行う制御関数を次に示します。これらは上記に示すMain関数から呼び出されます。
void lcd_init_4d(void) { _delay_ms(100); // initial 40 mSec delay lcd_RS_port &= ~(1<<lcd_RS_bit); // select the Instruction Register (RS low) lcd_E_port &= ~(1<<lcd_E_bit); // make sure E is initially low lcd_write_4(lcd_FunctionReset); // first part of reset sequence _delay_ms(10); // 4.1 mS delay (min) lcd_write_4(lcd_FunctionReset); // second part of reset sequence _delay_us(200); // 100uS delay (min) lcd_write_4(lcd_FunctionReset); // third part of reset sequence _delay_us(200); // this delay is omitted in the data sheet lcd_write_4(lcd_FunctionSet4bit); // set 4-bit mode _delay_us(80); // 40uS delay (min) lcd_write_instruction_4d(lcd_FunctionSet4bit); // set mode, lines, and font _delay_us(80); // 40uS delay (min) lcd_write_instruction_4d(lcd_DisplayOff); // turn display OFF _delay_us(80); // 40uS delay (min) lcd_write_instruction_4d(lcd_Clear); // clear display RAM _delay_ms(4); // 1.64 mS delay (min) lcd_write_instruction_4d(lcd_EntryMode); // set desired shift characteristics _delay_us(80); // 40uS delay (min) lcd_write_instruction_4d(lcd_DisplayOn); // turn the display ON _delay_us(80); // 40uS delay (min) } void lcd_write_string_4d(uint8_t theString[]) { volatile int i = 0; // character counter*/ while (theString[i] != 0) { lcd_write_character_4d(theString[i]); i++; _delay_us(80); // 40 uS delay (min) } } void lcd_write_character_4d(uint8_t theData) { lcd_RS_port |= (1<<lcd_RS_bit); // select the Data Register (RS high) lcd_E_port &= ~(1<<lcd_E_bit); // make sure E is initially low lcd_write_4(theData); // write the upper 4-bits of the data lcd_write_4(theData << 4); // write the lower 4-bits of the data } void lcd_write_instruction_4d(uint8_t theInstruction) { lcd_RS_port &= ~(1<<lcd_RS_bit); // select the Instruction Register (RS low) lcd_E_port &= ~(1<<lcd_E_bit); // make sure E is initially low lcd_write_4(theInstruction); // write the upper 4-bits of the data lcd_write_4(theInstruction << 4); // write the lower 4-bits of the data } void lcd_write_4(uint8_t theByte) { lcd_D7_port &= ~(1<<lcd_D7_bit); // assume that data is '0' if (theByte & 1<<7) lcd_D7_port |= (1<<lcd_D7_bit); // make data = '1' if necessary lcd_D6_port &= ~(1<<lcd_D6_bit); // repeat for each data bit if (theByte & 1<<6) lcd_D6_port |= (1<<lcd_D6_bit); lcd_D5_port &= ~(1<<lcd_D5_bit); if (theByte & 1<<5) lcd_D5_port |= (1<<lcd_D5_bit); lcd_D4_port &= ~(1<<lcd_D4_bit); if (theByte & 1<<4) lcd_D4_port |= (1<<lcd_D4_bit); lcd_E_port |= (1<<lcd_E_bit); // Enable pin high _delay_us(1); // implement 'Data set-up time' (80 nS) and 'Enable pulse width' (230 nS) lcd_E_port &= ~(1<<lcd_E_bit); // Enable pin low _delay_us(1); // implement 'Data hold time' (10 nS) and 'Enable cycle time' (500 nS) }