FPGA基础入门【16】开发板加速度传感器配置

上一篇教程介绍的是NEXYS4开发板上的温度传感器,用上了串口通信和I2C接口,这次使用的加速度传感器使用的是SPI接口,是除了I2C之外另一种常用的接口,实用性很高

FPGA基础入门【16】开发板加速度传感器配置

  • 开发板加速度传感器
    • ADXL362引脚定义
    • SPI接口
    • 寄存器表
  • 逻辑设计
    • SPI控制模块
    • 顶层逻辑
  • 模拟仿真
    • Testbench
    • 仿真脚本
    • 仿真结果
  • 编译测试
    • 结果
  • 总结

开发板加速度传感器

NEXYS 4文档中写着它使用的加速度传感器是Analog Device的ADXL362,它和FPGA的连接如下
FPGA基础入门【16】开发板加速度传感器配置_第1张图片
它使用的接口是SPI,在系列教程8的SPI Flash中使用过这种接口,我们可以从那份代码中提取相应的控制逻辑

ADXL362的文档

ADXL362引脚定义

在芯片datasheet中,可以看到其引脚如下
FPGA基础入门【16】开发板加速度传感器配置_第2张图片
定义如下

  • SCLK是SPI的通信时钟
  • MOSI,Master Output Slave Input,这个名称很好的避免了定义方向时候的混淆
  • MISO,Master Input Slave Output,和上面的MOSI相对应,对于Master来说,MISO是输入,MOSI是输出
  • CS#,Chip Select片选,低电平有效
  • INT1/INT2,中断信号
  • 其他是电源、接地、保留

FPGA基础入门【16】开发板加速度传感器配置_第3张图片

SPI接口

ADXL362文档中提到时钟SCK的频率范围是2.4kHz到8MHz,NEXYS4开发板文档中推荐频率在1MHz到5MHz。芯片文档中的应该是上下极限,我们这里采用5MHz作为SPI借口频率,只需要将100MHz的系统时钟减慢20倍。

SPI接口中写寄存器的时序如下
FPGA基础入门【16】开发板加速度传感器配置_第4张图片
读寄存器的时序如下
FPGA基础入门【16】开发板加速度传感器配置_第5张图片
由于SPI没有I2C接口中那样输入输出共用的引脚,不需要频繁切换方向,因此逻辑相对简单,不过比起I2C的两根线多出了两根。

寄存器表

ADXL362的寄存器表如下
FPGA基础入门【16】开发板加速度传感器配置_第6张图片
这里我们关注的寄存器是这几个

  1. 设备ID,地址0x00-0x03,只读,默认0xAD1DF201
  2. 状态寄存器,地址0x0B,只读,默认0x40,每一位的具体含义如下
    FPGA基础入门【16】开发板加速度传感器配置_第7张图片
  3. 低精度XYZ数据,地址0x08-0x0A,默认0x000000,在节能情况下,12位精度的加速度只去最高8位,精度被压缩了4位,但可以一次性读出数据
  4. 高精度XYZ数据,地址0x0C-0x12,默认都是0,0x0C是X方向的数据的低8位,0x0D是X方向数据的高4位加上4位的符号延展,总共加起来12位,YZ方向数据同理
  5. 温度数据,地址0x14-0x15,和XYZ高精度数据一样,0x15是高4位,0x14是低8位
  6. 动作阈值,地址0x20-0x21,0x21是高3位,0x20是低8位,是无符号正数,当XYZ数据的绝对值超过这个数时就判断有动作
  7. 无动作阈值,地址0x23-0x24,0x24是高3位,0x23是低8位,是无符号正数,当XYZ数据的绝对值小于这个值时判断没有动作
  8. 过滤控制寄存器,地址0x2C,默认0x13,其定义如下,头2位控制了数据范围,默认正负2g时,如果读出低精度数据0x10,除掉最高位为符号位,它就代表正0.5g
    FPGA基础入门【16】开发板加速度传感器配置_第8张图片

