STM32 und LCD2004A ohne I2C-Schnittstelle

Vor kurzem habe ich angefangen, STM32-Controller zu studieren und brauchte Interaktion mit dem LCD-Display. Von den Displays fand ich nur 2004A und ohne I2C-Schnittstelle. Er wird in diesem Artikel besprochen.



Zunächst müssen Sie das Display an die Steuerung anschließen. Wir verbinden nach dem Schema:



Bild



PB0 - PB7 - Controller-Ausgänge.



Pinbelegung anzeigen:
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 (-). .




Das Display ist also angeschlossen. Es ist Zeit, dem Mikrocontroller beizubringen, damit zu arbeiten. Ich habe beschlossen, eine eigene Bibliothek zu erstellen, um sie in verschiedenen Projekten verwenden zu können. Es besteht aus zwei Dateien - lcd_20x4.h und lcd_20x4.c Beginnen



wir mit der Header-Datei.



#ifndef LCD_LCD_20X4_2004A_LCD_20X4_H_
#define LCD_LCD_20X4_2004A_LCD_20X4_H_

#include "stm32f1xx.h"
#include "delay.h"


Zu Beginn enthalten wir die CMSIS-Bibliotheksdatei stm32f1xx.h, da ich einen STM32F103C8T6-Stein habe. Mit der nächsten Aufnahme fügen wir die Datei delay.h hinzu - dies ist meine Bibliothek für die Arbeit mit Verzögerungen basierend auf dem System-Timer. Ich werde es hier nicht beschreiben, hier ist sein Code:



Delay.h Datei

#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-Datei

#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);
}





Das 2004A-Display basiert auf dem HITACHI HD44780-Controller. Schauen wir uns daher das Datenblatt für diesen Controller an. Tabelle 6 zeigt das Befehlssystem sowie die Zeitabläufe dieser Befehle.



Bild



Schreiben wir die erforderlichen Befehle in Makros in der Header-Datei um:




// 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


Jetzt müssen Sie die Controller-Pins so konfigurieren, dass sie mit dem Display funktionieren. Bestimmen Sie die Position der Bits im ODR-Port der Steuerung. Achten Sie auf PIN_D4. Ich habe dort das 10. Bit anstelle von 4 registriert. Der 4. Ausgang funktioniert auf meinem Controller nicht. Ich weiß nicht, womit es verbunden ist, aber im ODR-Register ist dieses Bit immer eins, noch vor dem Start der Initialisierung des Controller-Takts. Ich weiß nicht, womit das zusammenhängt, vielleicht ist der Stein nicht original.




//     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


Als nächstes richten wir die Steuerregister für die Ausgänge ein. Ich habe mich dazu entschlossen, dies in Form von Präprozessor-Makros zu tun:




#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    


Am Ende der Header-Datei definieren wir die Funktionen für die Arbeit mit der Anzeige:




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_ */


Wir sind mit der Header-Datei fertig. Schreiben wir nun die Implementierung der Funktionen in die Datei lcd_20x4.c.

Der erste Schritt besteht darin, die Pins für die Arbeit mit der Anzeige zu konfigurieren. Dies erfolgt durch die Funktion 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 ;
}


Die Funktion lcdInit () ist die Funktion zur Initialisierung der Anzeige. Lass es uns auch schreiben. Es basiert auf einem Flussdiagramm zum Initialisieren einer Anzeige aus einem Datenblatt:



Bild




//---------------------  -----------------------------------------------------------
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 ;
}


Die Initialisierungsfunktion verwendet die Funktion void sendByte (char byte, int isData). Schreiben wir die Implementierung. Es basiert auf einem Zeitdiagramm aus einem Datenblatt:



Bild




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;
}


Jetzt können wir auf einem 4-Bit-Bus ein Byte an das Display senden. Dieses Byte kann entweder ein Befehl oder ein Symbol sein. Sie wird ermittelt, indem die Variable isData an die Funktion übergeben wird. Es ist Zeit zu lernen, wie man Strings überträgt.



Das 2004A-Display besteht aus 4 Zeilen mit 20 Zeichen, wie im Titel angegeben. Um die Funktion nicht zu komplizieren, werde ich das Trimmen von Zeilen auf 20 Zeichen nicht implementieren. Wir werden eine Zeichenfolge und eine Zeichenfolge senden, in der sie an die Funktion ausgegeben werden.



Um das Symbol auf dem Bildschirm anzuzeigen, müssen Sie es in DDRAM schreiben. Die DDRAM-Adressierung entspricht der Tabelle:



Bild




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
}


Das war's, die Bibliothek für das Display ist fertig. Jetzt ist die Zeit, es zu benutzen. In der main () Funktion schreiben wir:




portInit();//    

	sendStr("    HELLO, HABR", 1);
	sendStr("     powered by", 2);
	sendStr("   STM32F103C8T6", 3);
	sendStr("Nibiru", 4);


Und wir erhalten das Ergebnis:



Bild



Abschließend werde ich eine vollständige Liste der Dateien geben:



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
}






All Articles