首页 > 其他分享 >基于MCU和FPGA的DDS信号发生器——MCU与FPGA通信部分

基于MCU和FPGA的DDS信号发生器——MCU与FPGA通信部分

时间:2024-07-05 12:55:52浏览次数:20  
标签:DL 20 MAT FPGA DDS rx uart 5208 MCU

前言

由于项目制作时间有限,考虑到改变方案的风险,我们在遇到许多问题时并没有选择改变路线,而是在现有成果上缝缝补补,造就了现在看来十分笨重的通信模块,不过错误也是宝贵的学习经验,对于电子领域的工作者更是如此,因而笔者保留了我们制作时的失误和思考历程,供广大读者参考借鉴。

总体思路

一般FPGA不适合作为一个完整系统,因为FPGA更擅长流水处理,而不擅长控制,并且资源有限,像DDS信号发生器这种需要多个IP核的项目,on chip memory很容易写满。因而我们选择使用MCU作为控制端,一方面减轻FPGA负担,另一方面可以利用MCU的OLED外设提供用户交互界面。

FPGA需要从MCU接收10k到10M的载波信号频率,1k到5k调制信号频率,20到100调幅系数,10到20调频系数,五种波形选择,总计五个数据。分别对应24位、13位,8位,7位,3位二进制数。我们选用的UART配置为9600的波特率,一次发送一个起始位,八个数据位,一个终止位,无校验位。

我们在初步测试时,利用singal tap发现接收FPGA接收到许多杂乱无章的数据(后来发现时MCU发送端接错了管脚。。。),当时初步判断是噪声干扰,因而在之后的绝大部分工作都花在了排除噪声上。当时已经写好的FPGA接收代码和MCU发送代码都没有加校验位,因而我们提出的排除噪声的方案是:FPGA添加一个FIFO模块用来暂时储存MCU发送过来的数据,MCU在用户输入了所需的所有数据后,将所有数据打包成十个八位码元连续不间断发送,这十个码元中只有中间八个是数据位(24位的载波信号频率需要三个八位码元发送,一次类推,五种数据共需要八个码元),前后两位都固定发送0xff,当且仅当FPGA接收到首位都位0xff的数据时,才进行拆包,一点出现噪声,首位的数据将不再是0xff,如此就有效避免了噪声的干扰。

FPGA接收部分

UART接收部分

Verilog代码

端口及变量定义

module uart1(clk,rst,uart_rx,r_rx_data,rx_done);
	input clk;
	input rst;
	input uart_rx;//输入信号
	output reg rx_done;//接收完成标志
	output reg [7:0]r_rx_data;//接收到的数据
	
	parameter clk_fre = 50000000;
	parameter baud = 9600;//接受信号波特率
	parameter MCNT_BAUD = clk_fre / baud - 1;//波特率计数最大值
	
	reg [29:0]baud_div_cnt;//波特率计数
	reg en_baud_cnt;//波特率计数使能
	reg [3:0]bit_cnt;//位计数
	reg [7:0]rx_data;//接收数据暂存
	
	reg r_uart_rx;//最终接收数据
	
	reg dff0_uart_rx;
	reg dff1_uart_rx;//打拍
	
	wire negedge_uart_rx;//下降沿标志,用于检测数据起始位
	wire w_rx_done;//接收完成标志

波特率计数模块

always @(posedge clk)
		dff0_uart_rx <= uart_rx;
	always @(posedge clk)
	dff1_uart_rx <= dff0_uart_rx;//若在时钟上升沿附近uart_rx触发则会出现亚稳态问题,故进行两次同步,以将uart_rx同步到clk时钟域上,俗称打拍
		
		
	always @(posedge clk)
		r_uart_rx <= dff1_uart_rx;//相当于一个D触发器,暂存当前状态
		
		assign negedge_uart_rx = ((dff1_uart_rx == 0) && (r_uart_rx == 1));

 打拍及下降沿判断

always @(posedge clk or negedge rst)//波特率计数模块
		if(!rst)
			baud_div_cnt <= 0;
		else if(en_baud_cnt)
			begin
				if(baud_div_cnt == MCNT_BAUD)
					baud_div_cnt <= 0;
				else
					baud_div_cnt <= baud_div_cnt + 1'd1;
			end
			else
				baud_div_cnt <= 0;

波特率计数使能模块

