当前位置: 首页 > news >正文

龙芯杯个人赛串口——做一个 UART串口——RS-232

文章目录

      • Async transmitter
      • Async receiver
        • 1. RS-232 串行接口的工作原理
          • DB-9 connector
          • Asynchronous communication
          • How fast can we send data?
        • 2.波特率时钟生成器
          • Parameterized FPGA baud generator
        • 3.RS-232 transmitter
          • 数据序列化
          • 完整代码:
        • 4.RS-232 receiver
          • Oversampling
          • The design
          • 完整代码
        • 5.How to use the RS-232 transmitter and receiver
      • 龙芯杯个人赛串口信号连接

串行接口是将 FPGA 连接到 PC 的简单方法。我们只需要一个发射器和接收器模块。

Async transmitter

它通过对要传输的数据进行序列化,创建一个信号 “TxD”。
在这里插入图片描述

Async receiver

它从 FPGA 外部获取信号 “RxD”,并将其 “去序列化”,以便在 FPGA 内部使用。

在这里插入图片描述
本项目包括五个部分

    1. RS-232 串行接口的工作原理
    1. 波特发生器
    1. 发射器
    1. 接收器
    1. 使用示例
1. RS-232 串行接口的工作原理

RS-232 接口具有以下特点:
- 使用 9 针连接器 “DB-9”(老式 PC 使用 25 针连接器 “DB-25”)。
- 允许双向全双工通信(个人电脑可同时发送和接收数据)。
- 最大通信速度约为 10KB/s。

DB-9 connector

可能在电脑背面看到过这个连接器。
在这里插入图片描述
它有 9 个引脚,但重要的有 3 个:

第 2 针:RxD(接收数据)。
第 3 针:TxD(发送数据)。
第 5 针:GND(接地)。

只需 3 根导线,就能发送和接收数据。

数据通常以 8 位为单位发送(我们称之为字节),并进行 “序列化”:先发送 LSB(数据位 0),然后是位 1,…最后是 MSB(位 7)。

Asynchronous communication

该接口使用异步协议。这意味着没有时钟信号与数据一起传输。接收器必须有办法根据接收到的数据位 “计时”。

在 RS-232 的情况下,可以这样做:

电缆两端事先就通信参数(速度、格式......)达成一致。这需要在通信开始前手动完成。
只要线路处于空闲状态,发送器就会发送 "空闲"(="1")。
发送器在传输每个字节前都会发送 "开始"(="0"),以便接收器知道有字节到来。
发送字节数据的 8 位。
发送器在每个字节后发送 "停止"(="1")。

让我们看看 0x55 字节在传输时的样子:
在这里插入图片描述
字节 0x55 的二进制形式是 01010101。
但由于它是先传输 LSB(第 0 位),因此该行会像这样切换: 1-0-1-0-1-0-1-0.

下面是另一个例子:
在这里插入图片描述

How fast can we send data?

速度以波特为单位,即每秒可发送多少比特。例如,1000 波特表示每秒 1000 比特,或每个比特持续一毫秒。

RS-232 接口的常见实现(如 PC 中使用的接口)不允许使用任何速度。如果你想使用 123456 波特,那就没戏了。你必须采用某种 "标准 "速度。常见的值有

1200 波特。
9600 波特。
38400 波特。
115200 波特(通常是最快的速度)。

115200 波特时,每个比特持续 (1/115200) = 8.7µs。如果传输 8 位数据,则持续时间为 8 x 8.7µs = 69µs。但每个字节需要一个额外的起始位和停止位,因此实际上需要 10 x 8.7µs = 87µs。这意味着最高速度为每秒 11.5KB。

在 115200 波特的情况下,一些带有错误芯片的 PC 需要一个 "长 "停止位(1.5 或 2 位长…),这使得最高速度降至每秒 10.5KB 左右。
物理层

电线上的信号使用正/负电压方案。

1 "使用 -10V(或 -5V 至 -15V)电压发送。
0 "使用 +10V(或 5V 至 15V 之间)发送。

因此,空闲线路的电压类似于 -10V。

2.波特率时钟生成器

在这里,我们希望以最大速度使用串行链路,即 115200 波特(较慢的速度也很容易生成)。FPGA 通常以 MHz 速度运行,远高于 115200Hz(按当今标准,RS-232 的速度相当慢)。我们需要找到一种方法,(通过 FPGA 时钟)产生尽可能接近每秒 115200 次的 "滴答 "声。

传统上,RS-232 芯片使用 1.8432MHz 时钟,因为这样可以很容易地产生标准波特频率… 1.8432MHz 除以 16 得到 115200Hz。

// let's assume the FPGA clock signal runs at 1.8432MHz
// we create a 4-bit counter
reg [3:0] BaudDivCnt;
always @(posedge clk) BaudDivCnt <= BaudDivCnt + 1; // count forever from 0 to 15// and a tick signal that is asserted once every 16 clocks (so 115200 times a second)
wire BaudTick = (BaudDivCnt==15); 

这很简单。但如果时钟频率不是 1.8432MHz,而是 2MHz,该怎么办呢?要从 2MHz 时钟产生 115200Hz 的频率,我们需要将时钟除以 “17.361111111…”。这可不是一个整数。解决办法是有时除以 17,有时除以 18,确保比率保持 “17.361111111”。这其实很容易做到。

请看下面的 "C "代码

while(1) // repeat forever
{acc += 115200;if(acc>=2000000) printf("*"); else printf(" ");acc %= 2000000;
}

这样,平均每 "17.361111111… "循环一次,就能以精确的比例打印出 “*”。

要在 FPGA 中高效地实现同样的功能,我们需要依靠串行接口能够容忍波特频率发生器中百分之几的误差。

我们希望 2000000 是 2 的幂。显然,2000000 不是。因此,我们要改变比率… 用 “1024/59” = 17.356 代替 “2000000/115200”。这非常接近我们的理想比率,而且可以高效地在 FPGA 上实现:我们使用一个 10 位累加器,以 59 为增量,每当累加器溢出时打一个勾。

