文章使用的工具及板子类型
工具:Quartus II 18.0
开发板:Cyclone IV E EP4CE115F29C7
一、Nios II软核实现流水灯
(一)硬件部分设计
1.完成基本的硬件部分设计
请先参考下面链接中的硬件部分设计,完成相应的硬件部分设计
https://blog.csdn.net/qq_43279579/article/details/115933154
2.添加其他IP核
添加PIO
在搜索框上输入pio,选择PIO(Parallel I/O),点击Add
设置宽度(这里我设置为4,表示4个灯的流水灯),默认为8,其他保持默认设置
3.连接时钟和复位
4.设置led的输出
5.系统分配地址
选择System->Assign Base Address
6.使用FPGA资源
选择Generate->Generate,保持默认设置,点击Generate,选择Save
7.创建顶层文件
回到Quarters,选择New->Verilog HDL File
顶层文件内容
module hello_world(
input clk,
input reset_n,
output [7:0] led
);
system_qsys u0 (
.clk_clk (clk), // clk.clk
.reset_reset_n (reset_n), // reset.reset_n
.led_export (led) // led.export
);
endmodule
保存文件,并编译
8.芯片引脚设置
菜单里选择 Assignments-device,点击 Device pin options
进行 unused pin 设置,可能会收到外部信号的干扰,将未用引脚设置为 As input tri-stated
特殊引脚设置,设置为常规引脚
9.编译完成后,分配管脚
(二)软件设计
1.基本软件设计过程
请参考下面链接中,软件设计的过程,完成文件的创建
https://blog.csdn.net/qq_43279579/article/details/115933154
2.修改代码文件
打开hello_world中的.c文件
内容如下
#include "system.h"
#include "altera_avalon_pio_regs.h"
#include "alt_types.h"
#include "stdio.h"
const alt_u8
led_data[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
int main (void) {
int count=0;
alt_u8 led;
volatile int i;
while (1)
{
if (count==7)
{count=0;}
else
{count++;}
led=led_data[count];
IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, led);
i = 0;
printf("Hello Nios-II\n");
while (i<500000)
i++;
}
return 0;
}
3.保存编译
点击保存,选择hello_wold_bsp,右键后,选择Nios II中的Generate BSP,编译应用文件,选择hello_world右键后,点击Build Project
(三)下载硬件和软件
1.硬件的下载
2.软件的下载
在Nios II - Eclipse中
(四)实验结果
二、Verilog实现流水灯
编译烧录以下代码即可
module horse_led#(parameter TIME_1500MS = 75_000_000)(
input clk ,
input rst_n ,
output reg[3:0] led
);
//1.5s计数器
reg [26:0] cnt ;
wire add_cnt ;
wire end_cnt ;
reg [1:0] cnt1 ;
wire add_cnt1;
wire end_cnt1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 27'b0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt <= 27'b0;
end
else begin
cnt <= cnt + 1'b1;
end
end
else begin
cnt <= cnt;
end
end
assign add_cnt = 1'b1;
assign end_cnt = add_cnt && cnt == TIME_1500MS - 1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 2'b0;
end
else if(add_cnt1)begin
if(end_cnt1)begin
cnt1 <= 2'b0;
end
else begin
cnt1 <= cnt1 + 1'b1;
end
end
else begin
cnt1 <= cnt1;
end
end
assign add_cnt1 = end_cnt;
assign end_cnt1 = add_cnt1 && add_cnt == 3;
//跑马灯
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
led <= 4'b0001;
end
else if(end_cnt)begin
led <= {led[2:0],~led[3]};
end
else begin
led <= led;
end
end
endmodule
三、Nios II软核实现UART通信
(一)硬件设计部分
1.添加其他IP核
添加UART
在搜索框上输入uart,选择UART(RS-232 Serial Port),点击Add
进行数据设置
连接时钟和复位,以及数据位,中断的设置,Export设置
2.系统分配地址
选择System->Assign Base Address
3.使用FPGA资源
选择Generate->Generate,保持默认设置,点击Generate,选择Save
4.创建顶层文件
回到Quarters,选择New->Verilog HDL File
顶层文件内容
module uart(
input clk,
input reset_n,
//uart的接收和发送端
input rxd,//接收
output txd//发送
);
system_qsys u0 (
.clk_clk (clk), // clk.clk
.reset_reset_n (reset_n), // reset.reset_n
.uart_rxd (rxd), // uart.rxd
.uart_txd (txd) // .txd
);
endmodule
保存文件,并编译
5.芯片引脚设置
菜单里选择 Assignments-device,点击 Device pin options
进行 unused pin 设置,可能会收到外部信号的干扰,将未用引脚设置为 As input tri-stated,特殊引脚设置,设置为常规引脚
6.编译完成后,分配管脚
再次编译
(二)软件设计
1.修改代码文件
打开uart中的hello_world.c文件
内容如下
#include <stdio.h>
#include "unistd.h"
#include "system.h"
#include "alt_types.h"
#include "altera_avalon_uart_regs.h"
#include "sys\alt_irq.h"
alt_u8 txdata=0;
alt_u8 rxdata=0;
//UART中断服务函数
void IRQ_UART_Interrupts(){
rxdata = IORD_ALTERA_AVALON_UART_RXDATA(UART_BASE);//将rxdata寄存器中存储的值读入变量rxdata中
txdata = rxdata;//串口自收发,将变量rxdata的值赋给txdata
while(!(IORD_ALTERA_AVALON_UART_STATUS(UART_BASE)& ALTERA_AVALON_UART_STATUS_TRDY_MSK));
//查询发送准备接收信号,如果没有准备好,则等待
IOWR_ALTERA_AVALON_UART_TXDATA(UART_BASE,txdata);//发送准备好,发送txdata
}
//中断初始化函数
void IRQ_init()
{
//清除状态寄存器
IOWR_ALTERA_AVALON_UART_STATUS(UART_BASE, 0);
//使能接收准备中断,给控制寄存器相应位写1
IORD_ALTERA_AVALON_UART_CONTROL(UART_BASE);
alt_ic_isr_register(
UART_IRQ_INTERRUPT_CONTROLLER_ID,//注册ISR
UART_IRQ,//中断控制器标号,从system.h复制
IRQ_UART_Interrupts,//UART中断服务函数
0x0,//指向与设备驱动实例相关的数据结构体
0x0);//flags,保留未用
}
int main()
{
/*while(1){
IOWR_ALTERA_AVALON_UART_TXDATA(UART_BASE, "hello world!\n");
int i=0;
while(i<5000)
{
i++;
}
}*/
IRQ_init();
while(1);
return 0;
}
2.保存编译
点击保存,选择uart_bsp,右键后,选择Nios II中的Generate BSP,编译应用文件,选择uart右键后,点击Build Project
(三)下载硬件和软件
1.硬件下载
2.软件下载
四、Verilog实现UART通信
编译烧录以下代码即可
`timescale 1ns/1ns
module rs232
(
input wire sys_clk , //系统时钟50MHz
input wire sys_rst_n , //全局复位
input wire rx , //串口接收数据
output wire tx //串口发送数据
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter UART_BPS = 20'd9600 , //比特率
CLK_FREQ = 26'd50_000_000 ; //时钟频率
localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS ;
//wire define
wire en_h_flag;
wire [7:0] po_data; //接收的数据
wire po_flag; //接收完1字节数据标志位,高电平有效
wire flag; //识别到接收数据与密码对应标志位
wire tx_flag; //发送完1字节数据标志位,高电平有效
reg [39:0] datain_reg; //存储接收的数据,5字节
reg [47:0] dataout_reg;//存储的要发送的数据,6字节
reg [1:0] state; //状态位
reg [7:0] data_tx; //发送的1字节数据
reg en_tx; //发送允许标志位
reg [2:0] tx_cnt; //发送字节计数器,发送6个后置0
reg en; //发送控制开关
reg [12:0] baud_cnt; //收到发送成功的tx_flag后延迟1个波特
reg bit_flag; //计满1baud有效
reg work; //波特计数器baud_cnt有效
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------------------ uart_rx_inst ------------------------
uart_rx
#(
.UART_BPS (UART_BPS ), //串口波特率
.CLK_FREQ (CLK_FREQ ) //时钟频率
)
uart_rx_inst
(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.rx (rx ), //input rx
.po_data (po_data ), //output [7:0] po_data
.po_flag (po_flag ) //output po_flag
);
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
en <= 1'b1;
else if(en_h_flag)
en <= 1'b1;
else if(tx_cnt>=3'd5)
en <= 1'b0;
//接收数据寄存
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
datain_reg <= 40'd0;
else if(po_flag)
datain_reg <= {datain_reg[31:0],po_data[7:0]};
//接收到tx_flag后,延迟一个baud时间再发送下一个
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
work <= 1'b0;
else if(tx_flag)
work <= 1'b1;
else if(state != 2'd2)
work <= 1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
baud_cnt <= 13'd0;
else if((baud_cnt == BAUD_CNT_MAX - 1) || en_tx)
baud_cnt <= 13'b0;
else if(work)
baud_cnt <= baud_cnt + 1'd1;
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
bit_flag <= 1'b0;
else if(baud_cnt == BAUD_CNT_MAX - 1)
bit_flag <= 1'b1;
else if(state != 2'd2)
bit_flag <= 1'b0;
//hello的ASCII码
assign flag = (datain_reg == 40'h68656c6c6f)? 1'b1:1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
begin
state <= 2'd0;
dataout_reg <= 48'h6e692c68616f;//ni,hao的ASCII码
data_tx <= 8'd0;
en_tx <= 1'b0;
tx_cnt <= 3'd0;
end
else
case(state)
2'd0:
begin
if(flag && en)
state <= 2'd1;
else
state <= 2'd0;
end
2'd1://发送数据
begin
state <= 2'd2;
data_tx <= dataout_reg[47:40];
en_tx <= 1'b1;
dataout_reg <= dataout_reg << 8;
end
2'd2://等待数据发送完成,并计数+1
begin
if(bit_flag)
begin
if(tx_cnt>=3'd5)begin
state <= 2'd0;
tx_cnt <= 3'd0;
end
else begin
state <= 2'd1;
tx_cnt <= tx_cnt + 1'd1;
end
end
else
begin
en_tx <= 1'b0;
state <= 2'd2;
end
end
default : state <= 2'd0;
endcase
//------------------------ uart_tx_inst ------------------------
uart_tx
#(
.UART_BPS (UART_BPS ), //串口波特率
.CLK_FREQ (CLK_FREQ ) //时钟频率
)
uart_tx_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.pi_data (data_tx ),
.pi_flag (en_tx ),
.tx (tx ),
.tx_flag (tx_flag )
);
endmodule