Controlador de pantalla LCD1602 basado en FPGA

1. Principio de visualización LCD1602

1. Función de pin

Su diagrama de bloques funcional interno se muestra en la siguiente figura:

En términos generales, LCD1602 tiene 16 pines. Los LCD1602 de varios fabricantes pueden ser ligeramente diferentes, pero básicamente iguales. Las funciones de los 16 pines son las siguientes:

Función de pin LCD1602
Número de PIN Nombre del pin Nivel de voltaje Caracteristicas
1 VSS 0V Tierra de potencia
2 VDD + 5V Poder positivo
3 V0 0V Sesgo de voltaje
4 RS H / L Comando / datos
5 R / W H / L Leer escribir
6 mi H / L Habilitar
7 ~ 14 DB0 ~ DB7 H / L Puerto de datos
15 DIRIGIR + 5V Luz de fondo positiva
dieciséis LEDK 0V Luz de fondo negativa

Explicación de esta tabla:

1. Conecte VSS a la toma de tierra.

2. Conecte VDD a + 5V.

3. VO es la señal de polarización de la pantalla de cristal líquido, que se puede conectar a un potenciómetro de precisión 10K 3296. O resistencias regulables RM065 / RM063 azul y blanco de la misma resistencia. Vea abajo.

4. RS es el pin de selección de comando / datos, que está conectado a una E / S del microcontrolador. Cuando RS es bajo, se selecciona el comando, cuando RS es alto, se seleccionan los datos.

5. RW es el pin de selección de lectura / escritura, que está conectado a una E / S del microcontrolador. Cuando RW es bajo, escriba comandos o datos en LCD1602, cuando RW sea alto, lea estado o datos de LCD1602. Si no necesita realizar una operación de lectura, puede conectarlo directamente a VSS.

6. E, el pin de habilitación para ejecutar comandos, está conectado a una E / S del microcontrolador.

7. D0 — D7, pines de entrada / salida de datos en paralelo, que se pueden conectar a cualquiera de los 8 puertos de E / S de P0 — P3 del microcontrolador. Si está conectado al puerto P0, el puerto P0 debe conectarse a una resistencia pull-up 4.7K-10K. Si se trata de una unidad en paralelo de 4 cables, solo se requieren 4 puertos de E / S.

8. Un ánodo de luz de fondo, puede conectar una resistencia limitadora de corriente de 10-47 ohmios a VDD.

9. El polo negativo de la luz de fondo K, conéctelo a VSS. Vea la figura siguiente.

2. Operación básica

Las operaciones básicas de LCD1602 se dividen en cuatro tipos:

1. Leer estado: entrada RS = 0, RW = 1, E = pulso alto. Salida: D0 — D7 son palabras de estado.

2. Leer datos: entrada RS = 1, RW = 1, E = pulso alto. Salida: D0-D7 son datos.

3. Comando de escritura: entrada RS = 0, RW = 0, E = pulso alto. Salida: Ninguno.

4. Escribir datos: entrada RS = 1, RW = 0, E = pulso alto. Salida: Ninguno.

Lea el diagrama de tiempos de operación y las restricciones:

 Diagrama de tiempos de la operación de escritura:

Parámetros de tiempo de temporización:

3. DDRAM, CGROM y CGRAM

DDRAM (Display Data RAM) es la RAM de datos de visualización, que se utiliza para registrar el código de carácter que se mostrará. Un total de 80 bytes, la relación correspondiente entre la dirección y la pantalla es la siguiente:

DDRAM es equivalente a la memoria de video de la computadora, para mostrar los caracteres en la pantalla, enviamos el código del carácter a la memoria de video para que el personaje pueda ser mostrado en la pantalla. De manera similar, el LCD1602 tiene un total de 80 bytes de memoria de video, a saber, DDRAM. Pero la pantalla de visualización de LCD1602 es solo 16 × 2. Por lo tanto, no todos los códigos de caracteres escritos en DDRAM se pueden mostrar en la pantalla. Solo se pueden mostrar los caracteres escritos en el rango que se muestra en la figura anterior, y están escritos fuera del rango. No se pueden mostrar los caracteres. De esta manera, podemos usar el siguiente "comando de movimiento del cursor o de la pantalla" en el programa para mover lentamente el personaje al rango de pantalla visible y ver el efecto de movimiento del personaje.

