まず、ディスプレイをコントローラーに接続する必要があります。
PB0-PB7-コントローラー出力のスキームに従って接続します。
ディスプレイピンの割り当て:
| 1 | GND | ( ) |
| 2 | VCC | + 5 |
| 3 | VEE | . . 10-20 , . |
| 4 | RS | : 0 – ; 1 – . |
| 5 | R/W | :
0 – ; 1 – . , . |
| 6 | EN | . , «» . |
| 7 | DB0 | . . |
| 8 | DB1 | |
| 9 | DB2 | |
| 10 | DB3 | |
| 11 | DB4 | . |
| 12 | DB5 | |
| 13 | DB6 | |
| 14 | DB7 | |
| 15 | A | (+) |
| 16 | K | (-). . |
だから、ディスプレイが接続されています。マイクロメータにそれを操作するように教える時が来ました。さまざまなプロジェクトで使用できるようにするために、独自のライブラリを作成することにしました。これは、lcd_20x4.hとlcd_20x4.cの2つのファイルで構成されてい
ます。ヘッダーファイルから始めましょう。
#ifndef LCD_LCD_20X4_2004A_LCD_20X4_H_
#define LCD_LCD_20X4_2004A_LCD_20X4_H_
#include "stm32f1xx.h"
#include "delay.h"
まず、STM32F103C8T6ストーンを使用しているため、CMSISライブラリファイルstm32f1xx.hを含めます。次のインクルードでは、ファイルdelay.hをインクルードします。これは、システムタイマーに基づいて遅延を処理するための私のライブラリです。ここでは説明しません。コードは次のとおりです。
Delay.hファイル
#ifndef DELAY_DELAY_H_
#define DELAY_DELAY_H_
#include "stm32f1xx.h"
#define F_CPU 72000000UL
#define US F_CPU/1000000
#define MS F_CPU/1000
#define SYSTICK_MAX_VALUE 16777215
#define US_MAX_VALUE SYSTICK_MAX_VALUE/(US)
#define MS_MAX_VALUE SYSTICK_MAX_VALUE/(MS)
void delay_us(uint32_t us); // 233
void delay_ms(uint32_t ms); // 233
void delay_s(uint32_t s);
#endif /* DELAY_DELAY_H_ */
Delay.cファイル
#include "delay.h"
/* */
void delay_us(uint32_t us){ // 233 016
if (us > US_MAX_VALUE || us == 0)
return;
SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk; // 0
SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk; //
SysTick->LOAD = (US * us-1); //
SysTick->VAL = 0; // SYST_CVR
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //
while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); // COUNFLAG SYST_CSR
SysTick->CTRL &= ~SysTick_CTRL_COUNTFLAG_Msk; // COUNTFLAG
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //
}
void delay_ms(uint32_t ms){ // 233
if(ms > MS_MAX_VALUE || ms ==0)
return;
SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;
SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk;
SysTick->LOAD = (MS * ms);
SysTick->VAL = 0;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
SysTick->CTRL &= ~SysTick_CTRL_COUNTFLAG_Msk;
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
void delay_s(uint32_t s){
for(int i=0; i<s*5;i++) delay_ms(200);
}
2004Aディスプレイは、HITACHIHD44780コントローラーをベースにしています。したがって、このコントローラーのデータシートを見てみましょう。表6に、コマンドのシステムとこれらのコマンドのタイミングを示します。
必要なコマンドをヘッダーファイルのマクロに書き直してみましょう。
// display commands
#define CLEAR_DISPLAY 0x1
#define RETURN_HOME 0x2
#define ENTRY_MODE_SET 0x6 // mode cursor shift rihgt, display non shift
#define DISPLAY_ON 0xC // non cursor
#define DISPLAY_OFF 0x8
#define CURSOR_SHIFT_LEFT 0x10
#define CURSOR_SHIFT_RIGHT 0x14
#define DISPLAY_SHIFT_LEFT 0x18
#define DISPLAY_SHIFT_RIGHT 0x1C
#define DATA_BUS_4BIT_PAGE0 0x28
#define DATA_BUS_4BIT_PAGE1 0x2A
#define DATA_BUS_8BIT_PAGE0 0x38
#define SET_CGRAM_ADDRESS 0x40 // usage address |= SET_CGRAM_ADDRESS
#define SET_DDRAM_ADDRESS 0x80
次に、ディスプレイと連動するようにコントローラーピンを構成する必要があります。コントローラのODRポートのビットの位置を決定します。PIN_D4に注意してください。4番目ではなく10番目のビットが登録されています。4番目の出力がコントローラーで機能しません。何に接続されているのかわかりませんが、ODRレジスタでは、コントローラのクロック初期化が開始される前であっても、このビットは常に1です。何に関係しているのかわかりません。おそらく石はオリジナルではありません。
// ODR
#define PIN_RS 0x1
#define PIN_EN 0x2
#define PIN_D7 0x80
#define PIN_D6 0x40
#define PIN_D5 0x20
#define PIN_D4 0x400
次に、出力の制御レジスタを設定します。私はそれをプリプロセッサマクロの形で行うことにしました:
#define LCD_PORT GPIOB
#define LCD_ODR LCD_PORT->ODR
#define LCD_PIN_RS() LCD_PORT->CRL &= ~GPIO_CRL_CNF0; \
LCD_PORT->CRL |= GPIO_CRL_MODE0; // PB0 -, 50
#define LCD_PIN_EN() LCD_PORT->CRL &= ~GPIO_CRL_CNF1;\
LCD_PORT->CRL |= GPIO_CRL_MODE1; // PB1
#define LCD_PIN_D7() LCD_PORT->CRL &= ~GPIO_CRL_CNF7;\
LCD_PORT->CRL |= GPIO_CRL_MODE7; // PB7
#define LCD_PIN_D6() LCD_PORT->CRL &= ~GPIO_CRL_CNF6;\
LCD_PORT->CRL |= GPIO_CRL_MODE6; // PB6
#define LCD_PIN_D5() LCD_PORT->CRL &= ~GPIO_CRL_CNF5;\
LCD_PORT->CRL |= GPIO_CRL_MODE5; // PB5
#define LCD_PIN_D4() LCD_PORT->CRH &= ~GPIO_CRH_CNF10;\
LCD_PORT->CRH |= GPIO_CRH_MODE10; // PB10
#define LCD_PIN_MASK (PIN_RS | PIN_EN | PIN_D7 | PIN_D6 | PIN_D5 | PIN_D4) // 0b0000000011110011
ヘッダーファイルの最後に、ディスプレイを操作するための関数を定義します。
void portInit(void); //
void sendByte(char byte, int isData);
void lcdInit(void); //
void sendStr(char *str, int row ); //
#endif /* LCD_LCD_20X4_2004A_LCD_20X4_H_ */
ヘッダーファイルは完成です。次に、関数の実装をlcd_20x4.cファイルに記述します。
最初のステップは、ディスプレイと連動するようにピンを構成することです。これは、void portInit(void)関数によって実行されます。
void portInit(void){
//---------------------- ----------------------------------------------------
if(LCD_PORT == GPIOB) RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
else if (LCD_PORT == GPIOA) RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
else return;
//--------------------- LCD-----------------------------------------------------
LCD_PIN_RS();//
LCD_PIN_EN();
LCD_PIN_D7();
LCD_PIN_D6();
LCD_PIN_D5();
LCD_PIN_D4();
lcdInit(); //
return ;
}
lcdInit()関数は、表示初期化関数です。それも書きましょう。これは、データシートから表示を初期化するフローチャートに基づいています。
//--------------------- -----------------------------------------------------------
void lcdInit(void){
delay_ms(15); //
sendByte(0x33, 0); // 0011
delay_us(100);
sendByte(0x32, 0); // 00110010
delay_us(40);
sendByte(DATA_BUS_4BIT_PAGE0, 0); // 4
delay_us(40);
sendByte(DISPLAY_OFF, 0); //
delay_us(40);
sendByte(CLEAR_DISPLAY, 0); //
delay_ms(2);
sendByte(ENTRY_MODE_SET, 0); //
delay_us(40);
sendByte(DISPLAY_ON, 0);//
delay_us(40);
return ;
}
初期化関数は、void sendByte(char byte、int isData)関数を使用します。その実装を書いてみましょう。これは、データシートのタイミングチャートに基づいています。
void sendByte(char byte, int isData){
//
LCD_ODR &= ~LCD_PIN_MASK;
if(isData == 1) LCD_ODR |= PIN_RS; // RS
else LCD_ODR &= ~(PIN_RS); // RS
LCD_ODR |= PIN_EN; // E
//
if(byte & 0x80) LCD_ODR |= PIN_D7;
if(byte & 0x40) LCD_ODR |= PIN_D6;
if(byte & 0x20) LCD_ODR |= PIN_D5;
if(byte & 0x10) LCD_ODR |= PIN_D4;
LCD_ODR &= ~PIN_EN; //
LCD_ODR &= ~(LCD_PIN_MASK & ~PIN_RS);// RS
LCD_ODR |= PIN_EN;// E
//
if(byte & 0x8) LCD_ODR |= PIN_D7;
if(byte & 0x4) LCD_ODR |= PIN_D6;
if(byte & 0x2) LCD_ODR |= PIN_D5;
if(byte & 0x1) LCD_ODR |= PIN_D4;
LCD_ODR &= ~(PIN_EN);//
delay_us(40);
return;
}
これで、4ビットバスのディスプレイにバイトを送信できます。このバイトは、コマンドまたはシンボルのいずれかです。isData変数を関数に渡すことによって決定されます。文字列を転送する方法を学ぶ時が来ました。
2004Aディスプレイは、タイトルに反映されているように、20文字の4行で構成されています。機能を複雑にしないために、20文字への行のトリミングは実装しません。文字列とそれを関数に出力する文字列を送信します。
画面にシンボルを表示するには、DDRAMに書き込む必要があります。DDRAMアドレス指定は次の表に対応します。
void sendStr(char *str, int row ){
char start_address;
switch (row) {
case 1:
start_address = 0x0; // 1
break;
case 2:
start_address = 0x40; // 2
break;
case 3:
start_address = 0x14; // 3
break;
case 4:
start_address = 0x54; // 4
break;
}
sendByte((start_address |= SET_DDRAM_ADDRESS), 0); // DDRAM
delay_ms(4);
while(*str != '\0'){//
sendByte(*str, 1);
str++;
}// while
}
これで、ディスプレイ用のライブラリの準備が整いました。今がそれを使用する時です。main()関数で次のように記述します。
portInit();//
sendStr(" HELLO, HABR", 1);
sendStr(" powered by", 2);
sendStr(" STM32F103C8T6", 3);
sendStr("Nibiru", 4);
そして、結果
が得られます。結論として、ファイルの完全なリストを示します。
lcd_20x4.h
#ifndef LCD_LCD_20X4_2004A_LCD_20X4_H_
#define LCD_LCD_20X4_2004A_LCD_20X4_H_
#include "stm32f1xx.h"
#include "delay.h"
// display commands
#define CLEAR_DISPLAY 0x1
#define RETURN_HOME 0x2
#define ENTRY_MODE_SET 0x6 // mode cursor shift rihgt, display non shift
#define DISPLAY_ON 0xC // non cursor
#define DISPLAY_OFF 0x8
#define CURSOR_SHIFT_LEFT 0x10
#define CURSOR_SHIFT_RIGHT 0x14
#define DISPLAY_SHIFT_LEFT 0x18
#define DISPLAY_SHIFT_RIGHT 0x1C
#define DATA_BUS_4BIT_PAGE0 0x28
#define DATA_BUS_4BIT_PAGE1 0x2A
#define DATA_BUS_8BIT_PAGE0 0x38
#define SET_CGRAM_ADDRESS 0x40 // usage address |= SET_CGRAM_ADDRESS
#define SET_DDRAM_ADDRESS 0x80
// ODR
#define PIN_RS 0x1
#define PIN_EN 0x2
#define PIN_D7 0x80
#define PIN_D6 0x40
#define PIN_D5 0x20
#define PIN_D4 0x400
#define LCD_PORT GPIOB
#define LCD_ODR LCD_PORT->ODR
#define LCD_PIN_RS() LCD_PORT->CRL &= ~GPIO_CRL_CNF0; \
LCD_PORT->CRL |= GPIO_CRL_MODE0; // PB0 -, 50
#define LCD_PIN_EN() LCD_PORT->CRL &= ~GPIO_CRL_CNF1;\
LCD_PORT->CRL |= GPIO_CRL_MODE1; // PB1
#define LCD_PIN_D7() LCD_PORT->CRL &= ~GPIO_CRL_CNF7;\
LCD_PORT->CRL |= GPIO_CRL_MODE7; // PB7
#define LCD_PIN_D6() LCD_PORT->CRL &= ~GPIO_CRL_CNF6;\
LCD_PORT->CRL |= GPIO_CRL_MODE6; // PB6
#define LCD_PIN_D5() LCD_PORT->CRL &= ~GPIO_CRL_CNF5;\
LCD_PORT->CRL |= GPIO_CRL_MODE5; // PB5
#define LCD_PIN_D4() LCD_PORT->CRH &= ~GPIO_CRH_CNF10;\
LCD_PORT->CRH |= GPIO_CRH_MODE10; // PB10
#define LCD_PIN_MASK (PIN_RS | PIN_EN | PIN_D7 | PIN_D6 | PIN_D5 | PIN_D4) // 0b0000000011110011
void portInit(void); //
void sendByte(char byte, int isData);
void lcdInit(void); //
void sendStr(char *str, int row ); //
#endif /* LCD_LCD_20X4_2004A_LCD_20X4_H_ */
lcd_20x4.c
#include "lcd_20x4.h"
// LCD
void sendByte(char byte, int isData){
//
LCD_ODR &= ~LCD_PIN_MASK;
if(isData == 1) LCD_ODR |= PIN_RS; // RS
else LCD_ODR &= ~(PIN_RS); // RS
//
if(byte & 0x80) LCD_ODR |= PIN_D7;
if(byte & 0x40) LCD_ODR |= PIN_D6;
if(byte & 0x20) LCD_ODR |= PIN_D5;
if(byte & 0x10) LCD_ODR |= PIN_D4;
// E
LCD_ODR |= PIN_EN;
LCD_ODR &= ~PIN_EN; //
// RS
LCD_ODR &= ~(LCD_PIN_MASK & ~PIN_RS);
//
if(byte & 0x8) LCD_ODR |= PIN_D7;
if(byte & 0x4) LCD_ODR |= PIN_D6;
if(byte & 0x2) LCD_ODR |= PIN_D5;
if(byte & 0x1) LCD_ODR |= PIN_D4;
// E
LCD_ODR |= PIN_EN;
//delay_us(10);
//
LCD_ODR &= ~(PIN_EN);
delay_us(40);
return;
}
// 50
void portInit(void){
//---------------------- ----------------------------------------------------
if(LCD_PORT == GPIOB) RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
else if (LCD_PORT == GPIOA) RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
else return;
//--------------------- LCD-----------------------------------------------------
LCD_PIN_RS();
LCD_PIN_EN();
LCD_PIN_D7();
LCD_PIN_D6();
LCD_PIN_D5();
LCD_PIN_D4();
lcdInit();
return ;
}
//--------------------- -----------------------------------------------------------
void lcdInit(void){
delay_ms(15); //
sendByte(0x33, 0); // 0011
delay_us(100);
sendByte(0x32, 0); // 00110010
delay_us(40);
sendByte(DATA_BUS_4BIT_PAGE0, 0); // 4
delay_us(40);
sendByte(DISPLAY_OFF, 0); //
delay_us(40);
sendByte(CLEAR_DISPLAY, 0); //
delay_ms(2);
sendByte(ENTRY_MODE_SET, 0); //
delay_us(40);
sendByte(DISPLAY_ON, 0);//
delay_us(40);
return ;
}
void sendStr(char *str, int row ){
char start_address;
switch (row) {
case 1:
start_address = 0x0; // 1
break;
case 2:
start_address = 0x40; // 2
break;
case 3:
start_address = 0x14; // 3
break;
case 4:
start_address = 0x54; // 4
break;
}
sendByte((start_address |= SET_DDRAM_ADDRESS), 0); // DDRAM
delay_ms(4);
while(*str != '\0'){
sendByte(*str, 1);
str++;
//delay_ms(100);
}// while
}