平台:vivado2017.4

芯片:xc7k325tffg-2

记录一下学习PCIE接口的过程。

使用XILINX官方的PCIE核,实现使用windriver加载并测试读写。方案主要在XILINX官方的例子上进行了修改,可以更加方便的实现对PCIE读写。

目录

新建工程

源码分析

修改源码

下载调试


新建工程

新建PCIE核。

这里选择第一种,XILINX提供了三种PCIE的相关的IP核。

第一种:7 series intergrated block for pci express,开发难度较大,需要学习PCIE的传输的TLP包内容。适合学校,Github有关于他的开源项目,RIffa,可以实现FPGA和上位机之间的高速通信。

第二种:AXI Memory Mapped to PCI Express,axi_pcie,Xilinx将PCIE进一步封装,用户无需掌握复杂的PCIE协议,既可以实现PCIE通信。难度适中。

第三种:DMA/Bridge subsystem for PCI express,俗称XDMA,Xilinx将pcie再一次封装,用户只需配置IP核后,既可以高速数据传输。

这里我们学习使用第一种IP,来实现PCIE的复杂通信。

IP的配置,根据你实际的硬件情况设置。这里不再多说。

我的配置如下,X4,2.5GT/S,参考时钟100mhz,axi接口时钟125mhz,axi接口位宽64bit。

新建IP后,直接打开example design。

源码分析

可以看到代码分几部分组成。

从图中可以看出来,模块分为几个部分。

EP_TX:发送引擎,用于组装,发送TLP。

EP_RX:接收引擎,用于接收,解析TLP。

EP_MEM:用于控制FPGA的存储器的读写。

RX引擎解析一个TLP请求,判断此TLP是哪一种类型的,从中提出所需要的信息,从而传递给内存控制器。从一个TLP中解析出来的类容大致分为两类。

这里重点看下面解析出来的wr_addr,we_be,wr_data,wr_en,wr_busy等信号。

接收引擎从TLP中解析出来的数据,是控制写入内存的数据。

从信号的名字和注释可以看出来,这里面信号的作用。

Wr_addr

写入的地址

Wr_be

写数据字节使能

Wr_data

写入的数据

wr_en

写使能

wr_busy

写忙碌,根据此信号来进行判断处理TLP是否完成

TX发送引擎模块,主要完成对TLP的组装和发送。

从信号的名字可以看出来,大致的过程是从内存中获取读出的数据,在TX发送引擎打包TLP发送出去。

具体的过程为接收引擎接收到TLP解析,解析命令数读还是写。当TX引擎接收到读取命令后,产生读使能,将数据从内存中获取,并且在打包TLP将数据发送出去。

EP_MEM,此模块作用为写入从RX接收到的写入数据(内存写入或者IO写入),并提供从内存中读出数据,以响应TX模块的TLP读。

阅读手册PG054看一看PIO模式下的读写操作时序。

PIO读:在RX接收到一个完整的TLP请求,m_axis_rx_tready就拉低,表示不能传输。等待TX发出compl_done_o后,拉高m_axis_rx_tready信号。表示可以进行新一轮饿TLP传输。

PIO写操作:同读操作一致,在RX接收完一个TLP后,m_axis_rx_tready就拉低表示不能继续传输。等待RX模块发出wr_busy_o后,才能接收下一个写处理。

从代码中也可以看出,MEM包涵两个路径,一个是读取,一个是写入。下面来看一下源码。