Como se mencionó anteriormente, para mostrar caracteres en la pantalla LCD, el código de carácter se envía a DDRAM. Por ejemplo, si desea mostrar el carácter'A 'en la esquina superior izquierda de la pantalla, escriba el código de carácter 41H del carácter'A' en la dirección 00H de la DDRAM. En cuanto a cómo escribir, habrá instrucciones más adelante. Entonces, ¿por qué escribir el código de carácter en DDRAM para que el carácter de este código se pueda mostrar en la posición correspondiente? Sabemos que LCD1602 es una especie de pantalla de matriz de puntos de caracteres. Para mostrar la fuente de un carácter, debe haber los datos de fuente de este carácter. ¿Cuáles son los datos de fuente de un carácter? Solo mire la figura a continuación para entender.

El lado izquierdo de la figura anterior son los datos de fuente del carácter'A ', y el lado derecho son los datos del lado izquierdo con "○" representando 0 y "■" representando 1. Visualizando así el carácter "A". Como puede verse en la figura siguiente, los 4 bits superiores del carácter'A 'son 0100 y los 4 bits inferiores son 0001. Juntos, son 01000001b, que es 41H. Resulta ser consistente con el código ASCII del personaje, lo que nos da mucha conveniencia Podemos usar la sintaxis P2 = 'A' en la PC. Después de la compilación, resulta ser el código de carácter de este personaje.

La memoria de fuentes se solidifica en el módulo LCD1602, que es CGROM y CGRAM. HD44780 tiene fuentes incorporadas de 192 caracteres de uso común, que se almacenan en el generador de caracteres CGROM (ROM del generador de caracteres). También hay 8 RAM de generación de caracteres definidos por el usuario. , Llamado CGRAM (Generador de caracteres RAM). La siguiente figura (Figura 12) ilustra la correspondencia entre CGROM y CGRAM y los caracteres. Por los nombres de ROM y RAM, también podemos saber que la ROM ya está solidificada en el módulo LCD1602 y solo se puede leer; mientras que la RAM es legible y escribible. En otras palabras, si solo necesita mostrar los caracteres que ya existen en el CGROM en la pantalla, solo necesita escribir su código de carácter en la DDRAM; pero si desea mostrar los caracteres que no están en el CGROM, como el símbolo de la escala de temperatura Celsius Luego, defínalo solo en CGRAM y luego escriba el código de carácter de este carácter personalizado en DDRAM. A diferencia de los caracteres solidificados en CGROM, no hay caracteres en CGRAM, por lo que para escribir un carácter que no existe en CGROM en DDRAM, debe estar definido en CGRAM antes de su uso. Una vez que el programa sale, los caracteres definidos en CGRAM ya no existen y deben redefinirse la próxima vez que se utilicen.

La figura anterior ilustra la fuente y la posición del cursor de 5 × 8 caracteres de matriz de puntos y 5 × 10 caracteres de matriz de puntos. Primero hablemos de la matriz de puntos de 5 × 8, tiene 8 filas y 5 columnas. Luego, se necesitan 8 bytes para definir tal carácter y los primeros 3 bits de cada byte no se utilizan. Por ejemplo, defina el símbolo {0x10,0x06,0x09,0x08,0x08,0x09,0x06,0x00} para la escala de temperatura Celsius.

