Fourier00 发表于 2013-11-16 15:25:40

开源 新鲜出炉,16路串口接收模块

在坛子里面潜水多,看到很多大侠都发过一些串口模块,很多初学者最初学习也是从串口开始,但是找了很多资料发现大部分的串口接收都是单路的,或者说多路是由单路组成的,也就是每一路都要耗资源,这样如果是对于小规模的器件如果需要多几路串口,对资源影响还是挺大的,
目前的思路是做一个16通道的串口,资源尽量少,并且可扩展

Fourier00 发表于 2013-11-16 15:29:50

上面是基本的框图,通过时钟分配器产生16路的串口处理时钟,最后把这16路时钟合成一个时钟,

16路最后合成一个时钟,通过时钟来生成通道 0~ 15

Fourier00 发表于 2013-11-16 15:34:01

通过时钟和通道对输入进来的数据进行采样,采样以后就变成了一个带通道的数据如下图
输入的多路串口数据
attach://152457.jpg
采样得到的数据

Fourier00 发表于 2013-11-16 15:35:21

状态机对多路进行处理

Fourier00 发表于 2013-11-16 15:42:34

最终通过时分的方法吧多路的串口数据进行了接收解析
状态机做了几个事情
首先接收到了一个下降沿,然后如果发现下降沿后的一个采样点是低电平,就认为是一个正确的头,如果不是一个为低电平,就认为是一个错误的头,重新搜索下降沿,当接收到一个正确的起始位之后,字节计数器和比特计数器分别计数,比特计数器用了记录采样点,字节计数器用了记录是哪个字节,如果是字节接收完成,需要判断是不是校验成功,停止位是否正确,如果正确则将这一路的串口上送,否则就丢弃,
在接收数据的时候采用了多数判决,当采样点前一个和后一个以及采样点本身中有2个一样就认为是一个正确的采样,用了增强可靠性

Fourier00 发表于 2013-11-16 15:44:54

下面是代码,仅仅供学习使用,请勿商用////////////////////////////////////////////////////////////////////////////////
// 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

Fourier00 发表于 2013-11-16 15:46:46

//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

Fourier00 发表于 2013-11-16 15:53:41

modelsim 工程

wkman 发表于 2013-11-16 16:03:15

{:handshake:} 不明觉厉{:victory:}

Fourier00 发表于 2013-11-16 20:35:22

没什么人感兴趣吗,多串口系统爱在FPGA上面应该还是很常用吧,还有就是现在我手头没有板子,所以没有上板测试过,误码率等指标还没用,如果谁有板子,又刚好要用到串口或者多串口系统,可以使用我的这个模块,我可以提供友情支持

javabean 发表于 2013-11-16 21:12:18

不明觉厉!

ghostxdy 发表于 2013-11-16 21:44:22

市面上有串口扩展芯片,那怎在选择方案时用fpga做好还是串口扩展芯片好呢?

lnskngdc 发表于 2013-11-16 22:04:27

很不错的思路,作为串口服务器或网关是很好的。
我想知道的是,PC如何跟这些串口通讯?如何管理呢?

jm2011 发表于 2013-11-16 22:10:24

在看代码,感觉不错,看不懂啊,再学习学习

xckhmf 发表于 2013-11-16 22:11:06

强贴!留名。感谢楼主分享。

wuguoyan 发表于 2013-11-16 22:42:09


不明觉厉!

sky_walker 发表于 2013-11-16 22:57:04

很好,支持一下

全频阻塞干扰 发表于 2013-11-16 23:31:03

先MARK一下

xivisi 发表于 2013-11-16 23:47:37

595输出,165输入,同一个模块处理逻辑,更优哦,我就这么搞过

xivisi 发表于 2013-11-16 23:51:22

FPGA灵活,IO速度那么高,串口速度那么低

zend 发表于 2013-11-18 05:56:42

MARK 先收藏起来

sunocean 发表于 2013-11-18 09:20:17

谁手头有板子啊,速来一个小白鼠

cqfeiyu 发表于 2013-11-18 09:22:38

SPI才是王道

Fourier00 发表于 2013-11-20 21:43:04

sunocean 发表于 2013-11-18 09:20 static/image/common/back.gif
谁手头有板子啊,速来一个小白鼠

同求{:lol:}

Fourier00 发表于 2013-11-20 21:45:15

lnskngdc 发表于 2013-11-16 22:04 static/image/common/back.gif
很不错的思路,作为串口服务器或网关是很好的。
我想知道的是,PC如何跟这些串口通讯?如何管理呢? ...

