开源 新鲜出炉,16路串口接收模块
在坛子里面潜水多,看到很多大侠都发过一些串口模块,很多初学者最初学习也是从串口开始,但是找了很多资料发现大部分的串口接收都是单路的,或者说多路是由单路组成的,也就是每一路都要耗资源,这样如果是对于小规模的器件如果需要多几路串口,对资源影响还是挺大的,目前的思路是做一个16通道的串口,资源尽量少,并且可扩展
上面是基本的框图,通过时钟分配器产生16路的串口处理时钟,最后把这16路时钟合成一个时钟,
16路最后合成一个时钟,通过时钟来生成通道 0~ 15 通过时钟和通道对输入进来的数据进行采样,采样以后就变成了一个带通道的数据如下图
输入的多路串口数据
attach://152457.jpg
采样得到的数据
状态机对多路进行处理
最终通过时分的方法吧多路的串口数据进行了接收解析
状态机做了几个事情
首先接收到了一个下降沿,然后如果发现下降沿后的一个采样点是低电平,就认为是一个正确的头,如果不是一个为低电平,就认为是一个错误的头,重新搜索下降沿,当接收到一个正确的起始位之后,字节计数器和比特计数器分别计数,比特计数器用了记录采样点,字节计数器用了记录是哪个字节,如果是字节接收完成,需要判断是不是校验成功,停止位是否正确,如果正确则将这一路的串口上送,否则就丢弃,
在接收数据的时候采用了多数判决,当采样点前一个和后一个以及采样点本身中有2个一样就认为是一个正确的采样,用了增强可靠性 下面是代码,仅仅供学习使用,请勿商用////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
// Create Date: 2013/11/16
// Design Name:
// Module Name: uart_rx
// Project Name:
// Target Device:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
////////////////////////////////////////////////////////////////////////////////
`timescale 1ns / 1ps
module uart_rx
(
input clk ,
input rst_n ,
input uart_rx , //16个通道的串口数据,这个是可以扩展的
outputreg uart_dat, // 接收到的串口并行数据 n通道数据 ;
outputreg uart_en // 接收到的串口并行数据使能n通道使能
);
parameter DELAY =1 ;
parameter IDLE = 2'b01; // 状态机IDLE状态
parameter REC = 2'b10; // 状态机正在接收数据状态
parameter S_POINT = 4'd7 ; // 采样点
parameter BAUD_RATE = 115200;//波特率设置
parameter CLK_FRE = (10**8);//时钟频率设置,单位赫兹
parameter CLK_DIV_RATE = ((2**16)*(16)*BAUD_RATE)/CLK_FRE; //小数分频系数
reg uart_rx_ff1 ;
reg uart_rx_ff2 ;
reg uart_rx_ff3 ;
reg div_cnt ;
reg clk_en ;
reg ch_cnt ;
reg clk_en_ff1 ;
reg clk_en_ff2 ;
reg ch_cnt_ff1 ;
reg ch_cnt_ff2 ;
wire rx_dat_next ;
wire rx_dat_curr ;
wire rx_dat_curr_ff1;
wire rx_dat_curr_ff2;
regsta_next ;
regbit_cnt_next ;
regbyte_cnt_next;
wire sta_curr ;
wire bit_cnt_curr ;
wire byte_cnt_curr;
regshift_next ;
wire shift_curr ;
reg sample_dat ;
regrx_dat ;
wire get_dat ;
wire sample_point ;
wire dat_sta ;
wire stop_sta ;
wire start_sta ;
ram_infer
#(
.DATA_WIDTH(22 ),
.ADDR_WIDTH(4 )
)
u_ram_state
(
.data ({shift_next,sta_next,bit_cnt_next,byte_cnt_next,rx_dat_next,rx_dat_curr,rx_dat_curr_ff1} ),
.raddr (ch_cnt ),
.waddr (ch_cnt_ff2 ),
.wren (clk_en_ff2 ),
.clk (clk ),
.q ({shift_curr,sta_curr,bit_cnt_curr,byte_cnt_curr,rx_dat_curr,rx_dat_curr_ff1,rx_dat_curr_ff2})
);
assign rx_dat_next = uart_rx_ff3;
assign start_sig = ~rx_dat_curr&rx_dat_curr_ff1;//下降沿确定起始
assign sample_point = (bit_cnt_curr == S_POINT)?1'b1:1'b0; //采样点
assign start_sta = (byte_cnt_curr == 4'd0)?1'b1:1'b0; //起始状态
assign stop_sta = (byte_cnt_curr == 4'd10)?1'b1:1'b0;//结束状态
assign dat_sta = (byte_cnt_curr >= 4'd1 &&byte_cnt_curr <= 4'd9)?1'b1:1'b0;
always @(*)
begin
case(sta_curr)
IDLE:
begin
bit_cnt_next = 4'd0;
byte_cnt_next = 4'd0;
if(start_sig == 1'b1)
sta_next = REC; //接收到下降沿,跳转到接收状态
else
sta_next = IDLE;
end
REC:
begin
if(sample_point == 1'b1) //在采样点处看状态
begin
if(start_sta == 1'b1) //如果是起始状态
begin
if(sample_dat == 1'b0)//看采样数据是否为0,为0认为为真,否则假
begin
sta_next = REC;
bit_cnt_next = bit_cnt_curr + 4'd1;
byte_cnt_next = byte_cnt_curr +4'd1;
end
else
begin
sta_next = IDLE;
bit_cnt_next = 4'd0;
byte_cnt_next = 4'd0;
end
end
else if(stop_sta == 1'b1)//停止位进入IDLE
begin
sta_next = IDLE;
bit_cnt_next = bit_cnt_curr + 4'd1;
byte_cnt_next = 4'd0;
end
else
begin
sta_next = REC;
bit_cnt_next = bit_cnt_curr + 4'd1;
byte_cnt_next = byte_cnt_curr +4'd1;
end
end
else
begin
sta_next = REC;
bit_cnt_next = bit_cnt_curr + 4'd1;
byte_cnt_next = byte_cnt_curr ;
end
end
default:
begin
sta_next = IDLE;
bit_cnt_next = 4'd0;
byte_cnt_next = 4'd0;
end
endcase
end
always @(*) //多数判决,当采样点的前一个点和后一个点以及采样点有两个相同则去这个值
begin
if(rx_dat_curr == rx_dat_curr_ff1)
sample_dat = rx_dat_curr;
else if(rx_dat_curr == rx_dat_curr_ff2)
sample_dat = rx_dat_curr;
else
sample_dat = rx_dat_curr_ff1;
end
always @(*)
begin
if(sta_curr == REC &&sample_point == 1'b1 && dat_sta == 1'b1) //数据接收移位
shift_next = {shift_curr,sample_dat}; //这里把校验位也移入
else
shift_next = shift_curr;
end
assign get_dat = (sta_curr == REC &&sample_point ==1'b1 &&stop_sta == 1'b1)?1'b1:1'b0;
always @(*)
begin
if(get_dat == 1'b1)
rx_dat = {sample_dat&(~(^shift_curr)),shift_curr}; //如果校验错误数据丢弃
else
rx_dat = 2'd0;
end
generate
genvar i;
for (i=0;i<16;i=i+1)
begin:TX_DAT
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
begin
uart_dat <= 'd0;
uart_en<= 'd0;
end
else if(clk_en_ff2 == 1'b1&&(ch_cnt_ff2 == i)&&get_dat == 1'b1)
begin
uart_dat <= #DELAY rx_dat;
uart_en <= #DELAY rx_dat;
end
else
begin
uart_dat <= #DELAY 8'd0;
uart_en <= #DELAY 1'd0;
end
end
end
endgenerate
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
div_cnt <= #DELAY 17'd0;
else
div_cnt <= #DELAY {1'b0,div_cnt} + CLK_DIV_RATE;
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n ==1'b0)
clk_en <= 16'd0;
else
clk_en <= #DELAY {clk_en,div_cnt};
end
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
ch_cnt <= 4'd0;
else if((|clk_en) == 1'b1)
ch_cnt <= #DELAY ch_cnt + 4'd1;
else
ch_cnt <= #DELAY 4'd0;
end
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
clk_en_ff1 <=1'b0;
clk_en_ff2 <=1'b0;
ch_cnt_ff1 <=4'd0;
ch_cnt_ff2 <=4'd0;
end
else
begin
clk_en_ff1 <= #DELAY |clk_en;
clk_en_ff2 <= #DELAY clk_en_ff1;
ch_cnt_ff1 <= #DELAY ch_cnt;
ch_cnt_ff2 <= #DELAY ch_cnt_ff1;
end
end
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
uart_rx_ff1 <= 16'd0;
uart_rx_ff2 <= 16'd0;
end
else
begin
uart_rx_ff1 <= #DELAY uart_rx;
uart_rx_ff2 <= #DELAY uart_rx_ff1;
end
end
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
uart_rx_ff3 <= 1'b0;
else if(clk_en_ff1 == 1'b1)
uart_rx_ff3 <= #DELAY uart_rx_ff2;
else
uart_rx_ff3 <= #DELAY 1'b0;
end
endmodule
//copyright all rights reserved
//Author :Sun-Technology
//Data :2013-11-2
//Description :altera ram
`timescale 1ns/1ns
module ram_infer
(
data ,
raddr ,
waddr ,
wren ,
clk ,
q
);
parameter DATA_WIDTH =1;
parameter ADDR_WIDTH = 13;
parameter DEPTH = 2**ADDR_WIDTH;
parameter U_DLY = 1 ;
input data ;
input raddr ;
input waddr ;
input wren ;
input clk ;
output q ;
reg q ;
reg inter_reg;
reg ram_mem;
always@(posedge clk)
begin
if(wren== 1'b1)
ram_mem <=#U_DLY data;
end
always @(posedge clk)
begin
inter_reg <= #U_DLY ram_mem;
q <= #U_DLY inter_reg ;
end
endmodule
modelsim 工程 {:handshake:} 不明觉厉{:victory:} 没什么人感兴趣吗,多串口系统爱在FPGA上面应该还是很常用吧,还有就是现在我手头没有板子,所以没有上板测试过,误码率等指标还没用,如果谁有板子,又刚好要用到串口或者多串口系统,可以使用我的这个模块,我可以提供友情支持 不明觉厉! 市面上有串口扩展芯片,那怎在选择方案时用fpga做好还是串口扩展芯片好呢?
很不错的思路,作为串口服务器或网关是很好的。
我想知道的是,PC如何跟这些串口通讯?如何管理呢? 在看代码,感觉不错,看不懂啊,再学习学习 强贴!留名。感谢楼主分享。
不明觉厉! 很好,支持一下 先MARK一下 595输出,165输入,同一个模块处理逻辑,更优哦,我就这么搞过 FPGA灵活,IO速度那么高,串口速度那么低 MARK 先收藏起来 谁手头有板子啊,速来一个小白鼠 SPI才是王道 sunocean 发表于 2013-11-18 09:20 static/image/common/back.gif
谁手头有板子啊,速来一个小白鼠
同求{:lol:} lnskngdc 发表于 2013-11-16 22:04 static/image/common/back.gif
很不错的思路,作为串口服务器或网关是很好的。
我想知道的是,PC如何跟这些串口通讯?如何管理呢? ...
不太懂,这个是一个fpga的多串口解决方案,只负责接收,其他的可能要定义其他的协议来支持 刚想弄个40路串口的模块,芯片打算用XC6SLX9,淘宝大概35元 ,QFP封装。就是不知道资源够不够?
本帖最后由 Fourier00 于 2013-11-20 22:02 编辑
jm2011 发表于 2013-11-16 22:10 static/image/common/back.gif
在看代码,感觉不错,看不懂啊,再学习学习
这个是有点麻烦,尤其是看波形基本没法看,但是弄懂了这个模块,我相信您在fpga设计技巧上一定会有所收获,这种技巧说不定哪天就可以帮上您的忙 jiangchun9981 发表于 2013-11-20 21:49 static/image/common/back.gif
刚想弄个40路串口的模块,芯片打算用XC6SLX9,淘宝大概35元 ,QFP封装。就是不知道资源够不够?
...
我不知道你的波特率需要支持多少,还有就是 主时钟频率是多少,还有就是是不是发送和接收都需要做? 我看了一下X9的资源,如果用我的这个方案,接收方向肯定是没有问题的,发送方向还需要重新做一个省资源的方案 我对此有一定的兴趣 ,也愿 意提供一定的支持。
你还在吗?
SPI才是王道 请问SPI的优势 在哪里? lineling 发表于 2013-11-20 22:03
我对此有一定的兴趣 ,也愿 意提供一定的支持。
你还在吗?
你好,我在,不过现在是手机上的 大概要做一个串口上行收发(接上位机),然后下行40个左右收发的(通过上面的那个串口和上位机通讯)。
请LZ估算下资源情况。 下行的40个口波特率115200左右,因为要接的芯片已经固定这个速率不能调整。
上行那个可以别的。 不明觉厉下载慢慢研究下~~ 不明觉厉 看的一头雾水,不过还是觉得楼主牛比。{:victory:} 不明觉厉 貌似很厉害的样子 先收藏 回头慢慢研究 jiangchun9981 发表于 2013-11-20 22:33
下行的40个口波特率115200左右,因为要接的芯片已经固定这个速率不能调整。
上行那个可以别的。 ...
这个速率,如果主时钟是100兆,或者更高,是没有问题的,我算了一下资源带fifo一路收发最多就50个lut,那个器件上万的资源,还有32快可以做成64块的ram,做上100路都没什么问题 lineling 发表于 2013-11-20 22:05
SPI才是王道 请问SPI的优势 在哪里?
应用场景不一样,没有什么可比性 感谢楼主分享 关注多串口,希望有实物验证!
很好,支持一下
这么好的东西 没有人用? 十六路是统一波特率还是单独可配置的啊 linjpxt 发表于 2014-6-1 11:40
十六路是统一波特率还是单独可配置的啊
统一的阿,分开的不太好做 SPI转16路串口已经实现,可以在xc6slx9上实现,每路都是独立的设置波特率,没路收发至少256字节缓冲,没路都有485流向控制,另外没路还附带一个IO输出,可用于控制232与485的选择
页:
[1]