// let's assume the FPGA clock signal runs at 2.0000MHz
// we use a 10-bit accumulator plus an extra bit for the accumulator carry-out
reg [10:0] acc;   // 11 bits total!// add 59 to the accumulator at each clock
always @(posedge clk)acc <= acc[9:0] + 59; // use 10 bits from the previous accumulator result, but save the full 11 bits resultwire BaudTick = acc[10]; // so that the 11th bit is the accumulator carry-out 

使用我们的 2MHz 时钟,"BaudTick "每秒断言 115234 次,与理想的 115200 误差为 0.03%。

Parameterized FPGA baud generator

之前的设计使用的是 10 位累加器,但随着时钟频率的增加,需要更多的位数。
下面是一个使用 25MHz 时钟和 16 位累加器的设计。该设计是参数化的,因此很容易定制。

parameter ClkFrequency = 25000000; // 25MHz
parameter Baud = 115200;
parameter BaudGeneratorAccWidth = 16;
parameter BaudGeneratorInc = (Baud<<BaudGeneratorAccWidth)/ClkFrequency;reg [BaudGeneratorAccWidth:0] BaudGeneratorAcc;
always @(posedge clk)BaudGeneratorAcc <= BaudGeneratorAcc[BaudGeneratorAccWidth-1:0] + BaudGeneratorInc;wire BaudTick = BaudGeneratorAcc[BaudGeneratorAccWidth]; 

最后一个实现问题:"BaudGeneratorInc "的计算是错误的,这是因为 Verilog 使用 32 位中间结果,而计算结果超过了 32 位。为解决这个问题,请修改如下内容。

parameter BaudGeneratorInc = ((Baud<<(BaudGeneratorAccWidth-4))+(ClkFrequency>>5))/(ClkFrequency>>4); 

这条线的另一个优点是将结果四舍五入,而不是截断。

现在我们有了足够精确的波特发生器,就可以继续使用 RS-232 发送器和接收器模块了。

  1. 代码实现
module BaudTickGen(input  wire clk, enable,output wire tick  // generate a tick at the specified baud rate * oversampling
);
parameter ClkFrequency = 25000000;
parameter Baud = 115200;
parameter Oversampling = 1;function integer log2(input integer v); begin log2=0; while(v>>log2) log2=log2+1; end endfunction
localparam AccWidth = log2(ClkFrequency/Baud)+8;  // +/- 2% max timing error over a byte
reg [AccWidth:0] Acc = 0;
localparam ShiftLimiter = log2(Baud*Oversampling >> (31-AccWidth));  // this makes sure Inc calculation doesn't overflow
localparam Inc = ((Baud*Oversampling << (AccWidth-ShiftLimiter))+(ClkFrequency>>(ShiftLimiter+1)))/(ClkFrequency>>ShiftLimiter);
always @(posedge clk) if(enable) Acc <= Acc[AccWidth-1:0] + Inc[AccWidth:0]; else Acc <= Inc[AccWidth:0];
assign tick = Acc[AccWidth];
  1. 解释
    这段Verilog代码看起来是在实现一个串口通信的波特率时钟生成器。我们逐段解释一下代码:
  • 模块定义:

    module BaudTickGen(input  wire clk, enable,output wire tick  // 根据指定的波特率 * 过采样率生成时钟脉冲
    );
    

    这个模块有三个端口:clkenable 作为输入,tick 作为输出。它旨在根据指定的波特率和过采样率生成一个时钟脉冲信号。

  • 参数声明:

    parameter ClkFrequency = 25000000;
    parameter Baud = 115200;
    parameter Oversampling = 1;
    

    这些参数定义了时钟频率 (ClkFrequency)、所需波特率 (Baud) 以及过采样因子 (Oversampling)。您可以根据需要自定义这些值。

  • Log2 函数:

    function integer log2(input integer v);beginlog2 = 0;while (v >> log2)log2 = log2 + 1;end
    endfunction
    

    这是一个简单的函数,用于计算输入整数 v 的以2为底的对数。

  • 局部参数:

    localparam AccWidth = log2(ClkFrequency/Baud) + 8;
    

    AccWidth 是根据所需的位数来表示累加值 (Acc),以实现在一个字节内最大的时序误差不超过 +/- 2%。

    localparam ShiftLimiter = log2(Baud*Oversampling >> (31-AccWidth));
    

    ShiftLimiter 用于限制左移操作在计算 Inc 时以防止溢出。

    localparam Inc = ((Baud*Oversampling << (AccWidth-ShiftLimiter)) + (ClkFrequency>>(ShiftLimiter+1))) / (ClkFrequency>>ShiftLimiter);
    

    Inc 是每个时钟周期添加到累加器的增量值,以实现所需的波特率。

    always @(posedge clk) if(enable) Acc <= Acc[AccWidth-1:0] + Inc[AccWidth:0];else Acc <= Inc[AccWidth:0];
    

    这个总是块在时钟的上升沿触发 (posedge clk)。如果 enable 为真,则将 Inc 的值累加到累加器 Acc 中;否则,将 Acc 设置为 Inc 的初始值。

  • 赋值语句:

    assign tick = Acc[AccWidth];
    

    这将累加器的最高位赋给 tick 输出,这就是生成的时钟脉冲信号。

3.RS-232 transmitter

我们正在构建一个参数固定的 “异步发射机”:8 个数据位、2 个停止位、无奇偶校验。
在这里插入图片描述
工作原理是这样的:

发送器在 FPGA 内获取 8 位数据并将其序列化(从 "TxD_start "信号断定时开始)。
在传输过程中,"忙 "信号被断开(在此期间 "TxD_start "信号被忽略)。
数据序列化

要完成起始位、8 个数据位和停止位,似乎应该使用状态机。

reg [3:0] state;// the state machine starts when "TxD_start" is asserted, but advances when "BaudTick" is asserted (115200 times a second)
always @(posedge clk)
case(state)4'b0000: if(TxD_start) state <= 4'b0100;4'b0100: if(BaudTick) state <= 4'b1000; // start4'b1000: if(BaudTick) state <= 4'b1001; // bit 04'b1001: if(BaudTick) state <= 4'b1010; // bit 14'b1010: if(BaudTick) state <= 4'b1011; // bit 24'b1011: if(BaudTick) state <= 4'b1100; // bit 34'b1100: if(BaudTick) state <= 4'b1101; // bit 44'b1101: if(BaudTick) state <= 4'b1110; // bit 54'b1110: if(BaudTick) state <= 4'b1111; // bit 64'b1111: if(BaudTick) state <= 4'b0001; // bit 74'b0001: if(BaudTick) state <= 4'b0010; // stop14'b0010: if(BaudTick) state <= 4'b0000; // stop2default: if(BaudTick) state <= 4'b0000;
endcase