La figura anterior ilustra la instrucción para configurar la dirección CGRAM. Por el formato de esta instrucción, podemos ver que tiene un total de 6 bits aaaaaa, lo que puede representar un total de 64 direcciones, es decir, 64 bytes. Un carácter de matriz de puntos de 5 × 8 ocupa un total de 8 bytes, por lo que se pueden personalizar un total de 8 caracteres para estos 64 bytes. En otras palabras, DB5DB4DB3 en la dirección de 6 bits en la figura anterior se usa para representar 8 caracteres personalizados, y DB2DB1DB0 se usa para representar 8 bytes de cada carácter. Los 8 caracteres personalizados (0-7) representados por DB5DB4DB3 son los códigos de caracteres que se escribirán en DDRAM. Sabemos que solo se pueden definir 8 caracteres personalizados en CGRAM, es decir, solo hay 8 códigos de caracteres del 0 al 7, pero hay un total de 16 códigos de caracteres en la siguiente tabla (× × × × × 0000b-- × × × × 1111b). De hecho, como se muestra en la figura, solo puede representar 8 caracteres personalizados (× × × × × 0000b = × × × × × 1000b, × × × × × × 0001b = × × × × × 1001b ... y así sucesivamente). En otras palabras, el código de carácter 0 y el código de carácter 8 escritos en la DDRAM son el mismo carácter personalizado. Cada carácter de la matriz de puntos de 5 × 10 ocupa un total de 16 bytes de espacio, por lo que solo 4 de estos caracteres personalizados se pueden definir en CGRAM.

Entonces, ¿cómo personalizar personajes en CGRAM? En la introducción anterior, sabemos que hay una instrucción de dirección CGRAM establecida, que es similar a la instrucción de escritura DDRAM, simplemente configure los datos de fuente de un determinado carácter personalizado y luego configure la dirección CGRAM de acuerdo con el método descrito anteriormente, y escriba esto a su vez Los datos de la fuente están bien. Lo explicaremos en el siguiente ejemplo.

4. Instrucciones de LCD1602

(1). Instrucción de configuración del modo de trabajo

×: No importa, es decir, este bit puede ser 0 o 1, generalmente 0.

DL: establece el número de bits de la interfaz de datos.

DL = 1: interfaz de datos de 8 bits (D7 — D0).

DL = 0: interfaz de datos de 4 bits (D7 — D4).

N = 0: pantalla de una línea.

N = 1: pantalla de dos líneas.

F = 0: 5 × 8 caracteres de matriz de puntos.

F = 1: 5 × 10 caracteres de matriz de puntos.

Nota: Debido a que es una palabra de comando de escritura, tanto RS como RW son 0. LCD1602 solo puede funcionar en modo paralelo, no en modo serie. Y el modo paralelo puede elegir una interfaz de datos de 8 bits o una interfaz de datos de 4 bits. Aquí elegimos la interfaz de datos de 8 bits (D7-D0). Nuestra configuración es una interfaz de datos de 8 bits, pantalla de dos líneas, matriz de puntos de 5 × 8, es decir, 0b00111000, que es 0x38. (Nota: el efecto de que NF sea 10 u 11 es el mismo, ambos son una matriz de puntos de 5 × 8 de dos líneas. Debido a que no se puede mostrar en una matriz de puntos de 5 × 10 de dos líneas, en otras palabras, es lo mismo usar 0x38 o 0x3c aquí de).

(2). Mostrar instrucciones de control del interruptor

D = 1: pantalla encendida, D = 0: pantalla apagada.

C = 1: se muestra el cursor, C = 0: no se muestra el cursor.

B = 1: el cursor parpadea, B = 0: el cursor no parpadea.

Nota: La configuración aquí es que la pantalla está encendida, el cursor no se muestra, el cursor no parpadea y la palabra de configuración es 0x0c.

(3). Introduzca las instrucciones de configuración del modo

I / D = 1: el cursor se mueve hacia la derecha después de escribir nuevos datos.

I / D = 0: el cursor se mueve hacia la izquierda después de escribir nuevos datos.

S = 1: Movimiento de la pantalla.

S = 0: La pantalla no se mueve.