还有很多传感器,可以在芯片文档中一一阅读,这里给出一个文档中的初始化流程example:
FPGA基础入门【16】开发板加速度传感器配置_第9张图片
这一通操作之后,每当开发板被至少0.25个重力加速度移动的时候,INT2中断引脚就会变成高电平,板子不动5秒左右会恢复低电平,引到LED上即可观察到。

逻辑设计

这篇教程的计划是利用串口,输送控制命令给SPI接口控制模块,读写相应寄存器。

和前面的教程不同的是,前面的教程用单一数字代表预先配置好的指令,这篇教程的计划是打算通过串口输送指令,而不是一个单一数字。为此我们需要改进收到串口指令后的控制逻辑

SPI控制模块

首先我们需要一个SPI控制模块SPI_transmitter.v,这部分代码由之前的SPI flash的代码修改得来,不过做了很大的改动:

顶层接口定义,除了SPI接口外,控制接口有:

  • ready,上升沿代表传输开始
  • inst,读写指令,读是0x0B,写是0x0A
  • rdh_wrl,高电平代表读,低电平代表写。虽然根据inst可以判断读写,但以后可能存在一些其他应用,还是保留了这个控制引脚
  • reg_addr,寄存器地址
  • dout,写给设备的数据
  • din,设备传回的数据
  • din_valid,设备传回数据准备,高电平时代表传回的数据已经准备好了
module SPI_transmitter(
    input      clk,
    input      rst,
    
    // SPI port
    output reg CSN,
    output reg SCLK,
    output reg MOSI,
    input      MISO,
    
    // Control port
    input            ready,
    input      [7:0] inst,
    input            rdh_wrl,
    input      [7:0] reg_addr,
    input      [7:0] dout,
    output reg [7:0] din,
    output reg       din_valid
);

SPI时钟SCLK生成器,减缓20倍成5MHz

// SCK generator, 5MHz output
reg         SCLK_en;
reg         SCLK_d;
reg  [7:0]  SCLK_count;
wire        SCLK_posedge;
wire        SCLK_negedge;

