UART 串口通信:从帧格式到收发回显(附完整 Verilog 实操)

本文是《FPGA入门到实战》专栏第14篇。第13篇完成了 SPI Master 设计,本篇转向最常用的异步串行通信接口------UART。我们将从 8N1 帧格式、波特率误差与中点采样讲起,完成发送模块、16 倍过采样接收模块和字符回显顶层,并在 Vivado 中用 Testbench 验证完整收发链路。


1. UART 为什么适合 FPGA 入门

UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)只需要两根数据线:

  • TX:发送端输出
  • RX:接收端输入

它不传输独立时钟,通信双方提前约定波特率和帧格式。开发板通过 USB 转串口芯片连接电脑后,就可以使用串口助手观察 FPGA 的输出,或向 FPGA 发送命令。

UART 特别适合作为 FPGA 的第一个通信接口,原因有三点:

  1. 信号少,硬件连接简单;
  2. 时序直观,逻辑分析仪和示波器都容易观察;
  3. 上位机工具成熟,调试成本低。

不过,"没有时钟线"也意味着接收端必须自己恢复采样节奏。波特率误差、起始位检测和亚稳态处理,正是 UART 设计的核心。


2. UART 帧格式

2.1 8N1 的含义

工程中最常见的配置是 8N1

  • 8:8 个数据位;
  • N:无奇偶校验;
  • 1:1 个停止位。

线路空闲时保持高电平,一帧数据从低电平起始位开始:

text 复制代码
空闲    起始位       8 个数据位(低位先发)          停止位   空闲
 1   |    0    | D0 D1 D2 D3 D4 D5 D6 D7 |    1    |  1

例如发送 8'h55,二进制为 0101_0101。UART 低位先发,因此线上顺序为:

text 复制代码
起始位  D0 D1 D2 D3 D4 D5 D6 D7  停止位
   0     1  0  1  0  1  0  1  0      1

2.2 波特率与位时间

波特率表示每秒传输的符号数。在普通 UART 中,一个符号就是一个数据位,因此 115200 baud 的位时间为:

text 复制代码
Tbit = 1 / 115200 ≈ 8.6806 us

一帧 8N1 数据共 10 位,所以理论吞吐量为:

text 复制代码
115200 / 10 = 11520 Byte/s

注意:115200 baud 不等于每秒传输 115200 字节,起始位和停止位同样占用时间。


3. 波特率计数器与误差

FPGA 使用系统时钟计数产生 UART 位周期。假设系统时钟为 50 MHz,目标波特率为 115200:

text 复制代码
计数周期 = 50_000_000 / 115200 ≈ 434.03

取整数 434 后,实际波特率为:

text 复制代码
50_000_000 / 434 ≈ 115207.37

误差约为 0.0064%,完全可以接受。通用计算式如下:

text 复制代码
误差率 = |实际波特率 - 目标波特率| / 目标波特率 × 100%

UART 收发两端的总误差过大时,采样点会逐位偏移。工程中通常希望双方累计误差控制在 2% 以内;帧越长,对误差越敏感。

计算分频值时必须先检查系统时钟和波特率是否匹配。整数除法会截断小数,不要默认任意参数组合都能得到低误差。


4. UART 发送模块设计

4.1 发送流程

发送模块接收到单周期 tx_start 后,依次输出:

  1. 1 个起始位;
  2. 8 个数据位,低位先发;
  3. 1 个停止位;
  4. 拉低 tx_busy,输出一个周期的 tx_done

把完整帧暂存为 {停止位, 数据, 起始位},发送时按位索引输出,代码会非常清晰。

4.2 完整代码