Nota: La configuración aquí es 0x06.

(4). Cursor o instrucción de movimiento de pantalla

Descripción: este comando es muy útil cuando necesita mover toda la pantalla y puede realizar el efecto de visualización de desplazamiento de la pantalla. Este comando no se utiliza durante la inicialización.

(5). Comando borrar pantalla

Descripción: borra el contenido de la pantalla. El cursor vuelve a la esquina superior izquierda de la pantalla. Llevará algún tiempo ejecutar este comando.

(6). Instrucción de inicio del cursor

Nota: El cursor regresa a la esquina superior izquierda de la pantalla, no cambia el contenido que se muestra en la pantalla.

(7). Establecer instrucción de dirección CGRAM

Descripción: este comando se ha introducido anteriormente. El uso se explica en el siguiente ejemplo.

(8). Establecer comando de dirección DDRAM

Descripción: este comando se utiliza para configurar la dirección DDRAM. Antes de leer y escribir en DDRAM, primero configure la dirección DDRAM y luego lea y escriba. Como dijimos antes, DDRAM es la memoria de pantalla de LCD1602. Si queremos mostrar en él, debemos escribir los caracteres que se mostrarán en DDRAM. De manera similar, si queremos saber qué caracteres hay en una determinada dirección de DDRAM, primero debemos configurar la dirección de DDRAM y luego leerla en el microcontrolador.

(9). Leer señal de ocupado y contador de direcciones AC

Descripción: Este comando se utiliza para leer el estado de LCD1602. Para el microcontrolador, LCD1602 es un dispositivo lento. Cuando el microcontrolador le envía una instrucción, ejecutará la instrucción. En este momento, si el microordenador de un solo chip envía la siguiente instrucción nuevamente, porque el LCD1602 es lento y la instrucción anterior aún no se ha ejecutado, no aceptará la nueva instrucción, lo que resultará en la pérdida de la nueva instrucción. Por lo tanto, esta instrucción de lectura ocupada se puede utilizar para determinar si el LCD1602 está ocupado y si puede recibir instrucciones del microcontrolador. Cuando BF = 1, significa que el LCD1602 está ocupado y no puede aceptar instrucciones del microcontrolador; cuando BF = 0, significa que el LCD1602 está inactivo y puede recibir instrucciones del microcontrolador. RS = 0, lo que significa que es un comando; RW = 1, lo que significa que es una lectura. Hay otro subproducto de esta instrucción: es decir, se puede obtener el valor del contador de direcciones AC. LCD1602 mantiene un contador de direcciones AC para registrar la posición de la siguiente lectura y escritura de CGRAM o DDRAM. Cabe destacar que: No ejecuté esta instrucción correctamente. Muchos internautas parecen hacer lo mismo. Afortunadamente, tenemos otra forma, que es retrasar. Al observar el tiempo de ejecución de cada instrucción y después de algunos experimentos, se puede determinar el retraso de la instrucción. De esta manera, la siguiente instrucción se puede ejecutar después de que se ejecute la instrucción anterior.

(10). Escribir datos en la instrucción CGRAM o DDRAM

Explicación: RS = 1, datos; RW = 0, escribir. Cuando se ejecuta la instrucción, primero configure los datos que se escribirán en DB7 — DB0, y luego ejecute el comando de escritura.

(11). Leer comando de datos de CGRAM o DDRAM

Explicación: RS = 1, datos; RW = 1, leer. Primero configure la dirección de CGRAM o DDRAM y luego ejecute el comando de lectura. Los datos se leen en DB7-DB0.

