Tabla de contenido
1. Módulo receptor de puerto serie
Dos, módulo de envío de puerto serie
Módulo antirrebote de tres teclas
Enlace de descarga de este archivo de proyecto de diseño (incluidas las notas): https://download.csdn.net/download/qq_33231534/12450178
Diseño general del sistema
Este diseño es principalmente para usar ADC y DAC. La realización principal del proceso funcional es: en primer lugar, envíe señales de control a FPGA a través del puerto serie, controle el chip DAC tlv5618 para el reemplazo de DA, los datos convertidos se almacenan en ROM y los datos en ROM se leen cuando comienza la conversión Realizar conversión de lectura. En segundo lugar, use el botón para controlar adc128s052 para realizar la conversión de analógico a digital 100 veces. Los datos de conversión de analógico a digital se almacenan en el FIFO, y luego los datos se leen del FIFO y se muestran en la PC a través del puerto serie. El diagrama de bloques general del sistema es el siguiente:
Como se puede ver en la figura, el sistema incluye principalmente 9 módulos: módulo receptor de puerto serie, módulo antirrebote de llave, módulo de control de llave, módulo ROM, módulo de unidad DAC, módulo de unidad ADC, módulo FIFO síncrono, módulo de control FIFO, módulo de transmisión de puerto serie . Las funciones de cada módulo son las siguientes:
(1) Módulo receptor de puerto serie (UART_Byte_Rx.v): complete la recepción de datos del puerto serie, convierta los datos serie en datos paralelos y la salida.
(2) Módulo antirrebote de tecla (key_filter.v): para antirrebote de tecla, puede generar un signo de pulsación de tecla de pulso y un signo de tiempo de pulsación de tecla.
(3) Módulo de control de botones (key_ctrl.v): cuando el DA haya emitido señales analógicas, presione el botón para controlar la conversión de ADC 100 veces.
(4) Módulo ROM (single_port_rom.v): almacena los datos convertidos por DA y puede almacenar datos de onda sinusoidal.
(5) Módulo de unidad DAC (dac_driver.v): módulo de unidad de conversión de digital a analógico, conectado a un chip DAC externo, que proporciona señales de datos y reloj del chip DAC, etc.
(6) Módulo de unidad ADC (adc_driver.v): módulo de unidad de conversión de analógico a digital, conectado al chip ADC externo, que proporciona señales de control y reloj del chip ADC, etc.
(7) Módulo FIFO síncrono (sync_fifo.v): almacena los datos después de la conversión ADC.
(8) Módulo de control FIFO (fifo_ctrl.v): cuando hay datos en FIFO, los datos en FIFO se convierten en datos que se pueden enviar por el puerto serie UART.
(9) Módulo de envío de puerto serie (Uart_Byte_Tx.v): Los datos convertidos por el módulo de control FIFO se envían al puerto serie a través del módulo de envío de puerto serie y se muestran en el lado de la PC.
(10) Módulo de control DAC (dac_ctrl.v): Al recibir el comando especificado por el puerto serie, comienza a convertir los datos sinusoidales de la ROM a DAC.
1. Módulo receptor de puerto serie
El módulo receptor del puerto serie se ha escrito antes y se puede utilizar directamente aquí. Aquí, el formato de datos del puerto serie incluye: 1 bit de inicio, 8 bits de datos, 1 bit de parada, sin bit de paridad. La lista de interfaces del módulo se ingresa de la siguiente manera:
Nombre de la señal | E / S | Dígitos | Función descriptiva |
clk | yo | 1 | Reloj del sistema 50MHz |
rst_n | yo | 1 | Reinicio de sistema |
rs232_tx | yo | 1 | Puerto de datos en serie |
baud_set | yo | 3 | Señal de selección de velocidad en baudios |
data_byte | EL | 8 | Salida de datos en paralelo |
rx_done | EL | 1 | Recibir un indicador de datos completos de 1 byte |
El código es el siguiente: UART_Byte_Rx.v
//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 PHF的CSDN
//File name: UART_Byte_Rx.v
//Last modified Date: 2020/5/22
//Last Version:
//Descriptions: 工业级别串口数据接收模块,防干扰。对每位数据内部采样16个点,
// 对中间6位数据进行判定数据是1还是0
//-------------------------------------------------------------------
module UART_Byte_Rx(
input clk ,//系统时钟50MHz
input rst_n ,//系统复位
input rs232_tx ,//串口串行数据发送数据口
input [ 2: 0] baud_set ,//波特率选择信号
output reg [ 7: 0] data_byte ,//并行数据输出
output reg rx_done //接收1字节数据完成标志,rx_done可以作为输出有效信号使用
);
reg [ 13: 0] baud_c ;//波特率对应计数次数(4800bps-10416),(9600bps-5208),(19200bps-2604),
//(38400bps-1302),(57600bps-868),(115200bps-434)
reg rs232_tx_ff0 ;
reg rs232_tx_ff1 ;
reg rs232_tx_ff2 ;
wire tx_neg_flag ;
reg add_flag ;
reg [ 13: 0] cnt0 ;
reg [ 3: 0] cnt1 ;
reg [ 9: 0] cnt2 ;
reg [ 3: 0] cnt3 ;
reg [ 2: 0] cnt_0 ;
reg [ 2: 0] cnt_1 ;
wire add_cnt0 ;
wire end_cnt0 ;
wire add_cnt1 ;
wire end_cnt1 ;
wire add_cnt2 ;
wire end_cnt2 ;
wire add_cnt3 ;
wire end_cnt3 ;
//查找表
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
baud_c <= 5208;
end
else begin
case(baud_set)
0: baud_c = 14'd10416;
1: baud_c = 14'd5208 ;
2: baud_c = 14'd2604 ;
3: baud_c = 14'd1302 ;
4: baud_c = 14'd868 ;
5: baud_c = 14'd434 ;
default:baud_c = 14'd5208 ;//默认9600bps
endcase
end
end
//打两拍 防止亚稳态,同时scan negedge
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rs232_tx_ff0 <= 1;
rs232_tx_ff1 <= 1;
rs232_tx_ff2 <= 1;
end
else begin
rs232_tx_ff0 <= rs232_tx;
rs232_tx_ff1 <= rs232_tx_ff0;
rs232_tx_ff2 <= rs232_tx_ff1;
end
end
//扫描下降沿
assign tx_neg_flag = rs232_tx_ff2 && !rs232_tx_ff1;
//计数标志信号
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
add_flag <= 0;
end
else if(tx_neg_flag) begin
add_flag <= 1;
end
else if(rx_done)begin
add_flag <= 0;
end
end
//计数器,计数1bit数据长度
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1'b1;
end
end
assign add_cnt0 = add_flag;
assign end_cnt0 = add_cnt0 && cnt0==baud_c-1;
//计数器,计数8位接收数据长度
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1'b1;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1== 8;
//比特内部采样点时钟计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt2 <= 0;
end
else if(add_cnt2)begin
if(end_cnt2)
cnt2 <= 0;
else
cnt2 <= cnt2 + 1'b1;
end
end
assign add_cnt2 = add_flag;
assign end_cnt2 = add_cnt2 && (cnt2== (baud_c/16)-1 || end_cnt0);
//一个bit数据中16个采样点计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt3 <= 0;
end
else if(add_cnt3)begin
if(end_cnt3)
cnt3 <= 0;
else
cnt3 <= cnt3 + 1'b1;
end
end
assign add_cnt3 = add_cnt2 && cnt2== (baud_c/16)-1;
assign end_cnt3 = end_cnt0 || (end_cnt2 && cnt3==16-1);
//比特内选取6个采样点是0或1计数
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
cnt_0 <= 0;
cnt_1 <= 0;
end
else if(add_flag) begin
if(cnt3>=6 && cnt3<=11)begin
if(cnt2==baud_c/16/2 && rs232_tx_ff1==0)
cnt_0 <= cnt_0 + 1'b1;
else if(cnt2==baud_c/16/2 && rs232_tx_ff1==1)
cnt_1 <= cnt_1 + 1'b1;
end
else if(end_cnt0)begin
cnt_0 <= 0;
cnt_1 <= 0;
end
end
else begin
cnt_0 <= 0;
cnt_1 <= 0;
end
end
//输出并行数据data_byte
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
data_byte <= 0;
end
else if(end_cnt0 && cnt1>0 && cnt1 <9) begin
if(cnt_0 >= cnt_1)
data_byte[cnt1-1] = 0;
else if(cnt_0 < cnt_1)
data_byte[cnt1-1] = 1;
end
end
//输出接收完成标志信号
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rx_done <= 0;
end
else if(end_cnt1) begin
rx_done <= 1;
end
else begin
rx_done <= 0;
end
end
endmodule
Los gráficos de simulación son los siguientes:
Dos, módulo de envío de puerto serie
En correspondencia con el módulo receptor del puerto serie aquí, se envían datos de 8 bits, más un bit de inicio y un bit de parada. La lista de señales de la interfaz del módulo es la siguiente:
Nombre de la señal | E / S | Dígitos | Función descriptiva |
clk | yo | 1 | Reloj del sistema 50MHz |
rst_n | yo | 1 | Reinicio de sistema |
send_en | yo | 1 | Enviar habilitar |
data_byte | yo | 8 | Datos enviados |
baud_set | yo | 3 | Ajuste de velocidad en baudios |
rs232_tx | EL | 1 | FPGA convierte datos en datos en serie y los envía |
tx_done | EL | 1 | Enviar marca de datos completos |
uart_state | EL | 1 | Estado de envío del puerto serie, 1 significa ocupado, 0 significa inactivo |
El código es el siguiente: Uart_Byte_Tx.v
//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 phf的CSDN
//File name: Uart_Byte_Tx.v
//Last modified Date: 2020/5/22
//Last Version:
//Descriptions: 串口发送模块,8位数据位、1位起始位和1位停止位、无校验位
//-------------------------------------------------------------------
module Uart_Byte_Tx(
input clk , //系统时钟
input rst_n , //系统复位
input send_en , //发送使能
input [ 7 : 0 ] data_byte , //发送的数据
input [ 2 : 0 ] baud_set , //波特率设置
output reg rs232_tx , //FPGA将数据转换成串行数据发出
output reg tx_done , //发送数据完毕标志
output reg uart_state //串口发送状态,1为忙,0为空闲
);
reg [ 13: 0] baud_c ;//(4800bps-10416),(9600bps-5208),(19200bps-2604),
//(38400bps-1302),(57600bps-868),(115200bps-434)
wire [ 9: 0] data_out ;
reg [ 15: 0] cnt0 ; //1bit数据长度计数
reg [ 3: 0] cnt1 ; //发送一字节数据对每个字节计数
wire add_cnt0 ; //计数器cnt0加一条件
wire add_cnt1 ; //计数器cnt1加一条件
wire end_cnt0 ; //计数器cnt0结束条件
wire end_cnt1 ; //计数器cnt1结束条件
reg [ 7: 0] data_byte_ff ; //发送使能时将发送的数据寄存下来
//波特率查找表
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
baud_c <= 5208;
end
else begin
case(baud_set)
0: baud_c = 14'd10416;
1: baud_c = 14'd5208 ;
2: baud_c = 14'd2604 ;
3: baud_c = 14'd1302 ;
4: baud_c = 14'd868 ;
5: baud_c = 14'd434 ;
default:baud_c = 14'd5208 ;//默认9600bps
endcase
end
end
//串口状态标志,0为空闲,1为忙
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
uart_state <= 0;
end
else if(send_en) begin
uart_state <= 1;
end
else if(end_cnt1)begin
uart_state <= 0;
end
else begin
uart_state <= uart_state;
end
end
//1bit数据长度计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1'b1;
end
end
assign add_cnt0 = uart_state==1;
assign end_cnt0 = add_cnt0 && cnt0== baud_c-1;
//发送一字节数据对每个字节计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1'b1;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1== 10-1;
//串口发送结束标志
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
tx_done <= 0;
end
else if(end_cnt1) begin
tx_done <= 1;
end
else begin
tx_done <= 0;
end
end
//发送使能时将发送的数据寄存下来
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
data_byte_ff <= 0;
end
else if(send_en) begin
data_byte_ff <= data_byte;
end
end
//发送串行数据到串口
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rs232_tx <= 1;
end
else if(uart_state && cnt0==0) begin
rs232_tx <= data_out[cnt1];
end
end
assign data_out = {1'b1,data_byte_ff,1'b0};
endmodule
La forma de onda de simulación es la siguiente:
Módulo antirrebote de tres teclas
Este es un módulo necesario para usar botones. La lista de señales se ingresa a continuación, y este módulo es universal.
Nombre de la señal | E / S | Dígitos | Función descriptiva |
clk | yo | 1 | Reloj del sistema 50MHz |
rst_n | yo | 1 | Reinicio de sistema |
key_in | yo | 1 | Entrada clave |
key_flag | EL | 1 | Salida de una señal válida de tecla de pulso |
estado_clave | EL | 1 | Estado del botón de salida, 1 significa no presionado, 0 significa presionado |
El código es el siguiente: key_filter.v
//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 潘洪峰的CSDN博客
//File name: key_filter.v
//Last modified Date: 2020/5/22
//Last Version:
//Descriptions: 按键消抖模块
//-------------------------------------------------------------------
module key_filter(
input clk ,//系统时钟50MHz
input rst_n ,//系统复位
input key_in ,//按键输入
output reg key_flag ,//输出一个脉冲按键有效信号
output reg key_state //输出按键状态,1为未按下,0为按下
);
parameter IDLE = 4'b0001 ;//空闲状态,读取按键按下的下降沿,读取到下降沿转到下一个状态
parameter FILTER1 = 4'b0010 ;//计数20ms状态,计数结束转到下一个状态
parameter STABLE = 4'b0100 ;//数据稳定状态,等待按键松开上升沿,读取到上升沿转到下一个状态
parameter FILTER2 = 4'b1000 ;//计数20ms状态,计数结束转到空闲状态
parameter TIME_20MS = 20'd1000_000 ;
reg [ 3: 0] state_c ;//寄存器改变状态
reg [ 3: 0] state_n ;//现在状态
wire IDLE_to_FILTER1 ;//IDLE状态转到FILTER1状态条件
wire FILTER1_to_STABLE;//FILTER1状态转到STABLE状态条件
wire STABLE_to_FILTER2;//STABLE状态转到FILTER2状态条件
wire FILTER2_to_IDLE ;//FILTER2状态转到IDLE状态条件
reg key_in_ff0 ;
reg key_in_ff1 ;
reg key_in_ff2 ;
wire key_in_pos ;//检测上升沿标志
wire key_in_neg ;//检测下降沿标志
reg [ 19: 0] cnt ;
wire add_cnt ;
wire end_cnt ;
//状态机第一段,状态转换
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
//状态机第二段,状态转换条件
always @(*)begin
case(state_c)
IDLE :begin
if(IDLE_to_FILTER1)
state_n = FILTER1;
else
state_n = state_c;
end
FILTER1:begin
if(FILTER1_to_STABLE)
state_n = STABLE;
else
state_n = state_c;
end
STABLE :begin
if(STABLE_to_FILTER2)
state_n = FILTER2;
else
state_n = state_c;
end
FILTER2:begin
if(FILTER2_to_IDLE)
state_n = IDLE;
else
state_n = state_c;
end
default:state_n = IDLE;
endcase
end
//状态转换条件
assign IDLE_to_FILTER1 = key_in_neg ;
assign FILTER1_to_STABLE = state_c==FILTER1 && end_cnt;
assign STABLE_to_FILTER2 = key_in_pos ;
assign FILTER2_to_IDLE = state_c==FILTER2 && end_cnt;
//打两拍,防止亚稳态
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_in_ff0 <= 1;
key_in_ff1 <= 1;
key_in_ff2 <= 1;
end
else begin
key_in_ff0 <= key_in;
key_in_ff1 <= key_in_ff0;
key_in_ff2 <= key_in_ff1;
end
end
//下降沿和上升沿检测
assign key_in_pos = (state_c==STABLE) ?(key_in_ff1 && !key_in_ff2):1'b0;
assign key_in_neg = (state_c==IDLE) ?(!key_in_ff1 && key_in_ff2):1'b0;
//计数20ms
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1'b1;
end
else begin
cnt <= 0;
end
end
assign add_cnt = state_c==FILTER1 || state_c==FILTER2;
assign end_cnt = add_cnt && cnt== TIME_20MS-1;
//key_flag按键按下输出一个脉冲信号
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_flag <= 0;
end
else if(state_c==FILTER1 && end_cnt) begin
key_flag <= 1;
end
else begin
key_flag <= 0;
end
end
//key_state按键按下状态信号
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_state <= 1;
end
else if(state_c==STABLE || state_c==FILTER2) begin
key_state <= 0;
end
else begin
key_state <= 1;
end
end
endmodule
Cuatro, módulo ROM
Omita el módulo de control clave aquí, primero termine algunos módulos básicos y luego describa el módulo de control.
El módulo ROM es una memoria de solo lectura y hay tres formas de escribir este módulo.
(1) Utilice el núcleo IP de quartus (yo uso quartus prime 17.1), seleccione 1-PORT y configure los parámetros
(2) Cree un nuevo archivo HDL de verilog, seleccione Editar—> Insertar plantilla y seleccione de acuerdo con la siguiente figura para generar directamente una plantilla, que se puede utilizar directamente.
(3) Escriba un archivo ROM usted mismo, al igual que el código generado por el método (2), puede establecer el ancho de bits de datos y el ancho de la dirección.
Tenga en cuenta la declaración de inicialización de ROM en el código: $ readmemb ("./ sin_12bit.txt", rom); inicialice leyendo un archivo externo, el archivo de inicialización puede ser escrito por usted mismo o generado por Excel o matlab. Entre ellos, $ readmemb ("./ sin_12bit.txt", rom); el uso de la oración también se introduce en mi blog, por lo que puede consultarlo si lo necesita.
El archivo verilog generado es el siguiente: single_port_rom.v
module single_port_rom
#(parameter DATA_WIDTH=12, parameter ADDR_WIDTH=12)
(
input [(ADDR_WIDTH-1):0] addr,
input clk,
output reg [(DATA_WIDTH-1):0] q
);
// Declare the ROM variable
reg [DATA_WIDTH-1:0] rom[2**ADDR_WIDTH-1:0];
initial
begin
$readmemb("./sin_12bit.txt", rom);
end
always @ (posedge clk)
begin
q <= rom[addr];
end
endmodule
Los gráficos de simulación son los siguientes:
Los datos que contiene son el archivo de onda de 12 bits de la función sin que generé en matlab. Puede ver que lee los datos de la dirección correspondiente en el flanco ascendente de clk.