always @(posedge clk or negedge rst)
		if(!rst)
			en_baud_cnt <= 0;
		else if(negedge_uart_rx)
			en_baud_cnt <= 1;
		else if((baud_div_cnt == MCNT_BAUD/2) && (bit_cnt == 0) && (dff1_uart_rx == 1))//如果是毛刺则停止计数
			en_baud_cnt <= 0;
		else if((baud_div_cnt == MCNT_BAUD) && (bit_cnt == 9))//计数完成清零

位计数模块

always @(posedge clk or negedge rst)//位计数器模块
		if(!rst)
			bit_cnt <= 0;
		else if(baud_div_cnt == MCNT_BAUD)
			begin
				if(bit_cnt == 9)
					bit_cnt <= 0;
				else
					bit_cnt <= bit_cnt + 1'd1;
			end

位接收模块 

always @(posedge clk or negedge rst)//位接受逻辑
		if(!rst)
			rx_data <= 8'd0;
		else if(baud_div_cnt == MCNT_BAUD/2)
			begin
				case(bit_cnt)
					1:rx_data[0] <= dff1_uart_rx;
					2:rx_data[1] <= dff1_uart_rx;
					3:rx_data[2] <= dff1_uart_rx;
					4:rx_data[3] <= dff1_uart_rx;
					5:rx_data[4] <= dff1_uart_rx;
					6:rx_data[5] <= dff1_uart_rx;
					7:rx_data[6] <= dff1_uart_rx;
					8:rx_data[7] <= dff1_uart_rx;
					
					//8:rx_data[8] <= dff1_uart_rx;
					default:rx_data<=rx_data;
				endcase
			end

接收完成逻辑

assign w_rx_done = (baud_div_cnt == MCNT_BAUD) && (bit_cnt == 9);
	
	always @(posedge clk)//接受完成标志信号
		rx_done <= w_rx_done;
		
	always @(posedge clk)
		if(w_rx_done)
		r_rx_data<=rx_data;
串口仿真

用test bench写的,大概修改了一下

timescale 1 ns/ 1 ns
module uart1_vlg_tst();
// constants                                           
// general purpose registers
reg eachvec;
// test vector input registers
reg clk;
reg rst;
reg uart_rx;
// wires                                               
wire [7:0]  rx_data;
wire rx_done;

// assign statements (if any)                          
uart1 i1 (
// port map - connection between master ports and signals/registers   
	.clk(clk),
	.rst(rst),
	.rx_data(rx_data),
	.rx_done(rx_done),
	.uart_rx(uart_rx)
);
initial clk = 1;
	always #10 clk = ~clk;

initial                                                
begin
	clk = 1;
	rst = 0;
	uart_rx = 1;
	#201;
	rst = 1;
	#200;
	
	
	
	//8'b01010101
	uart_rx = 0;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 0;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 0;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 0;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	#(5208*20*10);
	
	uart_rx = 0;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 0;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	#(5208*20*10);
	
	uart_rx = 0;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 0;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 0;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	#(5208*20*10);
	
	uart_rx = 0;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 0;	#(5208*20);
	uart_rx = 0;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 0;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	#(5208*20*10);
	
	uart_rx = 0;	#(5208*20);
	uart_rx = 0;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 0;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 0;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	uart_rx = 1;	#(5208*20);
	#(5208*20*10);
	
	
// code that executes only once                        
// insert code here --> begin                          
                                                       
// --> end                                             
$display("Running testbench");                       
end                                                    
/*always                                                
// optional sensitivity list                           
// @(event1 or event2 or .... eventn)                  
begin 
																
// code executes for every event on sensitivity list   
// insert code here --> begin                          
                                                       
@eachvec;                                              
// --> end                                             
end  */                                                  
endmodule

仿真波形

FIFO IP核

http://t.csdnimg.cn/g1y0F

这篇文章中有非常详细的IP核配置教程,我们选用普通单时钟模式

端口定义以及变量使用

module decide  
(  
    input clk,  
    input rst,  
    input uart_tx, // 输入信号
	 
	 input data_valid, 
    output reg [23:0] car_wave_fre, // 10k-10M载波信号频率  
    output reg [12:0] mod_wave_fre, // 1k-5k调制信号频率  
    output reg [7:0] ma, // 20-100调幅系数  
    output reg [6:0] kf, // 10-50调频系数  
    output reg [2:0] wave_select // 信号选择  
);