5. Pasos generales de cambio inicial de LCD1602 (debe inicializarse en FPGA)

  1. Retraso 15 mS
  2. Escribir instrucción 38H (no detectar señal de ocupado)
  3. Retraso 15 mS
  4. Escribir instrucción 38H (no detectar señal de ocupado)
  5. Retraso 15 mS
  6. Escribir instrucción 38H (no detectar señal de ocupado)
  7. Cada instrucción de escritura subsiguiente, la operación de lectura / escritura de datos necesita detectar la señal de ocupado
  8. Instrucción de escritura 38H: configuración del modo de visualización
  9. Comando de escritura 08H: pantalla apagada
  10. Comando de escritura 01H: mostrar pantalla clara
  11. Instrucción de escritura 06H: ajuste de movimiento del cursor de visualización
  12. Escribir comando 0CH: visualización y ajuste del cursor

Las dos secuencias de 11 y 12 no se pueden intercambiar. No puedo depurarlo si las cambié mientras escribo. Es normal intercambiar las dos secuencias en la parte posterior izquierda.

 

Dos, implementación de FPGA

Función de realización: muestra dos líneas en la pantalla LCD, la primera línea muestra: Pan-Hong-Feng; la segunda línea muestra: LCD1602-Test

El código es el siguiente: (el código es un poco desordenado, demasiado vago para comentar)


// Company  : 
// Engineer : 
// -----------------------------------------------------------------------------
// https://blog.csdn.net/qq_33231534    PHF's CSDN blog
// -----------------------------------------------------------------------------
// Create Date    : 2020-09-07 15:48:40
// Revise Data    : 2020-09-08 09:53:32
// File Name      : lcd.v
// Target Devices : XC7Z015-CLG485-2
// Tool Versions  : Vivado 2019.2
// Revision       : V1.1
// Editor         : sublime text3, tab size (4)
// Description    : LCD1602 driver