verilog 复制代码
module uart_tx #(
  parameter integer CLK_FREQ  = 50_000_000,
  parameter integer BAUD_RATE = 115_200
) (
  input  wire       clk     ,  // 系统时钟
  input  wire       rstn    ,  // 低有效复位
  input  wire       tx_start,  // 单周期发送请求
  input  wire [7:0] tx_data ,  // 待发送数据
  output reg        tx      ,  // UART 发送信号
  output reg        tx_busy ,  // 发送忙标志
  output reg        tx_done    // 单周期完成脉冲
);

  localparam integer BAUD_CNT_MAX = CLK_FREQ / BAUD_RATE - 1;

  reg [31:0] baud_cnt;
  reg [3:0]  bit_cnt;
  reg [9:0]  tx_frame;

  always @(posedge clk or negedge rstn)
    begin
      if (rstn == 1'b0)
        begin
          baud_cnt <= 32'd0;
          bit_cnt  <= 4'd0;
          tx_frame <= 10'h3ff;
          tx       <= 1'b1;
          tx_busy  <= 1'b0;
          tx_done  <= 1'b0;
        end
      else
        begin
          tx_done <= 1'b0;

          if ((tx_start == 1'b1) && (tx_busy == 1'b0))
            begin
              tx_frame <= {1'b1, tx_data, 1'b0};
              baud_cnt <= 32'd0;
              bit_cnt  <= 4'd0;
              tx       <= 1'b0;
              tx_busy  <= 1'b1;
            end
          else if (tx_busy == 1'b1)
            begin
              if (baud_cnt == BAUD_CNT_MAX)
                begin
                  baud_cnt <= 32'd0;

                  if (bit_cnt == 4'd9)
                    begin
                      bit_cnt <= 4'd0;
                      tx      <= 1'b1;
                      tx_busy <= 1'b0;
                      tx_done <= 1'b1;
                    end
                  else
                    begin
                      bit_cnt <= bit_cnt + 1'b1;
                      tx      <= tx_frame[bit_cnt + 1'b1];
                    end
                end
              else
                begin
                  baud_cnt <= baud_cnt + 1'b1;
                end
            end
          else
            begin
              baud_cnt <= 32'd0;
              bit_cnt  <= 4'd0;
              tx       <= 1'b1;
            end
        end
    end

endmodule

4.3 为什么在启动时锁存数据

tx_data 可能在发送过程中变化。如果直接从输入端口逐位读取,帧内容就可能前后不一致。因此发送开始时必须把数据锁存到 tx_frame,后续只操作内部寄存器。

tx_start 应当是单周期脉冲。模块忙碌时的新请求会被忽略,上层必须先检查 tx_busy,或者在前面增加 FIFO。


5. UART 接收为什么要在中间采样

接收端不知道起始位何时到来,只能先检测 RX 从高变低,再按照约定的波特率采样。

如果在数据跳变边缘附近采样,轻微抖动就可能读错。最稳妥的位置是每一位的中心:

text 复制代码
RX   ________----------------________
          ↑         ↑
       跳变边缘   中心采样点

常用方法是生成波特率 16 倍的采样节拍:

  1. 检测到低电平后等待 8 个采样节拍;
  2. 在起始位中心再次确认电平为低;
  3. 此后每隔 16 个节拍采样一个数据位;
  4. 最后检查停止位是否为高。

16 倍过采样并不代表必须保存 16 个样本。入门设计只在中点取样即可;抗干扰要求更高时,可以在中心附近连续取 3 次并做多数表决。


6. 16 倍过采样接收模块

6.1 输入同步

RX 来自 FPGA 外部,与系统时钟异步,直接送入状态机可能产生亚稳态。首先用两级寄存器同步:

verilog 复制代码
  reg rx_r1;
  reg rx_r2;

  always @(posedge clk or negedge rstn)
    begin
      if (rstn == 1'b0)
        begin
          rx_r1 <= 1'b1;
          rx_r2 <= 1'b1;
        end
      else
        begin
          rx_r1 <= rx;
          rx_r2 <= rx_r1;
        end
    end

两级同步会带来两个系统时钟周期的延迟,但相对于一个 UART 位周期非常小,可以忽略。

6.2 完整代码

verilog 复制代码
module uart_rx #(
  parameter integer CLK_FREQ  = 50_000_000,
  parameter integer BAUD_RATE = 115_200
) (
  input  wire       clk        ,  // 系统时钟
  input  wire       rstn       ,  // 低有效复位
  input  wire       rx         ,  // UART 接收信号
  output reg  [7:0] rx_data    ,  // 接收数据
  output reg        rx_valid   ,  // 单周期数据有效脉冲
  output reg        frame_error   // 停止位错误脉冲
);

  localparam integer SAMPLE_CNT_MAX = CLK_FREQ / (BAUD_RATE * 16) - 1;
  localparam [1:0] STATE_IDLE  = 2'd0;
  localparam [1:0] STATE_START = 2'd1;
  localparam [1:0] STATE_DATA  = 2'd2;
  localparam [1:0] STATE_STOP  = 2'd3;

  reg        rx_r1;
  reg        rx_r2;
  reg [31:0] sample_div_cnt;
  reg [3:0]  sample_cnt;
  reg [2:0]  bit_cnt;
  reg [7:0]  rx_shift;
  reg [1:0]  state;

  wire sample_tick;

  assign sample_tick = (sample_div_cnt == SAMPLE_CNT_MAX);

  always @(posedge clk or negedge rstn)
    begin
      if (rstn == 1'b0)
        begin
          rx_r1 <= 1'b1;
          rx_r2 <= 1'b1;
        end
      else
        begin
          rx_r1 <= rx;
          rx_r2 <= rx_r1;
        end
    end

  always @(posedge clk or negedge rstn)
    begin
      if (rstn == 1'b0)
        begin
          sample_div_cnt <= 32'd0;
        end
      else if (sample_tick == 1'b1)
        begin
          sample_div_cnt <= 32'd0;
        end
      else
        begin
          sample_div_cnt <= sample_div_cnt + 1'b1;
        end
    end

  always @(posedge clk or negedge rstn)
    begin
      if (rstn == 1'b0)
        begin
          sample_cnt <= 4'd0;
          bit_cnt    <= 3'd0;
          rx_shift   <= 8'd0;
          rx_data    <= 8'd0;
          rx_valid   <= 1'b0;
          frame_error <= 1'b0;
          state      <= STATE_IDLE;
        end
      else
        begin
          rx_valid    <= 1'b0;
          frame_error <= 1'b0;

          case (state)
            STATE_IDLE:
              begin
                sample_cnt <= 4'd0;
                bit_cnt    <= 3'd0;

                if (rx_r2 == 1'b0)
                  begin
                    state <= STATE_START;
                  end
              end

            STATE_START:
              begin
                if (sample_tick == 1'b1)
                  begin
                    if (sample_cnt == 4'd7)
                      begin
                        sample_cnt <= 4'd0;

                        if (rx_r2 == 1'b0)
                          begin
                            state <= STATE_DATA;
                          end
                        else
                          begin
                            state <= STATE_IDLE;
                          end
                      end
                    else
                      begin
                        sample_cnt <= sample_cnt + 1'b1;
                      end
                  end
              end

            STATE_DATA:
              begin
                if (sample_tick == 1'b1)
                  begin
                    if (sample_cnt == 4'd15)
                      begin
                        sample_cnt       <= 4'd0;
                        rx_shift[bit_cnt] <= rx_r2;

                        if (bit_cnt == 3'd7)
                          begin
                            bit_cnt <= 3'd0;
                            state   <= STATE_STOP;
                          end
                        else
                          begin
                            bit_cnt <= bit_cnt + 1'b1;
                          end
                      end
                    else
                      begin
                        sample_cnt <= sample_cnt + 1'b1;
                      end
                  end
              end

            STATE_STOP:
              begin
                if (sample_tick == 1'b1)
                  begin
                    if (sample_cnt == 4'd15)
                      begin
                        sample_cnt <= 4'd0;
                        state      <= STATE_IDLE;

                        if (rx_r2 == 1'b1)
                          begin
                            rx_data  <= rx_shift;
                            rx_valid <= 1'b1;
                          end
                        else
                          begin
                            frame_error <= 1'b1;
                          end
                      end
                    else
                      begin
                        sample_cnt <= sample_cnt + 1'b1;
                      end
                  end
              end

            default:
              begin
                sample_cnt <= 4'd0;
                bit_cnt    <= 3'd0;
                state      <= STATE_IDLE;
              end
          endcase
        end
    end

endmodule

6.3 停止位错误有什么用

如果停止位中心仍为低,说明当前帧不符合 8N1 格式。常见原因包括:

  • 双方波特率不一致;
  • 数据位、校验位或停止位配置不一致;
  • 接线错误或电平标准不匹配;
  • 线路干扰导致停止位被拉低。

因此不要只输出 rx_valid,同时保留 frame_error 能显著提高板级排错效率。


7. 字符回显顶层

回显实验的数据链路如下:

text 复制代码
电脑串口助手 -> USB 转串口 TX -> FPGA RX
FPGA uart_rx -> 缓冲寄存器 -> uart_tx
FPGA TX -> USB 转串口 RX -> 电脑串口助手

接收到一个字节后,顶层把它暂存起来;发送模块空闲时发回电脑。单字节缓冲可以处理普通连续输入,如果上位机突发发送大量数据,应替换为 FIFO。

verilog 复制代码
module uart_echo_top #(
  parameter integer CLK_FREQ  = 50_000_000,
  parameter integer BAUD_RATE = 115_200
) (
  input  wire clk ,  // 系统时钟
  input  wire rstn,  // 低有效复位
  input  wire rx  ,  // UART 接收信号
  output wire tx     // UART 发送信号
);

  wire [7:0] rx_data;
  wire       rx_valid;
  wire       frame_error;
  wire       tx_busy;
  wire       tx_done;

  reg [7:0] tx_data;
  reg       tx_start;
  reg [7:0] pending_data;
  reg       pending_valid;

  uart_rx #(
    .CLK_FREQ (CLK_FREQ ),
    .BAUD_RATE(BAUD_RATE)
  ) u_uart_rx (
    .clk        (clk        ),
    .rstn       (rstn       ),
    .rx         (rx         ),
    .rx_data    (rx_data    ),
    .rx_valid   (rx_valid   ),
    .frame_error(frame_error)
  );

  uart_tx #(
    .CLK_FREQ (CLK_FREQ ),
    .BAUD_RATE(BAUD_RATE)
  ) u_uart_tx (
    .clk     (clk     ),
    .rstn    (rstn    ),
    .tx_start(tx_start),
    .tx_data (tx_data ),
    .tx      (tx      ),
    .tx_busy (tx_busy ),
    .tx_done (tx_done )
  );

  always @(posedge clk or negedge rstn)
    begin
      if (rstn == 1'b0)
        begin
          tx_data       <= 8'd0;
          tx_start      <= 1'b0;
          pending_data  <= 8'd0;
          pending_valid <= 1'b0;
        end
      else
        begin
          tx_start <= 1'b0;

          if ((pending_valid == 1'b1) && (tx_busy == 1'b0))
            begin
              tx_data       <= pending_data;
              tx_start      <= 1'b1;
              pending_valid <= 1'b0;
            end

          if (rx_valid == 1'b1)
            begin
              pending_data  <= rx_data;
              pending_valid <= 1'b1;
            end
        end
    end

endmodule

frame_errortx_done 在最小回显工程中没有连接到其他逻辑,调试时可以用 ILA 观察。正式项目可增加错误计数器,并把发送完成信号用于数据队列管理。


8. Testbench 仿真完整收发链路

仿真不必真的等待 50 MHz 与 115200 baud 的长时间比例。把系统时钟设为 16 MHz、波特率设为 1 MHz,可以显著缩短仿真时间,同时保持 16 倍采样关系和协议行为不变。

verilog 复制代码
`timescale 1ns/1ps

module tb_uart_echo_top;

  localparam integer CLK_FREQ  = 16_000_000;
  localparam integer BAUD_RATE = 1_000_000;
  localparam integer BIT_TIME  = 1_000_000_000 / BAUD_RATE;

  reg  clk;
  reg  rstn;
  reg  rx;
  wire tx;

  integer bit_index;
  reg [7:0] received_data;

  uart_echo_top #(
    .CLK_FREQ (CLK_FREQ ),
    .BAUD_RATE(BAUD_RATE)
  ) u_dut (
    .clk (clk ),
    .rstn(rstn),
    .rx  (rx  ),
    .tx  (tx  )
  );

  initial
    begin
      clk = 1'b0;
      forever #31.25 clk = ~clk;
    end

  task send_uart_byte;
    input [7:0] data;
    integer index;
    begin
      rx = 1'b0;
      #(BIT_TIME);

      for (index = 0; index < 8; index = index + 1)
        begin
          rx = data[index];
          #(BIT_TIME);
        end

      rx = 1'b1;
      #(BIT_TIME);
    end
  endtask

  task receive_uart_byte;
    output [7:0] data;
    integer index;
    begin
      @(negedge tx);
      #(BIT_TIME + BIT_TIME / 2);

      for (index = 0; index < 8; index = index + 1)
        begin
          data[index] = tx;
          #(BIT_TIME);
        end

      if (tx != 1'b1)
        begin
          $display("ERROR: stop bit is low");
        end
    end
  endtask

  initial
    begin
      rstn          = 1'b0;
      rx            = 1'b1;
      received_data = 8'd0;

      repeat (10) @(posedge clk);
      rstn = 1'b1;
      repeat (10) @(posedge clk);

      fork
        send_uart_byte(8'h55);
        receive_uart_byte(received_data);
      join

      if (received_data == 8'h55)
        begin
          $display("PASS: echo data = 0x%02h", received_data);
        end
      else
        begin
          $display("FAIL: echo data = 0x%02h", received_data);
        end

      #(BIT_TIME * 2);
      $finish;
    end

endmodule

8.1 需要观察的波形

在 Vivado 仿真窗口中至少添加以下信号:

  • rx:Testbench 发送给 FPGA 的串行帧;
  • u_uart_rx.state:接收状态机;
  • u_uart_rx.sample_cnt:16 倍采样计数;
  • u_uart_rx.rx_datarx_valid:接收结果;
  • tx_starttx_busy:发送握手;
  • tx:FPGA 回显的数据帧。

正确结果应满足:

  1. rx 先出现 8'h55 的 8N1 帧;
  2. 停止位中心产生一个周期的 rx_valid
  3. 顶层在发送模块空闲时拉高 tx_start
  4. tx 输出内容相同的 8N1 帧;
  5. Testbench 打印 PASS: echo data = 0x55

如果波形中所有数据位整体错位,优先检查位时间;如果只有位顺序相反,检查是否误写成高位先发。


9. Vivado 工程与 XDC 约束

9.1 添加源码

在 Vivado 中依次添加:

  1. uart_tx.v
  2. uart_rx.v
  3. uart_echo_top.v
  4. 仿真文件 tb_uart_echo_top.v

uart_echo_top 设置为综合顶层,将 tb_uart_echo_top 设置为仿真顶层。

9.2 约束示例

下面仅展示约束格式,管脚编号和电平标准必须替换为开发板原理图中的实际值:

tcl 复制代码
set_property PACKAGE_PIN <CLK_PIN> [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
create_clock -period 20.000 -name sys_clk [get_ports clk]

set_property PACKAGE_PIN <RST_PIN> [get_ports rstn]
set_property IOSTANDARD LVCMOS33 [get_ports rstn]

set_property PACKAGE_PIN <UART_RX_PIN> [get_ports rx]
set_property IOSTANDARD LVCMOS33 [get_ports rx]

set_property PACKAGE_PIN <UART_TX_PIN> [get_ports tx]
set_property IOSTANDARD LVCMOS33 [get_ports tx]

不要直接复制其他开发板的管脚号。约束错误可能导致串口无数据,严重时还可能造成 IO 电气冲突。


10. 下板回显步骤

  1. 综合、实现并生成比特流;
  2. 通过 JTAG 下载到 FPGA;
  3. 确认电脑识别到开发板的 USB 转串口;
  4. 串口助手选择正确 COM 口;
  5. 设置为 115200、8 数据位、无校验、1 停止位;
  6. 关闭硬件流控;
  7. 发送字符串 Hello FPGA
  8. 接收窗口应显示相同内容。

如果开发板没有板载 USB 转串口,可以使用外置 USB-TTL 模块。连接关系为交叉连接:

text 复制代码
USB-TTL TX -> FPGA RX
USB-TTL RX -> FPGA TX
USB-TTL GND -> FPGA GND

必须共地,并确认 USB-TTL 模块输出电平与 FPGA IO 电压兼容。不要把 RS-232 的正负电压信号直接接到 FPGA 引脚。


11. 常见问题排查

11.1 串口助手完全收不到数据

按以下顺序检查:

  1. COM 口是否选择正确;
  2. FPGA 是否已经下载最新比特流;
  3. TX/RX 是否交叉连接;
  4. 双方是否共地;
  5. XDC 管脚是否与原理图一致;
  6. CLK_FREQ 是否等于真实系统时钟;
  7. tx 空闲时是否保持高电平。

可以先让发送模块周期性发送固定字节 8'h550x55 在线上形成交替电平,非常适合用示波器测量位宽。

11.2 收到乱码

乱码通常不是"字符编码问题",而是帧采样错误:

  • 波特率设置不一致;
  • 系统时钟参数填写错误;
  • 发送端和接收端的数据位/校验位/停止位不同;
  • 时钟分频误差过大;
  • 输入信号存在严重毛刺。

先测量一个数据位的持续时间,再对照理论值 8.6806 us,比盲目改代码有效得多。

11.3 第一个字节正确,连续数据丢失

单字节缓冲只能吸收有限的时序差异。上位机连续发送而下游来不及处理时,新数据会覆盖旧数据。解决方法是:

  1. uart_rx 后加入同步 FIFO;
  2. 使用 empty 控制发送启动;
  3. 只有在 FIFO 非空且 tx_busy 为低时读取一个字节;
  4. 根据最大突发长度选择 FIFO 深度。

这也是后续 FIFO 章节与 UART 工程结合的典型场景。

11.4 偶尔出现错误字节

如果大部分数据正确、偶尔错误,应重点检查:

  • RX 是否经过两级同步;
  • 电源和地连接是否可靠;
  • 线缆是否过长;
  • IO 电平是否匹配;
  • 是否需要中心 3 点多数表决;
  • 是否存在跨时钟域数据直接使用的问题。

可以把 rx_r2statesample_cntrx_shiftframe_error 加入 ILA,在错误发生时触发抓取。


12. UART 与 SPI 对比

对比项 UART SPI
时钟 无独立时钟,双方约定波特率 Master 输出 SCLK
信号线 TX、RX,通常再加 GND SCLK、MOSI、MISO、CS
双工方式 可全双工 全双工
从设备选择 通常点对点 使用独立 CS 选择多个从设备
速率 常见 9600~数 Mbps 常见数 MHz~数十 MHz
设计重点 波特率误差、中点采样 CPOL/CPHA、片选与边沿关系
典型用途 调试日志、命令交互、低速模块 ADC、DAC、Flash、传感器

UART 适合低成本点对点通信,SPI 适合板内高速外设访问。两者没有绝对优劣,应根据设备接口、距离、速率和引脚数量选择。


13. 工程扩展方向

完成基础回显后,可以继续扩展:

  1. 增加奇偶校验 :发送端生成校验位,接收端输出 parity_error
  2. 增加 FIFO:支持连续数据和模块间速率解耦;
  3. 增加命令解析器:识别帧头、长度、命令字、数据和校验和;
  4. 加入超时判断:半帧停顿过久时自动丢弃异常数据;
  5. 实现可配置波特率:通过寄存器动态修改分频参数;
  6. 连接 AXI-Stream:让 UART 与 DMA、FIFO 或处理器系统对接。

真正的工程协议通常不是"收到一个字节就处理一个字节",而是在 UART 之上再定义数据帧。建议先把底层收发模块验证稳定,再实现上层协议,避免把链路问题和解析问题混在一起排查。


14. 本篇总结

本篇完成了 UART 从原理到下板的完整闭环:

  • 理解 8N1 帧格式和低位先发规则;
  • 根据系统时钟计算波特率分频并评估误差;
  • 实现带 busy/done 握手的 UART 发送模块;
  • 使用两级同步和 16 倍过采样实现接收模块;
  • 检查停止位并输出 frame_error
  • 搭建字符回显顶层和自检 Testbench;
  • 掌握串口乱码、丢字节和无输出的排查顺序;
  • 明确 FIFO 在连续通信中的作用。

下一篇将进入 I2C 通信,完成起始、停止、应答、地址帧和 EEPROM 读写时序设计。与 UART 相比,I2C 的双向开漏信号和 ACK 处理更复杂,也更能体现状态机分层设计的重要性。

相关推荐
仲南音6 小时前
Xilinx FPGA—— DDR4读写
fpga开发
思尔芯S2C10 小时前
思尔芯RCF RTL自动分割工具,助力大规模AI/HPC芯片原型验证
人工智能·fpga开发·内存模型·自动编译·ddr5·prototyping·原型验证
Terasic友晶科技1 天前
答疑解惑|Agilex 3 FPGA QSPI时钟设置注意事项
fpga开发·de23-lite·terasic·qspi flash
国科安芯1 天前
卫星星务计算机数据管理中SEU容错机制的技术实现研究——以AS32S601存储器ECC架构为例
网络·单片机·算法·fpga开发·架构·云计算·risc-v
国科安芯1 天前
卫星电推进系统阀门驱动控制中的宽温域高可靠MCU应用研究——基于AS32S601电气特性与辐照试验数据的工程分析
单片机·嵌入式硬件·fpga开发·云计算·信息与通信·卫星·抗辐射芯片
郝旭帅电子设计团队2 天前
图像处理是什么
图像处理·fpga开发·郝旭帅电子设计团队·郝旭帅
ARM|X86+FPGA工业主板厂家2 天前
基于ARM+FPGA平台在精密测控领域的应用:8路同步IEPE振动采集系统
arm开发·fpga开发
weixin_452600692 天前
D2632大电流低差压可调线性稳压器介绍,D2632可替换MIC29302
fpga开发·机器人·无人机·电动工具·车载摄像头
ARM|X86+FPGA工业主板厂家2 天前
基于ARM+FPGA平台在精密测控领域的应用:ZYNQ千兆以太网高速数据记录仪
arm开发·fpga开发