UART控制器及驱动的实现
verilog新手一枚~本文讲述uart控制器及驱动的实现,并在zynq7020平台进行实验验证。文中附带verilog实现的uart控制器全部源码,附带linux驱动关键部分源码
开发环境
开发板 | ZYNQ PI extend R2 2022A版 (zynq7020) |
---|---|
开发主机 | Win11 and ubuntu20.04 |
IDE版本 | Xilinx Vitis IDE v2021.1.0 (64-bit) Vivado v2021.1 (64-bit) |
uboot源码 | Xilinx官方 |
linux源码 | Xilinx官方 |
使用verilog实现UART控制器
UART原理
- 8位数据,起始位低,结束位高
- 在中间稳定位置进行数据采样
- 检测到下降沿记作起始开始
发送模块实现
设计状态机如下:
- counter0为波特率计数器
- counter1为发送位数计数器
- 检测到send_go上升沿,开始发送数据
- 第一个always块中进行状态切换
- 第二个always块中执行状态动作
module uart_send_byte(
input wire rstn, /* 复位 */
input wire clk, /* 时钟 */
input wire[19:0] baud_rate, /* 波特率 */
input wire send_go, /* 开始发送 */
input wire[7:0] byte, /* 发送字节 */
output reg tx, /* tx信号 */
output reg tx_down /* 发送完成 */
);
parameter SYS_CLK = 100_000_000;
parameter MCNT1 = 8;
parameter STATE_IDLE = 2'b00; /* 空闲状态 */
parameter STATE_START = 2'b01; /* 发送起始位 */
parameter STATE_DATA = 2'b10; /* 发送数据 */
parameter STATE_STOP = 2'b11; /* 发送结束位 */
reg[31:0] counter0; /* 波特率计数 */
reg[3:0] counter1; /* 字节计数 */
reg[1:0] state; /* 状态 */
initial begin
counter0 <= 0;
counter1 <= 0;
state <= STATE_IDLE;
tx <= 1;
end
reg en_data0;
reg en_data1;
wire en_posedge;
/* 检测send_go信号 */
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
en_data0 <= 0;
en_data1 <= 0;
end
else begin
en_data0 <= send_go;
en_data1 <= en_data0;
end
end
assign en_posedge = en_data0 & (~en_data1);
/* 波特率计数器 */
always @(posedge clk or negedge rstn) begin
if (!rstn)
counter0 <= 0;
else if(counter0 == (SYS_CLK / baud_rate) - 1)
counter0 <= 0;
else if (state == STATE_IDLE)
counter0 <= 0;
else
counter0 <= counter0 + 1;
end
/* 发送位计数器 */
always @(posedge clk or negedge rstn) begin
if (!rstn)
counter1 <= 0;
else if (counter0 == (SYS_CLK / baud_rate) - 1) begin
if(counter1 == MCNT1 - 1)
counter1 <= 0;
else if (state == STATE_DATA)
counter1 <= counter1 + 1;
else
counter1 <= 0;
end
else
counter1 <= counter1;
end
/* 切换状态 */
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
state <= STATE_IDLE;
tx_down <= 1;
end
else begin
case (state)
STATE_IDLE:
if (en_posedge) begin
tx_down <= 0;
state <= STATE_START;
end
STATE_START: begin
if (counter0 == (SYS_CLK / baud_rate) - 1)
state <= STATE_DATA;
end
STATE_DATA:
if (counter1 == MCNT1 - 1 && counter0== (SYS_CLK / baud_rate) - 1)
state <= STATE_STOP;
STATE_STOP:
if (counter0 == (SYS_CLK / baud_rate) - 1) begin
state <= STATE_IDLE;
tx_down <= 1;
end
endcase
end
end
/* 执行状态 */
always @(posedge clk or negedge rstn) begin
if (!rstn)
tx <= 1;
else
case (state)
STATE_IDLE:
tx <= 1;
STATE_START: begin
tx <= 0;
end
STATE_DATA:
tx <= byte[counter1];
STATE_STOP: begin
tx <= 1;
end
endcase
end
endmodule
接收模块实现
设计状态机如下:
- counter0为波特率计数器
- counter1为接收字节计数器
- DETECT_START状态下,检测到下降沿进入START_CHECK
- START_CHECK成功进行READ_DATA,否则继续DETECT_START
- READ_DATA完毕进入STOP_CHECK
- STOP_CHECK成功拉高down,否则拉高err
/* 对于强干扰的环境,UART的接收需要进行16倍过采样;取中间789三次结果,两次以上为1则为1,两次以上为0则为0
* 异步信号(使用两个时钟)容易出现亚稳态:
* 什么是亚稳态:D触发器输入信号在数据窗口期内发生变化,会导致输出进入亚稳态,可能发送震荡,并最终随机稳定在高电平或者低电平
* 亚稳态的存在可能会影响其他逻辑对该信号值的判断
* 通过2级或者更多的D触发器打拍的方式,降低亚稳态的传播,让后续逻辑使用的该信号正常
* 100MHz时钟时,第一级D触发器有出现亚稳态的可能,第二级D触发器出现亚稳态的概率很小,第三级D触发器出现亚稳态概率非常小
*/
module uart_read_byte(
input wire rstn, /* 复位 */
input wire clk, /* 时钟 */
input wire rx, /* rx信号 */
input wire[19:0] baud_rate, /* 波特率 */
output reg rx_down, /* rx一字节完成 */
output reg[7:0] data, /* 接收的数据 */
output reg err /* 接收错误 */
);
parameter SYS_CLK = 100_000_000;
parameter MCNT1 = 8;
parameter STATE_DETECT_START = 0; /* 检测起始位下降沿 */
parameter STATE_START_CHECK = 1; /* 校验起始位电平是否正确 */
parameter STATE_READ_DATA = 2; /* 读取数据 */
parameter STATE_STOP_CHECK = 3; /* 检验结束位 */
reg[31:0] counter0; /* 波特率计数 */
reg[2:0] counter1; /* 接收字节计数 */
reg[1:0] state; /* 接受状态 */
reg[7:0] temp_data; /* 存储接收数据 */
reg rx_temp; /* 存储rx的值 */
/* 波特率计数 */
always @(posedge clk or negedge rstn) begin
if (!rstn)
counter0 <= 0;
else if (state == STATE_DETECT_START)
counter0 <= 0;
else if (counter0 == (SYS_CLK / baud_rate) - 1)
counter0 <= 0;
else
counter0 <= counter0 + 1;
end
/* 接收位计数 */
always @(posedge clk or negedge rstn) begin
if (!rstn)
counter1 <= 0;
else if (state == STATE_READ_DATA)
if (counter0 == (SYS_CLK / baud_rate) - 1)
if (counter1 == MCNT1 - 1)
counter1 <= 0;
else
counter1 <= counter1 + 1;
else
counter1 <= counter1;
else
counter1 <= 0;
end
reg[2:0] start_data;
wire start_negedge;
/* 检测下降沿起始位,打2拍防止亚稳态 */
always @(posedge clk or negedge rstn) begin
if (!rstn)
start_data <= 0;
else
if (state == STATE_DETECT_START) begin
start_data[0] <= rx;
start_data[2:1] <= start_data[1:0];
end
else begin
start_data <= 0;
end
end
assign start_negedge = start_data[2] & (~start_data[1]);
/* 状态判断 */
always @(posedge clk or negedge rstn) begin
/* rstn中尽量减少非必须的复位赋值,有利于综合布线 */
if (!rstn)
state <= STATE_DETECT_START;
else
case (state)
STATE_DETECT_START:
if (start_negedge)
state <= STATE_START_CHECK;
STATE_START_CHECK:
if (counter0 == (SYS_CLK / baud_rate) / 2)
rx_temp <= rx;
else if (counter0 == (SYS_CLK / baud_rate) - 1) begin
if (rx_temp == 0)
state <= STATE_READ_DATA;
else
state <= STATE_DETECT_START;
end
else
state <= STATE_START_CHECK;
STATE_READ_DATA:
if (counter1 == MCNT1 - 1 && counter0 == (SYS_CLK / baud_rate) - 1)
state <= STATE_STOP_CHECK;
STATE_STOP_CHECK:
if (counter0 == (SYS_CLK / baud_rate) / 2)
state <= STATE_DETECT_START;
endcase
end
/* 状态执行 */
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
rx_down <= 0;
data <= 0;
err <= 0;
end
else
case (state)
STATE_DETECT_START:
temp_data <= 0;
STATE_START_CHECK: begin
rx_down <= 0;
err <= 0;
end
STATE_READ_DATA:
if (counter0 == (SYS_CLK / baud_rate) / 2)
temp_data[counter1] <= rx;
STATE_STOP_CHECK:
/* 停止位只使用一半的时间,提前进行检测起始位,防止因为频率误差导致起始位未检测到 */
if (counter0 == (SYS_CLK / baud_rate) / 2) begin
err <= rx ? 0 : 1;
rx_down <= 1;
data <= temp_data;
end
endcase
end
endmodule
增加接收fifo
- 增加位宽8,深度32的fifo
- uart_read_byte的rx_down上升沿时将数据写入
module uart_read_fifo(
input rstn,
input clk,
input en,
input wire[19:0] baud_rate, /* 波特率 */
input fifo_rd_en,
output wire[7:0] rx_date,
input rx,
output wire[4:0] rx_fifo_count,
output wire[3:0] rx_fifo_state,
output rx_ack
);
wire rx_down;
wire rx_err;
wire[7:0] rx_date_temp;
/* 检测rx_down上升沿 */
reg[1:0] rx_down_data;
wire read_posedge;
always @(negedge clk or negedge rstn) begin
if (!rstn)
rx_down_data <= 0;
else begin
rx_down_data[0] <= rx_down;
rx_down_data[1] <= rx_down_data[0];
end
end
assign read_posedge = rx_down_data[0] & (~rx_down_data[1]);
fifo_generator fifo_generator_uart_rx(
.clk(clk),
.srst(~rstn),
.din(rx_date_temp),
.wr_en(read_posedge && (~rx_err) && en),
.rd_en(fifo_rd_en),
.dout(rx_date),
.full(rx_fifo_state[3]),
.almost_full(rx_fifo_state[2]),
.wr_ack(rx_ack),
.empty(rx_fifo_state[1]),
.almost_empty(rx_fifo_state[0]),
.data_count(rx_fifo_count)
);
uart_read_byte uart_read_byte_obj0(
.rstn(rstn),
.clk(clk),
.rx(rx),
.baud_rate(baud_rate),
.rx_down(rx_down),
.data(rx_date_temp),
.err(rx_err)
);
endmodule
增加发送fifo
- 增加位宽8,深度32的fifo
- 通过状态机,将fifo中的数据通过uart_send_byte模块发送
module uart_send_fifo(
input rstn,
input clk,
input en,
input wire[19:0] baud_rate, /* 波特率 */
input fifo_wr_en,
input wire[7:0] tx_date,
output tx,
output wire[4:0] tx_fifo_count,
output wire[3:0] tx_fifo_state
);
parameter STATE_WAIT_DATA = 0; /* 等待FIFO中有数据 */
parameter STATE_START_SEND = 1; /* 开始发送数据 */
parameter STATE_WAIT_SEND = 2; /* 等待发送完成 */
parameter STATE_READ_FIFO = 3; /* 读FIFO中的数据 */
reg[1:0] state;
wire tx_down;
reg rd_en_reg;
reg sd_go_reg;
wire[7:0] dout;
/* 判断状态 */
always @(posedge clk or negedge rstn) begin
if (!rstn)
state <= STATE_WAIT_DATA;
else begin
case (state)
STATE_WAIT_DATA: begin
/* fifo不为空 */
if (tx_fifo_state[1] == 0)
state <= STATE_READ_FIFO;
end
STATE_READ_FIFO:
state <= STATE_START_SEND;
STATE_START_SEND:
if (tx_down == 0)
state <= STATE_WAIT_SEND;
STATE_WAIT_SEND:
if (tx_down == 1)
state <= STATE_WAIT_DATA;
endcase
end
end
/* 执行状态 */
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
rd_en_reg <= 0;
sd_go_reg <= 0;
end
else begin
case (state)
STATE_WAIT_DATA:
;
STATE_READ_FIFO:
rd_en_reg <= 1;
STATE_START_SEND: begin
rd_en_reg <= 0;
sd_go_reg <= 1;
end
STATE_WAIT_SEND: begin
sd_go_reg <= 0;
rd_en_reg <= 0;
end
endcase
end
end
fifo_generator fifo_generator_uart_tx(
.clk(clk),
.srst(~rstn),
.din(tx_date),
.wr_en(fifo_wr_en && en),
.rd_en(rd_en_reg),
.dout(dout),
.full(tx_fifo_state[3]),
.almost_full(tx_fifo_state[2]),
.empty(tx_fifo_state[1]),
.almost_empty(tx_fifo_state[0]),
.data_count(tx_fifo_count)
);
uart_send_byte uart_send_byte_obj0(
.rstn(rstn),
.clk(clk),
.baud_rate(baud_rate),
.send_go(sd_go_reg),
.byte(dout),
.tx(tx),
.tx_down(tx_down)
);
endmodule
优化中断
- 为减少CPU中断开销,当串口接收到多个包,只上报一次中断
- 包个数不够产生中断时,上报超时中断
module interrupt(
input rstn,
input clk,
input pkg_ack,
input[14:0] item_num,
input[15:0] time_out,
input period,
output irq
);
parameter SYS_CLK = 100_000_000;
reg[14:0] counter0;
reg[31:0] counter1;
reg irq_reg;
/* pkg_ack计数 */
always @(negedge clk or negedge rstn) begin
if (!rstn)
counter0 <= 0;
else if (counter1 == ((SYS_CLK / 1_000_000) * time_out) - 1 || counter0 == item_num - 1)
counter0 <= 0;
else if (pkg_ack)
counter0 <= counter0 + 1;
else
counter0 <= counter0;
end
/* 定时器,控制timeout */
always @(posedge clk or negedge rstn) begin
if (!rstn)
counter1 <= 0;
else if (counter0 || period) begin
if (counter1 == ((SYS_CLK / 1_000_000) * time_out) - 1 || counter0 == item_num - 1)
counter1 <= 0;
else
counter1 <= counter1 + 1;
end
else
counter1 <= 0;
end
/* 输出中断 */
always @(posedge clk) begin
if (counter1 == ((SYS_CLK / 1_000_000) * time_out) - 1 || counter0 == item_num - 1) begin
irq_reg <= 1;
end
else
irq_reg <= 0;
end
assign irq = irq_reg;
endmodule
使用Xillybus Lite连接PL & PS
Xillybus Lite 是一个简单的套件,用于通过在 Linux 下运行的 user space 程序轻松访问 logic fabric ( PL ) 中的 registers
Xillybus IP使用
Xillybus Lite读写时序如下:
Xillybus Lite连接如下:
xillybus读写及寄存器定义
- 增加一个程序版本寄存器,方便核对程序版本
- uart控制器寄存器依次为:
- 0x00 串口使能寄存器
- 0x04 串口读写控制寄存器
- 0x08 串口数据寄存器
- 0x0c 串口发送波特率寄存器
- 0x10 串口发送fifo数据个数寄存器
- 0x14 串口发送fifo状态寄存器
- 0x18 串口接收波特率寄存器
- 0x1c 串口接收fifo数据个数寄存器
- 0x20 串口接收fifo状态寄存器
- 0x24 串口中断控制寄存器
module xillybus_rw(
input wire rstn,
input wire user_clk,
input wire [31:0]user_addr,
input wire [31:0]user_wren,
input wire user_rden,
output reg [31:0]user_rd_data,
input wire [31:0]user_wr_data,
input wire [3:0]user_wstrb,
output wire uart_tx,
input wire uart_rx,
output wire[7:0] led,
output wire irqf2p_0,
output wire irqf2p_1,
output wire irqf2p_10,
output wire irqf2p_11,
output wire irqf2p_12,
output wire irqf2p_13,
output wire irqf2p_14,
output wire irqf2p_15,
output wire irqf2p_2,
output wire irqf2p_3,
output wire irqf2p_4,
output wire irqf2p_5,
output wire irqf2p_6,
output wire irqf2p_7,
output wire irqf2p_8,
output wire irqf2p_9
);
/* OFFSET:0x00 版本信息 */
parameter VERSION = 32'h23121504; //程序版本
/* OFFSET:0x10 串口模块 */
reg uart_flag_tx_reg;
reg uart_flag_rx_reg;
reg[1:0] uart_en_reg; /* 00:0x00 串口使能寄存器 */
reg[31:0] uart_ctr_reg; /* 01:0x04 串口读写控制寄存器*/
reg[31:0] uart_data_reg; /* 02:0x08 串口数据寄存器 */
wire[31:0] uart_data_rx_wire;
reg[19:0] uart_tx_baud_rate; /* 03:0x0c 串口发送波特率寄存器 */
wire[31:0] fifo_count_tx_wire; /* 04:0x10 串口发送fifo数据个数寄存器 */
wire[31:0] uart_state_tx_wire; /* 05:0x14 串口发送fifo状态寄存器 */
reg[19:0] uart_rx_baud_rate; /* 06:0x18 串口接收波特率寄存器 */
wire[31:0] fifo_count_rx_wire; /* 07:0x1c 串口接收fifo数据个数寄存器 */
wire[31:0] uart_state_rx_wire; /* 08:0x20 串口接收fifo状态寄存器 */
reg[31:0] uart_int_ctr_reg; /* 09:0x24 串口中断控制寄存器 */
wire rx_pkg_ack;
always @(posedge user_clk) begin
if (!rstn) begin
uart_flag_rx_reg <= 0;
uart_flag_tx_reg <= 0;
uart_int_ctr_reg <= 32'h0010_03e8;
uart_tx_baud_rate <= 9600;
uart_rx_baud_rate <= 9600;
end
else begin
if (user_wren) begin
/* 15:0 共6MByte可访问 */
case (user_addr[15:2])
4:uart_en_reg <= user_wr_data;
5: begin
uart_ctr_reg <= user_wr_data;
if (user_wr_data & (1 << 31))
uart_flag_rx_reg <= 1;
else if (user_wr_data & (1 << 30))
uart_flag_tx_reg <= 1;
end
6:uart_data_reg <= user_wr_data;
7:uart_tx_baud_rate <= user_wr_data;
10:uart_rx_baud_rate <= user_wr_data;
13:uart_int_ctr_reg <= user_wr_data;
endcase
end
else begin
uart_flag_rx_reg <= 0;
uart_flag_tx_reg <= 0;
end
if (user_rden) begin
case (user_addr[15:2])
0:user_rd_data <= VERSION;
4:user_rd_data <= uart_en_reg;
5:user_rd_data <= uart_ctr_reg;
6: begin
user_rd_data <= uart_data_rx_wire;
end
7:user_rd_data <= uart_tx_baud_rate;
8:user_rd_data <= fifo_count_tx_wire;
9:user_rd_data <= uart_state_tx_wire;
10:user_rd_data <= uart_rx_baud_rate;
11:user_rd_data <= fifo_count_rx_wire;
12:user_rd_data <= uart_state_rx_wire;
13:user_rd_data <= uart_int_ctr_reg;
default: user_rd_data <= 0;
endcase
end
end
end
uart_send_fifo uart_send_fifo_obj0(
.rstn(rstn),
.clk(user_clk),
.en(uart_en_reg[0]),
.baud_rate(uart_tx_baud_rate),
.fifo_wr_en(uart_flag_tx_reg),
.tx_date(uart_data_reg),
.tx(uart_tx),
.tx_fifo_count(fifo_count_tx_wire),
.tx_fifo_state(uart_state_tx_wire)
);
uart_read_fifo uart_read_fifo_obj0(
.rstn(rstn),
.clk(user_clk),
.en(uart_en_reg[1]),
.baud_rate(uart_rx_baud_rate),
.fifo_rd_en(uart_flag_rx_reg),
.rx_date(uart_data_rx_wire),
.rx(uart_rx),
.rx_fifo_count(fifo_count_rx_wire),
.rx_fifo_state(uart_state_rx_wire),
.rx_ack(rx_pkg_ack)
);
interrupt interrupt_obj0(
.rstn(rstn),
.clk(user_clk),
.pkg_ack(rx_pkg_ack),
.item_num(uart_int_ctr_reg[30:16]),
.time_out(uart_int_ctr_reg[15:0]),
.period(uart_int_ctr_reg[31]),
.irq(irqf2p_0)
);
endmodule
linux下UART驱动编写
轮询发送
- 简单起见,uart控制器未设计发送中断,所以使用轮询发送
static void uart_start_tx(struct uart_port *port)
{
struct dev_priv *priv;
struct peng_dev *p_pdev;
struct circ_buf *xmit;
struct tty_port *tport;
u32 state, n;
priv = container_of(port, struct dev_priv, port);
p_pdev = &priv->pdev;
tport = &port->state->port;
xmit = &port->state->xmit;
dev_dbg(priv->pdev.p_dev, "uart_start_tx\n");
if (uart_tx_stopped(port))
return;
if (uart_circ_empty(&port->state->xmit))
return;
n = PDEV_CSR_READ(p_pdev, PREG_REG, UART_TX_FIFO_COUNT);
state = PDEV_CSR_READ(p_pdev, PREG_REG, UART_TX_FIFO_STATE);
/* UART_TX_FIFO_COUNT满或空时都为0,需要根据UART_TX_FIFO_STATE来判断是否满 */
if (state & UART_FIFO_STATE_FULL)
return;
n = UART_FIFO_SIZE - n;
while (n) {
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
dev_dbg(p_pdev->p_dev, "uart_circ_empty\n");
/* 发送完成 */
break;
} else {
dev_dbg(p_pdev->p_dev, "T:0x%02x", xmit->buf[xmit->tail]);
priv->txBytCnt++;
n--;
spin_lock(&port->lock);
PDEV_CSR_WRITE(p_pdev, PREG_REG, UART_DATA, (u32)xmit->buf[xmit->tail]);
PDEV_CSR_WRITE(p_pdev, PREG_REG, UART_CTL, UART_CTL_TX);
spin_unlock(&port->lock);
/* 调整循环缓冲的位置 */
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
/* 发送的数据量加1 */
port->icount.tx++;
}
}
/* 等待发送完成 */
while(1) {
state = PDEV_CSR_READ(p_pdev, PREG_REG, UART_TX_FIFO_STATE);
if(state & UART_FIFO_EMPTY)
break;
mdelay(1);
}
/* 如果循环缓冲里面的数据小于WAKEUP_CHARS(256),则唤醒之前阻塞的发送进程 */
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
}
中断接收
- 通过IRQ_F2P,PL将中断发送给PS
- 中断中将FIFO中缓存的数据提交给TTY层
static irqreturn_t dev_isr(int irq, void *dev_id)
{
u32 n, state;
u8 data;
struct dev_priv *priv;
struct peng_dev *p_pdev;
struct uart_port *port;
struct tty_port *tport;
struct circ_buf *xmit;
priv = (struct dev_priv *)dev_id;
p_pdev = &priv->pdev;
port = &priv->port;
tport = &port->state->port;
xmit = &port->state->xmit;
spin_lock(&port->lock);
priv->nRxIntCount++;
n = PDEV_CSR_READ(p_pdev, PREG_REG, UART_RX_FIFO_COUNT);
state = PDEV_CSR_READ(p_pdev, PREG_REG, UART_RX_FIFO_STATE);
if (state & UART_FIFO_STATE_FULL)
n = UART_FIFO_SIZE;
dev_dbg(p_pdev->p_dev, "rx,n=%d\n", n);
mb();
/* 接收fifo中字节数 */
priv->rxBytCnt += n;
while (n--) {
/* 将收到的数据提交给TTY层 */
PDEV_CSR_WRITE(p_pdev, PREG_REG, UART_CTL, UART_CTL_RX);
data = PDEV_CSR_READ(p_pdev, PREG_REG, UART_DATA);
if (!uart_handle_sysrq_char(port, data))
uart_insert_char(port, priv->intStatus, 0, data, TTY_NORMAL);
dev_dbg(p_pdev->p_dev, "R:0x%02x", data);
}
tty_flip_buffer_push(tport);
spin_unlock(&port->lock);
return IRQ_HANDLED;
}
控制器功能测试
接收测试
输入命令查看uart数据
cat /dev/ttyFpga0
电脑串口助手发送51字节数据(超过fifo深度)
左侧为zynq控制台,右侧为电脑串口助手
- 51字节产生4次中断,中断优化符合预期
- 右侧发送数据和左侧接收一致,符合预期
发送测试
输入命令发送uart数据,(数据大小超过fifo深度)文章来源:https://www.toymoban.com/news/detail-829951.html
echo asdfghjklasdfghjklasdfghjklasdfghjklasdfghjkl > /dev/ttyFpga0
文章来源地址https://www.toymoban.com/news/detail-829951.html
- 左侧发送数据和右侧接收一致,符合预期
到了这里,关于串口控制器及驱动实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!