串口接收-控制LED闪烁(VerilogVivado)
前言
此文介绍uart串口串口接收-控制LED的verilog实现和testbench的编写,仿真环境为vivado 2018.3。
提示:以下是本篇文章正文内容,下面案例可供参考
一、实现原理
【设计思路】:
1.uart_cmd模块:将uart_byte_rx的输出rx_data和rx_done作为uart_cmd的输入,将led控制模块的输入作为uart_cmd的输出,将两个模块连接起来。
2.协议制定:ctrl[7:0]与Time[31:0]的值怎么取,使用串口接收模块可能得到多个数据,那么哪些数据是我们所需要的Ctrl和Time呢?于是制定如下协议:
0x55 0xA5 | Time[7:0] Time[15:8] Time[23:16] Time[31:14] Ctrl[7:0] | 0xF0
当检测到第一个字节为8’h55,第二个字节为8‘hA5,第八个字节为8’hF0时将接收到的3-6字节作为TIme值,7字节作为ctrl值,这样能实现对LED等闪烁的控制。
3.协议判断:当每接收到8个字节时如何通过代码判断是否符合上述协议?
reg [7:0] data_str [7:0];always@(posedge Clk)if(rx_done)begindata_str[7] <= #1 rx_data;data_str[6] <= #1 data_str[7];data_str[5] <= #1 data_str[6];data_str[4] <= #1 data_str[5];data_str[3] <= #1 data_str[4];data_str[2] <= #1 data_str[3];data_str[1] <= #1 data_str[2];data_str[0] <= #1 data_str[1]; endalways@(posedge Clk or negedge Reset_n)if(!Reset_n) beginctrl <= #1 0;time_set <= #1 0;end else if(r_rx_done)beginif((data_str[0] == 8'h55) && (data_str[1] == 8'hA5) && (data_str[7] == 8'hF0))begintime_set[7:0] <= #1 data_str[2];time_set[15:8] <= #1 data_str[3];time_set[23:16] <= #1 data_str[4];time_set[31:24] <= #1 data_str[5];ctrl <= #1 data_str[6];endend
使用8字节的reg用作存储接收到的data,每次循环右移。将接收到的data与设定好的协议作比较,取出Time和Ctrl。
二、顶层模块
module uart_rx_ctrl_led(Clk,Reset_n,Led,uart_rx
);input Clk;input Reset_n;output Led;input uart_rx;wire [7:0]ctrl;wire [31:0]time_set;wire [7:0]rx_data;wire rx_done;counter_led_4 counter_led(.Clk(Clk),.Reset_n(Reset_n),.Ctrl(ctrl),.Time(time_set),.Led(Led)); uart_cmd uart_cmd(.Clk(Clk),.Reset_n(Reset_n),.rx_data(rx_data),.rx_done(rx_done),.ctrl(ctrl),.time_set(time_set));uart_byte_rx uart_byte_rx(.Clk(Clk),.Reset_n(Reset_n),.Baud_Set(4),.uart_rx(uart_rx),.Data(rx_data),.Rx_Done(rx_done) ); endmodule
顶层模块用于例化下面三个模块,进行连接。
三、uart_cmd模块
代码如下(示例):
`timescale 1ns/1nsmodule uart_cmd(Clk,Reset_n,rx_data,rx_done,ctrl,time_set
);input Clk;input Reset_n;input [7:0]rx_data;input rx_done;output reg[7:0]ctrl;output reg[31:0]time_set;reg [7:0] data_str [7:0];always@(posedge Clk)if(rx_done)begindata_str[7] <= rx_data;data_str[6] <= data_str[7];data_str[5] <= data_str[6];data_str[4] <= data_str[5];data_str[3] <= data_str[4];data_str[2] <= data_str[3];data_str[1] <= data_str[2];data_str[0] <= data_str[1]; endreg r_rx_done; //延迟一个时钟周期,当rx_done检测为1时,并没有做到8个字节全赋值,所以可以使用延迟的方法always@(posedge Clk)r_rx_done <= rx_done;always@(posedge Clk or negedge Reset_n)if(!Reset_n) beginctrl <= #1 0;time_set <= #1 0;end else if(r_rx_done)beginif((data_str[0] == 8'h55) && (data_str[1] == 8'hA5) && (data_str[7] == 8'hF0))begintime_set[7:0] <= #1 data_str[2];time_set[15:8] <= #1 data_str[3];time_set[23:16] <= #1 data_str[4];time_set[31:24] <= #1 data_str[5];ctrl <= #1 data_str[6];endend endmodule
将上述代码注释处删除后会出现:
时间延迟后,可以明显看出在时钟上升沿,rx_done=1时,还没有完全赋值。于是让rx_done晚一拍。
四、uart_byte_rx模块
代码如下(示例):
`timescale 1ns/1nsmodule uart_byte_rx(Clk,Reset_n,Baud_Set,uart_rx,Data,Rx_Done,
);input Clk;input Reset_n;input [2:0]Baud_Set;input uart_rx;output reg[7:0]Data; output reg Rx_Done;reg [1:0]uart_rx_r;always@(posedge Clk)beginuart_rx_r[0] <= #1 uart_rx;uart_rx_r[1] <= #1 uart_rx_r[0] ;endwire pedge_uart_rx;
// assign pedge_uart_rx = ((uart_rx_r[1] == 0) && (uart_rx_r[0] == 1));assign pedge_uart_rx = (uart_rx_r == 2'b01);wire nedge_uart_rx;
// assign nedge_uart_rx = ((uart_rx_r[1] == 1) && (uart_rx_r[0] == 0)); assign nedge_uart_rx = (uart_rx_r == 2'b10); reg [8:0] Bps_DR;always@(*)case(Baud_Set)0:Bps_DR = 1000000000/9600/16/20 - 1;1:Bps_DR = 1000000000/19200/16/20 - 1;2:Bps_DR = 1000000000/38400/16/20 - 1;3:Bps_DR = 1000000000/57600/16/20 - 1;4:Bps_DR = 1000000000/115200/16/20 - 1;default:Bps_DR = 1000000000/9600/16/20 - 1;endcasewire bps_clk_16x;assign bps_clk_16x = (div_cnt == Bps_DR / 2);reg RX_EN;always@(posedge Clk or negedge Reset_n)if(!Reset_n) RX_EN <= 0;else if(nedge_uart_rx)RX_EN <= 1;else if(Rx_Done || (sta_bit >= 4))RX_EN <= 0;reg [8:0]div_cnt;always@(posedge Clk or negedge Reset_n)if(!Reset_n) div_cnt <= 0;else if(RX_EN)beginif(div_cnt == Bps_DR)div_cnt <= 0;elsediv_cnt <= div_cnt + 1'b1;endelsediv_cnt <= 0;reg [7:0]bps_cnt;always@(posedge Clk or negedge Reset_n)if(!Reset_n) bps_cnt <= 0;else if(RX_EN)beginif(bps_clk_16x)beginif(bps_cnt == 159)bps_cnt <= 0;elsebps_cnt <= bps_cnt + 1'b1;endelsebps_cnt <= bps_cnt;endelsebps_cnt <= 0;//reg width name number/depthreg[2:0]r_data[7:0];reg [2:0]sta_bit;reg [2:0]sto_bit;always@(posedge Clk or negedge Reset_n)if(!Reset_n) beginsta_bit <= 0;sto_bit <= 0;r_data[0] <= 0;r_data[1] <= 0;r_data[2] <= 0;r_data[3] <= 0;r_data[4] <= 0;r_data[5] <= 0;r_data[6] <= 0;r_data[7] <= 0;endelse if(bps_clk_16x)begincase(bps_cnt)0:beginsta_bit <= 0;sto_bit <= 0;r_data[0] <= 0;r_data[1] <= 0;r_data[2] <= 0;r_data[3] <= 0;r_data[4] <= 0;r_data[5] <= 0;r_data[6] <= 0;r_data[7] <= 0;end5,6,7,8,9,10,11:sta_bit <= #1 sta_bit + uart_rx;21,22,23,24,25,26,27: r_data[0] <= r_data[0] + uart_rx;37,38,39,40,41,42,43: r_data[1] <= r_data[1] + uart_rx;53,54,55,56,57,58,59: r_data[2] <= r_data[2] + uart_rx;69,70,71,72,73,74,75: r_data[3] <= r_data[3] + uart_rx;85,86,87,88,89,90,91: r_data[4] <= r_data[4] + uart_rx;101,102,103,104,105,106,107: r_data[5] <= r_data[5] + uart_rx;117,118,119,120,121,122,123: r_data[6] <= r_data[6] + uart_rx;133,134,135,136,137,138,139: r_data[7] <= r_data[7] + uart_rx;149,150,151,152,153,154,155: sto_bit <= sto_bit + uart_rx;default:;endcaseendalways@(posedge Clk or negedge Reset_n)if(!Reset_n) Data <= #1 0; else if(bps_clk_16x && (bps_cnt == 159))beginData[0] <= (r_data[0] >= 4)?1'b1:1'b0;Data[1] <= (r_data[1] >= 4)?1'b1:1'b0;Data[2] <= (r_data[2] >= 4)?1'b1:1'b0;Data[3] <= (r_data[3] >= 4)?1'b1:1'b0;Data[4] <= (r_data[4] >= 4)?1'b1:1'b0;Data[5] <= (r_data[5] >= 4)?1'b1:1'b0;Data[6] <= (r_data[6] >= 4)?1'b1:1'b0;Data[7] <= (r_data[7] >= 4)?1'b1:1'b0;end // always@(posedge Clk or negedge Reset_n)
// if(!Reset_n)
// Data <= #1 0;
// else if(bps_clk_16x && (bps_cnt == 159))begin
// Data[0] <= #1 r_data[0][2];
// Data[1] <= #1 r_data[1][2];
// Data[2] <= #1 r_data[2][2];
// Data[3] <= #1 r_data[3][2];
// Data[4] <= #1 r_data[4][2];
// Data[5] <= #1 r_data[5][2];
// Data[6] <= #1 r_data[6][2];
// Data[7] <= #1 r_data[7][2];
// end always@(posedge Clk or negedge Reset_n)if(!Reset_n) Rx_Done <= 0;else if((div_cnt == Bps_DR/2) && (bps_cnt == 159))Rx_Done <= 1;elseRx_Done <= 0; endmodule
五、counter_led_4模块
module counter_led_4(Clk,Reset_n,Ctrl,Time,Led
);input Clk;input Reset_n;input [7:0]Ctrl;input [31:0]Time;output reg Led;reg [31:0]counter;always@(posedge Clk or negedge Reset_n)if(!Reset_n)counter <= 0;else if(Time - 1 <= counter)counter <= 0;elsecounter <= counter + 1'b1;reg [2:0]counter2;always@(posedge Clk or negedge Reset_n)if(!Reset_n) counter2 <= 0; else if( Time - 1 <= counter)counter2 <= counter2 + 1'b1;always@(posedge Clk or negedge Reset_n)if(!Reset_n)Led <= 0;else case(counter2)0:Led <= Ctrl[0];1:Led <= Ctrl[1];2:Led <= Ctrl[2];3:Led <= Ctrl[3];4:Led <= Ctrl[4];5:Led <= Ctrl[5];6:Led <= Ctrl[6];7:Led <= Ctrl[7];default:Led <= Led;endcaseendmodule
【注】:上述代码中写成Time - 1 <= counter而不是counter = Time - 1的原因:
上电的时候, counter ==0-1发现不满足,就 couter 一直自加,直到自加到32’ hFFFFFFFFFFFFFFFF '才会清零。←由于实际板级运行的时候,当我们的 time 值更新时(25000000), counter 的值已经大于该值,所以无法通过计数比较的方式清零, 能一直自加下去,直到32位计满了,溢出清零,然后才能正常的循环技术清零。于是写成Time - 1 <= counter能解决这个问题,且这样写利大于弊。
【附件:】链接:https://pan.baidu.com/s/1nIBn_n_6DYezlvRN4OfkCQ?pwd=h7ub
提取码:h7ub
串口接收-控制LED闪烁(VerilogVivado)相关推荐
- STM32通过串口控制LED闪烁或者呼吸效果
STM32通过串口控制LED闪烁或者呼吸效果 目录 STM32通过串口控制LED闪烁或者呼吸效果 1.准备工作 2.思路分析 3.实际操作 4.小结 1.准备工作 1.首先我们需要准备32的最小系统板 ...
- STM32F103C8T6串口控制LED闪烁
任务:开启时单片机回复已打开:关闭时在电脑显示已关闭:发 送 1 打开:发送 2 关闭. 本次学习是基于STM32的通用定时器结合串口,进行对LED灯闪烁的控制,使得延时函数时带来的误差性,以及消耗大 ...
- ZigBee-CC2530单片机 - 实现计算机串口通讯控制LED发光二极管
ZigBee-CC2530单片机 - 实现计算机串口通讯控制LED发光二极管 程序源码 /******************************************************* ...
- 嵌入式STM32入门之定时器控制LED闪烁与产生PWM脉冲宽度调制信号
定时器控制LED闪烁与产生PWM脉冲宽度调制信号 一.前言 二.定时器基本介绍 (一)STM32定时器 (二)通用定时器主要功能 (三)计数器模式 (四)定时器工作原理 三.实验(1)初识定时器 (一 ...
- NE555时基电路实验(二)NE555控制LED闪烁
NE555时基电路实验(二)NE555控制LED闪烁 实验用到的元器件: 1.NE555芯片,1片: 2. 470uF.100uF电解电容,耐压值16v,1个: 3.发光 LED,2个: 4.10千欧 ...
- 定时器中断控制LED闪烁(每隔1s)---普中科技开发仪
定时器中断o,利用中断控制LED闪烁每隔1s闪烁一次(精确的1s) notes: (1)工作方式寄存器TMOD ,低四位用于To,高四位用于T1,(GATE,C/T- ,M1,M0) 一般让GATE ...
- 用定时器T0查询方式P0口8位控制LED闪烁
#include<reg52.h> #define uchar unsigned char #define uint unsigned int void main (void) { uch ...
- STM32实现定时器控制LED闪烁
文章目录 一.定时器介绍 二.STM32C配置项目 1.新建工程 2.配置引脚 3.配置SYS 4.配置GPIO 5.配置定时器 6.配置中断 7.时钟配置 8.创建代码 三.修改keil代码 四.实 ...
- CC2530控制LED闪烁
CC2530控制LED闪烁 CC2530 控制LED闪烁 电路图 知识点 CC2530有两个高频时钟 输入输出配置 程序及注释 CC2530 控制LED闪烁 CC2530拥有21个数字I/O(输入/输 ...
最新文章
- 手把手教你 用C++实现一个 可持久化 的http_server
- 密码6-12位数字和字母组成
- linux安装软件测试报告,软件测试实习报告
- 第五天 断点续传和下载
- 明显调用的表达式前的括号必须具有函数类型_Chisel(二) Scala语法 变量与函数...
- django mysql filter_Django filter中用contains 在mysql中的问题
- staruml java_非常详细的StarUML使用教程,推荐阅读!
- C/C++ sizeof(上)
- Python读取系统文件夹内所有文件并统计数量
- mysql和mybaits自增长序列详解_MyBatis Oracle 自增序列的实现方法
- HTTPHEAD中referer应用
- 写论文时遇到的问题及解决办法
- C --cp2 类似cat工具的小程序。
- HDLBITS笔记15:组合逻辑之7420芯片
- Python:实现jaccard similarity相似度无平方因子数算法(附完整源码)
- 微信公众平台编辑模式
- 乐高大颗粒作品14:导弹发射车
- 计算机毕业设计SSM电影售票管理系统【附源码数据库】
- UDP 实现多收多发,广播发送,组播发送 TCP 实现多收多发
- Cache的地址结构,tag到底与Cache什么关系,Cache容量与总容量,Cache行长,Cache字地址?
热门文章
- 关于编程,程序员的一些语录
- sqlzoo刷题(部分题目含解题思路)
- IDC发布视频云市场报告:公有云厂商占主导,腾讯云持续领跑
- 【数据结构功法】第1话 · 数据结构入门竟如此简单?
- #### 标题关于Quartus Ⅱ启动ModelSim仿真软件时提示Can't lauch the ModelSim的问题
- Android 13(targetSdkVersion:33)必需添加com.google.android.gms.permission.AD_ID
- 一般的在线教育平台需具备哪些功能?
- js中两个感叹号的作用
- 工具及方法 - 安装播放器pot player
- vue阻止穿透滚动穿透