串口控制器及驱动实现

这篇具有很好参考价值的文章主要介绍了串口控制器及驱动实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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原理

串口控制器及驱动实现,linux驱动开发经验,FPGA,fpga开发,驱动开发

  • 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读写时序如下:
串口控制器及驱动实现,linux驱动开发经验,FPGA,fpga开发,驱动开发
Xillybus Lite连接如下:
串口控制器及驱动实现,linux驱动开发经验,FPGA,fpga开发,驱动开发

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深度)
串口控制器及驱动实现,linux驱动开发经验,FPGA,fpga开发,驱动开发

左侧为zynq控制台,右侧为电脑串口助手

  • 51字节产生4次中断,中断优化符合预期
  • 右侧发送数据和左侧接收一致,符合预期

发送测试

输入命令发送uart数据,(数据大小超过fifo深度)

echo asdfghjklasdfghjklasdfghjklasdfghjklasdfghjkl > /dev/ttyFpga0

串口控制器及驱动实现,linux驱动开发经验,FPGA,fpga开发,驱动开发文章来源地址https://www.toymoban.com/news/detail-829951.html

  • 左侧发送数据和右侧接收一致,符合预期

到了这里,关于串口控制器及驱动实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于IMX6ULLmini的Linux裸机开发系列五:通用中断控制器(GIC)

    目录 GIC结构 获取GIC中断控制器基地址 方法一:查询芯片数据手册 方法二:查询cp15协处理器 CBAR寄存器 SCTLR 寄存器 VBAR寄存器 GIC用于管理单核或多核芯片中的中断资源 ARM公司开发了4 个版本GIC规范 ,V1~V4 ARMv7-A内核搭配GIC-400使用 GIC结构 GIC官方手册:ARM® Generic Interrupt Contr

    2024年02月12日
    浏览(39)
  • TMC4671-LA完全集成伺服控制器电机驱动芯片带FOC控制

    TMC4671是一个完全集成的伺服控制器,为BLDC/PMSM和2相步进电机以及直流电机和语音线圈提供现场定向控制。   应用 •机器人 • 拾取和放置机器 • 工厂自动化 • 电动汽车 • 实验室自动化 •鼓风机 •泵 • 带磁场定向控制(FOC)的伺服控制器 – 扭矩(和磁通)控制模式

    2024年02月12日
    浏览(36)
  • 森泰克sumtak控制器维修伺服驱动器维修SQ-12

    日本森泰克sumtak控制器维修全系列型号。 控制器常见维修故障:短路,模块损坏,带不动负载,主轴准备未绪,驱动器未使能,编码器故障,主轴驱动模块故障,输出电压低,红色灯亮,无显示,缺相,输出控制点坏,使能不正常,报故障,不能启动、过流、过压、欠压、过

    2024年02月09日
    浏览(39)
  • Spring MVC学习随笔-控制器(Controller)开发详解:控制器跳转与作用域(一)

    学习视频:孙哥说SpringMVC:结合Thymeleaf,重塑你的MVC世界!|前所未有的Web开发探索之旅 3.流程跳转 在web.xml里添加Servlet然后执行 可以看到通过url拼接可以获取传递的数据 四种跳转指的是:在SpringMVC中控制器与JSP或者控制器与控制器之间的跳转。 Controller - - forward — JSP Co

    2024年02月05日
    浏览(57)
  • Spring MVC学习随笔-控制器(Controller)开发详解:控制器跳转与作用域(二)视图模板、静态资源访问

    学习视频:孙哥说SpringMVC:结合Thymeleaf,重塑你的MVC世界!|前所未有的Web开发探索之旅 衔接上文Spring MVC学习随笔-控制器(Controller)开发详解:控制器跳转与作用域(一) SpingMVC中request作用域的处理 代码 Model、ModelMap相关细节分析 通过Model、ModelMap进行作用域处理,可以解决

    2024年02月05日
    浏览(47)
  • Linux6.39 Kubernetes Pod控制器

    第三章 LINUX Kubernetes Pod控制器 一、Pod控制器及其功用 Pod控制器,又称之为工作负载(workload),是用于实现管理pod的中间层,确保pod资源符合预期的状态,pod的资源出现故障时,会尝试进行重启,当根据重启策略无效,则会重新新建pod的资源 二.pod控制器有多种类型 1.Replic

    2024年02月12日
    浏览(44)
  • 模糊PID控制器的实现

    本文讨论有关模糊PID相关的问题。模糊PID是一种将PID控制和模糊算法结合起来的控制算法,其实质上是将模糊算法用在了PID的参数整定上,以此来满足需要动态调整PID参数的系统的要求。 (1)传统的控制方法有时无法满足控制精度的要求,而且抗干扰的能力较弱,模糊控制可以

    2023年04月16日
    浏览(45)
  • linux高级---k8s中的五种控制器

    Kubernetes中内建了很多controller(控制器),这些相当于一个状态机,用来控制Pod的具体状态和行为 总体来说,K8S有五种控制器,分别对应处理无状态应用、有状态应用、守护型应用和批处理应用 无状态服务的特点: 有状态服务的特点: Deployment主要功能有下面几个: 支持R

    2024年02月06日
    浏览(75)
  • Linux 中断子系统中GIC 中断控制器基本分析

    GIC 是 ARM 公司给 Cortex-A/R 内核提供的一个中断控制器,类似 Cortex-M 内核(STM32)中的 NVIC。 GIC:Generic Interrupt Controller,通用中断控制器。 NVIC:Nested Vectored Interrupt Controller,嵌套中断向量控制器。 目前 GIC 有 4 个版本:V1~V4,V1 是最老的版本,已经被废弃了。V2~V4 目前正在大

    2024年02月07日
    浏览(49)
  • 一级倒立摆控制 —— MPC 控制器设计及 MATLAB 实现

    最优控制介绍 一级倒立摆控制 —— 系统建模(传递函数模型与状态空间方程表示) 一级倒立摆控制 —— PID 控制器设计及 MATLAB 实现 一级倒立摆控制 —— 最优控制 线性二次型控制(LQR)及 MATLAB 实现 一级倒立摆控制 —— MPC 控制器设计及 MATLAB 实现 一级倒立摆控制 ——

    2024年02月03日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包