always @(posedge clk or posedge rst) begin
	if(rst || ~SCLK_en) begin
		SCLK <= 1'b0;
        SCLK_count <= 8'd0;
	end
	else if(SCLK_en && (SCLK_count<8'd10)) begin
        SCLK_count <= SCLK_count + 8'd1;
	end
    else begin
        SCLK <= ~SCLK;
        SCLK_count <= 8'd0;
    end
end

监测SCLK的上升沿和下降沿

always @(posedge clk) begin
    SCLK_d <= SCLK;
end
assign SCLK_posedge = ({SCLK_d, SCLK}==2'b01) ? 1'b1 : 1'b0;
assign SCLK_negedge = ({SCLK_d, SCLK}==2'b10) ? 1'b1 : 1'b0;

监测ready的上升沿

// Ready rising edge detection
reg  ready_d;
wire ready_posedge;
always @(posedge clk) begin
    ready_d <= ready;
end
assign ready_posedge = ({ready_d, ready} == 2'b01) ? 1'b1 : 1'b0;

状态机配置

// State machine
reg  [3:0]  state;
reg  [3:0]  next_state;

parameter IDLE       = 4'd0;
parameter START      = 4'd1;
parameter INST_OUT   = 4'd2;
parameter ADDR_OUT   = 4'd3;
parameter WRITE_DATA = 4'd4;
parameter READ_DATA  = 4'd5;
parameter ENDING     = 4'd6;

reg  [6:0]  MISO_buf;
reg  [7:0]  MOSI_buf;
reg  [3:0]  MOSI_count;

always @(posedge clk or posedge rst) begin
	if(rst) begin
		state <= IDLE;
	end
	else begin
		state <= next_state;
	end
end

always @(posedge clk) begin
	case(state)
	IDLE: 
	begin	// IDLE state
        next_state <= START;
		MOSI <= 1'b0;
        CSN <= 1'b1;
        SCLK_en <= 1'b0;
        MOSI_buf <= inst;
        MOSI_count <= 4'd0;
        din <= 8'h00;
        din_valid <= 1'b0;
	end

当ready上升沿时,拉低CS,开启SCLK生成器,进入读写流程

	START:
	begin	// enable SCK and CS
        // start the process when ready rise, load instruction
        if(ready_posedge) begin
            next_state <= INST_OUT;
            CSN  <= 1'b0;
            SCLK_en <= 1'b1;
            MOSI_buf <= {inst[6:0], 1'b0};
            MOSI <= inst[7];
        end
	end

输出8位指令

	INST_OUT:
	begin	// send out instruction
		if(SCLK_negedge && (MOSI_count < 4'd7)) begin
			{MOSI, MOSI_buf} <= {MOSI_buf, 1'b0};
            MOSI_count <= MOSI_count + 4'd1;
		end
		else if(SCLK_negedge) begin
			{MOSI, MOSI_buf} <= {reg_addr, 1'b0};
            MOSI_count <= 4'd0;
            next_state <= ADDR_OUT;
		end
	end

输出8位地址,根据读写控制进入读数据流程或者写数据流程

	ADDR_OUT:
	begin	// send out register address
		if(SCLK_negedge && (MOSI_count < 4'd7)) begin
			{MOSI, MOSI_buf} <= {MOSI_buf, 1'b0};
            MOSI_count <= MOSI_count + 4'd1;
		end
		else if(SCLK_negedge) begin
			{MOSI, MOSI_buf} <= {dout, 1'b0};
            MOSI_count <= 4'd0;
            next_state <= (rdh_wrl) ? READ_DATA : WRITE_DATA;
		end
	end

读写数据流程,写完进入结尾。将来可能会加入多字节读写控制

	WRITE_DATA:
	begin	// send testing data out to flash
		if(SCLK_negedge && (MOSI_count < 4'd7)) begin
			{MOSI, MOSI_buf} <= {MOSI_buf, 1'b0};
            MOSI_count <= MOSI_count + 4'd1;
		end
		else if(SCLK_negedge) begin
			{MOSI, MOSI_buf} <= 9'h0;
            MOSI_count <= 4'd0;
            next_state <= ENDING;
		end
	end
	READ_DATA:
	begin	// get a byte
		if(SCLK_posedge && (MOSI_count < 4'd7)) begin
			MISO_buf <= {MISO_buf[5:0], MISO};
            MOSI_count <= MOSI_count + 4'd1;
		end
		else if(SCLK_posedge) begin
            MOSI_count <= 4'd0;
            next_state <= ENDING;
            din <= {MISO_buf, MISO};
            din_valid <= 1'b1;
		end
        else begin
            din_valid <= 1'b0;
        end
	end

结尾流程,一段时间后拉高CSN

	ENDING:
	begin	//disable SCK and CS
        if(SCLK_negedge) begin
            CSN <= 1'b1;
            next_state <= IDLE;
            SCLK_en <= 1'b0;
        end
	end
	endcase
end

endmodule

顶层逻辑

就像前面说的,这次要改进顶层逻辑,使得指令不再是预设好的。代码accel.v如下:

顶层引脚配置,时钟复位一如既往,两个LED分别和加速度传感器的中断引脚相连,再加上串口接口,以及加速度传感器的SPI接口

module accel(
    input  clk,
    input  rst,
    output reg LED_INT1,
    output reg LED_INT2,
    
    // UART port
    output TXD,
    input  RXD,
    output CTS,
    input  RTS,
    
    // SPI port
    output ACL_CSN,
    output ACL_MOSI,
    input  ACL_MISO,
    output ACL_SCLK,
    input  ACL_INT1,
    input  ACL_INT2
);

把LED接到两个中断引脚上

// Direct connect LED to interrupt pins
always @(posedge clk or posedge rst) begin
    if(rst) begin
        LED_INT1 <= 1'b0;
        LED_INT2 <= 1'b0;
    end
    else begin
        LED_INT1 <= ACL_INT1;
        LED_INT2 <= ACL_INT2;
    end
end

调用前面写好的SPI控制模块

// SPI controller
reg        SPI_ready;
reg  [7:0] SPI_inst;
reg        SPI_rdh_wrl;
reg  [7:0] SPI_reg_addr;
reg  [7:0] SPI_dout;
wire [7:0] SPI_din;
wire       SPI_din_valid;
SPI_transmitter SPI_transmitter(
    .clk        (clk),
    .rst        (rst),
    
    // SPI port
    .CSN        (ACL_CSN),
    .SCLK       (ACL_SCLK),
    .MOSI       (ACL_MOSI),
    .MISO       (ACL_MISO),
    
    // Control port
    .ready      (SPI_ready),
    .inst       (SPI_inst),
    .rdh_wrl    (SPI_rdh_wrl),
    .reg_addr   (SPI_reg_addr),
    .dout       (SPI_dout),
    .din        (SPI_din),
    .din_valid  (SPI_din_valid)
);

调用上一个教程写好的syn_fifo.v和UART_transmitter.v:

// Data IO with UART
wire [3:0] uart_din;
reg  [3:0] uart_din_d;
wire       uart_din_valid;
reg  [7:0] uart_dout;
reg        uart_dout_ready;
UART_transmitter UART_transmitter(
    .clk         (clk),
    .rst         (rst),
    
    // UART port
    .TXD         (TXD),
    .RXD         (RXD),
    .CTS         (CTS),
    .RTS         (RTS),
    
    // Control port
    .dout        (uart_dout),
    .dout_ready  (uart_dout_ready),
    .din         (uart_din),
    .din_valid   (uart_din_valid)
);

新的串口控制逻辑,加入了一个8byte的buffer,从高位到低位,分别表示读写控制、8位指令、寄存器地址、需要写入的8位数据。

同时从串口接收到的信号经过一级buffer后,以8位送回串口模块,因此这次打一个字符看不到,要打第二个才能看到

从SPI收到信号后会立刻输出

// Command control
// [27:24] rdh_wrl
// [23:16] inst
// [15: 8] reg_addr
// [ 7: 0] din
reg [27:0] UART_cmd_buf;
reg [3:0]  din_count;
always @(posedge clk) begin
    if(rst) begin
        UART_cmd_buf <= 28'h0000000;
        uart_dout_ready <= 1'b0;
        uart_dout <= 8'h00;
        din_count <= 4'd0;
        SPI_ready <= 1'b0;
        SPI_inst <= 8'h00;
        SPI_rdh_wrl <= 1'b0;
        SPI_reg_addr <= 8'h00;
        SPI_dout <= 8'h00;
    end
    else if(uart_din_valid && din_count < 4'd7) begin
        UART_cmd_buf <= {UART_cmd_buf[23:0], uart_din};
        if(din_count[0]) begin
            uart_dout <= {uart_din_d, uart_din};
            uart_dout_ready <= 1'b1;
        end
        else begin
            uart_din_d <= uart_din;
        end
        din_count <= din_count + 4'd1;
    end
    else if(uart_din_valid && din_count == 4'd7) begin
        uart_dout <= {uart_din_d, uart_din};
        uart_dout_ready <= 1'b1;
        din_count <= 4'd0;
        SPI_ready <= 1'b1;
        SPI_inst <= UART_cmd_buf[19:12];
        if(UART_cmd_buf[23:20] == 4'b0000) begin
            SPI_rdh_wrl <= 1'b0;
        end
        else if(UART_cmd_buf[23:20] == 4'b0001) begin
            SPI_rdh_wrl <= 1'b1;
        end
        SPI_reg_addr <= UART_cmd_buf[11:4];
        SPI_dout <= {UART_cmd_buf[3:0], uart_din};
    end
    else if(SPI_din_valid) begin
        uart_dout <= SPI_din;
        uart_dout_ready <= 1'b1;
    end
    else begin
        uart_dout_ready <= 1'b0;
        SPI_ready <= 1'b0;
    end
end

endmodule

模拟仿真

和之前一样,要写一个Testbench和一个仿真脚本来仿真

Testbench

代码tb_accel如下:

`timescale 1ns/1ns

module tb_accel;

reg         clock;
reg         reset;

reg         RXD;
wire        TXD;
wire        CTS;
reg         RTS;

reg  [9:0]  RXD_buf;

wire        ACL_CSN;
wire        ACL_MOSI;
reg         ACL_MISO;
wire        ACL_SCLK;

reg  [15:0] ACL_MOSI_buf;

复位之后,送一个8byte的指令到串口,如果是读操作,则在收到SPI指令后传回任意数据。

这里在注释中的一段是写数据0x57到寄存器0x20的操作,注释外的一段是从寄存器0x20读数据的操作,可以根据需要使用某一段

initial begin
    clock = 1'b0;
    reset = 1'b0;
    
    RXD = 1'b1;
    RTS = 1'b1;
    RXD_buf = 8'h00;
    
    ACL_MISO = 1'b0;
    
    // Reset for 1us
    #100 
    reset = 1'b1;
    #1000
    reset = 1'b0;
    /*
    // Testing command 0x000A2057
    // Send a number 0 into uart
    RXD_buf = 10'b0000011001;
    RTS = 1'b0;
    repeat(10) begin
        repeat(867) @(posedge clock);
        {RXD, RXD_buf} = {RXD_buf, 1'b1};
    end
    
    // Send a number 0 into uart
    RXD_buf = 10'b0000011001;
    RTS = 1'b0;
    repeat(10) begin
        repeat(867) @(posedge clock);
        {RXD, RXD_buf} = {RXD_buf, 1'b1};
    end
    
    // Send a number 0 into uart
    RXD_buf = 10'b0000011001;
    RTS = 1'b0;
    repeat(10) begin
        repeat(867) @(posedge clock);
        {RXD, RXD_buf} = {RXD_buf, 1'b1};
    end
    
    // Send a number A into uart
    RXD_buf = 10'b0100000101;
    RTS = 1'b0;
    repeat(10) begin
        repeat(867) @(posedge clock);
        {RXD, RXD_buf} = {RXD_buf, 1'b1};
    end
    
    // Send a number 2 into uart
    RXD_buf = 10'b0010011001;
    RTS = 1'b0;
    repeat(10) begin
        repeat(867) @(posedge clock);
        {RXD, RXD_buf} = {RXD_buf, 1'b1};
    end
    
    // Send a number 0 into uart
    RXD_buf = 10'b0000011001;
    RTS = 1'b0;
    repeat(10) begin
        repeat(867) @(posedge clock);
        {RXD, RXD_buf} = {RXD_buf, 1'b1};
    end
    
    // Send a number 5 into uart
    RXD_buf = 10'b0101011001;
    RTS = 1'b0;
    repeat(10) begin
        repeat(867) @(posedge clock);
        {RXD, RXD_buf} = {RXD_buf, 1'b1};
    end
    
    // Send a number 7 into uart
    RXD_buf = 10'b0111011001;
    RTS = 1'b0;
    repeat(10) begin
        repeat(867) @(posedge clock);
        {RXD, RXD_buf} = {RXD_buf, 1'b1};
    end
    */
    // Testing command 0x010B2000, return 0xAA in SPI
    // Send a number 0 into uart
    RXD_buf = 10'b0000011001;
    RTS = 1'b0;
    repeat(10) begin
        repeat(867) @(posedge clock);
        {RXD, RXD_buf} = {RXD_buf, 1'b1};
    end
    
    // Send a number 1 into uart
    RXD_buf = 10'b0100011001;
    RTS = 1'b0;
    repeat(10) begin
        repeat(867) @(posedge clock);
        {RXD, RXD_buf} = {RXD_buf, 1'b1};
    end
    
    // Send a number 0 into uart
    RXD_buf = 10'b0000011001;
    RTS = 1'b0;
    repeat(10) begin
        repeat(867) @(posedge clock);
        {RXD, RXD_buf} = {RXD_buf, 1'b1};
    end
    
    // Send a number B into uart
    RXD_buf = 10'b0010000101;
    RTS = 1'b0;
    repeat(10) begin
        repeat(867) @(posedge clock);
        {RXD, RXD_buf} = {RXD_buf, 1'b1};
    end
    
    // Send a number 2 into uart
    RXD_buf = 10'b0010011001;
    RTS = 1'b0;
    repeat(10) begin
        repeat(867) @(posedge clock);
        {RXD, RXD_buf} = {RXD_buf, 1'b1};
    end
    
    // Send a number 0 into uart
    RXD_buf = 10'b0000011001;
    RTS = 1'b0;
    repeat(10) begin
        repeat(867) @(posedge clock);
        {RXD, RXD_buf} = {RXD_buf, 1'b1};
    end
    
    // Send a number 0 into uart
    RXD_buf = 10'b0000011001;
    RTS = 1'b0;
    repeat(10) begin
        repeat(867) @(posedge clock);
        {RXD, RXD_buf} = {RXD_buf, 1'b1};
    end
    
    // Send a number 0 into uart
    RXD_buf = 10'b0000011001;
    RTS = 1'b0;
    repeat(10) begin
        repeat(867) @(posedge clock);
        {RXD, RXD_buf} = {RXD_buf, 1'b1};
    end
    
    // Take 16 bits from MOSI, and send 8 bits to MISO
    repeat(16) begin
        @(posedge ACL_SCLK)
        ACL_MOSI_buf = {ACL_MOSI_buf[14:0], ACL_MOSI};
    end
    
    repeat(8) begin
        @(negedge ACL_SCLK)
        ACL_MISO = ~ACL_MISO;
    end
end

// Generate 100MHz clock signal
always #5 clock <= ~clock;

accel accel(
    .clk      (clock),
    .rst      (reset),
    
    // SPI port
    .ACL_CSN  (ACL_CSN),
    .ACL_MOSI (ACL_MOSI),
    .ACL_MISO (ACL_MISO),
    .ACL_SCLK (ACL_SCLK),
    
    // UART port
    .RXD      (RXD),
    .TXD      (TXD),
    .CTS      (CTS),
    .RTS      (RTS)
);

endmodule

仿真脚本

写脚本sim.do如下:

vlib work
vlog ../src/accel.v ../src/SPI_transmitter.v ../src/UART_transmitter.v ../src/syn_fifo.v ./tb_accel.v
vsim work.tb_accel -voptargs=+acc +notimingchecks
log -depth 7 /tb_accel/*
#do wave.do
run 2ms

调用前面全部的代码,打开ModelSim后转到脚本在的路径,使用命令do sim.do即可开始仿真。

仿真时可以添加想要的信号到waveform窗口中观察,然后可以保存为wave.do,这样下次可以通过调用它来加入一样的信号,节省一个一个加入的时间,这时你可以把sim.do中被#注释掉的那行去注释

仿真结果

调用仿真脚本得到的结果如下,读取到0xAA:
waveform read
将Testbench中的注释部分修改后,变成SPI写操作,结果如下:
waveform write
这和SPI在文档中的时序一样

编译测试

新建一个叫accel的project,配置为开发板NEXYS4。添加代码文件accel.v、SPI_transmitter.v、UART_transmitter.v和syn_fifo.v

下一步加入约束constraint文件accel.xdc,同样这是用标准模板取自己需要部分修改出来的(NEXYS 4 DDR Master XDC):

## This file is a general .xdc for the Nexys4 DDR Rev. C
## To use it in a project:
## - uncomment the lines corresponding to used pins
## - rename the used ports (in each line, after get_ports) according to the top level signal names in the project

## Clock signal
set_property -dict {PACKAGE_PIN E3 IOSTANDARD LVCMOS33} [get_ports clk]
create_clock -period 10.000 -name sys_clk_pin -waveform {0.000 5.000} -add [get_ports clk]


##Switches

set_property -dict {PACKAGE_PIN J15 IOSTANDARD LVCMOS33} [get_ports rst]


## LEDs

set_property -dict {PACKAGE_PIN H17 IOSTANDARD LVCMOS33} [get_ports LED_INT1]
set_property -dict {PACKAGE_PIN K15 IOSTANDARD LVCMOS33} [get_ports LED_INT2]


##Accelerometer

set_property -dict {PACKAGE_PIN E15 IOSTANDARD LVCMOS33} [get_ports ACL_MISO]
set_property -dict {PACKAGE_PIN F14 IOSTANDARD LVCMOS33} [get_ports ACL_MOSI]
set_property -dict {PACKAGE_PIN F15 IOSTANDARD LVCMOS33} [get_ports ACL_SCLK]
set_property -dict {PACKAGE_PIN D15 IOSTANDARD LVCMOS33} [get_ports ACL_CSN]
set_property -dict {PACKAGE_PIN B13 IOSTANDARD LVCMOS33} [get_ports ACL_INT1]
set_property -dict {PACKAGE_PIN C16 IOSTANDARD LVCMOS33} [get_ports ACL_INT2]


##USB-RS232 Interface

set_property -dict {PACKAGE_PIN C4 IOSTANDARD LVCMOS33} [get_ports RXD]
set_property -dict {PACKAGE_PIN D4 IOSTANDARD LVCMOS33} [get_ports TXD]
set_property -dict {PACKAGE_PIN D3 IOSTANDARD LVCMOS33} [get_ports CTS]
set_property -dict {PACKAGE_PIN E5 IOSTANDARD LVCMOS33} [get_ports RTS]

到这里可以点击 Run Synthesis做综合,几分钟完成后用Set Up Debug配置ChipScope,加入和SPI有关的接口,并设置长度为8192,因为SPI接口的5MHz比其他的接口时钟速度要快不少:
FPGA基础入门【16】开发板加速度传感器配置_第10张图片
FPGA基础入门【16】开发板加速度传感器配置_第11张图片
下面就可以Run Implementation和Generate Bitstream生成bitstream了。

和前面的教程一样,USB线连接NEXYS4板子,开启Hardware Manager,然后auto连接上板子,Program Device烧写进程序,注意Debug probes file有对应的ltx文件。

结果

打开Putty串口接口,具体配置可以参考教程系列11,下面根据前面example里的流程,分别打入如下指令(注意大写锁定,没有做大小写识别):

  1. 000A20FA
  2. 000A2396
  3. 000A251E
  4. 000A273F
  5. 000A2B40
  6. 000A2D0A,到这里是example中的步骤,初始化完成
  7. 010B2000,读取前面写的第一个寄存器
  8. 010B0A00,读取Z方向的加速度传感器数据

在Putty窗口中看到结果如下:
FPGA基础入门【16】开发板加速度传感器配置_第12张图片
看到在读取寄存器0x20时,返回的是之前写入的0xFA

读取Z方向的加速度传感器时,返回的是0xC5,根据example中的描述,数据范围是正负2g,因此剔除符号位后除以64。0xC5取反加一得到其绝对值0x3B,也就是十进制59,除以64再考虑符号位,得到的结果是Z方向是-0.921875个重力加速度。

为了验证没读错,我把开发板反过来,再读取,得到0x4A,换算出来是1.15625个重力加速度,大概是正确的。

再打开ChipScope,设置trigger为CSN的下降沿,开启后输入000A2396,看到的SPI写操作如下:
FPGA基础入门【16】开发板加速度传感器配置_第13张图片

再尝试010B0A00,看到的SPI读操作如下:
FPGA基础入门【16】开发板加速度传感器配置_第14张图片
可以看到仿真看到的时序

如果你做完了这一系列操作,再看开发板,在不动时候,从右到左第二个LED是暗的,把开发板快速提起来就亮起,放下等待5秒之后,就自动暗下,直到下一次开发板被动

总结

板载的加速度传感器accelerometer ADXL362就到这里了,下一篇要介绍开发板上的音频控制

你可能感兴趣的:(FPGA)