搜索
bottom↓
回复: 317

开源Verilog HDL原创UART模块

  [复制链接]

出0入0汤圆

发表于 2013-6-6 00:22:57 | 显示全部楼层 |阅读模式
本帖最后由 skyxjh 于 2013-6-6 00:37 编辑
  1. // 帧格式        |---------- frame ----------|
  2. // rxdin -------\___x=x=x=x=x=x=x=x=x=/------x===
  3. //       idle  start   N_bits_data  [P]stop  idle/start
  4. // 起始位总是为低电平,数据位可变长度,校验位[P]可设置无(奇或偶)校验,结束位总是为高电平

  5. // 位采样   |----------- 1 bit -------------|
  6. // rxdin ==x===============================x==
  7. //         | | | | | | | | | | | | | | | | | |
  8. // time    0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1
  9. // sample                O O O
  10. module uart(rst,clk,rxd,txd); //UART串口收发顶层模块
  11.         input rst,clk,rxd; //复位,系统时钟,接收数据线
  12.         output txd; //发送数据线
  13.         parameter odd = 2'b01, even = 2'b10, none = 2'b00; //奇校验、偶校验、无校验
  14.         parameter low = 1'b0, hi = 1'b1, true = low, false = hi; //常量定义
  15.         wire txclk, rxclk, txbusy, rxbusy, txen, rxen, rdclk, dready, dovf, ferr; //发送时钟,接收时钟,发送忙,接收忙,发送使能,接收使能,读数据,数据准备好,数据被覆盖,帧错误
  16.         wire [7:0]txdin; //并行数据
  17.         reg [1:0]parityset = none; //校验设置
  18.         wire [1:0]parity;
  19.         assign parity = parityset; //奇偶校验设置
  20.         //接收转发控制模块实例化
  21.         controll ctrl(.rst(rst),.rxen(rxen),.dready(dready),.rdclk(rdclk),.txen(txen),.txbusy(txbusy),.rxclk(rxclk));
  22.         //波特率设置模块实例化
  23.         baudset #(.fsysclk(50_000_000),.baud(115_200)) baudset(.rst(rst),.sysclk(clk),.txclk(txclk),.rxclk(rxclk));
  24.         //发送模块实例化
  25.         transeiver tx(.rst(rst),.txclk(txclk),.txen(txen),.txdin(txdin),.parity(parity),.txdout(txd),.txbusy(txbusy));
  26.         //接收模块实例化
  27.         receiver rx(.rst(rst),.clk16x(rxclk),.rxen(rxen),.rxdin(rxd),.rdclk(rdclk),.parity(parity),.rxdout(txdin),.dready(dready),.dovf(dovf),.ferr(ferr),.rxbusy(rxbusy));
  28. endmodule
复制代码

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

 楼主| 发表于 2013-6-6 00:24:17 | 显示全部楼层
  1. // 帧格式        |---------- frame ----------|
  2. // txdin -------\___x=x=x=x=x=x=x=x=x=/------x===
  3. //       idle  start   N_bits_data  [P]stop  idle/start
  4. // 起始位总是为低电平,数据位可变长度,校验位[P]可设置无(奇或偶)校验,结束位总是为高电平

  5. module transeiver(rst,txclk,txen,txdin,parity,txdout,txbusy); //UART串口发送模块
  6.         parameter N = 4'd8; //发送N位串行数据
  7.         input rst,txclk,txen; //复位,发送时钟,发送使能
  8.         input [N-1:0]txdin; //输入数据
  9.         input [1:0]parity; //奇、偶校验设置
  10.         output reg txdout,txbusy; //串行数据输出,忙信号
  11.        
  12.         parameter low = 1'b0, hi = 1'b1, true = low, false = hi; //常量定义
  13.         parameter odd = 2'b01, even = 2'b10, none = 2'b00; //奇校验、偶校验、无校验
  14.        
  15.         reg txen_buf; //发送使能信号缓存
  16.         wire negedge_txen; //发送使能信号下降沿
  17.         assign negedge_txen = txen_buf & ~txen; //发送使能信号下降沿输出正脉冲
  18.         always @(negedge txclk) txen_buf <= txen; //缓存上一状态
  19.        
  20.         reg [3:0]cntbit = 4'd0; //发送位记数
  21.         reg [N-1:0]txdin_buf; //输入数据锁存器,发送使能下降沿锁存
  22.         always @(negedge txclk or negedge rst) begin
  23.                 if(rst == low) begin //低电平复位
  24.                         txdout <= hi; //输出数据线置高电平
  25.                         txbusy <= false; //清除忙信号
  26.                         end
  27.                 else if(txbusy == false) begin //空闲状态
  28.                         if(negedge_txen) begin //发送使能信号下降沿
  29.                                 txdin_buf <= txdin; //锁存输入数据
  30.                                 txbusy <= true; //置忙信号
  31.                                 txdout <= low; //数据线置低电平,发送起始位
  32.                                 cntbit <= 4'd0; //发送位记数器清零
  33.                                 end
  34.                         end
  35.                 else begin //发送数据
  36.                         cntbit <= cntbit + 1'b1; //发送位记数
  37.                         if(cntbit < N) begin //发送数据位
  38.                                 txdout <= txdin_buf[cntbit]; //低位在前
  39.                                 end
  40.                         else if(cntbit == N) begin //发送校验位
  41.                                 case(parity)
  42.                                         odd: txdout <= ~^txdin_buf; //奇校验
  43.                                         even: txdout <= ^txdin_buf; //偶校验
  44.                                         default: begin//无校验位
  45.                                                 txdout <= hi; //发送结束位
  46.                                                 txbusy <= false; //清除忙信号,准备发送下一帧数据
  47.                                                 end
  48.                                 endcase
  49.                                 end
  50.                         else begin
  51.                                 case(parity)
  52.                                         odd,even: begin
  53.                                                 txdout <= hi; //发送结束位
  54.                                                 txbusy <= false; //清除忙信号,准备发送下一帧数据
  55.                                                 end
  56.                                 endcase
  57.                                 end
  58.                         end
  59.                 end
  60. endmodule
复制代码

出0入0汤圆

 楼主| 发表于 2013-6-6 00:24:47 | 显示全部楼层
