Verilog Práctico
Capítulo 4. Display de 7 segmentos.
En esta entrada veremos cómo controlar un display de 7 segmentos.
En el esquema de conexión se muestra un display de ánodo común. Para un display de cátodo común hay que conectar los pines 3 y 8 a GND, los pines de los segmentos a-g se conectan igual. En caso de duda consulta el datasheet del display que vayas a utilizar.
He intercalado un 74HC244 entre la Alhambra y el display para mejorar la calidad de las señales. El 244 es un buffer o line driver no inversor que elimina el ruido y amplifica las señales. El uso de este CI es opcional, pero si experimentas problemas de funcionamiento, prueba a utilizar uno.
1. Contador hexadecimal
Veamos un ejemplo de funcionamiento con un contador hexadecimal accionado mediante el switch 1 de la Alhambra.
Declaramos un módulo que llamaremos display y que tiene como entrada el switch 1 y como salida un registro de 8 bits reg [7:0] segmentos este registro lo utilizaremos para iluminar los ledes del display.
module display ( input SW1, output reg [7:0] segmentos = 0);
Declaramos un registro de 4 bits que llamaremos reg [3:0] contador donde se almacena el valor del contador. Con 4 bits podemos contar de 0 a 15 que representaremos en hexadecimal como 0-F.
reg [3:0] contador = 0;
Con la declaración always @(posedge SW1) estamos diciendo que cada vez que pulsemos SW1 se incremente el contador contador <= contador + 1 y con case (contador) evaluamos el valor del contador y asignamos el registro segmentos distintos valores para iluminar los ledes del display de acuerdo al valor del contador. En caso de utilizar un display de cátodo común, cambiar los ceros por unos en la estructura case. Por ejemplo, el 0 quedaría así: 4'd0: segmentos <= 8'b00111111;
Como se declaran números en verilog:
4’d9 significa número de 4 bits, decimal de valor 9
8’b11001010 significa número de 8 bits, binario de valor 11001010
4’hF significa número de 4 bits, hexadecimal de valor F
Ponemos todo junto y el código queda así:
// decodificador HEX de 7 segmentos con contador manual
// accionado mediente SW1.
// declaramos las E/S. Los registros los inicializamos a 0
module display ( input SW1, output reg [7:0] segmentos = 0);
reg [3:0] contador = 0;
always @(posedge SW1)
begin
// con cada pulsación de SW1 incrementamos el contador
contador <= contador + 1;
// dependiendo del valor del contador,
// asignamos un valor al registro SEG
// para que ilumine los ledes correspondientes a cada número
case (contador) //pgfe_dcba segmentos
4'd0: segmentos <= 8'b1100_0000;
4'd1: segmentos <= 8'b1111_1001;
4'd2: segmentos <= 8'b1010_0100;
4'd3: segmentos <= 8'b1011_0000;
4'd4: segmentos <= 8'b1001_1001;
4'd5: segmentos <= 8'b1001_0010;
4'd6: segmentos <= 8'b1000_0010;
4'd7: segmentos <= 8'b1111_1000;
4'd8: segmentos <= 8'b1000_0000;
4'd9: segmentos <= 8'b1001_1000;
4'd10: segmentos <= 8'b1000_1000;
4'd11: segmentos <= 8'b1000_0011;
4'd12: segmentos <= 8'b1100_0110;
4'd13: segmentos <= 8'b1010_0001;
4'd14: segmentos <= 8'b1000_0110;
4'd15: segmentos <= 8'b1000_1110;
endcase
end
endmodule
El archivo .pfc es el siguiente:
set_io SW1 10
set_io segmentos[0] 119
set_io segmentos[1] 118
set_io segmentos[2] 117
set_io segmentos[3] 116
set_io segmentos[4] 115
set_io segmentos[5] 114
set_io segmentos[6] 113
set_io segmentos[7] 112
Lo subimos a la Alhambra y vemos que con cada pulsación de SW1 el display va mostrando los números 0-9 y las letras A-F.
2. Contador decimal
En el siguiente ejemplo veremos un contador decimal activado con el pulsador SW1. A grandes rasgos el código es similar al ejemplo anterior, pero eliminando de la estructura case aquellos dígitos que no se van a utilizar e introduciendo una sentencia if() para resetear el contador cuando llega a 10.
// decodificador decimal para display 7 segmentos con
// contador manual por flanco de SW1
// declaramos las E/S. Los registros los inicializamos a 0
module display( input SW1, output reg [7:0] segmentos = 0);
reg [3:0] contador;
always @ ( posedge SW1 ) begin
contador <= contador + 1;
// comprobamos el contador
// y si llega a 10 lo reseteamos
if (contador>=4'd9)
contador <= 4'd0;
case (contador) //pgfedcba segmentos
4'd0: segmentos <= 8'b1100_0000;
4'd1: segmentos <= 8'b1111_1001;
4'd2: segmentos <= 8'b1010_0100;
4'd3: segmentos <= 8'b1011_0000;
4'd4: segmentos <= 8'b1001_1001;
4'd5: segmentos <= 8'b1001_0010;
4'd6: segmentos <= 8'b1000_0010;
4'd7: segmentos <= 8'b1111_1000;
4'd8: segmentos <= 8'b1000_0000;
4'd9: segmentos <= 8'b1001_1000;
default: segmentos <= 8'b1111_1111;
endcase
end
endmodule
El archivo .pfc es igual que en el ejemplo 1.
Lo compilamos y lo subimos a la Alhambra y vemos que con cada pulsación de SW1 el display va mostrando los números 0-9.
3. Contador decimal con prescaler
En el capítulo 3 vimos cómo dividir la frecuencia del reloj en frecuencias menores mediante el uso de un prescaler. Añadiremos uno al código anterior para hacer un contador decimal que se incrementa de manera automática.
La velocidad con la que se incrementa el contador la seleccionamos mediante:
assign clk_out = prescaler[22];
El código es el siguiente:
// decodificador decimal para display 7 segmentos con prescaler
module display(input clk, output reg [7:0] segmentos = 0);
wire clk;
wire clk_out;
//-- Registro para implementar contador de N bits
reg [23:0] prescaler = 0;
//-- El bit más significativo se saca por la salida
assign clk_out = prescaler[22];
//-- Contador: se incrementa en flanco de subida
always @(posedge(clk))
begin
prescaler <= prescaler + 1;
end
// en este registro se almacena el valor del contador
reg [3:0] contador;
always @ ( posedge clk_out ) begin
contador <= contador + 1;
// comprobamos el contador
// y si llega a 10 lo reseteamos
if (contador>=4'd9)
contador <= 4'd0;
// dependiendo del valor del contador,
// asignamos un valor al registro SEG
// para que ilumine los ledes correspondientes a cada número
case (contador) //pgfedcba segmentos
4'd0: segmentos <= 8'b1100_0000;
4'd1: segmentos <= 8'b1111_1001;
4'd2: segmentos <= 8'b1010_0100;
4'd3: segmentos <= 8'b1011_0000;
4'd4: segmentos <= 8'b1001_1001;
4'd5: segmentos <= 8'b1001_0010;
4'd6: segmentos <= 8'b1000_0010;
4'd7: segmentos <= 8'b1111_1000;
4'd8: segmentos <= 8'b1000_0000;
4'd9: segmentos <= 8'b1001_1000;
default: segmentos <= 8'b1111_1111;
endcase
end
endmodule
El archivo pcf es el siguiente:
set_io clk 21
set_io segmentos[0] 119
set_io segmentos[1] 118
set_io segmentos[2] 117
set_io segmentos[3] 116
set_io segmentos[4] 115
set_io segmentos[5] 114
set_io segmentos[6] 113
set_io segmentos[7] 112
Lo compilamos y lo subimos a la Alhambra y vemos como el contador se incrementa de manera automática.
4. Contador hexadecimal con reset
En este último ejemplo vamos a añadir un prescaler y un reset manual al contador hexadecimal del ejemplo 1.
El reset lo implementamos con una declaración if() asociada al pulsador SW1
if (reset)
contador <= 4'd0;
Si solo vamos a declarar una acción no hace falta poner begin-end, pero si queremos declarar múltiples acciones cuando se cumpla if() hay que poner begin-end.
if (reset)
begin
accion 1;
accion 2;
accion 3;
end
El código queda así:
module display(input clk, input reset, output reg [7:0] segmentos = 0);
wire clk;
wire clk_out;
reg [23:0] prescaler = 0;
reg [3:0] contador;
assign clk_out = prescaler[22];
always @(posedge(clk))
begin
prescaler <= prescaler + 1;
end
always @ ( posedge clk_out ) begin
contador <= contador + 1;
if (reset)
begin
contador <= 4'd0;
end
case (contador) //pgfedcba segmentos
4'd0: segmentos <= 8'b1100_0000;
4'd1: segmentos <= 8'b1111_1001;
4'd2: segmentos <= 8'b1010_0100;
4'd3: segmentos <= 8'b1011_0000;
4'd4: segmentos <= 8'b1001_1001;
4'd5: segmentos <= 8'b1001_0010;
4'd6: segmentos <= 8'b1000_0010;
4'd7: segmentos <= 8'b1111_1000;
4'd8: segmentos <= 8'b1000_0000;
4'd9: segmentos <= 8'b1001_1000;
4'd10: segmentos <= 8'b100_01000;
4'd11: segmentos <= 8'b100_00011;
4'd12: segmentos <= 8'b110_00110;
4'd13: segmentos <= 8'b101_00001;
4'd14: segmentos <= 8'b100_00110;
4'd15: segmentos <= 8'b100_01110;
default: segmentos <= 8'b1111_1111;
endcase
end
endmodule
El archivo pcf es el siguiente:
set_io clk 21
set_io reset 10
set_io segmentos[0] 119
set_io segmentos[1] 118
set_io segmentos[2] 117
set_io segmentos[3] 116
set_io segmentos[4] 115
set_io segmentos[5] 114
set_io segmentos[6] 113
set_io segmentos[7] 112
Lo subimos a la Alhambra y vemos un contador hexadecimal automático. Si pulsamos el switch 1 reseteamos el contador a 0.
Eso es todo.
¡Hasta pronto!
Links
Más info sobre displays de 7 segmentos con la Alhambra aquí:
digital-electronics-with-open-FPGAs-tutorial. Vídeo 24: Display de 7 segmentos