前言
何以解忧,唯有串口。
相关文章:
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
针对串口号变化及串口被占用的问题,采用如下代码来自动识别并打开当前串口:文章来源地址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模板网!