uart_rx uart_rx_inst
(
	.clk(clk),
	rst_n(rst),
	.TX(data),
	
	.RX(uart_rx),
	.done(data_valid),

);
  
    // FIFO实例化  
    wire rdreq, wrreq, empty, full;  
    wire [7:0] q;  
    wire [4:0] usedw;
	 wire [7:0] data;//输入FPGA的数据
  
    FIFO FIFO_normal_inst (    
        .clock(clk),  
        .data(uart_tx),  
        .rdreq(rdreq),  //读使能
        .wrreq(!empty && !full), // 写入使能  
        .empty(empty),  //读空标志
        .full(full),  //写满标志
        .q(q),  
        .usedw(usedw)  //当前存储了多少字符
    );  
  
  
    reg [7:0] buffer[9:0]; // 用于储存数据 
    reg [1:0] index;        // 用于追踪当前储存的数据在数组中的位置 
    reg packet_start;       // 检测到0xff,开始
	 reg valid_packet;

 具体代码

always @(posedge clk or posedge rst) 
	 begin  
        if (rst) 
		  begin  
            index <= 0;  
            packet_start <= 0;  
            valid_packet <= 0;  
        end else 
		  begin  
            // 接收了十个数据时进行检测 
            if (index == 9) 
				begin  
                if (buffer[0] == 8'hff && buffer[9] == 8'hff) 
					 begin  
                    valid_packet <= 1; // Valid packet detected
						  car_wave_fre <= {buffer[1],buffer[2],buffer[3]};
						  mod_wave_fre <= {buffer[4],buffer[5]};
						  ma <= buffer[6];
						  kf <=  buffer[7];
						  wave_select <= buffer[8];
                end else 
					 begin  
						valid_packet <= 0; // 如果不满足规定的首位都为0xff的条件,停止传输  
                end  
                index <= 0;  
                packet_start <= 0;  
            end else if (data_valid) 
				begin  
                if (!packet_start && uart_tx == 8'hff) 
					 begin  
                    packet_start <= 1; // 有效数据检测到  
                    index <= 0;  
                end  
                if (packet_start) 
					 begin  
                    buffer[index] <= q; // 将数据存在数组中  
                    index <= index + 1;        // 当前数组位数  
                end
					 
            end  
        end  
    end  
endmodule

 MCU发送部分

本项目使用的是MSPM01306单片机

sysconfig配置

需要主义的一点,在Advanced Configuration中Oversampling选择16x保证传输精度,并且选择PA11,PA10这一组管脚

 

发送代码

由于主程序中涉及到OLED显示等库函数,非笔者所写,故在此仅分享发送函数和矩阵键盘函数

发送函数。值得注意的是,UART只能发送二进制数,故我们需要将用户输入的十进制数先转化为二进制数,并且将其分为若干段八位二进制数再进行发送,为了节省CPU资源(曾经我也认为MCU不需要节省CPU资源,直到有一次我在代码里写了指针,编译花了整整一分钟),我们使用逻辑右移运算符,并且与0xff进行位与运算,由此得到我们想要的八位数据

void transmit()
{
	
	  
    unsigned char byte1 = (csfre >> 16) & 0xFF; // 次高8位  
    unsigned char byte2 = (csfre >> 8) & 0xFF;  // 次低8位  
    unsigned char byte3 = csfre & 0xFF;          // 最低8位
	
	   
    unsigned char byte4 = (msfre >> 8) & 0xFF;  // 次低8位  
    unsigned char byte5 = msfre & 0xFF;          // 最低8位
	
	 
    unsigned char byte6 = ma & 0xFF;          // 最低8位
	
	unsigned char byte7 = kf & 0xFF;          // 最低8位
	
	unsigned char byte8 = wave_select & 0xFF;
	// 最低8位
	DL_UART_Main_transmitData(UART1,0xff);
	delay(1);
	DL_UART_Main_transmitData(UART1,byte1);
	delay(1);
	DL_UART_Main_transmitData(UART1,byte2);
	delay(1);
	DL_UART_Main_transmitData(UART1,byte3);
	delay(1);
	DL_UART_Main_transmitData(UART1,byte4);
	delay(1);
	DL_UART_Main_transmitData(UART1,byte5);
	delay(1);
	DL_UART_Main_transmitData(UART1,byte6);
	delay(1);
	DL_UART_Main_transmitData(UART1,byte7);
	delay(1);
	DL_UART_Main_transmitData(UART1,byte8);
	delay(1);

	DL_UART_Main_transmitData(UART1,0xff);
	delay(1);
		
}

delay函数。时钟频率为32M

void delay(int x)
{
  delay_cycles(CLK_HZ / 1000 * x);
}

矩阵键盘函数

uint32_t Key()
{
    uint8_t a =15;
    static uint8_t flag = 0;

    if (flag)
    {
        delay(300);
        flag = 0;
    }

    DL_GPIO_clearPins(MAT_PORT, MAT_ROW1_PIN);
    DL_GPIO_setPins(MAT_PORT, MAT_ROW2_PIN |MAT_ROW3_PIN | MAT_ROW4_PIN);
    delay(10);

    if (!(DL_GPIO_readPins(MAT_PORT, MAT_COL1_PIN)))
    {
        a = 1;
        flag = 1;
        return a;
    }
    else if (!(DL_GPIO_readPins(MAT_PORT, MAT_COL2_PIN)))
    {
        a = 2;
        flag = 1;
        return a;
    }
    else if (!(DL_GPIO_readPins(MAT_PORT, MAT_COL3_PIN)))
    {
        a = 3;
        flag = 1;
        return a;
    }
    else if (!(DL_GPIO_readPins(MAT_PORT, MAT_COL4_PIN)))
    {
        a = 4;
        flag = 1;
        return a;
    }

    DL_GPIO_clearPins(MAT_PORT, MAT_ROW2_PIN);
    DL_GPIO_setPins(MAT_PORT, MAT_ROW1_PIN |MAT_ROW3_PIN | MAT_ROW4_PIN);
    delay(10);

    if (!(DL_GPIO_readPins(MAT_PORT, MAT_COL1_PIN)))
    {
        a = 5;
        flag = 1;
        return a;
    }
    else if (!(DL_GPIO_readPins(MAT_PORT, MAT_COL2_PIN)))
    {
        a = 6;
        flag = 1;
        return a;
    }
    else if (!(DL_GPIO_readPins(MAT_PORT, MAT_COL3_PIN)))
    {
        a = 7;
        flag = 1;
        return a;
    }
    else if (!(DL_GPIO_readPins(MAT_PORT, MAT_COL4_PIN)))
    {
        a = 8;
        flag = 1;
        return a;
    }

    // Row 4
    DL_GPIO_clearPins(MAT_PORT, MAT_ROW3_PIN);
		DL_GPIO_setPins(MAT_PORT, MAT_ROW1_PIN |MAT_ROW2_PIN | MAT_ROW4_PIN);
    delay(10);
		
		if (!(DL_GPIO_readPins(MAT_PORT, MAT_COL1_PIN)))
    {
        a = 9;
        flag = 1;
        return a;
    }
    else if (!(DL_GPIO_readPins(MAT_PORT, MAT_COL2_PIN)))
    {
        a = 10;
        flag = 1;
        return a;
    }
    else if (!(DL_GPIO_readPins(MAT_PORT, MAT_COL3_PIN)))
    {
        a = 11;
        flag = 1;
        return a;
    }
    else if (!(DL_GPIO_readPins(MAT_PORT, MAT_COL4_PIN)))
    {
        a = 12;
        flag = 1;
        return a;
    }

    DL_GPIO_clearPins(MAT_PORT, MAT_ROW4_PIN);
		DL_GPIO_setPins(MAT_PORT, MAT_ROW1_PIN |MAT_ROW2_PIN | MAT_ROW3_PIN);
    delay(10);
		
		if (!(DL_GPIO_readPins(MAT_PORT, MAT_COL1_PIN)))
    {
        a = 13;
        flag = 1;
        return a;
    }
    else if (!(DL_GPIO_readPins(MAT_PORT, MAT_COL2_PIN)))
    {
        a = 14;
        flag = 1;
        return a;
    }
    else if (!(DL_GPIO_readPins(MAT_PORT, MAT_COL3_PIN)))
    {
        a = 15;
        flag = 1;
        return a;
    }
    else if (!(DL_GPIO_readPins(MAT_PORT, MAT_COL4_PIN)))
    {
        a = 0;
        flag = 1;
        return a;
    }

    return a;
}

矩阵键盘sysconfig配置

行设置,我们只需要将PORT设置为PORTA,Direction设为output,initial value设为set,并在Assigned Pin中配置相应管脚

列设置,仅需要将Direction改为intput,并配置相应管脚

标签:DL,20,MAT,FPGA,DDS,rx,uart,5208,MCU
From: https://blog.csdn.net/m0_74180999/article/details/140186071

相关文章

  • FPGA加扰与仿真
    对加扰仿真,输出结果符合预期 仿真代码如下 modulescrambler_64bit(inputwireclk,inputwirerst,inputwire[63:0]data_in,outputreg[63:0]data_out);reg[63:0]state;always@(posedgeclkorposedgerst)beginif(rst)begin......
  • 课程设计——基于FPGA的双向移位寄存器
    基于FPGA的双向移位寄存器摘 要本文使用verilogHDL语言设计双向移位寄存器,使电路受外部信号控制,实现数字信号的双向移位等功能,其电路设计模块主要分为三个部分,分别为接受判断控制信号的组合逻辑电路部分、实现存储、运算和输出数据的时序逻辑电路部分以及时钟信号输入部分......
  • FPGA必修课—FIFO
    FIFO基本概念FIFO,全称为“FirstInFirstOut”,译为“先进先出”。这是一种常见的数据存储和处理原则,其基本含义在于数据的存取顺序:最先进入的数据将最先被取出。FIFO可以被视为一种特殊类型的数据缓冲区,它按照元素到达的顺序进行数据的存取操作。学习FIFO的重要性在于它在......
  • MCU点灯
    MCU点灯芯片型号:STM32F407ZET64个LED灯,网络标号分别为LED0,LED1,FSMCD10,FSMCD11。对应的引脚号分别为PF9,PF10,PE12,PE13。原理图//1.定义变量GPIO_InitTypeDefGPIO_InitStructureF;//F端口GPIO_InitTypeDefGPIO_InitStructureE;//E端口intmain()//中文注释{......
  • SI3262_国产低功耗MCU+NFC+15触键三合一柜锁方案SoC芯片
    SI3262简介Si3262是高度集成ACD低功耗MCU+NFC+15通道防水触摸按键的SoC芯片。其MCU模块具有低功耗、LowPinCount、宽电压工作范围,集成了13/14/15/16位精度的ADC、LVD、UART、SPI、I2C、TIMER、WUP、IWDG、RTC、TSC等丰富的外设。内核采用RISC-VRV32IMAC(2.6CoreMark/MHz)。特......
  • 设计NOR Flash(SPI接口)的Flashloader(MCU: stm32f4)
    目录概述1软硬件1.1软硬件信息表1.2NORFlash芯片(W25Q64BVSSI)1.2.1W25Q64BVSSI芯片介绍1.2.2NORFlash接口1.3MCU与NORFlash接口2SPIFlash功能实现2.1软件框架结构2.2代码实现2.2.1 Dev_Inf文件2.2.2W25QXX驱动程序2.3Flashloader驱动接口程序3K......
  • FPGA学习网站推荐
    FPGA学习网站推荐本文首发于公众号:FPGA开源工坊引言FPGA的学习主要分为以下两部分语法领域内知识做FPGA开发肯定要首先去学习相应的编程语言,FPGA开发目前在国内采用最多的就是使用Verilog做开发,其次还有一些遗留下来的项目会采用VHDL做开发,现在有一部分公司也开始使用Syst......
  • AG32 MCU Start Kit 开发板快速入门及 21天体验活动
    AG32IDE开发环境搭建-完整版海振远科技2024-6-18AG32MCU开发板的使用使用准备在使用开发板前,请确认已经安装好开发环境。安装环境过程,请参考文档《AG32开发环境搭建.pdf》上电:给开发板5V供电,打开开关,可以看到电源旁边的小红灯亮起。使用example例程打开ex......
  • 芯片验证 | FPGA 原型验证
    更多完整内容访问:【芯片验证|FPGA原型验证】......
  • FPGA内部资源(一)DSP48E1
    一、实验过程中发现的问题使用ISE进行项目的实现时出现以下错误。意思很简单,就是使用DSP48E1的数量超出限制,因为没有接触过DSP48E1,所以尝试了很多错误的方法后,我找到项目下的.mrp文件,里面有一行显示NumberofDSP48E1s:496outof480103%,也就是说我使用的XC6V1x......