Sistema de adquisición de datos basado en FPGA (1)

Tabla de contenido

Diseño general del sistema

1. Módulo receptor de puerto serie

Dos, módulo de envío de puerto serie

Módulo antirrebote de tres teclas

Cuatro, módulo ROM


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:

Figura 1: Diagrama de bloques general del sistema de adquisición de datos FPGA

 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:

Tabla 1.1: Lista de interfaces del módulo receptor del puerto serie
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:
 

Tabla 2.1: Lista de señales de interfaz del módulo de envío del puerto serie
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.

Tabla 3.1: Lista de señales del módulo antirrebote de teclas
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.

 

 

 

Supongo que te gusta

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