ADC测试杂谈二:matlab操作串口向FPGA发信

这篇具有很好参考价值的文章主要介绍了ADC测试杂谈二:matlab操作串口向FPGA发信。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

 何以解忧,唯有串口。

 相关文章:

 ADC测试杂谈一:配置基于matlab+quartus的测试环境

 之前提到,FPGA的JTAG相比MCU的UART,读取数据的速度更快。但是matlab似乎只能通过JTAG收信,而不能通过JTAG向FPGA发信。为了便于通过FPGA向芯片写一些配置信息,我们采用UART串口来向FPGA发送信息。


一、串口的Verilog简易实现

 UART协议的基本原理是接收端通过一个16倍速的高频时钟对发送端的数据进行过采样,当检测到一个起始码后,就开始接收8位数据。Verilog代码如下:

//Author: Jiao
//Date: 2017
//clk is 50e6 clk50.
module newart(
	input dat,
	input clk,
	output ex0,
	output reg [7:0]bufout);
	
	reg ck;
	reg [7:0]buftmp;
	reg [7:0]count;
	reg enout;
	reg enout2;
	reg enout3;
	wire ck1;
	wire rst0;
	reg rst1;
	reg [8:0]count1;//325 freq divide;
	reg [7:0]count2;//180 times,make the ck high;
	wire dat0;
	
	assign rst0=enout2;
	
	always@(posedge clk)
	begin
		count1<=count1+1;
		count2<=count2+1;
		if(count1==0)begin
			ck<=0;
			count2<=0;
			end
		else begin
			if(count1==324) count1<=0;
			if(count2==180) begin ck<=1;count2<=0;end
			end
	end
	
	always@(negedge rst0 or negedge dat0)//it seems that it is a edge detect,however it is a level driven.
	begin
		if(~rst0)enout<=0;
		else enout<=1;
	end
	
	assign ck1=ck&enout;
	assign dat0=enout|dat;
	
	always@(posedge ck1 or negedge rst1)
	begin
		if(~rst1)begin enout2<=1;count<=0;end
		else begin
				count<=count+1;
				case(count)
				24:buftmp[0]<=dat;
				40:buftmp[1]<=dat;
				56:buftmp[2]<=dat;
				72:buftmp[3]<=dat;
				88:buftmp[4]<=dat;
				104:buftmp[5]<=dat;
				120:buftmp[6]<=dat;
				136:buftmp[7]<=dat;//the above get 8 bit;
				152:begin enout2<=0;bufout<=buftmp;end
				endcase
			end
	end
	
	always@(posedge ck)
	begin
		enout3<=enout2;
		rst1<=enout3;
	end
	
	assign ex0=~rst1;
	
	endmodule

 这里主要的输入端口是clk(全局的50MHz时钟)和dat(串口输入引脚),对应的波特率为9600,这也是matlab默认的通信速度。

 由于在ADC测试的实际场景下,PC端的上位机并不需要通过串口从FPGA读取数据,所以没有定义上行端口。

二、matlab对串口的操作

 matlab对串口的操作比较简单,假设当前可用串口为COM8,则代码如下:

    serial_port=serial('COM8');
    fopen(serial_port);
	fwrite(serial_port,int8(43));
	fclose(serial_port);
	delete(serial_port);

该代码会打开COM8,发送字符’+'(53的ASCII字符是+)后,关闭对串口的占用。

三、基于UART的寄存器

 UART最多能存8bit数据,假如我需要向FPGA发送更多数据呢?

 以ti的ADS7886测试为例,这是1个1MHz、12位、串行接口的ADC,我可能需要配置ADC的工作速度(1MHz/500KHz/200KHz/100KHz),可能需要读取ADC数据的时钟速度(20MHz/10MHz/5MHz/2MHz/1MHz)等等,所以,需要通过UART串口向FPGA发送更多位的信息。

 借鉴SPI协议中的地址,(参考verilog实现SPI从机 ),可以在UART接口的基础上封装一个类似的协议:规定发送字符1为地址,再发送字符2为该地址的校验码,发送字符3为数据,发送字符4为数据的校验码,发送字符5为中止码。通过这种方式,理论上可以向FPGA传送256*8bit数据。

 Verilog代码如下:

//Author: Jiao
//Date: 2020.
//外部连发5次uart,前四次分别发送地址、地址的校验、数据、数据的校验,第五次发送的空格用以产生一个采样时钟
`define reglength 42
module uart2reg(
	clk50,rst,uart_in,
	data0,data1,data2,data3,data4,data5,data6,data7,data8,data9,data10,
							data11,data12,data13,data14,data15,data16,data17,data18,data19,
							data20,data21,data22,data23,data24,data25,data26,data27,data28,
							data29,data30,data31,data32,data33,data34,data35,data36,data37,
							data38,data39,data40,data41,data42);
input clk50,rst,uart_in;
output wire [7:0]data0,data1,data2,data3,data4,data5,data6,data7,data8,data9,data10,
							data11,data12,data13,data14,data15,data16,data17,data18,data19,
							data20,data21,data22,data23,data24,data25,data26,data27,data28,
							data29,data30,data31,data32,data33,data34,data35,data36,data37,
							data38,data39,data40,data41,data42;

integer i;	
(* noprune *) reg [7:0]reg_out[`reglength:0];//理论上这里可以输出最高256个8位寄存器
wire data_samp;
wire [7:0]data_reg0;
newart u1(uart_in,clk50,data_samp,data_reg0);

reg [7:0]shift1,shift2,shift3,shift4;
always@(posedge data_samp or negedge rst)
begin
	if(~rst)begin
		shift1<=0;//data_verify
		shift2<=0;//data
		shift3<=0;//addr_verify
		shift4<=0;//addr
		for(i=0;i<=`reglength;i=i+1)
			reg_out[i]<=0;
	end
	else begin
		if((shift3==addr_verify(shift4))&&(shift1==addr_verify(shift2)))begin//满足校验条件,清零并采样
			reg_out[shift4]=shift2;
			shift1<=0;
			shift2<=0;
			shift3<=0;
			shift4<=0;
		end
		else begin
			shift4<=shift3;
			shift3<=shift2;
			shift2<=shift1;
			shift1<=data_reg0;
		end
	end
end

assign data0=reg_out[0];
assign data1=reg_out[1];
assign data2=reg_out[2];
assign data3=reg_out[3];
assign data4=reg_out[4];
assign data5=reg_out[5];
assign data6=reg_out[6];
assign data7=reg_out[7];
assign data8=reg_out[8];
assign data9=reg_out[9];
assign data10=reg_out[10];
assign data11=reg_out[11];
assign data12=reg_out[12];
assign data13=reg_out[13];
assign data14=reg_out[14];
assign data15=reg_out[15];
assign data16=reg_out[16];
assign data17=reg_out[17];
assign data18=reg_out[18];
assign data19=reg_out[19];
assign data20=reg_out[20];
assign data21=reg_out[21];
assign data22=reg_out[22];
assign data23=reg_out[23];
assign data24=reg_out[24];
assign data25=reg_out[25];
assign data26=reg_out[26];
assign data27=reg_out[27];
assign data28=reg_out[28];
assign data29=reg_out[29];
assign data30=reg_out[30];
assign data31=reg_out[31];
assign data32=reg_out[32];
assign data33=reg_out[33];
assign data34=reg_out[34];
assign data35=reg_out[35];
assign data36=reg_out[36];
assign data37=reg_out[37];
assign data38=reg_out[38];
assign data39=reg_out[39];
assign data40=reg_out[40];
assign data41=reg_out[41];
assign data42=reg_out[42];

function [7:0]addr_verify;
input [7:0]addr;
begin
addr_verify[7]=0^addr[6]^addr[5]^addr[4];   
addr_verify[6]=0^addr[5]^addr[4]^addr[3]; 
addr_verify[5]=1^addr[7]^addr[4]^addr[3]^addr[2];
addr_verify[4]=1^addr[6]^addr[3]^addr[2]^addr[1];
addr_verify[3]=0^addr[7]^addr[5]^addr[2]^addr[1]^addr[0];
addr_verify[2]=1^addr[5]^addr[1]^addr[0];
addr_verify[1]=1^addr[7]^addr[6]^addr[5]^addr[0];   
addr_verify[0]=0;
end
endfunction

endmodule

 MATLAB端对fwrite进行了一点封装,代码如下:

function uart_reg_write(serial_port,addr_uart,data_reg)
% 参数1是实例化的串口对象
% 参数2是地址,参数3是数据
addr_verify=bit8verify(addr_uart);
data_verify=bit8verify(data_reg);
space=32;

fwrite(serial_port,uint8(addr_uart));
fwrite(serial_port,uint8(addr_verify));
fwrite(serial_port,uint8(data_reg));
fwrite(serial_port,uint8(data_verify));
fwrite(serial_port,uint8(space));

function resu=bit8verify(data)
addr=zeros(8,1);
addr_verify=zeros(8,1);
dstr=dec2bin(data,8);
weight=[1 2 4 8 16 32 64 128];
for i=1:8
    addr(9-i)=str2num(dstr(i));
end
addr_verify(8)=xor(0,xor(addr(7),xor(addr(6),addr(5))));   
addr_verify(7)=xor(0,xor(addr(6),xor(addr(5),addr(4)))); 
addr_verify(6)=xor(1,xor(addr(8),xor(addr(5),xor(addr(4),addr(3)))));
addr_verify(5)=xor(1,xor(addr(7),xor(addr(4),xor(addr(3),addr(2)))));
addr_verify(4)=xor(0,xor(addr(8),xor(addr(6),xor(addr(3),xor(addr(2),addr(1))))));
addr_verify(3)=xor(1,xor(addr(6),xor(addr(2),addr(1))));
addr_verify(2)=xor(1,xor(addr(8),xor(addr(7),xor(addr(6),addr(1)))));
addr_verify(1)=0;
resu=weight*addr_verify;

四、matlab串口操作技巧

 串口调用的过程中存在两个主要的问题。

 一是串口号的变化。同样一根串口线,插到电脑的A孔,被识别为COM8,插到B孔被识别为COM9;今天被识别为COM8,明天被识别为COM9。总之,号码是不固定的。

 二是串口被占用。正常的串口操作过程是,serial声明串口,fopen打开串口,fwrite写串口,fclose+delete释放串口。但是,有可能串口被某些异常进程占用,又或者测试者的代码中途报错退出而没有释放串口,导致该串口不能被再次调用。

 针对串口号变化及串口被占用的问题,采用如下代码来自动识别并打开当前串口:文章来源地址https://www.toymoban.com/news/detail-693346.html

serial_resu=IdentifySerialComs; %获取可用串口
judge_serial=double(isempty(serial_resu));
if(judge_serial==0)
% 返回值不是空的,说明当前有串口
    serial_num=num2str(cell2mat(serial_resu(2)));
    com_serial=['COM' serial_num];
    msg6=['Serial is at ',com_serial];
    serial_port=serial(com_serial);
    fopen_ser(serial_port);
	% 该函数功能为:
	% 如果被占用,先释放所有串口再fopen指定串口;
	% 如果未被占用,直接fopen指定串口
end
% Author: Jiao 
% Date: 2022.
% 为了处理串口的异常,先检测所有的串口状态是否均为closed,是则继续fopen;
% 否则先fclose(instrfind)
function fopen_ser(serial_port)

serial_detect=instrfind();
serial_detect=serial_detect.Status;
device_len=size(serial_detect);
busy_en=0;
for i=1:device_len
    if strcmp(serial_detect(i),'open')==1
        busy_en=1;
        break;
    end
end
if(busy_en)
    fclose(instrfind);
end
fopen(serial_port);
function devices = IdentifySerialComs()

devices = [];

Skey = 'HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM';
[~, list] = dos(['REG QUERY ' Skey]);
if(max(size(list))>1)
    if ischar(list) && strcmp('ERROR',list(1:5))  %% strcmp 两个字符串相同返回1
        disp('Error: EnumSerialComs - No SERIALCOMM registry entry')
        return;
    end
else
    return;
end
list = strread(list,'%s','delimiter',' '); %#ok<FPARK> requires strread()
coms = 0;
for i = 1:numel(list)  %%numel 返回元素个数
    if strcmp(list{i}(1:3),'COM')
        if ~iscell(coms)
            coms = list(i);
        else
            coms{end+1} = list{i}; %#ok<AGROW> Loop size is always small
        end
    end
end
out = 0;
outK = 0;
for j=1:2
    switch j
        case 1
            key = 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\';
        case 2
            key = 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\FTDIBUS\';
    end
    [~, vals] = dos(['REG QUERY ' key ' /s /f "FriendlyName" /t "REG_SZ"']);
    if ischar(vals) && strcmp('ERROR',vals(1:5))
        disp('Error: EnumSerialComs - No Enumerated USB registry entry')
        return;
    end
    vals = textscan(vals,'%s','delimiter','\t');
    vals = cat(1,vals{:});
    for i = 1:numel(vals)
        if strcmp(vals{i}(1:min(12,end)),'FriendlyName')
            if ~iscell(out)
                out = vals(i);
            else
                out{end+1} = vals{i}; %#ok<AGROW> Loop size is always small
            end
            if ~iscell(outK)
                outK = vals(i-1);
            else
                outK{end+1} = vals{i-1}; %#ok<AGROW> Loop size is always small
            end
        end
    end
end

i_dev=1;Sservices=[];
for i = 1:numel(coms)
    match = strfind(out,[coms{i},')']);
    ind = 0;
    for j = 1:numel(match)
        if ~isempty(match{j})
            ind = j;
            [~, sers] = dos(['REG QUERY "' outK{ind} '" /f "Service" /t "REG_SZ"']);
            sers = textscan(sers,'%s','delimiter','\t');
            sers = cat(1,sers{:});
            if (numel(sers)>1)
                sers=strread(sers{2},'%s','delimiter',' ');
                Sservices{i_dev} = sers{3};
                i_dev=i_dev+1;
            end
        end
    end
end
Sservices=unique(Sservices);

i_dev=1;
for ss=1:numel(Sservices)
    key = ['HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\' Sservices{ss} '\Enum'];
    [~, vals] = dos(['REG QUERY ' key ' /f "Count"']);
    if ischar(vals) && strcmp('ERROR',vals(1:5))
                disp('Error: EnumSerialComs - No Enumerated services USB registry entry')
                return
    end
    vals = textscan(vals,'%s','delimiter','\t');
    vals = cat(1,vals{:});

    if (numel(vals)>1)
        vals=strread(vals{2},'%s','delimiter',' ');
        Count=hex2dec(vals{3}(3:end));
        if Count>0
            [~, vals] = dos(['REG QUERY ' key]);
            vals = textscan(vals,'%s','delimiter','\t');
            vals = cat(1,vals{:});
            out=0;
            j=0;
            for i = 1:numel(vals)
                Enums=strread(vals{i},'%s','delimiter',' ');
                try nums=hex2dec(Enums{1});
                catch
                    nums=-1;
                end
                if(nums==j)
                    out=['HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\' Enums{3}];
                    [~, listC] = dos(['REG QUERY "' out '" /s /f "PortName" /t "REG_SZ"']);
                    listC = textscan(listC,'%s','delimiter','\t');
                    listC = cat(1,listC{:});
                    if (numel(listC)>1)
                        listC=strread(listC{2},'%s','delimiter',' ');
                        for i = 1:numel(coms)
                            if strcmp(listC{3},coms{i})
                                [~, NameF] = dos(['REG QUERY "' out '" /s /f "FriendlyName" /t "REG_SZ"']);
                                NameF = textscan(NameF,'%s','delimiter','\t');
                                NameF = cat(1,NameF{:});
                                com = str2double(coms{i}(4:end));
                                if com > 9
                                    length = 8;
                                else
                                    length = 7;
                                end
                                devices{i_dev,1} = NameF{2}(27:end-length); %#ok<AGROW>
                                devices{i_dev,2} = com; %#ok<AGROW> Loop size is always small
                                i_dev=i_dev+1;
                            end
                        end
                    end
                    j=j+1;
                end
            end
        end
    end
end

end

到了这里,关于ADC测试杂谈二:matlab操作串口向FPGA发信的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA初步学习之串口发送模块【单字节和字符串的发送】

    UART 在发送或接收过程中的一帧数据由4部分组成,起始位、数据位、奇偶校验位和停止位,如图所示。其中,起始位标志着一帧数据的开始,停止位标志着一帧数据的结束,数据位是一帧数据中的有效数据。 通常用的串口数据帧格式是:8位数据位,无校验位,1位停止位。

    2024年02月14日
    浏览(45)
  • FPGA学习笔记(三)——串口通信之发送数据(调试过程)

    本学习笔记主要参考小梅哥B站教学视频,网址如下: https://www.bilibili.com/video/BV1va411c7Dz?p=1 使用的编译器为Vivado,HDL语言为verilog 一、串口通信之发送数据 原理 设计代码 测试代码 仿真结果 发现Send_en拉高之前,uart_tx就置为0了,不符合常理。 转到第二个发送信号处,发现Send

    2023年04月09日
    浏览(43)
  • 【FPGA零基础学习之旅#14】串口发送字符串

    🎉欢迎来到FPGA专栏~串口发送字符串 ☆* o(≧▽≦)o *☆ 嗨 ~我是 小夏与酒 🍹 ✨ 博客主页: 小夏与酒的博客 🎈该系列 文章专栏: FPGA学习之旅 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏 📜 欢迎大家关注! ❤️ 🥝 发送Hello: 🥝 发送数字字符并自

    2024年02月08日
    浏览(45)
  • 【FPGA零基础学习之旅#13】串口发送模块设计与验证

    🎉欢迎来到FPGA专栏~串口发送模块 ☆* o(≧▽≦)o *☆ 嗨 ~我是 小夏与酒 🍹 ✨ 博客主页: 小夏与酒的博客 🎈该系列 文章专栏: FPGA学习之旅 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏 📜 欢迎大家关注! ❤️ 🥝 发送测试: 🥝 issp调试测试: 数据

    2024年02月09日
    浏览(57)
  • FPGA自学笔记--串口通信发送多字节数据(verilog版)

            关于uart协议实现这部分大家可以参考我上一篇的博客。《FPGA自学笔记--串口通信实现(vivadoverilog版)》。在上一篇博客中,主要实现了将单字节的数据,我们其实就是用上一篇博客的模块来实现多字节数据的发送。         在真实的数据传输过程中,我们不

    2023年04月17日
    浏览(37)
  • FPGA串口接收解帧、并逐帧发送有效数据——1

    工程实现的功能:FPGA串口接收到串口调试助手发来的数据,将其数据解帧。判断到正确的帧头和帧尾之后,将有效数据存入rx_data中;另一方面发送端将有效数据逐帧发送出去。 参考:正点原子官方FPGA串口通信实验 模块构成: 在原子哥的基础上改的代码。 添加了接收状态机

    2024年02月05日
    浏览(40)
  • FPGA串口接收解帧、并逐帧发送有效数据-2

    工程实现的功能:FPGA串口接收到串口调试助手发来的数据,将其数据解帧。判断到正确的帧头和帧尾之后,将有效数据存入rx_data中;另一方面发送端将有效数据逐帧发送出去。 参考:正点原子官方FPGA串口通信实验 模块构成: 在原子哥的基础上改的代码。 添加了接收状态机

    2024年02月05日
    浏览(37)
  • MATLAB输出串口发送所需十六进制数据

      可以用一个文件来测试其是否符合我们的要求,将一个二进制向量转化为十六进制向量,每个十六进制数表示1byte(不足1byte前面补0),并且写入txt文件中,每byte之间用空格隔开。   打开存储路径下的txt文件,可见符合预期需求。   使用串口助手载入txt文件,可见

    2024年02月16日
    浏览(49)
  • 通过FPGA实现基于RS232串口的指令发送并控制显示器中目标位置

    目录 1.算法理论概述 串口通信模块 指令解析模块 位置控制模块 显示器驱动模块 2.部分核心程序 3.算法运行软件版本 4.算法运行效果图预览 5.算法完整程序工程         通过FPGA实现基于RS232串口的指令发送并控制显示器中目标位置是一种常见的应用场景,用于实现对显示器

    2024年02月16日
    浏览(55)
  • matlab发送串口数据,并进行串口数据头的添加,我们来看下pwm解析后并通过串口输出的效果

    uintt16位的话会在上面前面加上00,16位的话一定是两个字节,一共16位的数据 如果是unint8的话就不会, 注意这里给的是13,但是现实的00 0D,这是大小端的问题,在matlanb里设置,我们就默认用这个模式吧,没关系的,小端,小段的小数据在前,所以是00 0D。 下图是串口输出P

    2024年02月20日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包