//-----------------------------------------------------------------------------
//
// (c) Copyright 2010-2011 Xilinx, Inc. All rights reserved.
//
// This file contains confidential and proprietary information
// of Xilinx, Inc. and is protected under U.S. and
// international copyright and other intellectual property
// laws.
//
// DISCLAIMER
// This disclaimer is not a license and does not grant any
// rights to the materials distributed herewith. Except as
// otherwise provided in a valid license issued to you by
// Xilinx, and to the maximum extent permitted by applicable
// law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND
// WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES
// AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
// BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-
// INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and
// (2) Xilinx shall not be liable (whether in contract or tort,
// including negligence, or under any other theory of
// liability) for any loss or damage of any kind or nature
// related to, arising under or in connection with these
// materials, including for any direct, or any indirect,
// special, incidental, or consequential loss or damage
// (including loss of data, profits, goodwill, or any type of
// loss or damage suffered as a result of any action brought
// by a third party) even if such damage or loss was
// reasonably foreseeable or Xilinx had been advised of the
// possibility of the same.
//
// CRITICAL APPLICATIONS
// Xilinx products are not designed or intended to be fail-
// safe, or for use in any application requiring fail-safe
// performance, such as life-support or safety devices or
// systems, Class III medical devices, nuclear facilities,
// applications related to the deployment of airbags, or any
// other applications that could lead to death, personal
// injury, or severe property or environmental damage
// (individually and collectively, "Critical
// Applications"). Customer assumes the sole risk and
// liability of any use of Xilinx products in Critical
// Applications, subject only to applicable laws and
// regulations governing limitations on product liability.
//
// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS
// PART OF THIS FILE AT ALL TIMES.
//
//-----------------------------------------------------------------------------
// Project    : Series-7 Integrated Block for PCI Express
// File       : PIO_EP_MEM_ACCESS.v
// Version    : 3.3
//--
//-- Description: Endpoint Memory Access Unit. This module provides access functions
//--              to the Endpoint memory aperture.
//--
//--              Read Access: Module returns data for the specifed address and
//--              byte enables selected.
//--
//--              Write Access: Module accepts data, byte enables and updates
//--              data when write enable is asserted. Modules signals write busy
//--              when data write is in progress.
//--
//--------------------------------------------------------------------------------`timescale 1ps/1ps(* DowngradeIPIdentifiedWarnings = "yes" *)
module PIO_EP_MEM_ACCESS  #(parameter TCQ = 1
) (clk,rst_n,// Read Accessrd_addr,     // I [10:0]  Read Addressrd_be,       // I [3:0]   Read Byte Enablerd_data,     // O [31:0]  Read Data// Write Accesswr_addr,     // I [10:0]  Write Addresswr_be,       // I [7:0]   Write Byte Enablewr_data,     // I [31:0]  Write Datawr_en,       // I         Write Enablewr_busy      // O         Write Controller Busy);input            clk;input            rst_n;//  Read Portinput  [10:0]    rd_addr;//读地址input  [3:0]     rd_be;//读数据字节使能(单独读一个数据)output [31:0]    rd_data;//读出来的数据//  Write Portinput  [10:0]    wr_addr;//写地址input  [7:0]     wr_be;//写地址字节使能(单独写一个字节数据)input  [31:0]    wr_data;//写数据input            wr_en;//写使能output           wr_busy;//写wr_en忙碌(表示正在写)//定义参数,实现状态机localparam PIO_MEM_ACCESS_WR_RST   = 3'b000;localparam PIO_MEM_ACCESS_WR_WAIT  = 3'b001;//写等待localparam PIO_MEM_ACCESS_WR_READ  = 3'b010;//读写localparam PIO_MEM_ACCESS_WR_WRITE = 3'b100;//写字节有效wire   [31:0]     rd_data;reg   [31:0]      rd_data_raw_o;//根据读地址的[10:9]指示从哪一个内存中读出数据wire  [31:0]     rd_data0_o, rd_data1_o, rd_data2_o, rd_data3_o;//四个内存读出的数据wire             rd_data0_en, rd_data1_en, rd_data2_en, rd_data3_en;wire             wr_busy;//写忙reg              write_en;//写使能reg   [31:0]     post_wr_data;//写入数据reg   [31:0]     w_pre_wr_data;//预取数据reg   [2:0]      wr_mem_state;//写入状态reg   [31:0]     pre_wr_data;//预取数据存储wire  [31:0]     w_pre_wr_data0;wire  [31:0]     w_pre_wr_data1;wire  [31:0]     w_pre_wr_data2;wire  [31:0]     w_pre_wr_data3;wire  [7:0]      w_pre_wr_data_b0;wire  [7:0]      w_pre_wr_data_b1;wire  [7:0]      w_pre_wr_data_b2;wire  [7:0]      w_pre_wr_data_b3;wire  [7:0]      w_wr_data_b0;wire  [7:0]      w_wr_data_b1;wire  [7:0]      w_wr_data_b2;wire  [7:0]      w_wr_data_b3;// Memory Write Process//  Extract current data bytes. These need to be swizzled//  BRAM storage format ://    data[31:0] = { byte[3], byte[2], byte[1], byte[0] (lowest addr) }
//将预取的数据的不同位放入w_pre_wr_data_bx,方便后续对位有效判断
//assign w_pre_wr_data_b3 = pre_wr_data[31:24];assign w_pre_wr_data_b2 = pre_wr_data[23:16];assign w_pre_wr_data_b1 = pre_wr_data[15:08];assign w_pre_wr_data_b0 = pre_wr_data[07:00];//  Extract new data bytes from payload//  TLP Payload format ://    data[31:0] = { byte[0] (lowest addr), byte[2], byte[1], byte[3] }assign w_wr_data_b3 = wr_data[07:00];assign w_wr_data_b2 = wr_data[15:08];assign w_wr_data_b1 = wr_data[23:16];assign w_wr_data_b0 = wr_data[31:24];always @(posedge clk) beginif ( !rst_n )beginpre_wr_data <= #TCQ 32'b0;post_wr_data <= #TCQ 32'b0;pre_wr_data <= #TCQ 32'b0;write_en   <= #TCQ 1'b0;wr_mem_state <= #TCQ PIO_MEM_ACCESS_WR_RST;end // if !rst_nelsebegincase ( wr_mem_state )PIO_MEM_ACCESS_WR_RST : begin//进入PIO_MEM_ACCESS_WR_RST状态if (wr_en)//判断写使能信号,从RX中获取,表示写开始,跳状态begin // read statewr_mem_state <= #TCQ PIO_MEM_ACCESS_WR_WAIT; //Pipelining happens in RAM's internal output reg.endelsebeginwrite_en <= #TCQ 1'b0;wr_mem_state <= #TCQ PIO_MEM_ACCESS_WR_RST;endend // PIO_MEM_ACCESS_WR_RSTPIO_MEM_ACCESS_WR_WAIT : begin//write_en置零,不对内存写入操作,准备将该地址下的数据读出。跳状态write_en <= #TCQ 1'b0;wr_mem_state <= #TCQ PIO_MEM_ACCESS_WR_READ ;end // PIO_MEM_ACCESS_WR_WAITPIO_MEM_ACCESS_WR_READ : begin//读,将预读取的数据w_pre_wr_data放入pre_wr_data中,跳转状态// Now save the selected BRAM B port data outpre_wr_data <= #TCQ w_pre_wr_data;write_en <= #TCQ 1'b0;wr_mem_state <= #TCQ PIO_MEM_ACCESS_WR_WRITE;end // PIO_MEM_ACCESS_WR_READPIO_MEM_ACCESS_WR_WRITE : begin//判断写入的哪些字节有效,无效的将预先读出的数据替换为现在需要写入的数据,将数据重新写入。//Merge new enabled data and write target BlockRAM location//判断自己有效,将有效的写入,无效的保留post_wr_data <= #TCQ {{wr_be[3] ? w_wr_data_b3 : w_pre_wr_data_b3},{wr_be[2] ? w_wr_data_b2 : w_pre_wr_data_b2},{wr_be[1] ? w_wr_data_b1 : w_pre_wr_data_b1},{wr_be[0] ? w_wr_data_b0 : w_pre_wr_data_b0}};write_en     <= #TCQ 1'b1;wr_mem_state <= #TCQ PIO_MEM_ACCESS_WR_RST;end // PIO_MEM_ACCESS_WR_WRITEdefault : begin// default case stmtwr_mem_state <= #TCQ PIO_MEM_ACCESS_WR_RST;end // defaultendcase // case (wr_mem_state)end // if rst_nend// Write controller busyassign wr_busy = wr_en | (wr_mem_state != PIO_MEM_ACCESS_WR_RST);//  Select BlockRAM output based on higher 2 address bits
//预取出来的数据,根据地址判断从哪个内存中读出always @* begincase ({wr_addr[10:9]}) // synthesis parallel_case full_case2'b00 : w_pre_wr_data = w_pre_wr_data0;2'b01 : w_pre_wr_data = w_pre_wr_data1;2'b10 : w_pre_wr_data = w_pre_wr_data2;2'b11 : w_pre_wr_data = w_pre_wr_data3;endcaseend//  Memory Read Controller//  内存读控制,先根据地址判断从哪个BRAM中读出数据assign rd_data0_en = {rd_addr[10:9]  == 2'b00};assign rd_data1_en = {rd_addr[10:9]  == 2'b01};assign rd_data2_en = {rd_addr[10:9]  == 2'b10};assign rd_data3_en = {rd_addr[10:9]  == 2'b11};//将读出的数据赋值给rd_data_raw_oalways @(rd_addr or rd_data0_o or rd_data1_o or rd_data2_o or rd_data3_o)begincase ({rd_addr[10:9]}) // synthesis parallel_case full_case2'b00 : rd_data_raw_o = rd_data0_o;2'b01 : rd_data_raw_o = rd_data1_o;2'b10 : rd_data_raw_o = rd_data2_o;2'b11 : rd_data_raw_o = rd_data3_o;endcaseend// Handle Read byte enables// 判断读出的数据哪个字节有效,有效给数,无效置零assign rd_data = {{rd_be[0] ? rd_data_raw_o[07:00] : 8'h0},{rd_be[1] ? rd_data_raw_o[15:08] : 8'h0},{rd_be[2] ? rd_data_raw_o[23:16] : 8'h0},{rd_be[3] ? rd_data_raw_o[31:24] : 8'h0}};EP_MEM EP_MEM_inst (.clk_i(clk),.a_rd_a_i_0(rd_addr[8:0]),                              // I [8:0].a_rd_en_i_0(rd_data0_en),                                // I [1:0].a_rd_d_o_0(rd_data0_o),                                  // O [31:0].b_wr_a_i_0(wr_addr[8:0]),                              // I [8:0].b_wr_d_i_0(post_wr_data),                                // I [31:0].b_wr_en_i_0({write_en & (wr_addr[10:9] == 2'b00)}),    // I.b_rd_d_o_0(w_pre_wr_data0[31:0]),                        // O [31:0].b_rd_en_i_0({wr_addr[10:9] == 2'b00}),                 // I.a_rd_a_i_1(rd_addr[8:0]),                              // I [8:0].a_rd_en_i_1(rd_data1_en),                                // I [1:0].a_rd_d_o_1(rd_data1_o),                                  // O [31:0].b_wr_a_i_1(wr_addr[8:0]),                              // [8:0].b_wr_d_i_1(post_wr_data),                                // [31:0].b_wr_en_i_1({write_en & (wr_addr[10:9] == 2'b01)}),    // I.b_rd_d_o_1(w_pre_wr_data1[31:0]),                        // [31:0].b_rd_en_i_1({wr_addr[10:9] == 2'b01}),                 // I.a_rd_a_i_2(rd_addr[8:0]),                              // I [8:0].a_rd_en_i_2(rd_data2_en),                                // I [1:0].a_rd_d_o_2(rd_data2_o),                                  // O [31:0].b_wr_a_i_2(wr_addr[8:0]),                              // I [8:0].b_wr_d_i_2(post_wr_data),                                // I [31:0].b_wr_en_i_2({write_en & (wr_addr[10:9] == 2'b10)}),    // I.b_rd_d_o_2(w_pre_wr_data2[31:0]),                        // I [31:0].b_rd_en_i_2({wr_addr[10:9] == 2'b10}),                 // I.a_rd_a_i_3(rd_addr[8:0]),                              // [8:0].a_rd_en_i_3(rd_data3_en),                                // [1:0].a_rd_d_o_3(rd_data3_o),                                  // O [31:0].b_wr_a_i_3(wr_addr[8:0]),                              // I [8:0].b_wr_d_i_3(post_wr_data),                                // I [31:0].b_wr_en_i_3({write_en & (wr_addr[10:9] == 2'b11)}),    // I.b_rd_d_o_3(w_pre_wr_data3[31:0]),                        // I [31:0].b_rd_en_i_3({wr_addr[10:9] == 2'b11})                  // I);// synthesis translate_offreg  [8*20:1] state_ascii;always @(wr_mem_state)begincase (wr_mem_state)PIO_MEM_ACCESS_WR_RST    : state_ascii <= #TCQ "PIO_MEM_WR_RST";PIO_MEM_ACCESS_WR_WAIT   : state_ascii <= #TCQ "PIO_MEM_WR_WAIT";PIO_MEM_ACCESS_WR_READ   : state_ascii <= #TCQ "PIO_MEM_WR_READ";PIO_MEM_ACCESS_WR_WRITE  : state_ascii <= #TCQ "PIO_MEM_WR_WRITE";default                  : state_ascii <= #TCQ "PIO MEM STATE ERR";endcaseend// synthesis translate_onendmodule

具体的过程分为读和写。

修改源码

今天要做的是,实现对pcie读写通过WINDRIVER控制。

从上面的代码分析,对内存的读写集中在MEM模块,现在只需要对该模块进行修改,并将自己的内容接入即可实现。

创建自己的接口。

//------------------------------------------
//reg_cfg interface
//------------------------------------------
output  wire                lb_clk,
output  wire                lb_rst_n,
output  wire    [8:0]       lb_rd_addr,
input   wire    [31:0]      lb_rd_data,
output  wire                lb_rd_en,
output  wire    [8:0]       lb_wr_addr,
output  wire    [31:0]      lb_wr_data,
output  wire                lb_wr_en, 

接口分别为,读写使能,读写数据,读写地址,时钟复位。这样创建我们可以很方便的控制。

MEM代码已经很好了,我们只需要将我们的接口和他的接口连接即可。

//----------------------------------------------
//reg_cfg assign
//----------------------------------------------
assign      lb_clk = clk;
assign      lb_rst_n = rst_n;
assign      lb_rd_addr = rd_addr[8:0];
//assign        lb_rd_data = clk;
assign      lb_rd_en = rd_data0_en | rd_data1_en | rd_data2_en | rd_data3_en;
assign      lb_wr_addr = wr_addr[8:0];
assign      lb_wr_data = wr_data;
assign      lb_wr_en = wr_en;

接下来首先先写。在上面,我们对状态机进行分析。这里只需要将状态机中的写数据写使能引出即可。

在读方面。需要将我们自己的数据接入,替换原来从内存中读出的数据。只需要在读数据阶段将自己的数据替换到这里。

 always @(rd_addr or rd_data0_o or rd_data1_o or rd_data2_o or rd_data3_o)begincase ({rd_addr[10:9]}) // synthesis parallel_case full_case2'b00 : rd_data_raw_o = lb_rd_data;2'b01 : rd_data_raw_o = lb_rd_data;2'b10 : rd_data_raw_o = lb_rd_data;2'b11 : rd_data_raw_o = lb_rd_data;endcaseend

到这里我们代码修改完毕,接下来对代码进行调试。

//配置寄存器,写入
always@(posedge clk or negedge rst_n)
beginif(!rst_n)beginreg_bus_test <= #TCQ 32'b0;reg_led_test <= #TCQ 32'b0;reg_test_1 <= #TCQ 32'b0;reg_test <= #TCQ 1'b0;endelse if(wr_en)begincase(wr_addr)9'h000: reg_bus_test <= #TCQ WR_DA;9'h001: reg_led_test <= #TCQ WR_DA;9'h002: reg_test_1   <= #TCQ WR_DA;default:beginreg_test <= #TCQ 1'b0;endendcaseendelsebeginreg_test <= #TCQ 1'b0;endend
//读出
always@(posedge clk or negedge rst_n)
beginif(!rst_n)beginrd_data <= #TCQ 32'h0000_0000;endelsebegincase(rd_addr)9'h000:rd_data <= #TCQ ~reg_bus_test;9'h001:rd_data <= #TCQ reg_led_test;9'h002:rd_data <= #TCQ reg_test_1;default : rd_data <= #TCQ rd_data;endcaseend
end

下载调试

对我们的修改的模块配置ILA,查看内部信号的运行。

分别在MEM,RX和TX侧加入ILA。

下载bit到板子,重启电脑,进入hardware manager,配置,ila触发,上升沿触发,打开windriver写入地址0x0写入数据11223344。

采集rx模块的波形如下:

TLP包报文:0000000f40000001+44332211f7d00000

接着从0X0读取数据,

Tx模块发送的TLP包如下

FPGA发送的TLP报文:010000044a000001+4433221100000000

接着抓取MEM模块的数据。

Rd_addr:11’h200;

Rd_data:32’h44332211;

Rd_be:4’hf;

Wr_addr:11’h200;

Wr_be:8’h0f;

Wr_data:32’h44332211;

Wr_en:0;

关于PCIE偏移地址和内存之间定义的关系。

向0x01写入数据。采集MEM模块,发现没有信号进入。采集rx模块,出现了了TLP报文。根据以往的经验,向windriver的偏移地址加4,试试,向windriver的0x04写入数据1111_1111,如图。可以发现,PCIE偏移地址0x00对应内存地址中的0x00,0x04对应地址中的0x01。

向0X8写入数据2222_2222,如图

接着读取0x4的数据。如图

向0xf4写入11223344.读出数据如图。

可以看到写入的数据:44332211

可以看到读出的数据:44332211

重新测试,写入数据12345678,读出数据为78563412,数据的位置在PCIE中进行了重组,这里需要将数据在写入前重新组合,再写入。

更改代码:

assign WR_DA = {wr_data[7:0],wr_data[15:7],wr_data[23:16],wr_data[31:16]}  ;

用WR_DA赋值。

读写正常。

PCIE实现PIO模式寄存器读写调试记录相关推荐

  1. vivado的vio怎么使用_使用VIVADO中VIO模拟CPU接口进行在线寄存器读写调试(附源代码)...

    debug,尤其是通信芯片的debug,可以有很多的方法.一个数据帧从进入到输出,可以在通路上的关键节点处设置监测如各种计数器等,可通过VIO(xilinx)定时上报实时状态.可以把VIO的各个信号线 ...

  2. Nvidia Agx Xavier平台10Gb PCIE网卡速度限制为1Gb问题调试记录

    1. 背景 Xavier的PCIe插槽安装了一个10Gb以太网卡. 当运行速度超过1Gb/秒时,大量数据包被丢弃. Netstat确认接口丢弃的数据包. 系统似乎已经将卡标识为10Gb,但在1Gb时出 ...

  3. 【Android 逆向】ptrace 函数 ( ptrace 函数族 | 进程附着 | 进程脱离 | 进程数据读写权限 | 进程对应的主线程寄存器读写 | 单步调试 |ptrace 函数族状态转换 )

    文章目录 一.ptrace 函数族 1.进程附着 2.进程脱离 3.进程数据读写权限 4.进程对应的主线程寄存器读写 5.单步调试 6.继续向后执行 二.ptrace 函数族状态转换 一.ptrace ...

  4. FPGA SATA IP控制器的SATA接口调试记录

    本文档是基于FPGA K7 SATA IP控制器的SATA接口调试记录,接口遵循标准的ACHI协议. 操作系统内核版本:5.4.18 由于K7PCIE只有3个bar,AHCI协议规定SATA控制器是在 ...

  5. cortex-m3 操作模式 寄存器组 异常类型 堆栈 中断

    cortex-m3 操作模式 寄存器组 异常类型 堆栈 中断 参考 操作模式 处理器的操作模式:为了区别正在执行代码的类型.复位后,处理器进入线程模式.特权级. 处理者模式(handler mode) ...

  6. SX1278 FSK 调试记录

    SX1278 FSK 调试记录 先挖个sx1278 FSK的坑慢慢填 手中有两个SX1278模组 是安信可的产品 采用主从模式 SPI访问 MCU是STM32F107 数据格式 说明收据接收的第一步就 ...

  7. Xilinx AXI Crossbar相关调试记录

    Xilinx AXI Crossbar相关调试记录 本文记录在使用Xilinx AXI Crossbar IPcore现象 ** AXI Crossbar IPcore设置如下** 使用AXI Cro ...

  8. 【tm1650调试记录】

    tm1650调试记录 阅读芯片手册 调试遇到的问题 1:数据的写入 2:数码管不亮 3:数码管调试模式亮,正常运行不亮 TM1650的应用补充--仅适用于LED驱动 亮度 片选信号DIG 配置顺序 代 ...

  9. AML8726调试记录

    一:源代码下载: 1:Installing Repo # mkdir ~/bin # PATH=~/bin:$PATH # curl https://dl-ssl.google.com/dl/goog ...

  10. WK2204 - spi转uart调试记录

    WK2204 - spi转uart调试记录 硬件 芯片简介 电路设计 驱动 添加设备树 添加驱动 调试 查看启动加载 检查串口通信 数据乱码或丢失 RS485只能收不能发 系统中断响应异常 思考 硬件 ...

最新文章

  1. CrowdRec:众包环境中,基于信任感知的工人推荐
  2. 【BLE】BLE中常用的UUID(标准)
  3. 面向对象编程 object oriented programming(OOP)
  4. 设计模式中类之间的关系
  5. 生效linux内核,Linux内核
  6. 调查:Java程序员最伤心,C++程序员最年老
  7. Spring MVC开发环境搭建
  8. c html联调,JS与native 交互简单应用
  9. java实现兵乓球比赛_C语言实现乒乓球比赛
  10. SQL里面也能用Split()
  11. 在线客服代码,可以用
  12. java 批量修改图片名称_java 批量修改文件名称
  13. win10计算机怎么打开方式,Win10如何还原打开方式?还原打开方式的方法
  14. html怎么叠加透明图片,css – 您可以在图片上叠加透明div
  15. AndroidStudio 实现用户登录注册
  16. 操作系统中cpu如何工作
  17. 2008-09赛季NBA直播表(cctv5 广东体育)
  18. 原始资料的收集方法———定性资料的收集
  19. static Constant expression contains invalid operat
  20. 【编程题】【Scratch四级】2022.03 早餐组合

热门文章

  1. 前端开发 HTML一篇就够了(七七)
  2. [C#复习向整合]反射 -Assembly与Activator
  3. 【czy系列赛】czy的后宫4 bzoj1925 [Sdoi2010]地精部落
  4. Xbox one VS. ps4
  5. 手机网络IP地址问题
  6. docker+scrapy+scrapy_splash爬取大麦网
  7. Qt使用dump定位崩溃位置
  8. html页面的结构标记是什么意思,html页面的结构标记是什么
  9. 传感器相关 MPU9250
  10. 自然语言处理,计算机与人类“谈心”的关键