IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> MCU_DESIGN_06MUC外设APB_SPI -> 正文阅读

[嵌入式]MCU_DESIGN_06MUC外设APB_SPI

1.SPI

①SPI 是英语Serial Peripheral interface的缩写,也就是串行外围设备接口

②它是一种高速的,全双工,同步的通信总线,只占用四根线,主要应用在 EEPROM,FLASH

③接口:

SCLK:从master发过来的时钟信号
MOSI:master out slave in
MISO:master in slave out
SS: slave select (片选、从机选择)、有时候可以有多个SS,一般为0有效

在这里插入图片描述

④极性和相位

CPOL:决定开始和结束的条件;若设置为0,则开始为上升沿、结束为下降沿;若设置为1,则开始为下降沿、结束为上升沿;

CPHA:决定数据捕获以及数据改变的时序;若为0,master在下降沿改变数据,slave在上升沿采集数据;若为1,master在上升沿改变数据,slave在下降沿沿采集数据;

⑤两种片选SS的结构:多SS,以及SS译码

在这里插入图片描述

在这里插入图片描述

2.SPI架构及寄存器

  • 架构
    APB总线进行寄存器的配置,进而产生相应的SPI时序
    在这里插入图片描述
  • 寄存器:控制、状态、数据、扩展寄存器

在这里插入图片描述

①控制寄存器SPCR

  • SPIE [7]:中断使能
  • SPE [6]:工作使能
  • MSTR[4]:固定为1
  • CPOL[3]:极性
  • CPHA[2]:相位
  • SPR[1:0]:时钟速率选择

②状态寄存器SPSR

  • SPIF[7]:中断上报
  • WCOL[6]:写冲突
  • WFFULL[3]:写fifo满
  • WFEMPTY[2]:写fifo空
  • RFFULL[1]:读fifo满
  • RFEMPTY[0]:读fifo空

③数据寄存器SPDR

  • 一个寄存器完成读和写buffer[7:0]

④扩展寄存器SPER

  • ICNT[7:6]:四种状态,决定中断是在多少次完整传输后设置
    在这里插入图片描述

  • ESPR[1:0]:和前面的SPR结合决定分频
    在这里插入图片描述

3.RTL代码

①题外话:寄存器配置的电路——使能则通,不使能则保持

在这里插入图片描述

spi_top.v

①端口:包括APB、SPI、中断上报

module spi_top
(
    input   wire        PCLKG,
    input   wire        PRESETn,
    input   wire [11:2] PADDR,
    input   wire        PSEL,
    input   wire        PENABLE,
    input   wire        PWRITE,
    input   wire [31:0] PWDATA,
    output  wire [31:0] PRDATA,
    output  wire        PREADY,
    output  wire        PSLVERR,
    output  wire        spi_int,//传输完成上报中断

    output  wire        pad_spi_sck_out,
    output  wire        pad_spi_sck_oen,
    output  wire        pad_spi_do_out,//MOSI
    output  wire        pad_spi_do_oen,
    output  wire        pad_spi_cs0_out,
    output  wire        pad_spi_cs0_oen,
    output  wire        pad_spi_cs1_out,
    output  wire        pad_spi_cs1_oen,
    output  wire        pad_spi_di_in//MISO

);

②重定义:因为是用其他spi ip 改的,需要给APB定义一下,包括时钟复位等等

wire        clk_i = PCLKG ;
wire        rst i = PRESETn ;
wire [2:0]  adr_i = PADDR[4:2] ;
wire        we_i  = PSEL & (~PENABLE) & PWRITE;
wire [7:0]  dat_i = PYDATA[7:0] ;
wire        miso_i= pad_spi_di_in ;
reg [7:0]   dat_o;
reg         inta_o;
reg         sck_o;
wire        mosi_o;
reg         cs_sel;  //0:cs0   1:cs1

③内部信号:寄存器信号定义、fifo、中断、使能等信号

reg  [7:0] spcr;       // Serial Peripheral Control Register ('HC11 naming)
wire [7:0] spsr;       // Serial Peripheral Status register ('HC11 naming)
reg  [7:0] sper;       // Serial Peripheral Extension register
reg  [7:0] treg, rreg; // Transmit/Receive register