现在,我们只需生成 "TxD "输出。

reg muxbit;always @(state[2:0])
case(state[2:0])0: muxbit <= TxD_data[0];1: muxbit <= TxD_data[1];2: muxbit <= TxD_data[2];3: muxbit <= TxD_data[3];4: muxbit <= TxD_data[4];5: muxbit <= TxD_data[5];6: muxbit <= TxD_data[6];7: muxbit <= TxD_data[7];
endcase// combine start, data, and stop bits together
assign TxD = (state<4) | (state[3] & muxbit); 
完整代码:
module async_transmitter(input wire clk,input wire TxD_start,input wire [7:0] TxD_data,output wire TxD,output wire TxD_busy
);// Assert TxD_start for (at least) one clock cycle to start transmission of TxD_data
// TxD_data is latched so that it doesn't have to stay valid while it is being sentparameter ClkFrequency = 25000000;	// 25MHz
parameter Baud = 115200;// generate
// 	if(ClkFrequency<Baud*8 && (ClkFrequency % Baud!=0)) ASSERTION_ERROR PARAMETER_OUT_OF_RANGE("Frequency incompatible with requested Baud rate");
// endgenerate`ifdef SIMULATION
wire BitTick = 1'b1;  // output one bit per clock cycle
`else
wire BitTick;
BaudTickGen #(ClkFrequency, Baud) tickgen(.clk(clk), .enable(TxD_busy), .tick(BitTick));
`endifreg [3:0] TxD_state = 0;
wire TxD_ready = (TxD_state==0);
assign TxD_busy = ~TxD_ready;reg [7:0] TxD_shift = 0;
always @(posedge clk)
beginif(TxD_ready & TxD_start)TxD_shift <= TxD_data;elseif(TxD_state[3] & BitTick)TxD_shift <= (TxD_shift >> 1);case(TxD_state)4'b0000: if(TxD_start) TxD_state <= 4'b0100;4'b0100: if(BitTick) TxD_state <= 4'b1000;  // start bit4'b1000: if(BitTick) TxD_state <= 4'b1001;  // bit 04'b1001: if(BitTick) TxD_state <= 4'b1010;  // bit 14'b1010: if(BitTick) TxD_state <= 4'b1011;  // bit 24'b1011: if(BitTick) TxD_state <= 4'b1100;  // bit 34'b1100: if(BitTick) TxD_state <= 4'b1101;  // bit 44'b1101: if(BitTick) TxD_state <= 4'b1110;  // bit 54'b1110: if(BitTick) TxD_state <= 4'b1111;  // bit 64'b1111: if(BitTick) TxD_state <= 4'b0010;  // bit 74'b0010: if(BitTick) TxD_state <= 4'b0000;  // stop1//4'b0011: if(BitTick) TxD_state <= 4'b0000;  // stop2default: if(BitTick) TxD_state <= 4'b0000;endcase
endassign TxD = (TxD_state<4) | (TxD_state[3] & TxD_shift[0]);  // put together the start, data and stop bits
endmodule
4.RS-232 receiver

我们正在构建一个 “异步接收器”:
在这里插入图片描述
我们的实施工作就是这样:

模块从 RxD 线路中收集数据。
当接收到一个字节时,它就会出现在 "数据 "总线上。一旦接收到一个完整的字节,"data_ready "就会断言一个时钟。

请注意,"数据 "只有在 "数据就绪 "断言时才有效。其余时间不要使用它,因为可能会有新的数据出现,从而对它进行洗牌。

Oversampling

异步接收器必须以某种方式与接收到的信号同步(它通常无法访问发送器使用的时钟)。

为了确定新数据字节何时到来,我们以波特率频率的倍数对信号进行过采样,寻找 "起始 "位。
一旦检测到 "起始 "位,我们就以已知的波特率对线路进行采样,以获取数据位。

接收器通常以 16 倍波特率对输入信号进行过采样。这里我们使用 8 倍… 对于 115200 波特,采样率为 921600Hz。

假设我们有一个 "Baud8Tick "信号,每秒发出 921600 次。

The design

首先,输入的 "RxD "信号与我们的时钟无关。我们使用两个 D 触发器对其进行过采样,并使其与我们的时钟域同步。

reg [1:0] RxD_sync;
always @(posedge clk) if(Baud8Tick) RxD_sync <= {RxD_sync[0], RxD}; 

我们对数据进行过滤,以免将 RxD 线路上的短尖峰误认为是起始位。

reg [1:0] RxD_cnt;
reg RxD_bit;always @(posedge clk)
if(Baud8Tick)
beginif(RxD_sync[1] && RxD_cnt!=2'b11) RxD_cnt <= RxD_cnt + 1;elseif(~RxD_sync[1] && RxD_cnt!=2'b00) RxD_cnt <= RxD_cnt - 1;if(RxD_cnt==2'b00) RxD_bit <= 0;elseif(RxD_cnt==2'b11) RxD_bit <= 1;
end

一旦检测到 “start”,状态机就会对接收到的每个比特进行处理。

reg [3:0] state;always @(posedge clk)
if(Baud8Tick)
case(state)4'b0000: if(~RxD_bit) state <= 4'b1000; // start bit found?4'b1000: if(next_bit) state <= 4'b1001; // bit 04'b1001: if(next_bit) state <= 4'b1010; // bit 14'b1010: if(next_bit) state <= 4'b1011; // bit 24'b1011: if(next_bit) state <= 4'b1100; // bit 34'b1100: if(next_bit) state <= 4'b1101; // bit 44'b1101: if(next_bit) state <= 4'b1110; // bit 54'b1110: if(next_bit) state <= 4'b1111; // bit 64'b1111: if(next_bit) state <= 4'b0001; // bit 74'b0001: if(next_bit) state <= 4'b0000; // stop bitdefault: state <= 4'b0000;
endcase