不太懂,这个是一个fpga的多串口解决方案,只负责接收,其他的可能要定义其他的协议来支持

jiangchun9981 发表于 2013-11-20 21:49:23

刚想弄个40路串口的模块,芯片打算用XC6SLX9,淘宝大概35元 ,QFP封装。就是不知道资源够不够?

Fourier00 发表于 2013-11-20 21:49:41

本帖最后由 Fourier00 于 2013-11-20 22:02 编辑

jm2011 发表于 2013-11-16 22:10 static/image/common/back.gif
在看代码,感觉不错,看不懂啊,再学习学习

这个是有点麻烦,尤其是看波形基本没法看,但是弄懂了这个模块,我相信您在fpga设计技巧上一定会有所收获,这种技巧说不定哪天就可以帮上您的忙

Fourier00 发表于 2013-11-20 22:00:40

jiangchun9981 发表于 2013-11-20 21:49 static/image/common/back.gif
刚想弄个40路串口的模块,芯片打算用XC6SLX9,淘宝大概35元 ,QFP封装。就是不知道资源够不够?
...

我不知道你的波特率需要支持多少,还有就是 主时钟频率是多少,还有就是是不是发送和接收都需要做? 我看了一下X9的资源,如果用我的这个方案,接收方向肯定是没有问题的,发送方向还需要重新做一个省资源的方案

lineling 发表于 2013-11-20 22:03:55

我对此有一定的兴趣 ,也愿 意提供一定的支持。
你还在吗?

lineling 发表于 2013-11-20 22:05:01

SPI才是王道   请问SPI的优势 在哪里?

Fourier00 发表于 2013-11-20 22:16:30

lineling 发表于 2013-11-20 22:03
我对此有一定的兴趣 ,也愿 意提供一定的支持。
你还在吗?

你好,我在,不过现在是手机上的

jiangchun9981 发表于 2013-11-20 22:31:52

大概要做一个串口上行收发(接上位机),然后下行40个左右收发的(通过上面的那个串口和上位机通讯)。
请LZ估算下资源情况。

jiangchun9981 发表于 2013-11-20 22:33:07

下行的40个口波特率115200左右,因为要接的芯片已经固定这个速率不能调整。
上行那个可以别的。

pdd083051 发表于 2013-11-20 23:06:44

不明觉厉下载慢慢研究下~~

lixiansong 发表于 2013-11-21 00:15:18

不明觉厉

yongjia 发表于 2013-11-21 11:04:47

看的一头雾水,不过还是觉得楼主牛比。{:victory:}

sgj245609615 发表于 2013-11-21 11:25:31

不明觉厉   貌似很厉害的样子

freeny 发表于 2013-11-21 15:13:39

先收藏   回头慢慢研究

Fourier00 发表于 2013-11-22 07:54:01

jiangchun9981 发表于 2013-11-20 22:33
下行的40个口波特率115200左右,因为要接的芯片已经固定这个速率不能调整。
上行那个可以别的。 ...

这个速率,如果主时钟是100兆,或者更高,是没有问题的,我算了一下资源带fifo一路收发最多就50个lut,那个器件上万的资源,还有32快可以做成64块的ram,做上100路都没什么问题

Fourier00 发表于 2013-11-22 08:00:38

lineling 发表于 2013-11-20 22:05
SPI才是王道   请问SPI的优势 在哪里?

应用场景不一样,没有什么可比性

ourdemo 发表于 2013-12-14 00:53:15

感谢楼主分享

fanfanrenfan 发表于 2013-12-14 11:22:13

关注多串口,希望有实物验证!

seasung 发表于 2013-12-14 14:24:51


很好,支持一下

Fourier00 发表于 2014-6-1 11:21:33

这么好的东西 没有人用?

linjpxt 发表于 2014-6-1 11:40:07

十六路是统一波特率还是单独可配置的啊

Fourier00 发表于 2014-6-1 12:05:33

linjpxt 发表于 2014-6-1 11:40
十六路是统一波特率还是单独可配置的啊

统一的阿,分开的不太好做

flzili 发表于 2014-6-6 20:39:56

SPI转16路串口已经实现,可以在xc6slx9上实现,每路都是独立的设置波特率,没路收发至少256字节缓冲,没路都有485流向控制,另外没路还附带一个IO输出,可用于控制232与485的选择
页: [1]
查看完整版本: 开源 新鲜出炉,16路串口接收模块