// fifo signals
wire [7:0] rfdout;
reg        wfre, rfwe;
wire       rfre, rffull, rfempty;
wire [7:0] wfdout;
wire       wfwe, wffull, wfempty;
reg  [11:0]clkcnt;
wire ena = ~|clkcnt;

// misc signals
wire      tirq;     // transfer interrupt (selected number of transfers done)
wire      wfov;     // write fifo overrun (writing while fifo full)
reg [1:0] state;    // statemachine state
reg [2:0] bcnt;

//
// Wishbone interface
wire wb_acc =  we_i;      
wire wb_wr  =  we_i;      

④APB数据补够32位,SPI接口重定义,oen信号默认0

assign PRDATA ={{24{1'b0}},dat_o} ;
assign PREADY = 1'bl;
assign PSLVERR = 1'bl;

assign spi_int         = inta_o;
assign pad_spi_sck_out = sck_o;
assign pad spi_sck oen = 1'b0;
assign pad_spi_do_out  = mosi_o;
assign pad spi do oen  = 1'b0;
assign pad spi_cse_out = (~csel) & ena;
assign pad spi cs0_oen = 1'b0;
assign pad_spi_cs1_out = (cs_sel) & ena;
assign pad_spi_cs1_oen = 1'b0;

⑤根据地址和使能,给寄存器赋值(dat_i),再输出(dat_o)

// dat_i
always @(posedge clk_i or negedge rst_i)begin
    if (~rst_i)begin
          spcr <= #1 8'h10;  // set master bit
          sper <= #1 8'h00;
          cs_sel <= 1'b0;
          end
    else if (wb_wr)begin
        if (adr_i[1:0] == 2'b00)
          spcr <= #1 dat_i | 8'h10; // always set master bit

        if (adr_i[1:0] == 2'b11)
          sper <= #1 dat_i;

        if (adr_i[2] == 1'b0)
          cs_sel <= 1'b0;

          end
     else begin
          cs_sel <= 1'b1;
      end
  end
  // dat_o
always @(posedge clk_i)
  case(adr_i[1:0]) // synopsys full_case parallel_case
    2'b00: dat_o <= #1 spcr;
    2'b01: dat_o <= #1 spsr;
    2'b10: dat_o <= #1 rfdout;
    2'b11: dat_o <= #1 sper;
  endcase

⑥fifo的使能与过载

// write fifo
assign wfwe = wb_acc & (adr_i[1:0] == 2'b10) & ack_o &  we_i;
assign wfov = wfwe & wffull; //over
// read fifo
assign rfre = wb_acc & (adr_i[1:0] == 2'b10) & ~we_i;

⑦寄存器每个位的配置

// decode Serial Peripheral Control Register
wire       spie = spcr[7];   // Interrupt enable bit
wire       spe  = spcr[6];   // System Enable bit
wire       dwom = spcr[5];   // Port D Wired-OR Mode Bit
wire       mstr = spcr[4];   // Master Mode Select Bit
wire       cpol = spcr[3];   // Clock Polarity Bit
wire       cpha = spcr[2];   // Clock Phase Bit
wire [1:0] spr  = spcr[1:0]; // Clock Rate Select Bits 

// decode Serial Peripheral Extension Register
wire [1:0] icnt = sper[7:6]; // interrupt on transfer count
wire [1:0] spre = sper[1:0]; // extended clock rate select

wire [3:0] espr = {spre, spr};

// generate status register
wire wr_spsr = wb_wr & (adr_i[1:0] == 2'b01);

⑧中断

reg spif;
always @(posedge clk_i)
  if (~spe)
    spif <= #1 1'b0;
  else
    spif <= #1 (tirq | spif) & ~(wr_spsr & dat_i[7]); //产生中断并保持

reg wcol;
always @(posedge clk_i)
  if (~spe)
    wcol <= #1 1'b0;
  else
    wcol <= #1 (wfov | wcol) & ~(wr_spsr & dat_i[6]);//over 中断
    
	assign spsr[7]   = spif;
	assign spsr[6]   = wcol;
	assign spsr[5:4] = 2'b00;
	assign spsr[3]   = wffull;
	assign spsr[2]   = wfempty;
	assign spsr[1]   = rffull;
	assign spsr[0]   = rfempty;
    
// generate IRQ output (inta_o)
always @(posedge clk_i)
  inta_o <= #1 spif & spie;

⑨fifo例化(同步fifo)

// hookup read/write buffer fifo
fifo4 #(8)
rfifo(
  .clk   ( clk_i   ),
  .rst   ( rst_i   ),
  .clr   ( ~spe    ),
  .din   ( treg    ),
  .we    ( rfwe    ),
  .dout  ( rfdout  ),
  .re    ( rfre    ),
  .full  ( rffull  ),
  .empty ( rfempty )
),
wfifo(
  .clk   ( clk_i   ),
  .rst   ( rst_i   ),
  .clr   ( ~spe    ),
  .din   ( dat_i   ),
  .we    ( wfwe    ),
  .dout  ( wfdout  ),
  .re    ( wfre    ),
  .full  ( wffull  ),
  .empty ( wfempty )
);

⑩时钟分频:由espr寄存器决定

// generate clk divider
always @(posedge clk_i)
  if(spe & (|clkcnt & |state))
    clkcnt <= #1 clkcnt - 11'h1;
  else
    case (espr) // synopsys full_case parallel_case
      4'b0000: clkcnt <= #1 12'h0;   // 2   -- original M68HC11 coding
      4'b0001: clkcnt <= #1 12'h1;   // 4   -- original M68HC11 coding
      4'b0010: clkcnt <= #1 12'h3;   // 16  -- original M68HC11 coding
      4'b0011: clkcnt <= #1 12'hf;   // 32  -- original M68HC11 coding
      4'b0100: clkcnt <= #1 12'h1f;  // 8
      4'b0101: clkcnt <= #1 12'h7;   // 64
      4'b0110: clkcnt <= #1 12'h3f;  // 128
      4'b0111: clkcnt <= #1 12'h7f;  // 256
      4'b1000: clkcnt <= #1 12'hff;  // 512
      4'b1001: clkcnt <= #1 12'h1ff; // 1024
      4'b1010: clkcnt <= #1 12'h3ff; // 2048
      4'b1011: clkcnt <= #1 12'h7ff; // 4096
    endcase

?传输状态机:根据cpol、cpha决定极点和相位

// transfer statemachine
always @(posedge clk_i)
  if (~spe)
    begin
        state <= #1 2'b00; // idle
        bcnt  <= #1 3'h0;
        treg  <= #1 8'h00;
        wfre  <= #1 1'b0;
        rfwe  <= #1 1'b0;
        sck_o <= #1 1'b0;
    end
  else
    begin
       wfre <= #1 1'b0;
       rfwe <= #1 1'b0;

       case (state) //synopsys full_case parallel_case
         2'b00: // idle state
            begin
                bcnt  <= #1 3'h7;   // set transfer counter
                treg  <= #1 wfdout; // load transfer register
                sck_o <= #1 cpol;   // set sck

                if (~wfempty) begin
                  wfre  <= #1 1'b1;
                  state <= #1 2'b01;
                  if (cpha) sck_o <= #1 ~sck_o;
                end
            end

         2'b01: // clock-phase2, next data
            if (ena) begin
              sck_o   <= #1 ~sck_o;
              state   <= #1 2'b11;
            end

         2'b11: // clock phase1
            if (ena) begin
              treg <= #1 {treg[6:0], miso_i};
              bcnt <= #1 bcnt -3'h1;

              if (~|bcnt) begin
                state <= #1 2'b00;
                sck_o <= #1 cpol;
                rfwe  <= #1 1'b1;
              end else begin
                state <= #1 2'b01;
                sck_o <= #1 ~sck_o;
              end
            end

         2'b10: state <= #1 2'b00;
       endcase
    end

assign mosi_o = treg[7];

?传输计数器

// count number of transfers (for interrupt generation)
reg [1:0] tcnt; // transfer count

always @(posedge clk_i)
  if (~spe)
    tcnt <= #1 icnt;
  else if (rfwe) // rfwe gets asserted when all bits have been transfered
    if (|tcnt)
      tcnt <= #1 tcnt - 2'h1;
    else
      tcnt <= #1 icnt;

assign tirq = ~|tcnt & rfwe;

endmodule
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-06-14 22:50:26  更:2022-06-14 22:50:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 1:48:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码