请注意,我们使用了一个 "next_bit "信号,以便从一个比特到另一个比特。

reg [2:0] bit_spacing;always @(posedge clk)
if(state==0)bit_spacing <= 0;
else
if(Baud8Tick)bit_spacing <= bit_spacing + 1;wire next_bit = (bit_spacing==7); 

最后,移位寄存器将数据位收集起来。

reg [7:0] RxD_data;
always @(posedge clk) if(Baud8Tick && next_bit && state[3]) RxD_data <= {RxD_bit, RxD_data[7:1]}; 
完整代码
module async_receiver(input wire clk,input wire RxD,output reg RxD_data_ready,input wire RxD_clear,output reg [7:0] RxD_data  // data received, valid only (for one clock cycle) when RxD_data_ready is asserted
);parameter ClkFrequency = 25000000; // 25MHz
parameter Baud = 115200;parameter Oversampling = 8;  // needs to be a power of 2
// we oversample the RxD line at a fixed rate to capture each RxD data bit at the "right" time
// 8 times oversampling by default, use 16 for higher quality reception// generate
// 	if(ClkFrequency<Baud*Oversampling) ASSERTION_ERROR PARAMETER_OUT_OF_RANGE("Frequency too low for current Baud rate and oversampling");
// 	if(Oversampling<8 || ((Oversampling & (Oversampling-1))!=0)) ASSERTION_ERROR PARAMETER_OUT_OF_RANGE("Invalid oversampling value");
// endgenerate// We also detect if a gap occurs in the received stream of characters
// That can be useful if multiple characters are sent in burst
//  so that multiple characters can be treated as a "packet"
wire RxD_idle;  // asserted when no data has been received for a while
reg RxD_endofpacket; // asserted for one clock cycle when a packet has been detected (i.e. RxD_idle is going high)reg [3:0] RxD_state = 0;`ifdef SIMULATION
wire RxD_bit = RxD;
wire sampleNow = 1'b1;  // receive one bit per clock cycle`else
wire OversamplingTick;
BaudTickGen #(ClkFrequency, Baud, Oversampling) tickgen(.clk(clk), .enable(1'b1), .tick(OversamplingTick));// synchronize RxD to our clk domain
reg [1:0] RxD_sync = 2'b11;
always @(posedge clk) if(OversamplingTick) RxD_sync <= {RxD_sync[0], RxD};// and filter it
reg [1:0] Filter_cnt = 2'b11;
reg RxD_bit = 1'b1;always @(posedge clk)
if(OversamplingTick)
beginif(RxD_sync[1]==1'b1 && Filter_cnt!=2'b11) Filter_cnt <= Filter_cnt + 1'd1;else if(RxD_sync[1]==1'b0 && Filter_cnt!=2'b00) Filter_cnt <= Filter_cnt - 1'd1;if(Filter_cnt==2'b11) RxD_bit <= 1'b1;elseif(Filter_cnt==2'b00) RxD_bit <= 1'b0;
end// and decide when is the good time to sample the RxD line
function integer log2(input integer v); begin log2=0; while(v>>log2) log2=log2+1; end endfunction
localparam l2o = log2(Oversampling);
reg [l2o-2:0] OversamplingCnt = 0;
always @(posedge clk) if(OversamplingTick) OversamplingCnt <= (RxD_state==0) ? 1'd0 : OversamplingCnt + 1'd1;
wire sampleNow = OversamplingTick && (OversamplingCnt==Oversampling/2-1);
`endif// now we can accumulate the RxD bits in a shift-register
always @(posedge clk)
case(RxD_state)4'b0000: if(~RxD_bit) RxD_state <= `ifdef SIMULATION 4'b1000 `else 4'b0001 `endif;  // start bit found?4'b0001: if(sampleNow) RxD_state <= 4'b1000;  // sync start bit to sampleNow4'b1000: if(sampleNow) RxD_state <= 4'b1001;  // bit 04'b1001: if(sampleNow) RxD_state <= 4'b1010;  // bit 14'b1010: if(sampleNow) RxD_state <= 4'b1011;  // bit 24'b1011: if(sampleNow) RxD_state <= 4'b1100;  // bit 34'b1100: if(sampleNow) RxD_state <= 4'b1101;  // bit 44'b1101: if(sampleNow) RxD_state <= 4'b1110;  // bit 54'b1110: if(sampleNow) RxD_state <= 4'b1111;  // bit 64'b1111: if(sampleNow) RxD_state <= 4'b0010;  // bit 74'b0010: if(sampleNow) RxD_state <= 4'b0000;  // stop bitdefault: RxD_state <= 4'b0000;
endcasealways @(posedge clk)
if(sampleNow && RxD_state[3]) RxD_data <= {RxD_bit, RxD_data[7:1]};//reg RxD_data_error = 0;
always @(posedge clk)
beginif(RxD_clear)RxD_data_ready <= 0;elseRxD_data_ready <= RxD_data_ready | (sampleNow && RxD_state==4'b0010 && RxD_bit);  // make sure a stop bit is received//RxD_data_error <= (sampleNow && RxD_state==4'b0010 && ~RxD_bit);  // error if a stop bit is not received
end`ifdef SIMULATION
assign RxD_idle = 0;
`else
reg [l2o+1:0] GapCnt = 0;
always @(posedge clk) if (RxD_state!=0) GapCnt<=0; else if(OversamplingTick & ~GapCnt[log2(Oversampling)+1]) GapCnt <= GapCnt + 1'h1;
assign RxD_idle = GapCnt[l2o+1];
always @(posedge clk) RxD_endofpacket <= OversamplingTick & ~GapCnt[l2o+1] & &GapCnt[l2o:0];
`endifendmodule
5.How to use the RS-232 transmitter and receiver

该设计允许通过 PC 控制几个 FPGA 引脚(通过 PC 的串行端口)。

它在 FPGA 上创建了 8 个输出(端口名为 "GPout")。FPGA 接收到的任何字符都会更新 GPout。
同时在 FPGA 上创建 8 个输入端(名为 "GPin "的端口)。每当 FPGA 接收到一个字符,GPin 就会传输一次。

GP 输出可用于从电脑远程控制任何东西,可能是 LED 或咖啡机…

module serialGPIO(input clk,input RxD,output TxD,output reg [7:0] GPout,  // general purpose outputsinput [7:0] GPin  // general purpose inputs
);wire RxD_data_ready;
wire [7:0] RxD_data;
async_receiver RX(.clk(clk), .RxD(RxD), .RxD_data_ready(RxD_data_ready), .RxD_data(RxD_data));
always @(posedge clk) if(RxD_data_ready) GPout <= RxD_data;async_transmitter TX(.clk(clk), .TxD(TxD), .TxD_start(RxD_data_ready), .TxD_data(GPin));
endmodule

龙芯杯个人赛串口信号连接

// | 0xBFD003F8 | [7:0] | 串口数据,读、写地址分别表示串口接收、发送一个字节 |
// | 0xBFD003FC | [0]   | 只读,为1时表示串口空闲,可发送数据                |
// | 0xBFD003FC | [1]   | 只读,为1时表示串口收到数据                        |
module uart(input wire clk,input wire resetn,// read and write from cpu// input   wire          conf_en,     input   wire          conf_re,conf_we,// input   wire   [3 :0] conf_wen,      input   wire   [31:0] conf_addr,    input   wire   [31:0] conf_wdata,   output  wire   [31:0] conf_rdata,// read and write to device on board//直连串口信号output wire txd,  //直连串口发送端input  wire rxd  //直连串口接收端// output reg [15:0] led,// input wire [7:0] switch
);
wire read_flag;
wire write_uart;
wire read_uart;
assign read_flag = ((conf_addr == 32'hbfd003fc) && (conf_re)) ? 1'b1:1'b0;
assign read_uart = ((conf_addr == 32'hbfd003f8) && (conf_re)) ? 1'b1:1'b0;
assign write_uart = ((conf_addr == 32'hbfd003f8) && (conf_we)) ? 1'b1:1'b0;wire [7:0] ext_uart_wdata;// write data// buffers
reg [7:0] ext_uart_rbuffer;// read buffer
reg [1:0] ext_uart_flag; // uart flag for read and write at addr 0xbfd003fc
assign conf_rdata = read_flag?{30'h0,ext_uart_flag}:read_uart?{24'h0,ext_uart_rbuffer}:32'h0;wire [7:0] ext_uart_rx;
wire ext_uart_ready,ext_uart_clear;wire ext_uart_busy; // transmitter busy flag
reg ext_uart_start; // transmitter start work signal
always @(posedge clk) beginif(!resetn)ext_uart_flag <= 2'h1;else beginif(ext_uart_ready)ext_uart_flag[1] <= 1;else if(read_uart)  ext_uart_flag[1] <= 0;// write flag// if(write_uart)//     ext_uart_flag[0] <= 0;// else if(!ext_uart_busy)//     ext_uart_flag[0] <= 1;if (!ext_uart_busy)ext_uart_flag[0] <= 1;else if(write_uart)ext_uart_flag[0] <= 0;endend//uart reciever
async_receiver #(.ClkFrequency(60000000),.Baud(9600)) ext_uart_r(.clk(clk),.RxD(rxd),.RxD_data_ready(ext_uart_ready),.RxD_clear(ext_uart_clear),.RxD_data(ext_uart_rx));
// store RxD_data to read buffer and clear RxD_data after store
assign ext_uart_clear = ext_uart_ready;
// assign ext_uart_rdata = ext_uart_rx;
always @(posedge clk) beginif(ext_uart_ready)ext_uart_rbuffer <= ext_uart_rx;
end
// assign ext_uart_rbuffer = ext_uart_rx;always @(posedge clk) beginif(!ext_uart_busy && write_uart)ext_uart_start <= 1'b1;else ext_uart_start <= 1'b0;
endwire [7:0] ext_uart_tx;// write data
reg [7:0] last_data;
always @(posedge clk) beginlast_data <= ext_uart_tx[7:0];
end
assign ext_uart_tx = write_uart?conf_wdata[7:0]:last_data;
// assign ext_uart_tx = write_uart?conf_wdata[7:0]:8'h0;async_transmitter #(.ClkFrequency(6000_0000),.Baud(9600)) ext_uart_t(.clk(clk),.TxD(txd),.TxD_busy(ext_uart_busy),.TxD_start(ext_uart_start),.TxD_data(ext_uart_tx) // transmit the data in buffer to txd);

相关文章:

龙芯杯个人赛串口——做一个 UART串口——RS-232

文章目录 Async transmitterAsync receiver1. RS-232 串行接口的工作原理DB-9 connectorAsynchronous communicationHow fast can we send data? 2.波特率时钟生成器Parameterized FPGA baud generator 3.RS-232 transmitter数据序列化完整代码&#xff1a; 4.RS-232 receiver…...

验证码服务使用指南

验证码服务使用指南 1 部署验证码服务 1.1 基础环境 Java 1.8 Maven3.3.9 1.2 安装Redis 参考“Redis安装指南” 1.3 部署验证码服务 1.3.1 下载源码 使用git从远程下载验证码服务代码(开源)。 1.3.2 使用idea打开项目 使用idea打开上一步下载的sailing目录&#xf…...

js中Math.min(...arr)和Math.max(...arr)的注意点

当arr变量为空数组时&#xff0c;这两个函数和不传参数时的结果是一样的 Math.max() // -Infinity Math.max(...[]) // -InfinityMath.min() // Infinity Math.min(...[]) // Infinity...

【zookeeper特点和集群架构】

文章目录 1. Zookeeper介绍2、ZooKeeper数据结构3、Zookeeper集群架构 1. Zookeeper介绍 ZooKeeper 是一个开源的分布式协调框架&#xff0c;是Apache Hadoop 的一个子项目&#xff0c;主要用来解决分 布式集群中应用系统的一致性问题。Zookeeper 的设计目标是将那些复杂且容易…...

MySQL集群架构搭建以及多数据源管理实战

MySQL集群架构搭建以及多数据源管理实战 ​ 数据库的分库分表操作&#xff0c;是互联网大型应用所需要面对的最核心的问题。因为数据往往是一个应用最核心的价值所在。但是&#xff0c;在最开始的时候&#xff0c;需要强调下&#xff0c;在实际应用中&#xff0c;对于数据库&a…...

C# WPF上位机开发(从demo编写到项目开发)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 C# WPF编程&#xff0c;特别是控件部分&#xff0c;其实学起来特别快。只是后面多了多线程、锁、数据库、网络这部分稍微复杂一点&#xff0c;不过…...

微信小程序引入 vant组件(详细步骤)

vant官方地址 https://vant-contrib.gitee.io/vant-weapp/#/quickstart 步骤一、 通过 npm 安装 # 通过 npm 安装 npm i vant/weapp -S --production# 通过 yarn 安装 yarn add vant/weapp --production# 安装 0.x 版本 npm i vant-weapp -S --production步骤二 修改 app.js…...

Django之按钮(actions)

开篇就是道歉&#xff0c;哈哈哈哈&#xff0c;托更了好久好久&#xff0c;最近太忙了没啥时间更新&#xff0c;各位看官有催更的阔以给我私信哇&#xff0c;希望各位看官给个三连&#xff01;&#xff01;&#xff01;&#x1f60d;&#x1f60d;&#x1f60d;&#x1f60d; …...

从YOLOv1到YOLOv8的YOLO系列最新综述【2023年4月】

作者&#xff1a;Juan R. Terven 、Diana M. Cordova-Esparaza 摘要&#xff1a;YOLO已经成为机器人、无人驾驶汽车和视频监控应用的核心实时物体检测系统。我们对YOLO的演变进行了全面的分析&#xff0c;研究了从最初的YOLO到YOLOv8每次迭代的创新和贡献。我们首先描述了标准…...

浙江大唐乌沙山电厂选择ZStack Cloud打造新一代云基础设施

浙江大唐乌沙山电厂选择云轴科技ZStack Cloud云平台为其提供高性能、高可用的云主机、云存储和云网络&#xff0c;构建了简单、稳定、安全、高效的云基础设施&#xff1b;通过ZStackCloud为其提供可视化服务编排、多租户自服务等模块&#xff0c;帮助电厂提高IT资源利用率&…...

电脑开机快捷启动,启动菜单没有u盘怎么办

电脑开机快捷启动键找不到u盘怎么办 对于快捷启动键找不到u盘的问题&#xff0c;小编很了解其中的门道&#xff0c;因为开机找不到u盘是我们使用电脑时候的常见问题。那么我们到底要如何解决开机找不到u盘的问题呢?其实方法还是蛮简单的&#xff0c;下面小编就来教大家电脑开…...

线程的同步与互斥

抢票的例子 竞争过程 进程A被切走 进程B被切走 结论&#xff1a; 互斥 int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); mutex: 指向要初始化的互斥锁的指针。attr: 用于设置互斥锁属性的指针&#xff0c;通常可以传入 NULL 以使用默认属性…...

低代码实施复杂应用的实践方法

内容来自演讲&#xff1a;韦有炬 | 柳州知行远企业管理咨询有限公司 | 总经理 摘要 本文探讨了在全民开发时代如何使用低代码实施复杂应用并降低上线风险。文章分析了复杂系统实施失败的风险&#xff0c;包括项目规划不周、人员变动、企业基础管理不足等&#xff0c;并对比了低…...

算法学习系列(十一):KMP算法

目录 引言一、算法概念二、题目描述三、思路讲解三、代码实现四、测试 引言 这个KMP算法就是怎么说呢&#xff0c;就是不管算法竞赛还是找工作笔试面试&#xff0c;都是非常爱问爱考的&#xff0c;其实也是因为这个算法比较难懂&#xff0c;其实就是很难&#xff0c;所以非常个…...

****Linux下Mysql的安装和配置

1、安装mysql 1.1、安装mysql sudo aptitude search mysql sudo apt-get install mysql-server mysql-client1.2、启动停止mysql: service mysql stop service mysql restart mysql -u debian-sys-maint -p mysql命令详细解释如下: 一、 启动方式 1、使用 service 启动…...

第十六节TypeScript 类

1、简介 TypeScript是面向对象的JavaScript。 类描述了所创建的对象共同的属性与方法。 2、类的定义 class class_name { // 类作用域 } 定义类的关键字是class&#xff0c;后面紧跟类名&#xff0c;类可以包含以下几个模块&#xff1a; 字段 – 字段是类里面声明的变量。字…...

RocketMQ的Docker镜像部署(以及Dashboard的部署、ACL配置)

RocketMQ的Docker镜像部署&#xff08;以及Dashboard、ACL&#xff09; 准备 包含RocketMQ部署&#xff08;NameServer、Broker&#xff09;、Dashboard、ACL拉取镜像 RocketMQ$ docker pull apache/rocketmq:5.1.4Dashboard$ docker pull apacherocketmq/rocketmq-dashboard…...

数据仓库【2】:架构

数据仓库【2】&#xff1a;架构 1、架构图2、ETL流程2.1、ETL -- Extract-Transform-Load2.1.1、数据抽取&#xff08;Extraction&#xff09;2.1.2、数据转换&#xff08;Transformation&#xff09;2.1.3、数据加载&#xff08; Loading &#xff09; 2.2、ETL工具2.2.1、结构…...

JavaScript函数表达式

JavaScript函数表达式是一种将函数赋值给变量的方式。函数表达式可以以匿名形式或具名形式存在。 匿名函数表达式&#xff1a; var func function() {// 函数的逻辑 }在上面的例子中&#xff0c;将一个匿名函数赋值给变量func。 具名函数表达式&#xff1a; var func fun…...

LabVIEW在齿轮箱故障诊断中的应用

LabVIEW在齿轮箱故障诊断中的应用 在现代机械工业中&#xff0c;齿轮箱作为重要的传动设备&#xff0c;其性能稳定性对整体机械系统的运行至关重要。故障的及时诊断和处理不仅保障了设备的稳定运行&#xff0c;还减少了维护成本。利用LabVIEW强大数据处理和仿真能力&#xff0…...

图片转excel:“保留数字格式”在什么场景下该勾

保留数字格式是什么意思呢&#xff1f;顾名思义&#xff0c;就是将转出来的数字保留为数字格式&#xff0c;而不是文本格式。我们知道&#xff0c;OCR程序将图片上的文字识别为电脑可编辑的文字后&#xff0c;如果导入到excel不加处理&#xff0c;则单个数字过长的文字就会被ex…...

SpringMVC:整合 SSM 下篇

文章目录 SpringMVC - 05整合 SSM 下篇一、设计页面1. 首页&#xff1a;index.jsp2. 展示书页面&#xff1a;showBooks.jsp3. 增加书页面&#xff1a;addBook.jsp4. 修改书页面&#xff1a;updateBook.jsp5. 总结 二、控制层1. 查询全部书2. 增加书3. 修改书4. 删除书5. 搜索书…...

[2023-年度总结]凡是过往,皆为序章

原创/朱季谦 2023年12月初&#xff0c;傍晚&#xff0c;在深圳的小南山看了一场落日。 那晚我们坐在山顶的草地上&#xff0c;拍下了这张照片——仿佛在秋天的枝头上&#xff0c;结出一颗红透的夕阳。 这一天很快就会随着夜幕的降临&#xff0c;化作记忆的碎片&#xff0c;然…...

OpenCV之像素操作

我们首先了解一下什么是像素&#xff0c;计算机中是如何存储图像&#xff0c;以及opencv是如何表示图像的。 像素&#xff1a; 像素是指由图像的小方格即所谓的像素(pixel)组成的&#xff0c;这些小方块都有一个明确的位置和被分配的色彩数值&#xff0c;而这些一小方格的颜色…...

Transfer Learning(迁移学习)

1. 什么是迁移学习 迁移学习(Transfer Learning)是一种机器学习方法&#xff0c;就是把为任务 A 开发的模型作为初始点&#xff0c;重新使用在为任务 B 开发模型的过程中。迁移学习是通过从已学习的相关任务中转移知识来改进学习的新任务&#xff0c;虽然大多数机器学习算法都…...

NPM 的使用技巧:简化 JavaScript 开发和依赖管理

前言 NPM&#xff08;Node Package Manager&#xff09;是 JavaScript 生态系统中最流行的包管理工具之一。本文将介绍一些有用的 NPM 使用技巧&#xff0c;帮助开发者更好地利用 NPM 管理项目依赖、执行脚本、发布自己的包以及解决常见问题。 1. 初始化项目 使用 NPM 初始化…...

统计和绘图软件GraphPad Prism mac功能特点

GraphPad Prism mac是一款专业的统计和绘图软件&#xff0c;主要用于生物医学研究、实验设计和数据分析。 GraphPad Prism mac功能和特点 数据导入和整理&#xff1a;GraphPad Prism 可以导入各种数据格式&#xff0c;并提供直观的界面用于整理、编辑和管理数据。用户可以轻松…...

WWW 指南-万维网联盟(World Wide Web)

WWW - 万维网联盟 WWW通常称为网络。 web是一个世界各地的计算机网络。 电脑在Web上使用标准语言沟通。 万维网联盟&#xff08;W3C&#xff09;制定了Web标准 什么是WWW&#xff1f; WWW 代表 World Wide Web(万维网)万维网常常被称为 网络网络是世界各地的计算机网络网络中…...

Linux网络编程之TCP/IP实现高并发网络服务器设计指南

目录 引言&#xff1a; 多进程服务器 例程分享&#xff1a; 多线程服务器 例程分享&#xff1a; I/O多路复用服务器 select 例程分享&#xff1a; poll 例程分享&#xff1a; epoll 例程分享&#xff1a; 总结建议 引言&#xff1a; 随着互联网的迅猛发展&#xff…...

【SpringBoot实战】基于阿里云实现文件上传

【SpringBoot实战】基于阿里云实现文件上传 在实际项目开发中&#xff0c;不可避免地会使用到阿里云OSS进行文件存储。尽管阿里云有详细的开发文档&#xff0c;但本篇博客的目的是让我们能够用简明的代码快速实现这个功能。 引入依赖 <dependencies><!-- 阿里云oss…...

大数据技术学习笔记(十一)—— Flume

目录 1 Flume 概述1.1 Flume 定义1.2 Flume 基础架构 2 Flume 安装3 Flume 入门案例3.1 监控端口数据3.2 实时监控单个追加文件3.3 实时监控目录下多个新文件3.4 实时监控目录下的多个追加文件 4 Flume 进阶4.1 Flume 事务4.2 Flume Agent 内部原理4.3 Flume 拓扑结构4.3.1 简单…...

电路设计时,继电器线圈、风扇电机绕组等感性负载必须有续流二极管。

续流二极管(也常被称为“自由轮流二极管”或“反向并联二极管”)在感性负载电路中的应用非常重要,尤其是在继电器线圈、风扇电机绕组等设备中。感性负载是指那些在其线圈中会产生感应电动势的负载,例如电动机、变压器和继电器等。当这些设备的电源被切断时,它们的线圈会因…...

Mongodb基础介绍与应用场景

NoSql 解决方案第二种 Mongodb MongoDB 是一款开源 高性能 无模式的文档型数据库 当然 它是NoSql数据库中的一种 是最像关系型数据库的 非关系型数据库 首先 最需要注意的是 无模式的文档型数据库 这个需要后面我们看到它的数据才能明白 其次是 最像关系型数据库的非关系型数据…...

mysql参数配置binlog

官网地址&#xff1a; MySQL :: MySQL Replication :: 2.6.4 Binary Logging Options and Variables 欢迎关注留言&#xff0c;我是收集整理小能手&#xff0c;工具翻译&#xff0c;仅供参考&#xff0c;笔芯笔芯. MySQL 复制 / ... / 二进制日志记录选项和变量 2.6.4 二进…...

pytorch常用的几个函数详解

文章目录 view基本用法自动计算维度保持原始数据不变 t函数功能语法返回值示例注意事项 permute() 函数基本概念permute() 函数的使用 unsqueeze() 函数基本概念unsqueeze() 函数的使用 squeeze() 函数基本概念squeeze() 函数的使用 transpose() 函数基本概念transpose() 函数的…...

Linux下安装Flume

1 下载Flume Welcome to Apache Flume — Apache Flume 下载1.9.0版本 2 上传服务器并解压安装 3 删除lib目录下的guava-11.0.2.jar &#xff08;如同服务器安装了hadoop&#xff0c;则删除&#xff0c;如没有安装hadoop则保留这个文件&#xff0c;否则无法启动flume&#…...

20231225使用BLE-AnalyzerPro WCH升级版BLE-PRO蓝牙分析仪抓取BLE广播数据

20231225使用BLE-AnalyzerPro WCH升级版BLE-PRO蓝牙分析仪抓取BLE广播数据 2023/12/25 20:05 结论&#xff1a;硬件蓝牙分析仪 不一定比 手机端的APK的效果好&#xff01; 亿佰特E104-2G4U04A需要3片【单通道】&#xff0c;电脑端的UI为全英文的。 BLE-AnalyzerPro WCH升级版B…...

.net6使用Sejil可视化日志

&#xff08;关注博主后&#xff0c;在“粉丝专栏”&#xff0c;可免费阅读此文&#xff09; 之前介绍了这篇.net 5使用LogDashboard_.net 5logdashboard rootpath-CSDN博客 这篇文章将会更加的简单&#xff0c;最终的效果都是可视化日志。 在程序非常庞大的时候&…...

mysql(51) : 大数据导出为insert

代码 import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Objects;public class 大数据导出为insert {public …...

MFC查找错误的方法

在visual studio2005上Debug总是会出现各种问题&#xff0c;比如指针错误&#xff0c;乱码等&#xff0c;无法正确查看变量的值&#xff0c;这时候可以使用AfxMessageBox()方法对数据进行弹窗输出&#xff0c;但AfxMessageBox()函数只支持CString数据输出&#xff0c;我们就需要…...

Jave EE 网络原理之网络层与数据链路层

文章目录 1. 网络层1.1 IP 协议1.1.1 协议头格式1.1.2 地址管理1.1.2.1 认识 IP 地址 1.1.3 路由选择 2. 数据链路层2.1 认识以太网2.1.1 以太网帧格式2.1.2 DNS 应用层协议 1. 网络层 网络层要做的事情&#xff0c;主要是两个方面 地址管理 &#xff08;制定一系列的规则&am…...

ElasticSearch 使用映射定义索引结构

动态映射 dynamic 可选值解释true默认值&#xff0c;启用动态映射&#xff0c;新增的字段会添加到映射中runtime查询时动态添加到映射中false禁用动态映射&#xff0c;忽略未知字段strict发现未知字段&#xff0c;抛出异常 显示映射 创建映射 PUT user {"mappings&qu…...

HTML---网页布局

目录 文章目录 一.常见的网页布局 二.标准文档流 标准文档流常见标签 三.display属性 四.float属性 总结 一.常见网页布局 二.标准文档流 标准文档流常见标签 标准文档流的组成 块级元素<div>、<p>、<h1>-<h6>、<ul>、<ol>等内联元素<…...

python 普通存款(单利)计算公式:

python 普通存款&#xff08;单利&#xff09;计算公式&#xff1a; 代码如下&#xff1a; #普通存款 单利计算公式&#xff1a;a:原值&#xff0c;n:计算年限&#xff0c;li&#xff1a;利率&#xff08;小数&#xff09;, def danli(a,n,li):print("普通存款(单利)计…...

什么是 PHP 内存溢出 ?遇到了要如何解决呢 ?

PHP内存溢出指的是在PHP应用程序中&#xff0c;分配给脚本执行的内存超出了PHP配置文件中设置的限制。当脚本尝试使用比可用内存更多的内存时&#xff0c;就会发生内存溢出错误。 一、内存溢出可能由以下几个原因引起&#xff1a; 循环引用&#xff1a;如果存在循环引用&#…...

本地使用 docker 运行OpenSearch + Dashboard + IK 分词插件

准备基础镜像 注意一定要拉取和当前 IK 分词插件版本一致的 OpenSearch 镜像: https://github.com/aparo/opensearch-analysis-ik/releases 写这篇文章的时候 IK 最新版本 2.11.0, 而 dockerhub 上 OpenSearch 最新版是 2.11.1 如果版本不匹配的话是不能用的, 小版本号对不上…...

【JavaEE初阶一】线程的概念与简单创建

1. 认识线程&#xff08;Thread&#xff09; 1.1 关于线程 1.1.1 线程是什么 由前一节的内容可知&#xff0c;进程在进行频繁的创建和销毁的时候&#xff0c;开销比较大&#xff08;主要体现在资源的申请和释放上&#xff09;&#xff0c;线程就是为了解决上述产生的问题而提…...

三叠云工程劳务管理,优化建筑施工管理,提升效率与质量

随着建筑行业的蓬勃发展&#xff0c;工程施工现场管理变得愈发复杂。传统的人员管理方式已经无法满足企业快速发展的需求。如何提高施工效率、优化人力资源管理成为了建筑企业亟待解决的问题。逐渐走向数字化的工程建设行业&#xff0c;急需一种足以匹配这一时代变革、高效管理…...

RocketMQ连接报错RemotingConnectException: connect to <192.168.57.129:9876>解决

文章目录 前言一、RocketMQ 连接报错处理1.1 报错信息1.2 修改 broker.conf 文件1.3 Linux 开放端口1.4 项目启动成功 前言 上一篇文章&#xff1a;基于SpringBoot整合RocketMQ异步发送短信功能在项目启动的过程中报了 RocketMQ 连接错误。针对这个问题&#xff0c;本文给予记…...

设计模式--桥接模式

实验9&#xff1a;桥接模式 本次实验属于模仿型实验&#xff0c;通过本次实验学生将掌握以下内容&#xff1a; 1、理解桥接模式的动机&#xff0c;掌握该模式的结构&#xff1b; 2、能够利用桥接模式解决实际问题。 [实验任务]&#xff1a;两个维度的桥接模式 用桥接模式…...