module lcd(
	input				clk			,
	input				rst_n		,
	
	output	reg			lcd_rs		,
	output	wire		lcd_rw		,
	output	reg			lcd_en		,
	output	reg	[7:0]	lcd_data	
	);

	reg	[17:0]	cnt				;
	reg	[3:0]	state_c			;
	reg	[3:0]	state_n			;
	reg	[4:0]	char_cnt		;
	reg	[7:0]	data_display	;

	localparam
		IDLE			= 4'd0	,
		INIT 			= 4'd1	,
		S0				= 4'd2	,
		S1				= 4'd3	,
		S2				= 4'd4	,
		S3				= 4'd5	,
		ROW1_ADDR		= 4'd6	,
		WRITE			= 4'd7	,
		ROW2_ADDR		= 4'd8	,
		stop			= 4'd9	;


	assign lcd_rw = 1'b0;


	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			cnt <= 17'd0;
		end
		else begin
			if (cnt==17'd100_000 - 1) begin
				cnt <= 17'd0;
			end
			else begin
				cnt <= cnt + 1'b1;
			end
		end
	end

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			lcd_en <= 0;
		end
		else if (cnt==17'd50_000 - 1) begin
			lcd_en <= 1;
		end
		else if (cnt==17'd100_000 - 1) begin
			lcd_en <= 0;
		end
	end

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			char_cnt <= 0;
		end
		else if (state_c==WRITE && cnt==17'd50_000 - 1) begin
			if (char_cnt==5'd24) begin
				char_cnt <= 5'd0;
			end
			else begin
				char_cnt <= char_cnt + 1'b1;
			end
		end
	end

	always @(*) begin
		case(char_cnt)
			5'd0: data_display   = "P";
			5'd1: data_display   = "a";
			5'd2: data_display   = "n";
			5'd3: data_display   = "-";
			5'd4: data_display   = "H";
			5'd5: data_display   = "o";
			5'd6: data_display   = "n";
			5'd7: data_display   = "g";
			5'd8: data_display   = "-";
			5'd9: data_display   = "F";
			5'd10: data_display  = "e";
			5'd11: data_display  = "n";
			5'd12: data_display  = "g";
			5'd13: data_display  = "L";
			5'd14: data_display  = "C";
			5'd15: data_display  = "D";
			5'd16: data_display  = "1";
			5'd17: data_display  = "6";
			5'd18: data_display  = "0";
			5'd19: data_display  = "2";
			5'd20: data_display  = "-";
			5'd21: data_display  = "T";
			5'd22: data_display  = "e";
			5'd23: data_display  = "s";
			5'd24: data_display  = "t";
			default:data_display = "P";
		endcase
	end

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			state_c <= IDLE;
		end
		else if(cnt==17'd50_000 - 1) begin
			state_c <= state_n;
		end
	end

	reg	[19:0]	cnt_15ms;
	reg		flag	;
	always@(posedge clk or negedge rst_n)begin
		if (!rst_n) begin
			cnt_15ms <= 0;
		end
		else if (state_c == IDLE) begin
			cnt_15ms <= cnt_15ms + 1'b1;
		end
	end

	always@(posedge clk or negedge rst_n)begin
		if (!rst_n) begin
			flag <= 0;
		end
		else if (state_c==IDLE && cnt_15ms==20'd750000) begin
			flag <= 1;
		end
	end

	always @(*) begin
		case(state_c)
			IDLE		:
				begin
					if (flag) begin
						state_n = INIT;
					end
					else begin
						state_n = state_c;
					end
				end
			INIT 	:
				begin
					state_n = S0;
				end
			S0  	:
				begin
					state_n = S1;
				end
			S1  	:
				begin
					state_n = S2;
				end
			S2  	:
				begin
					state_n = S3;
				end
			S3  	:
				begin
					state_n = ROW1_ADDR;
				end
			ROW1_ADDR:
				begin
					state_n = WRITE;
				end
			WRITE		:
				begin
					if (char_cnt==5'd12) begin
						state_n = ROW2_ADDR;
					end
					else if (char_cnt==5'd24) begin
						state_n = stop;
					end
					else begin
						state_n = state_c;
					end
				end
			ROW2_ADDR:
				begin
					state_n = WRITE;
				end
			stop		:
				begin
					state_n = stop;
				end
			default:state_n = IDLE;
		endcase
	end

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			lcd_data <= 8'd0;
		end
		else begin
			case(state_c)
				IDLE		:begin lcd_data <= 8'h38; lcd_rs <= 0;end
				INIT 		:begin lcd_data <= 8'h38; lcd_rs <= 0;end
				S0			:begin lcd_data <= 8'h08; lcd_rs <= 0;end
				S1			:begin lcd_data <= 8'h01; lcd_rs <= 0;end
				S2			:begin lcd_data <= 8'h06; lcd_rs <= 0;end
				S3			:begin lcd_data <= 8'h0c; lcd_rs <= 0;end
				ROW1_ADDR	:begin lcd_data <= 8'h80; lcd_rs <= 0;end
				WRITE		:begin lcd_data <= data_display; lcd_rs <= 1;end
				ROW2_ADDR	:begin lcd_data <= 8'hc0; lcd_rs <= 0;end
				stop		:begin lcd_data <= 8'h38; lcd_rs <= 0;end
				default:;
			endcase
		end
	end
endmodule

Código de prueba:


`timescale 1ns/1ns

module lcd_tb (); /* this is automatically generated */

	reg rst_n;
	reg clk;

	wire       lcd_rs;
	wire       lcd_rw;
	wire       lcd_en;
	wire [7:0] lcd_data;

	lcd inst_lcd
		(
			.clk      (clk),
			.rst_n    (rst_n),
			.lcd_rs   (lcd_rs),
			.lcd_rw   (lcd_rw),
			.lcd_en   (lcd_en),
			.lcd_data (lcd_data)
		);

	initial clk = 0;
	always #10 clk = ~clk;

	initial begin
		#1;
		rst_n = 0;
		#200;
		rst_n = 1;
		#200;


		#100000000;
		$stop;

	end

endmodule

 Forma de onda de simulación:

Mapa físico:

 

Supongo que te gusta

Origin blog.csdn.net/qq_33231534/article/details/108484995
Recomendado
Clasificación