skyxjh 发表于 2013-6-6 00:24
  1. // 帧格式        |---------- frame ----------|
  2. // rxdin -------\___x=x=x=x=x=x=x=x=x=/------x===
  3. //       idle  start   N_bits_data  [P]stop  idle/start
  4. // 起始位总是为低电平,数据位可变长度,校验位[P]可设置无(奇或偶)校验,结束位总是为高电平

  5. // 位采样   |----------- 1 bit -------------|
  6. // rxdin ==x===============================x==
  7. //         | | | | | | | | | | | | | | | | | |
  8. // time    0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1
  9. // sample                O O O
  10. module receiver(rst,clk16x,rxen,rxdin,rdclk,parity,rxdout,dready,dovf,ferr,rxbusy); //UART串口接收模块
  11.         parameter N = 8; //接收N位串行数据
  12.         input wire rst; //复位
  13.         input wire clk16x; //16倍波特率时钟
  14.         input wire rxen; //接收使能
  15.         input wire rxdin; //串行数据输入
  16.         input wire rdclk; //并行数据读出时钟
  17.         input wire [1:0]parity; //校验位设置
  18.        
  19.         output reg [N-1:0]rxdout = {N{1'bz}}; //N位并行数据
  20.         output reg dready = false; //数据准备好
  21.         output reg dovf = false; //数据被覆盖
  22.         output reg ferr = false; //帧错误
  23.         output reg rxbusy = false; //忙信号
  24.        
  25.         parameter low = 1'b0, hi = 1'b1, true = low, false = hi; //常量定义
  26.         parameter odd = 2'b01, even = 2'b10, none = 2'b00; //奇校验、偶校验、无校验
  27.        
  28.         reg [N+2:0]dbuf; //数据缓存,1bit起始位 + Nbits数据位 + 1bit校验位 + 1bit结束位
  29.         reg [3:0]cnt_bit = 4'h0; //接收数据位记数
  30.         reg [3:0]cnt_sam = 4'h0; //每一位数据的采样点记数
  31.         reg [7:9]sample; //第7,8,9个采样点数据
  32.        
  33.         reg rdclk_buf; //读信号
  34.         wire read_done; //已读取信号(rdclk信号的下降沿)
  35.         assign read_done = rdclk_buf & ~rdclk; //rdclk信号的下降沿为真
  36.         always @(posedge clk16x) rdclk_buf <= rdclk; //保存上一状态
  37.        
  38.         reg rxdin_buf; //数据输入缓存
  39.         wire negedge_rxdin; //数据线下降沿信号
  40.         assign negedge_rxdin = rxdin_buf & ~rxdin; //rxdin信号的下降沿为真
  41.         always @(posedge clk16x) rxdin_buf <= rxdin; //保存上一状态
  42.        
  43.         always @(posedge clk16x) begin
  44.                 if(rst == low) begin //复位
  45.                         rxdout <= {N{1'bz}}; //释放数据总线
  46.                         dready <= false; //清除数据准备好信号
  47.                         dovf <= false; //清除数据被覆盖信号
  48.                         ferr <= false; //清除帧错误信号
  49.                         rxbusy <= false; //清除忙信号
  50.                         end
  51.                 else if(rxen == false) begin //不接收
  52.                         rxdout <= {N{1'bz}}; //释放数据总线
  53.                         dready <= false; //清除数据准备好信号
  54.                         dovf <= false; //清除数据被覆盖信号
  55.                         ferr <= false; //清除帧错误信号
  56.                         rxbusy <= false; //清除忙信号
  57.                         end
  58.                 else if(rxbusy == false) begin //空闲状态
  59.                         if(read_done) begin //数据已读出
  60.                                 rxdout <= {N{1'bz}}; //释放数据总线
  61.                                 dready <= false; //清除数据准备好信号
  62.                                 dovf <= false; //清除数据被覆盖信号
  63.                                 end
  64.                         if(negedge_rxdin) begin //rxdin下降沿开始接收数据
  65.                                 cnt_sam <= 4'h5; //采样点标记为7(斜率补偿,检测到下降沿时数据已有效)
  66.                                 cnt_bit <= 4'h0; //接收数据位记数器清零
  67.                                 rxbusy <= true; //置忙信号
  68.                                 end
  69.                         end
  70.                 else begin //接收数据
  71.                         if(read_done) begin //数据已读出
  72.                                 rxdout <= {N{1'bz}}; //释放数据总线
  73.                                 dready <= false; //清除数据准备好信号
  74.                                 dovf <= false; //清除数据被覆盖信号
  75.                                 end
  76.                         cnt_sam <= cnt_sam + 1'b1; //采样点记数
  77.                         case(cnt_sam)
  78.                                 4'h7,4'h8,4'h9: sample[cnt_sam] <= rxdin; //第7,8,9个采样点采样数据
  79.                                 4'hA: begin //数据处理
  80.                                         dbuf[cnt_bit] <= (sample[7] & sample[8]) | (sample[7] & sample[9]) | (sample[8] & sample[9]); //择多滤波(低通滤波)
  81.                                         if(dbuf[0] == hi) rxbusy <= false; //起始位错误,重新检测起始位
  82.                                         case(parity) //校验位设置检测
  83.                                                 even: begin //偶校验
  84.                                                         if(cnt_bit < N+2) cnt_bit <= cnt_bit + 1'b1; //接收位记数
  85.                                                         else if(^dbuf[N+1:1]) begin
  86.                                                                 ferr <= true; //偶校验错误
  87.                                                                 rxbusy <= false; //清除忙信号
  88.                                                                 end
  89.                                                         else if(dbuf[N+2] == low) begin
  90.                                                                 ferr <= true; //结束位错误
  91.                                                                 rxbusy <= false; //清除忙信号
  92.                                                                 end
  93.                                                         else begin
  94.                                                                 ferr <= false; //无帧错误
  95.                                                                 rxdout <= dbuf[N:1]; //输出数据
  96.                                                                 dready <= true; //置数据准备好信号
  97.                                                                 rxbusy <= false; //清除忙信号
  98.                                                                 if(dready == true) dovf <= true; //如果上一次数据未读出,则置数据被覆盖信号
  99.                                                                 end
  100.                                                         end
  101.                                                 odd: begin //奇校验
  102.                                                         if(cnt_bit < N+2) cnt_bit <= cnt_bit + 1'b1; //接收位记数
  103.                                                         else if(~^dbuf[N+1:1]) begin
  104.                                                                 ferr <= true; //奇校验错误
  105.                                                                 rxbusy <= false; //清除忙信号
  106.                                                                 end
  107.                                                         else if(dbuf[N+2] == low) begin
  108.                                                                 ferr <= true; //结束位错误
  109.                                                                 rxbusy <= false; //清除忙信号
  110.                                                                 end
  111.                                                         else begin
  112.                                                                 ferr <= false; //无帧错误
  113.                                                                 rxdout <= dbuf[N:1];//输出数据
  114.                                                                 dready <= true; //置数据准备好信号
  115.                                                                 if(dready == true) dovf <= true; //如果上一次数据未读出,则置数据被覆盖信号
  116.                                                                 rxbusy <= false; //清除忙信号
  117.                                                                 end
  118.                                                         end
  119.                                                 default: begin //无校验位
  120.                                                         if(cnt_bit < N+1) cnt_bit <= cnt_bit + 1'b1; //接收位记数
  121.                                                         else if(dbuf[N+1] == low) begin
  122.                                                                 ferr <= true; //结束位错误
  123.                                                                 rxbusy <= false; //清除忙信号
  124.                                                                 end
  125.                                                         else begin
  126.                                                                 ferr <= false; //无帧错误
  127.                                                                 rxdout <= dbuf[N:1]; //输出数据
  128.                                                                 dready <= true; //置数据准备好信号
  129.                                                                 rxbusy <= false; //清除忙信号
  130.                                                                 if(dready == true) dovf <= true; //如果上一次数据未读出,则置数据被覆盖信号
  131.                                                                 end
  132.                                                         end
  133.                                         endcase
  134.                                         end
  135.                         endcase
  136.                         end
  137.                 end
  138. endmodule
复制代码

出0入0汤圆

 楼主| 发表于 2013-6-6 00:25:17 | 显示全部楼层
skyxjh 发表于 2013-6-6 00:24
  1. module baudset(rst,sysclk,txclk,rxclk); //波特率设置模块
  2.         input rst,sysclk; //复位,系统时钟
  3.         output txclk; //串口发送时钟
  4.         output rxclk; //串口接收时钟
  5.        
  6.         parameter fsysclk = 50_000_000; //系统时钟频率Hz
  7.         parameter baud = 115_200; //波特率bps
  8.         parameter divp10x = (10 * fsysclk) / (16 * baud); //10倍分频系数
  9.         parameter divp1x = fixv(divp10x); //分频因子4舍5入
  10.         parameter N = wordsize(divp1x); //计数器变量位数
  11.         parameter divp = fixv2(divp1x); //分频因子位宽调整
  12.         parameter low = 1'b0, hi = 1'b1; //常量定义
  13.        
  14.         reg [N-1:0]cnt1 = {N{1'b0}}; //divp分频计数器
  15.         reg [3:0]cnt2 = 4'd0; //16分频计数器
  16.         reg rxclk; //串口接收时钟
  17.        
  18.         function [31:0]fixv; //4舍5入
  19.                 input [31:0]value10x;
  20.                 fixv = value10x % 10 < 5 ? value10x / 10 : (value10x / 10) + 1;
  21.         endfunction
  22.        
  23.         function [N-1:0]fixv2; //位宽调整
  24.                 input [31:0]value;
  25.                 fixv2 = value;
  26.         endfunction
  27.        
  28.         function [5:0]wordsize; //计算位宽
  29.                 input [31:0]value;
  30.                 wordsize = 1'b1;
  31.                 while((32'b1 << (wordsize - 1'b1)) < value) wordsize = wordsize + 1'b1;
  32.         endfunction
  33.        
  34.         always @(posedge sysclk or negedge rst) begin //系统时钟上升沿或复位信号下降沿
  35.                 if(rst == low) begin //复位
  36.                         cnt1 <= {N{1'b0}}; //分频计数器清零
  37.                         rxclk <= hi; //接收时钟置高电平
  38.                         end
  39.                 else if(cnt1 == (divp >> 1'b1)) begin //半个周期(当分频因子为奇数时,高电平时间比低电平时间短1个系统时钟周期)
  40.                         cnt1 <= cnt1 + 1'b1; //计数
  41.                         rxclk <= low; //接收时钟置低电平
  42.                         end
  43.                 else if(cnt1 == (divp - 1'b1)) begin //一个周期结束
  44.                         cnt1 <= {N{1'b0}}; //分频计数器清零
  45.                         rxclk <= hi; //接收时钟置高电平
  46.                         end
  47.                 else begin //中间值
  48.                         cnt1 <= cnt1 + 1'b1; //计数
  49.                         end
  50.                 end //always
  51.                
  52.         assign txclk = cnt2[3];        //16分频
  53.        
  54.         always @(posedge rxclk or negedge rst) begin //接收时钟上升沿或复位信号下降沿
  55.                 if(rst == low) begin //复位
  56.                         cnt2 <= 4'd0; //分频计数器清零
  57.                         end
  58.                 else begin
  59.                         cnt2 <= cnt2 + 1'b1; //计数
  60.                         end
  61.                 end //always
  62.        
  63. endmodule
复制代码

出0入0汤圆

 楼主| 发表于 2013-6-6 00:25:46 | 显示全部楼层
skyxjh 发表于 2013-6-6 00:25
  1. // uart接收转发控制
  2. module controll(rst,rxen,dready,rdclk,txen,txbusy,rxclk); //控制模块
  3.         input rst,dready,txbusy,rxclk; //复位(低电平有效),数据准备好(低电平有效),发送忙(低电平有效),接收时钟
  4.         output reg rxen,rdclk,txen; //接收使能(低电平有效),读数据时钟(下降沿有效),发送使能(下降沿有效)
  5.        
  6.         parameter low = 1'b0, hi = 1'b1, true = low, false = hi; //常量定义
  7.        
  8.         always @(negedge rxclk or negedge rst) begin //接收时钟下降沿或复位信号下降沿
  9.                 if(rst == low) begin //复位
  10.                         txen <= false; //禁止发送
  11.                         rxen <= false; //禁止接收
  12.                         rdclk <= false; //不读数据
  13.                         end
  14.                 else begin //接收转发
  15.                         rxen <= true; //使能接收
  16.                         if(dready == true) begin //数据准备好
  17.                                 if(txbusy == false) begin //发送空闲状态
  18.                                         txen <= true; //使能发送
  19.                                         rdclk <= false; //不读数据
  20.                                         end
  21.                                 else begin //发送数据
  22.                                         txen <= false; //发送使能信号复位
  23.                                         rdclk <= true; //读数据
  24.                                         end
  25.                                 end
  26.                         end
  27.                 end
  28. endmodule
复制代码

出0入0汤圆

 楼主| 发表于 2013-6-6 00:27:04 | 显示全部楼层
skyxjh 发表于 2013-6-6 00:25

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-6-6 00:29:54 | 显示全部楼层
skyxjh 发表于 2013-6-6 00:27

为方便大家引用,附上工程文件。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-6-6 00:42:29 | 显示全部楼层
RTL图

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-6-6 00:46:30 | 显示全部楼层
波特率设置模块是亮点,其中位宽处理可以节省大量资源。

出0入0汤圆

 楼主| 发表于 2013-6-6 00:50:39 | 显示全部楼层
所有模块都没有使用状态机,最大限度节省FPGA资源。

出0入0汤圆

 楼主| 发表于 2013-6-6 00:52:39 | 显示全部楼层
每一个模块都有详细注释,方便大家阅读。

出0入0汤圆

 楼主| 发表于 2013-6-6 00:57:50 | 显示全部楼层
当前控制模块实现的功能是接收串口发来的数据,实时转发出去。
只要接上串口线,用PC机串口调试助手发送任意数据,就能收到该数据。

出0入0汤圆

发表于 2013-6-6 07:19:05 | 显示全部楼层
真省资源,网上的那些都很大啊!epm240装了之后几乎所剩无几了!

出0入0汤圆

发表于 2013-6-6 07:48:08 | 显示全部楼层
这个要顶,,好贴

出0入0汤圆

发表于 2013-6-6 08:55:22 | 显示全部楼层
下载学习。

出0入0汤圆

发表于 2013-6-6 09:02:11 | 显示全部楼层
顶一下啊

出0入17汤圆

发表于 2013-6-6 09:06:12 | 显示全部楼层
确实很省资源,我之前的用了120多个le,你这个只需要96个

出0入0汤圆

发表于 2013-6-6 09:30:56 | 显示全部楼层
mark and mark

出0入0汤圆

发表于 2013-6-6 09:30:57 | 显示全部楼层
你这个确实省了不少资源!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-6-6 10:02:37 | 显示全部楼层
不错,不过最好还是加上4X,16X采样,TX/RX 反相选择,MSB/LSB选择,TX SHIFT REG EMPTY 中断,停止位可选,stamp 似的16BIT计数器,9BIT地址匹配传送等

出0入0汤圆

 楼主| 发表于 2013-6-6 12:44:28 | 显示全部楼层
固定奇偶校验设置,还可以省更多资源。

出0入0汤圆

 楼主| 发表于 2013-6-6 12:53:12 | 显示全部楼层
还可以定制输出信号,如果不用dready,dovf,ferr信号也可以裁减掉,用rxbusy信号上升沿同步数据处理,可以省更多资源。

出0入0汤圆

发表于 2013-6-6 12:59:54 | 显示全部楼层
有意思 能省点是点

出0入0汤圆

发表于 2013-6-6 13:01:15 | 显示全部楼层
这个注释真的给力

出0入0汤圆

 楼主| 发表于 2013-6-6 13:02:52 | 显示全部楼层
收发模块的复位处理也可以省了,只在控制模块处理复位信号就可以。

出0入0汤圆

 楼主| 发表于 2013-6-6 13:04:24 | 显示全部楼层
当CPLD或FPGA的资源紧张时,这样高度定制的UART模块是最佳选择。

出0入0汤圆

发表于 2013-6-6 14:31:29 | 显示全部楼层
本帖最后由 linjpxt 于 2013-6-6 14:34 编辑

顺道也分享一个自己写的串口,没有楼主的那么多功能, 只是N位1位无校验 , 编译才80多个LE,现在自己在用还挺稳定的.



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-6-6 14:36:46 | 显示全部楼层
本帖最后由 linjpxt 于 2013-6-6 14:52 编辑

不用另外接控制模块就可以实现收转发.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-6-6 15:11:55 | 显示全部楼层
本帖最后由 linjpxt 于 2013-6-6 16:07 编辑
linjpxt 发表于 2013-6-6 14:36
不用另外接控制模块就可以实现收转发.


借楼主的图说明一样采样的方式,

// 位采样  |------ 1 bit -----|
// rxdin ==x========x==
//        |   |    |    |    |    |    |
// time     0    1    2   3    0   1   2
// sample  x    s   s   s
// 三个采样点 s 中任何两个为 1 则判为 1,否则为 0

出0入0汤圆

发表于 2013-6-6 15:16:51 来自手机 | 显示全部楼层
回去也看一下我的串口要使用多少资源。写的比楼主的细很多,。

出0入0汤圆

发表于 2013-6-6 15:18:06 | 显示全部楼层
这个不错啊

出0入0汤圆

发表于 2013-6-6 15:21:20 来自手机 | 显示全部楼层
jlhgold 发表于  7 小时前
真省资源,网上的那些都很大啊!epm240装了之后几乎所剩无几了!...

我是4釆样,只要不是全0或全1就跟前一状态一样,全0就改为0,全1就1。所以可以很简单的改变釆样数。

出0入0汤圆

 楼主| 发表于 2013-6-6 18:46:30 | 显示全部楼层
用16倍时钟采样是为了在数据位的中点采样,采样3个点进行择多(低通)滤波是为了抗干扰。

出0入0汤圆

发表于 2013-6-6 18:54:31 | 显示全部楼层
我是这样对RX输入信号过滤的:
// ******************* RX的输入滤波 **************************
module Filter(clk,rst,in,out);
        input  clk,rst;
        input  in;
        output out;

        wire shift,en;
        reg  [3:0] cnt=4'h0;
        reg  [1:0] state=2'b00;
        //clk的16分频
        always@(posedge clk or negedge rst) begin
                if(!rst)
                        cnt<=4'h0;
                else
                        cnt<=cnt+1;
        end
        always@(posedge clk or negedge rst) begin
                if(!rst)
                        state<=2'b00;
                else
                        state<={state[0],cnt[3]};
        end
        assign shift = state[0] & (~state[1]);                //clk的32分频后的上升沿作采样移位输入使能
        assign en = (~state[0]) & state[1];                        //clk的32分频后的下降沿作采样输出使能
       
        wire all_1,all_0;
        reg  [3:0] ins=4'hF;
        always@(posedge clk or negedge rst) begin
                if(!rst)
                        ins<=4'hF;
                else if(shift)
                        ins<={ins[2:0],in};
                else
                        ins<=ins;
        end
        assign all_1 = &(ins);
        assign all_0 = ~(|(ins));
       
        reg outs=1'b1;
        always@(posedge clk or negedge rst) begin
                if(!rst)
                        outs<=1'b1;
                else if(en & (all_1 | all_0))
                        outs<=all_1;
                else
                        outs<=outs;
        end
        assign out=outs;
       
endmodule

出0入0汤圆

发表于 2013-6-6 18:56:51 | 显示全部楼层
串行口接收:

module UartRx(clk,rst,Ready,nRd,Q,RX);
        input  clk,rst;
        output Ready;
        input  nRd;
        output reg [7:0] Q;
        input  RX;
       
        parameter STEP = 4947802;
       
        wire Busy;
        reg  Stop=1'b0;
        reg  Start=1'b0;
        wire in;
       
        Filter u0(clk,rst,RX,in);

        assign Busy = Start ^ Stop;       
       
        always@(posedge clk or negedge rst) begin
                if(!rst) begin
                        Start<=1'b0;
                end else begin
                        if(Busy | in) begin
                                Start<=Start;
                        end else begin
                                Start<=~Start;                                // 置位Busy(Busy=Start^Stop),开始产生BaudRate的115200Hz信号
                        end
                end
        end
       
        wire BaudRate;                                                        //在没有毛刺的理想情况下,该信号的频率等于串口的波特率
        wire Capture;                                                        //捕捉(由BaudRate的上升沿触发)
        reg  [31:0] Count=32'h00000000;
       
        reg  [1:0] status=2'b00;
       
        reg  [9:0] data=10'h000;
        reg  [7:0] RxData=8'h00;
        reg  [3:0] sc=4'h0;
        reg  ReceiveComplete=1'b0;                        //接收完成
        reg  ReadComplete=1'b0;                                //读完成
       
        assign Ready = ReceiveComplete ^ ReadComplete;
       
        // 对clk分频得到115200Hz的频率
        always@(posedge clk or negedge rst) begin
                if(!rst)
                        Count<=32'h00000000;
                else if(Busy)
                        Count<=Count+STEP;
                else
                        Count<=32'h00000000;
        end
        assign BaudRate = (Count>32'h7FFFFFFF)?1'b1:1'b0;

        // 捕捉BaudRate的上升沿作为对RX(已滤波)的采样信号
        always@(posedge clk or negedge rst) begin
                if(!rst)
                        status<=2'b00;
                else
                        status<={status[0],BaudRate};
        end
        assign Capture = status[0] & (~status[1]);
       
        // 处理过程
        always@(posedge clk or negedge rst) begin
                if(!rst) begin
                        data<=10'h000;
                        sc<=4'h0;
                        Stop<=1'b0;
                        ReceiveComplete<=1'b0;
                        RxData<=8'h00;
                end else if(Capture) begin
                        if(sc==4'd9) begin
                                sc<=4'h0;
                                RxData<=data[9:2];
                                Stop<=~Stop;                                                                        //复位Busy
                                ReceiveComplete<=~ReceiveComplete;                //置位Ready
                        end else begin
                                data<={in,data[9:1]};
                                sc<=sc+1;
                        end
                end
        end
       
        reg  [1:0] rdstatus=2'b00;
        wire  rden;
        always@(posedge clk or negedge rst) begin
                if(!rst)
                        rdstatus<=2'b00;
                else
                        rdstatus<={rdstatus[0],~nRd};                                        //nRd信号取反后,下降沿采样变成上升沿采样
        end
        assign rden = rdstatus[0] & (~rdstatus[1]);
       
        always@(posedge clk or negedge rst) begin
                if(!rst) begin
                        Q<=8'h00;
                        ReadComplete<=1'b0;
                end else if(rden & Ready) begin
                        Q<=RxData;
                        ReadComplete<=~ReadComplete;                                        //清除 Ready
                end else begin
                        Q<=Q;
                        ReadComplete<=ReadComplete;
                end
        end

endmodule

出0入0汤圆

发表于 2013-6-6 18:58:00 | 显示全部楼层
串行口发送:

module UartTx(clk,rst,Busy,nWr,Data,TX);
        input  clk;
        input  rst;
        output Busy;
        input  nWr;
        input  [7:0] Data;
        output TX;
       
        reg tx_out=1'b0;
       
        parameter STEP = 4947802;                                        // BAUD_RATE *((2^32)/100M)

        reg  [1:0] ws=2'b00;
        wire wr;
        wire en;
        always@(posedge clk or negedge rst) begin
                if(!rst)
                        ws<=2'b00;
                else
                        ws<={ws[0],nWr};
        end
        assign wr = (~ws[0]) & ws[1];                        //捕捉nWr的下降沿
        assign en = wr & (~Busy);
       
        reg start=1'b0;
        reg stop=1'b0;
        reg [9:0] data=10'h000;
        reg [3:0] cnt=4'd0;
       
        wire shift;
        assign Busy = start ^ stop;
       
        // START信号发生器
        always@(posedge clk or negedge rst) begin
                if(!rst) begin
                        data<=8'h00;
                        start<=1'b0;
                end else if(en) begin
                        start<=~start;
                        data<={1'b0,~Data,1'b1};
                end else begin
                        start<=start;
                        if(shift) begin
                                data<={1'b0,data[9:1]};
                        end else begin
                                data<=data;
                        end
                end
        end
        assign TX=~data[0];
       
        // STOP信号发生器
        always@(posedge clk or negedge rst) begin
                if(!rst) begin
                        stop<=1'b0;
                        cnt<=4'd0;
                end else if(shift) begin
                        if(cnt==4'd10) begin
                                cnt<=4'd0;
                                stop<=~stop;
                        end else begin
                                cnt<=cnt+1;
                                stop<=stop;
                        end
                end else begin
                        cnt<=cnt;
                        stop<=stop;
                end
        end
       
        reg  [1:0] ss=2'b00;
        reg [31:0] count=32'h00000000;
        wire BaudRate;

        always@(posedge clk or negedge rst) begin
                if(!rst) begin
                        count<=32'h00000000;
                end else if(Busy) begin
                        count<=count+STEP;
                end else begin
                        count<=32'h00000000;
                end
        end
        assign BaudRate = (count>32'h7FFFFFFF)?1'b1:1'b0;                // 在不考虑毛刺的情况下,这个信号的频率就等于波特率       
       
        always@(posedge clk or negedge rst) begin
                if(!rst)
                        ss<=2'b00;
                else
                        ss<={ss[0],BaudRate};
        end
        assign shift = (~ss[0]) & ss[1];                                                                // 捕捉BaudRate的下降沿作移位信号
       
endmodule

出0入0汤圆

 楼主| 发表于 2013-6-6 19:04:31 | 显示全部楼层
楼上的方案没考虑资源优化。

出0入0汤圆

 楼主| 发表于 2013-6-6 19:37:33 | 显示全部楼层
linjpxt 发表于 2013-6-6 15:11
借楼主的图说明一样采样的方式,

// 位采样  |------ 1 bit -----|

你这种采样方式,如果信号的边沿很平缓的话,岂不是采样到全0的概率很大。

出0入0汤圆

 楼主| 发表于 2013-6-6 19:43:50 | 显示全部楼层
huatong 发表于 2013-6-6 15:21
我是4釆样,只要不是全0或全1就跟前一状态一样,全0就改为0,全1就1。所以可以很简单的改变釆样数。 ...

你这种采样方式在有干扰的情况下,通信会有问题,通信距离不会远。

出0入0汤圆

发表于 2013-6-6 19:49:56 | 显示全部楼层
skyxjh 发表于 2013-6-6 19:43
你这种采样方式在有干扰的情况下,通信会有问题,通信距离不会远。


你那个采样比我的更不靠谱,我的起码对RX的输入进行了滤波(4点),你就只是采样3个点取2点的结果,更不放心啊.

出0入0汤圆

 楼主| 发表于 2013-6-6 20:07:50 | 显示全部楼层
huatong 发表于 2013-6-6 19:49
你那个采样比我的更不靠谱,我的起码对RX的输入进行了滤波(4点),你就只是采样3个点取2点的结果,更不放心啊 ...

你的前置4点低通滤波比较好,我也采用了。只是判断方式改为4点里有3点为高(低)则取高(低),这样可以抗干扰。

出0入0汤圆

发表于 2013-6-6 20:12:02 | 显示全部楼层
不管怎么样 都谢谢楼主和 另一个大哥了。

出0入0汤圆

发表于 2013-6-6 20:19:19 | 显示全部楼层
skyxjh 发表于 2013-6-6 20:07
你的前置4点低通滤波比较好,我也采用了。只是判断方式改为4点里有3点为高(低)则取高(低),这样可以 ...

使用4点全1或者全0吧,这样才好写(&() 或者 |() 就可以了),我还想用8点呢,不过想想没有必要.还有我是100M的时钟,16分频后4点可以滤掉1.5M以上的干扰,你觉得太高的话可以使用32分频.

出0入0汤圆

 楼主| 发表于 2013-6-6 20:27:38 | 显示全部楼层
huatong 发表于 2013-6-6 20:19
使用4点全1或者全0吧,这样才好写(&() 或者 |() 就可以了),我还想用8点呢,不过想想没有必要.还有我是100M ...

你这样全0或全1把有用信号也滤掉了,不利于从干扰中恢复数据。

出0入0汤圆

发表于 2013-6-6 20:30:59 | 显示全部楼层
skyxjh 发表于 2013-6-6 20:27
你这样全0或全1把有用信号也滤掉了,不利于从干扰中恢复数据。

不会的,这是一个100M的移位寄存器,不是每隔一段采4个样的那种方式

出0入0汤圆

发表于 2013-6-6 20:35:39 | 显示全部楼层
是100M的流水线式采样,流水式过滤.反正你想千象一下流水线就可以了.

出0入0汤圆

 楼主| 发表于 2013-6-6 20:40:35 | 显示全部楼层
huatong 发表于 2013-6-6 20:30
不会的,这是一个100M的移位寄存器,不是每隔一段采4个样的那种方式

输入:11111111111110000111011101110111
输出:xxx11111111111110000000000000000
你的滤波方案应用在上面的系列,滤波后的结果显然不对。

出0入0汤圆

发表于 2013-6-6 21:14:11 | 显示全部楼层
skyxjh 发表于 2013-6-6 20:40
输入:11111111111110000111011101110111
输出:xxx11111111111110000000000000000
你的滤波方案应用在上 ...

看来你还是不明白,你的输入的间隔是多少ns的?比如0000这里是多长的时间

出0入0汤圆

发表于 2013-6-6 21:21:25 | 显示全部楼层
如果你上面的输入大于6.25M的话,那几个干扰是不会影响滤波输出的,上面的输入将会得出全是1的结果.

出0入0汤圆

 楼主| 发表于 2013-6-6 21:55:26 | 显示全部楼层
就是以你的滤波时钟采样到的数据是47楼的数据时,结果就不对了。

出0入0汤圆

 楼主| 发表于 2013-6-6 21:57:42 | 显示全部楼层
本帖最后由 skyxjh 于 2013-6-6 22:31 编辑

  1. module lowpass(clk,lin,lout); //低通滤波模块
  2.         input clk,lin; //滤波时钟,输入线
  3.         output lout; //输出线
  4.         reg [3:0]linbuf; //4点滤波缓冲器
  5.         wire lout1, lout0; //
  6.         assign lout1 = (&linbuf[3:1]) | (&linbuf[2:0]) | (&{linbuf[3],linbuf[1:0]}) | (&{linbuf[3:2],linbuf[0]}); //多数为1时输出1
  7.         assign lout0 = (|linbuf[3:1]) & (|linbuf[2:0]) & (|{linbuf[3],linbuf[1:0]}) & (|{linbuf[3:2],linbuf[0]}); //多数为0时输出0
  8.         assign lout = lout1 ? 1'b1 : lout0 ? lout : 1'b0; //择多滤波(低通输出)
  9.         reg [3:0]cnt; //分频计数器
  10.         always @(posedge clk) begin
  11.                 cnt <= cnt + 1'b1; //计数
  12.                 end
  13.         always @(posedge cnt[3]) begin //16分频
  14.                 linbuf <= {linbuf[2:0], lin}; //4点滑动采样
  15.                 end
  16. endmodule
复制代码

出0入0汤圆

 楼主| 发表于 2013-6-6 22:02:08 | 显示全部楼层
输入:11111111111110000111011101110111
输出:xx111111111111100001111111111111
用上面的滤波模块后的输出

出0入0汤圆

 楼主| 发表于 2013-6-6 22:05:24 | 显示全部楼层
串口调试结果如下:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-6-6 22:07:06 | 显示全部楼层
串口调试工具

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-6-6 22:16:06 | 显示全部楼层
skyxjh 发表于 2013-6-6 22:02
输入:11111111111110000111011101110111
输出:xx111111111111100001111111111111
用上面的滤波模块后的输 ...


这是仿真的结果
in = signal & (ifen | interfere10m);//这是RX的输入(其中signal是信号,interfere10m是10M的干扰源,ifen是控制是否将干拢加入到信号中)

out 是过滤后的输出

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-6-6 22:19:51 | 显示全部楼层
skyxjh 发表于 2013-6-6 19:37
你这种采样方式,如果信号的边沿很平缓的话,岂不是采样到全0的概率很大。 ...

其实我有考虑到你说的这个问题,所以第0点是不采的,考虑到超调震荡的话,一般都在这个阶段,然后从25%,50%,75%三处取三个点,就算25%那点掉了,后面两个点还是可以保证正确的。而且前面有个检测4个点中有三个全0的话,才认为起始位开始,所以在线上的延时一般都已经被去除掉了。

出0入0汤圆

 楼主| 发表于 2013-6-6 22:36:53 | 显示全部楼层
25%的点坏了,怎么能保证75%的点不坏呢?

出0入0汤圆

发表于 2013-6-6 22:42:43 | 显示全部楼层
哦 不错 谢谢分享啊

出0入0汤圆

发表于 2013-6-6 23:02:40 | 显示全部楼层
skyxjh 发表于 2013-6-6 22:36
25%的点坏了,怎么能保证75%的点不坏呢?

事实上在实际应用中,并不多像上面说的那种不停的交变干扰,而更多的是在传输太长跟离后,前端信号的变形。
当然,25%处的点跟75%处的点没有什么必然的联系,但谁都没办法保证哪个点是好的,最多就是这个字节坏掉呗。
那就算75%处的点是好的,那76%处呢,所以我觉得如果你真的是在一个很糟糕的环境中通信的话,就不是考虑这么简单的一些模块了。
从这里取3个点做判决,是一个性能跟规模的平衡。

出0入0汤圆

发表于 2013-6-6 23:10:27 | 显示全部楼层
duo xie fen xiang

出0入0汤圆

 楼主| 发表于 2013-6-6 23:11:47 | 显示全部楼层
linjpxt 发表于 2013-6-6 15:11
借楼主的图说明一样采样的方式,

// 位采样  |------ 1 bit -----|

你这个是以4倍速时钟采样,一般建议用8倍速以上时钟采样。

出0入0汤圆

发表于 2013-6-6 23:13:36 来自手机 | 显示全部楼层
Mark…
来自:amoBBS 阿莫电子论坛 Windows Phone 7 客户端

出0入0汤圆

发表于 2013-6-6 23:16:01 | 显示全部楼层
支持这样的分享。

出0入0汤圆

发表于 2013-6-6 23:24:35 | 显示全部楼层
UART芯片设计还是参考16C550芯片为好,很经典的芯片

出0入0汤圆

 楼主| 发表于 2013-6-6 23:25:18 | 显示全部楼层
huatong 发表于 2013-6-6 22:16
这是仿真的结果
in = signal & (ifen | interfere10m);//这是RX的输入(其中signal是信号,interfere10m是 ...

这种规则的干扰信号的滤除与滤波采样时钟的取值相关,与相位相关。

出0入0汤圆

 楼主| 发表于 2013-6-6 23:33:13 | 显示全部楼层
我这个模块是参照AVR的UART串口模块来设计的。

出0入0汤圆

发表于 2013-6-7 16:18:31 | 显示全部楼层
学习中,楼主大牛

出0入0汤圆

 楼主| 发表于 2013-6-7 22:17:16 | 显示全部楼层
linjpxt 发表于 2013-6-6 23:02
事实上在实际应用中,并不多像上面说的那种不停的交变干扰,而更多的是在传输太长跟离后,前端信号的变形 ...

尽量在信号的中点处采样,才能保证采样到正确的信号,所以采样时钟就得快,用4倍速时钟采样到的样本太分散,只有一个点在中点处。用8倍速以上时钟采样,采样的3个点都在中点附近,效果会更好。

出0入0汤圆

发表于 2013-6-7 22:32:14 | 显示全部楼层
skyxjh 发表于 2013-6-7 22:17
尽量在信号的中点处采样,才能保证采样到正确的信号,所以采样时钟就得快,用4倍速时钟采样到的样本太分 ...

对于尽量采用高的倍速来采样,这个我赞同,因为这样可以尽量控制不同步时间,比如8倍的话,可以控制在 1/8 位时间内,而 4位则只能控制在 1/4 位时间。
而对于说要在中点采样才能保证采样的正确,这个我不认同。只能说,在中点采样,你能够兼容的频率误差的范围能更宽一点而已。比如你在 3/8,4/8,6/8采样,你的三点全正确的频率误差范围为 (+-37.5% / 11位)。
如果频率误差能控制的比较好的话,其实在靠后面采样还是比较好的。
比如CAN总线,就推荐在 75% 左右处采样,实现采用中,更多是在85%到90%处采样。

出0入0汤圆

 楼主| 发表于 2013-6-7 22:42:45 | 显示全部楼层
“在中点采样能够兼容的频率误差的范围能更宽” 与 “在中点采样保证采样的正确” 实际意义是一样的。

出0入0汤圆

 楼主| 发表于 2013-6-7 22:47:02 | 显示全部楼层
当然,如果能够保证频率误差很小的话,尽量靠后的时刻采样更好,比如在75%处采样。如果用16倍速时钟采样的话,在第11、12、13个采样时刻采样3个点。

出0入0汤圆

 楼主| 发表于 2013-6-9 12:50:17 | 显示全部楼层
更正接收模块上电后丢失接收到的第1个字节数据的BUG。原因是检查结束位时实际该帧数据结束位的采样还没结束,所以第1个字节会认为是错误帧。后续字节检查结束位时是用的上一个字节的结束位数据,所以后续字节接收不显示帧错误。也就是说结束位检测错位了。
  1. // 帧格式        |---------- frame ----------|
  2. // rxdin -------\___x=x=x=x=x=x=x=x=x=/------x===
  3. //       idle  start   N_bits_data  [P]stop  idle/start
  4. // 起始位总是为低电平,数据位可变长度,校验位[P]可设置无(奇或偶)校验,结束位总是为高电平

  5. // 位采样   |----------- 1 bit -------------|
  6. // rxdin ==x===============================x==
  7. //         | | | | | | | | | | | | | | | | | |
  8. // time    0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1
  9. // sample                O O O
  10. module receiver(rst,clk16x,rxen,rxdin,parity,rxdouten,rxdout,dready,dovf,ferr,rxbusy); //UART串口接收模块
  11.         parameter N = 8; //接收N位串行数据,N<14
  12.         input wire rst,clk16x; //复位,16倍波特率时钟
  13.         input wire rxen; //接收使能
  14.         input wire rxdin; //串行数据输入
  15.         input wire rxdouten; //并行数据输出允许
  16.         input wire [1:0]parity; //校验位设置
  17.        
  18.         output wire [N-1:0]rxdout; //N位并行数据
  19.         output reg dready = false; //数据准备好
  20.         output reg dovf = false; //数据被覆盖
  21.         output reg ferr = false; //帧错误
  22.         output reg rxbusy = false; //忙信号
  23.        
  24.         parameter low = 1'b0, hi = 1'b1, true = low, false = hi; //常量定义
  25.         parameter odd = 2'b01, even = 2'b10, none = 2'b00; //奇校验、偶校验、无校验
  26.        
  27.         reg [N+2:0]dbuf; //数据缓存,1bit起始位 + Nbits数据位 + 1bit校验位 + 1bit结束位
  28.         reg [3:0]cnt_bit = 4'h0; //接收数据位记数
  29.         reg [3:0]cnt_sam = 4'h0; //每一位数据的采样点记数
  30.         reg [7:9]sample; //第7,8,9个采样点数据
  31.        
  32.         reg rxdoutenbuf; //输出允许信号(读信号)
  33.         wire read_done; //已读取信号(输出允许信号的上升沿)
  34.         assign read_done = ~rxdoutenbuf & rxdouten; //输出允许信号的上升沿为真
  35.         always @(posedge clk16x) rxdoutenbuf <= rxdouten; //保存上一状态
  36.        
  37.         reg rxdin_buf; //数据输入缓存
  38.         wire negedge_rxdin; //数据线下降沿信号
  39.         assign negedge_rxdin = rxdin_buf & ~rxdin; //rxdin信号的下降沿为真
  40.         always @(posedge clk16x) rxdin_buf <= rxdin; //保存上一状态
  41.        
  42.         reg [N-1:0]rxdoutbuf; //输出缓冲器
  43.         assign rxdout = rxdouten == true ? rxdoutbuf : {N{1'bz}}; //输出允许时输出数据,否则释放数据总线
  44.        
  45.         wire samplevalue; //择多滤波(低通滤波)
  46.         assign samplevalue = (sample[7] & sample[8]) | (sample[7] & sample[9]) | (sample[8] & sample[9]);
  47.        
  48.         wire reset; //复位信号
  49.         assign reset = (rxen == false) | (rst == true); //禁止接收或系统复位
  50.        
  51.         initial begin //初始化
  52.                 dready <= false; //清除数据准备好信号
  53.                 dovf <= false; //清除数据被覆盖信号
  54.                 ferr <= false; //清除帧错误信号
  55.                 rxbusy <= false; //清除忙信号
  56.                 end //初始化
  57.                
  58.         always @(posedge clk16x or posedge reset) begin
  59.                 if(reset) begin //复位
  60.                         dready <= false; //清除数据准备好信号
  61.                         dovf <= false; //清除数据被覆盖信号
  62.                         ferr <= false; //清除帧错误信号
  63.                         rxbusy <= false; //清除忙信号
  64.                         end //复位
  65.                 else if(rxbusy == false) begin //空闲状态
  66.                         if(read_done) begin //数据已读出
  67.                                 dready <= false; //清除数据准备好信号
  68.                                 dovf <= false; //清除数据被覆盖信号
  69.                                 end //数据已读出
  70.                         if(negedge_rxdin) begin //rxdin下降沿开始接收数据
  71.                                 cnt_sam <= 4'h0; //采样点标记为7(斜率补偿,检测到下降沿时数据已有效)
  72.                                 cnt_bit <= 4'h0; //接收数据位记数器清零
  73.                                 rxbusy <= true; //置忙信号
  74.                                 end //rxdin下降沿开始接收数据
  75.                         end //空闲状态
  76.                 else begin //接收数据
  77.                         if(read_done) begin //数据已读出
  78.                                 dready <= false; //清除数据准备好信号
  79.                                 dovf <= false; //清除数据被覆盖信号
  80.                                 end //数据已读出
  81.                         cnt_sam <= cnt_sam + 1'b1; //采样点记数
  82.                         case(cnt_sam) //检测采样点时刻
  83.                                 4'h7,4'h8,4'h9: sample[cnt_sam] <= rxdin; //第7,8,9个采样点采样数据
  84.                                 4'hA: begin //第10个采样点更新数据
  85.                                         dbuf[cnt_bit] <= samplevalue; //当前位采样
  86.                                         end //第10个采样点更新数据
  87.                                 4'hB: begin //第11个采样点处理数据
  88.                                         if(dbuf[0] == hi) rxbusy <= false; //起始位错误,重新检测起始位
  89.                                         cnt_bit <= cnt_bit + 1'b1; //接收位记数
  90.                                         case(cnt_bit)
  91.                                                 N + 2'd1: begin //接收到校验位或结束位
  92.                                                         case(parity) //检测校验设置
  93.                                                                 even: begin //偶校验
  94.                                                                         if(^dbuf[N+1:1]) begin //偶校验错误
  95.                                                                                 ferr <= true; //置帧错误标志
  96.                                                                                 rxbusy <= false; //清除忙信号
  97.                                                                                 end //偶校验错误
  98.                                                                         end //偶校验
  99.                                                                 odd: begin //奇校验
  100.                                                                         if(~^dbuf[N+1:1]) begin //奇校验错误
  101.                                                                                 ferr <= true; //置帧错误标志
  102.                                                                                 rxbusy <= false; //清除忙信号
  103.                                                                                 end //奇校验错误
  104.                                                                         end //奇校验
  105.                                                                 default: begin //无校验
  106.                                                                         if(dbuf[N+1] == low) begin //结束位错误
  107.                                                                                 ferr <= true; //置帧错误标志
  108.                                                                                 dready <= false; //清除数据准备好信号
  109.                                                                                 end //结束位错误
  110.                                                                         else begin //无帧错误
  111.                                                                                 rxdoutbuf <= dbuf[N:1]; //输出数据
  112.                                                                                 ferr <= false; //清除帧错误标志
  113.                                                                                 dready <= true; //置数据准备好信号
  114.                                                                                 if(dready == true) dovf <= true; //如果上一次数据未读出,则置数据被覆盖信号
  115.                                                                                 end //无帧错误
  116.                                                                         rxbusy <= false; //清除忙信号
  117.                                                                         end //无校验
  118.                                                         endcase //parity
  119.                                                         end //N + 2'd1
  120.                                                 N + 2'd2: begin //接收到结束位
  121.                                                         if(dbuf[N+1] == low) begin //结束位错误
  122.                                                                 ferr <= true; //置帧错误标志
  123.                                                                 dready <= false; //清除数据准备好信号
  124.                                                                 end //结束位错误
  125.                                                         else begin //无帧错误
  126.                                                                 rxdoutbuf <= dbuf[N:1]; //输出数据
  127.                                                                 ferr <= false; //清除帧错误标志
  128.                                                                 dready <= true; //置数据准备好信号
  129.                                                                 if(dready == true) dovf <= true; //如果上一次数据未读出,则置数据被覆盖信号
  130.                                                                 end //无帧错误
  131.                                                         rxbusy <= false; //清除忙信号
  132.                                                         end //接收到结束位
  133.                                         endcase //cnt_bit
  134.                                         end //第11个采样点处理数据
  135.                         endcase //cnt_sam
  136.                         end //接收数据
  137.                 end //always
  138. endmodule
复制代码

出0入0汤圆

 楼主| 发表于 2013-6-9 13:45:54 | 显示全部楼层
再发一个fifo模块,可以用作UART接收处理。


  1. module fifo(rst,rdclk,wrclk,idat,odat,full,empty);
  2.         parameter datbits = 8, addrbits = 4;
  3.         input rst,rdclk,wrclk; //复位,fifo读、写时钟
  4.         input [datbits-1:0]idat; //输入数据
  5.         output reg [datbits-1:0]odat; //输出数据
  6.         output full,empty; //fifo满,空
  7.        
  8.         parameter low = 1'b0, hi = 1'b1, true = low, false = hi;
  9.        
  10.         reg [datbits-1:0]mem[0:2**addrbits-1]; //2^addrbits字节RAM块
  11.         reg [addrbits-1:0]rdaddr; //fifo读地址
  12.         reg [addrbits-1:0]wraddr; //fifo写地址
  13.        
  14.         wire [addrbits-1:0]delt; //有效数据数
  15.         assign delt = wraddr - rdaddr; //有效数据数
  16.         assign empty = delt == {addrbits{1'b0}} ? true : false; //fifo空信号
  17.         assign full = delt == {addrbits{1'b1}} ? true : false; //fifo满信号
  18.        
  19.         initial begin //初始化
  20.                 wraddr = {addrbits{1'b0}}; //写地址清零
  21.                 rdaddr = {addrbits{1'b0}}; //读地址清零
  22.                 end //初始化
  23.                
  24.         always @(negedge wrclk or negedge rst) begin //push
  25.                 if(rst == low) begin //复位
  26.                         wraddr <= {addrbits{1'b0}}; //写地址清零
  27.                         end
  28.                 else begin //进栈
  29.                         mem[wraddr] <= idat; //进栈
  30.                         wraddr <= wraddr + 1'b1; //写地址调整
  31.                         end
  32.                 end //push
  33.        
  34.         always @(negedge rdclk or negedge rst) begin //pop
  35.                 if(rst == low) begin //复位
  36.                         odat <= {datbits{1'b0}}; //输出数据清零
  37.                         rdaddr <= {addrbits{1'b0}}; //读地址清零
  38.                         end
  39.                 else begin //出栈
  40.                         odat <= mem[rdaddr]; //输出数据
  41.                         rdaddr <= rdaddr + 1'b1; //读地址调整
  42.                         end
  43.                 end //pop
  44.        
  45. endmodule

复制代码

出0入0汤圆

发表于 2013-6-10 11:20:44 | 显示全部楼层
注释很给力,谢谢分享。

出0入0汤圆

发表于 2013-6-10 11:46:44 | 显示全部楼层
好资料,谢谢分享

出0入0汤圆

发表于 2013-6-13 15:51:11 | 显示全部楼层
高手,受教了。

出0入0汤圆

发表于 2013-6-13 22:34:04 | 显示全部楼层
路过......

出0入4汤圆

发表于 2013-6-14 11:51:58 | 显示全部楼层
多谢楼主!下来学习一下,虽然用过很多类型的USART了。

出0入0汤圆

发表于 2013-6-15 09:23:11 | 显示全部楼层
skyxjh 发表于 2013-6-6 00:52
每一个模块都有详细注释,方便大家阅读。

请问:如果晶振是20M该如何设置,谢谢!

出0入0汤圆

发表于 2013-6-15 10:21:04 | 显示全部楼层
skyxjh 发表于 2013-6-6 00:25

学习了

出0入0汤圆

 楼主| 发表于 2013-6-15 13:17:25 | 显示全部楼层
wsfry 发表于 2013-6-15 09:23
请问:如果晶振是20M该如何设置,谢谢!

baudset #(.fsysclk(20_000_000),.baud(11_5200)) bst(rst,sysclk,txclk,rxclk);

出0入0汤圆

发表于 2013-6-15 13:52:24 | 显示全部楼层
本帖最后由 wsfry 于 2013-6-15 14:00 编辑
skyxjh 发表于 2013-6-15 13:17
baudset #(.fsysclk(20_000_000),.baud(11_5200)) bst(rst,sysclk,txclk,rxclk);


在50M的条件下 如果要修改波特率为9600,为什么改了好久都还是115200发送的时候可以显示数据,是初学者,可以告诉下你的具体思路吗?谢谢你!
还有parameter divp10x = (10 * fsysclk) / (16 * baud); //10倍分频系数 这其中的16你是如何计算出来的,不明白请指导下,谢谢!

出0入31汤圆

发表于 2013-6-15 14:17:30 | 显示全部楼层
skyxjh 把代码打个包吧,中间几次修改后的最终版以及fifo代码都放进去,大家看着方便

出0入0汤圆

发表于 2013-6-15 17:14:14 | 显示全部楼层
skyxjh 发表于 2013-6-15 13:17
baudset #(.fsysclk(20_000_000),.baud(11_5200)) bst(rst,sysclk,txclk,rxclk);

您好:
今天修改了你的程序,想修改波特率为9600,
baudset #(.fsysclk(50_000_000),.baud(9600)) baudset(.rst(rst),.sysclk(clk),.txclk(txclk),.rxclk(rxclk));
parameter baud = 9600; //波特率bps
但是编译后,通过电脑串口发送指令为什么还是115200的波特率发送正常,用9600的波特率没反应,或者返回乱码!这个程序看了两天了,还是这样,
请大侠指导一下,谢谢!!

出0入0汤圆

 楼主| 发表于 2013-6-15 19:22:35 | 显示全部楼层
wsfry 发表于 2013-6-15 13:52
在50M的条件下 如果要修改波特率为9600,为什么改了好久都还是115200发送的时候可以显示数据,是初学者, ...

直接实例化时修改两个参数就可以了,16是用16倍速采样,见接收模块。

出0入0汤圆

 楼主| 发表于 2013-6-15 19:24:29 | 显示全部楼层
或者直接在波特率设置模块里修改两个参数,实例化时不带参数。

出0入0汤圆

 楼主| 发表于 2013-6-15 19:35:56 | 显示全部楼层
修改后的工程代码打包如下,请不要用于商用,如果用于出版或教程之类,请联系本人。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-6-15 19:40:08 | 显示全部楼层
FIFO及测试代码如下,请不要用于商用,如果用于出版或教程之类,请联系本人。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-6-15 19:41:27 | 显示全部楼层
zchong 发表于 2013-6-15 14:17
skyxjh 把代码打个包吧,中间几次修改后的最终版以及fifo代码都放进去,大家看着方便 ...

工程代码已打包,请版主更新到楼主位。

出0入0汤圆

发表于 2013-6-15 19:49:37 | 显示全部楼层
学习了!谢谢

出0入0汤圆

发表于 2013-6-15 20:16:12 | 显示全部楼层
这个要顶,,好贴啊

出0入0汤圆

发表于 2013-6-15 21:16:18 | 显示全部楼层
skyxjh 发表于 2013-6-15 19:41
工程代码已打包,请版主更新到楼主位。

很感谢你,今天搞了一天都有问题 ,明天把程序烧录到板子上面去,呵呵!

出0入0汤圆

 楼主| 发表于 2013-6-15 21:20:42 | 显示全部楼层

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-6-15 21:43:57 | 显示全部楼层
skyxjh 发表于 2013-6-15 19:40
FIFO及测试代码如下,请不要用于商用,如果用于出版或教程之类,请联系本人。
...


parameter fsysclk = 50_000_000; //系统时钟频率Hz
parameter baud = 115_200; //波特率bps 为什么这个波特率不修改为9600,不理解,明明是9600的波特率为什么这个地方不修改为9600???
parameter divp10x = (10 * fsysclk) / (16 * baud); //10倍分频系数

baudset #(.fsysclk(50_000_000),.baud(9600)) baudset(.rst(rst),.sysclk(clk),.txclk(txclk),.rxclk(rxclk));//这里设置了波特率9600
您好:
请问为什么上面需要这样处理,感觉好矛盾啊!可以指导下吗?谢谢!

出0入0汤圆

 楼主| 发表于 2013-6-15 22:13:01 | 显示全部楼层
wsfry 发表于 2013-6-15 21:43
parameter fsysclk = 50_000_000; //系统时钟频率Hz
parameter baud = 115_200; //波特率bps 为什么这个 ...

实例化时直接传递这两个参数到模块里。如果参数设置与模块里面的一致就不用带参数实例化。

出0入0汤圆

发表于 2013-6-16 15:40:06 | 显示全部楼层
本帖最后由 wsfry 于 2013-6-16 15:43 编辑
skyxjh 发表于 2013-6-15 22:13
实例化时直接传递这两个参数到模块里。如果参数设置与模块里面的一致就不用带参数实例化。 ...


非常感谢,波特率的设置让人非常耳目一新
我想请教下,如果我的输入端是一个8位并行数据,应该怎么处理,需要跟波特率挂钩么,最好能写一段并转串的小程序。
如下图所示,我想在rxd前端加一个模块,输入端口是一个8位并行数据,通过并转串直接给rxd端口,应该如何处理?

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-6-16 19:37:24 | 显示全部楼层
wsfry 发表于 2013-6-16 15:40
非常感谢,波特率的设置让人非常耳目一新
我想请教下,如果我的输入端是一个8位并行数据,应该怎么处理, ...

并转串直接用发送模块,将发送模块的TXD连接到接收模块的RXD就可以。

出0入0汤圆

发表于 2013-6-17 17:58:46 | 显示全部楼层
skyxjh 发表于 2013-6-16 19:37
并转串直接用发送模块,将发送模块的TXD连接到接收模块的RXD就可以。

你好:请教你一个问题:
      lout = lout1 ? 1'b1 : lout0 ? lout : 1'b0;
        这句话,怎么理解,真不知道如何理解,请指导下!谢谢!

出0入0汤圆

 楼主| 发表于 2013-6-17 18:15:47 | 显示全部楼层
给你用if,else描述一下:
lout = lout1 ? 1'b1 : lout0 ? lout : 1'b0;
等价于:
if(lout1==1) lout = 1;
else if(lout0==1) lout = lout;
else lout = 0;

出0入0汤圆

发表于 2013-6-17 20:20:35 | 显示全部楼层
skyxjh 发表于 2013-6-16 19:37
并转串直接用发送模块,将发送模块的TXD连接到接收模块的RXD就可以。


能不能写一个这个串口通讯的测试文件啊?虽然实际用串口调试已经验证过了,但是仍然想看下仿真波形!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-29 20:52

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表