1 2 3 4 5 6 7 8 9 10 11 | always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin flag_add <= 0 ; end else if(rx_uart_ff1==0 && rx_uart_ff2==1)begin flag_add <= 1 ; end else if(end_cnt1)begin flag_add <= 0 ; end end |
设计下data信号,该信号的值来自于图中第2~第9比特的值。第2比特的值赋给data[0],第3比特的值赋给data[1],以此类推,第9比特的值赋给data[7]。
图 215 由于每一个比特都持续5208个时钟周期,我们必须选定一个时刻,将值赋给data。 图 216 首先,不能在end_cnt0的时候赋值,如上图的点。因为我们这里的5208个时钟周期是理想、估算的数值,实际上是非常有可能有偏差的。如果我们在end_cnt0的时候取值,就有可能采错。 最保险的做法是在中间点取值。这样,即使有比较多的偏差,都不会影响到采样的正确性。 图 217 综上所述,我们在cnt0数到一半时采到当前rx_uart的值赋给dout,其中第2比特赋给led[0],第3比特赋给led[1],以此类推,第9比特赋给led[7]。 进一步用信号表示,可翻译成:数到add_cnt0 && cnt0==5208/2 -1时,如果cnt1==1,则将rx_uart_ff1赋给led[0]。如果cnt1==2,则将rx_uart_ff1赋给led[1],以此类推,如果cnt1==8,将rx_uart_ff1赋给led[7]。 那么直接翻译成代码。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin led <= 0 ; end else if(add_cnt0 && cnt0==5208/2-1)begin if(cnt1==1) led [0] <= rx_uart_ff1 ; else if(cnt1==2) led [1] <= rx_uart_ff1 ; else if(cnt1==3) led [2] <= rx_uart_ff1 ; else if(cnt1==4) led [3] <= rx_uart_ff1 ; else if(cnt1==5) led [4] <= rx_uart_ff1 ; else if(cnt1==6) led [5] <= rx_uart_ff1 ; else if(cnt1==7) led [6] <= rx_uart_ff1 ; else led [7] <= rx_uart_ff1 ; end end |
上面代码可优化,简写成如下: 1 2 3 4 5 6 7 8 | always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin led <= 0 ; end else if(add_cnt0 && cnt0==5208/2-1 && cnt1>=1 && cnt1<9)begin led [cnt1-1] <= rx_uart_ff1 ; end end |
通常我们设计时,首先是想到实现功能,所以会先写出前面代码。在功能实现的前提下,再考虑有没有优化空间,从而写出后面代码。好代码都是一步步优化出来的。 注意,上面代码,我们采集的是rx_uart_ff1而不是rx_uart信号。这是因为rx_uart是异步信号,我们只能用同步化后的信号,否则会引起亚稳态。所以只能是rx_uart_ff1。 至此,主体程序已经完成。接下来是将module补充完整。
3.3 信号定义cnt0是用always产生的信号,因此类型为reg。cnt0计数的最大值为5208,需要用13根线表示,即位宽是13位。因此代码如下: add_cnt0和end_cnt0都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1个线表示即可。因此代码如下: 1 2 | wire add_cnt0 ; wire end_cnt0 ; |
cnt1是用always产生的信号,因此类型为reg。cnt1计数的最大值为9,需要用4根线表示,即位宽是4位。因此代码如下: add_cnt1和end_cnt1都是用assign方式设计的,因此类型为wire。并且其值是0或者1,1根线表示即可。因此代码如下: 1 2 | wire add_cnt1; wire end_cnt1; |
flag_add是用always方式设计的,因此类型为reg。并且其值是0或者1,1根线表示即可。因此代码如下: rx_uart_ff0、rx_uart_ff1和rx_uart_ff2是用always方式设计的,因此类型为reg。并且其值是0或1,需要1根线表示即可。因此代码如下: 1 2 3 | reg rx_uart_ff0; reg rx_uart_ff1; reg rx_uart_ff2; |
至此,整个代码的设计工作已经完成。下一步是新建工程和上板查看现象。
4 综合工程和上板4.1 新建工程1. 首先在d盘中创建名为“uart”的工程文件夹,将写的代码命名为“uart.v”,顶层模块名为“uart”。 图 218 图 219 2. 然后打开Quartus Ⅱ,点击File下拉列表中的New Project Wzard...新建工程选项。 图 220
3.在出现的界面中直接点击最下方的“Next”。 图 221 4.之后出现的是工程文件夹、工程名、顶层模块名设置界面。按照之前的命名进行填写,第一栏选择工程文件夹“uart”,第二栏选择工程文件“uart.v”,最后一栏选择顶层模块名“uart”,然后点击”Next”,再出现的界面选择empty project。 图 222 图 223 5.之后是文件添加界面。在上方一栏中添加之前写的uart.v文件,点击右侧的“Add”按钮,之后文件还会出现在大方框中,之后点击“Next”。 图 224 6. 器件型号选择界面。在“Device family”处选择Cyclone ⅣE,在“Available devices”处选择EP4CE15F23C8,然后点击“Next”。 图 225 7. EDA工具界面。该页面用默认的就行,直接点击最下方“Next”。 图 226 8. 之后出现的界面是我们前面的设置的总结,确认没有错误后点击“Finish”。 图 227
4.2 综合1.新建工程步骤完成后,就会出现以下界面。在“Project Navigator”下选中要编译的文件,点击上方工具栏中“Start Compila tion”编译按钮(蓝色三角形)。 图 228 2.编译成功后会出现以下界面,点击“OK”。 图 229
4.3 配置管脚图 230 在菜单栏中,选中Assignments,然后选择Pin Planner,就会弹出配置管脚的窗口。 图 231 在配置窗口最下方中的location一列,参考下表中最右两列配置好 FPGA管脚。 器件 | 原理图信号 | FPGA管脚 | FPGA工程信号 | LED6 | LED1_NET | AA4 | led[0] | LED7 | LED2_NET | AB4 | led[1] | LED8 | LED3_NET | AA5 | led[2] | LED9 | LED4_NET | AB8 | led[3] | LED10 | LED5_NET | AA10 | led[4] | LED11 | LED6_NET | AB13 | led[5] | LED12 | LED7_NET | AB14 | led[6] | LED13 | LED8_NET | AB16 | led[7] | U11 | USB_RXD | D6 | rx_uart | X1 | SYS_CLK | G1 | clk | K1 | SYS_RST | AB12 | rst_n |
配置完成后,关闭Pin Planner,软件自动会保存管脚配置信息。
4.4 再次综合图 232 在菜单栏中,选中Processing,然后选择Start Compilation,再次对整个工程进行编译和综合。 图 233 出现上面的界面,就说明编译综合成功。
4.5 连接开发板图中,下载器接入电脑USB接口, 电源接入电源,uart线连接电脑USB,然后摁下电源开关,看到开发板灯亮。 图 234 4.6 上板1.双击Tasks一栏中”Program Device”。 图 235 2.会出现如下界面,点击add file添加.sof文件,在右侧点击“Start”,会在上方的“Progress”处显示进度。 图 236 3.进度条中提示成功后,即可在显示器上观察到相应的现象。
4.7 串口调试1.安装串口调试工具。 图 237 2.开发板连接完成,打开电源后,在设备管理器中查看串口名。 图 238 3.打开串口调试助手,在串口处选择之前查看的串口名,波特率、校验位、数据位、停止位根据之前给出的数据进行填写,发送和接收都选择十六进制。 图 239 4.上板成功之后,在发送数据栏输入相应的数据(将8个LED灯对应的8位二进制数转化为十六进制),然后发送,即可在开发板上看到相应的现象。
|