当前位置:   article > 正文

自定义AXI总线形式SPI接口IP核,点亮OLED

spi vs aix

一、前言

  最近花费很多精力在算法仿真和实现上,外设接口的调试略有生疏。本文以FPGA控制OLED中的SPI接口为例,重新夯实下基础。重点内容为SPI时序的RTL设计以及AXI-Lite总线分析。当然做些项目时可以直接调用Xilinx提供的SPI IP核,这里仅出于练习的目的考虑。

二、接口时序分析

   本项目用的OLED型号为UG-2832HSWEG04,核心控制器是SSD1306。该芯片支持并口、I2C以及SPI接口,这里采用4线SPI作为数据总线。4线SPI接口包括:

SCLK:串行时钟,SSD1306上升沿采集数据

SDIN:串行数据输入,数据顺序为MSB

D/C:数据命令控制,高电平为数据,低电平为控制命令

CS:片选信号,低电平有效

时序图如下:

   片选信号有效期间,每第8个时钟周期上升沿时刻,控制芯片会采样D/C并同时将进入的一字节数据写入到显示缓存GDDRAM或控制寄存器中。

  根据datasheet中的AC Characteristics中参数,选择SPI串行时钟周期为200ns,占空比为50%以保证足够的时序裕量。此时传输速率为:5MHZ/8 = 625KHZ。

三、SPI接口模块设计

   根据上述分析,很容易可以设计出用于传输数据或命令的SPI时序接口模块。接口定义如下:

用户侧:clk rst_n com din busy,依次是系统时钟,复位,指令信号(1为发送控制信息,2则发送数据),待传输字节以及忙等待指示。

外设侧:SCLK SDIN CS D/C

  逻辑状态分为:IDLE SEND和DONE,具体时序如下:

  直接对照上图编写HDL:

  1 `timescale 1ns / 1ps
  2 
  3 module spi_4wire#(parameter DIV_CYC = 20)
  4 (
  5     //本地接口
  6     input clk,//100MHZ
  7     input rst_n,
  8     input [2-1:0] com,//1发送控制信息,2发送数据 其他无效
  9     input [8-1:0] din,
 10     output busy,
 11     //芯片侧接口
 12     output reg sclk = 0,
 13     output reg sdin = 0,
 14     output reg cs = 1'b1,
 15     output reg dc = 0//1是数据,0是控制命令
 16     );
 17 //**************************参数定义********************************************
 18     function integer clogb2 (input integer bit_depth);
 19       begin
 20         for(clogb2=0; bit_depth>0; clogb2=clogb2+1)
 21           bit_depth = bit_depth >> 1;
 22       end
 23     endfunction
 24 
 25 localparam DIV_CNT_W = clogb2(DIV_CYC-1),
 26            BIT_CNT_W = clogb2(8-1);
 27 
 28 localparam  IDLE = 0 ,
 29             SEND = 1 ,
 30             DONE = 2 ;       
 31 
 32 //************************变量定义****************************************
 33 reg [ (DIV_CNT_W-1):0]  div_cnt =0    ;
 34 wire        add_div_cnt ;
 35 wire        end_div_cnt ;
 36 reg [ (BIT_CNT_W-1):0]  bit_cnt =0    ;
 37 wire        add_bit_cnt ;
 38 wire        end_bit_cnt ;
 39 reg [2-1:0] state_c = IDLE,state_n = IDLE;
 40 wire idle2send,send2done,done2idle;
 41 reg [8+2-1:0] data_tmp = 0;
 42 wire din_vld;
 43 wire start_send;
 44 reg busy_flag = 0;
 45 //************************逻辑****************************************
 46 //sclk时钟分频 T:10ns --> 200ns 20倍
 47 //分频计数器
 48 always @(posedge clk or negedge rst_n) begin 
 49     if (rst_n==0) begin
 50         div_cnt <= 0; 
 51     end
 52     else if(add_div_cnt) begin
 53         if(end_div_cnt)
 54             div_cnt <= 0; 
 55         else
 56             div_cnt <= div_cnt+1 ;
 57    end
 58 end
 59 assign add_div_cnt = (1);
 60 assign end_div_cnt = add_div_cnt  && div_cnt == (DIV_CYC)-1 ;
 61 
 62 //比特计数器
 63 always @(posedge clk or negedge rst_n) begin 
 64     if (rst_n==0) begin
 65         bit_cnt <= 0; 
 66     end
 67     else if(add_bit_cnt) begin
 68         if(end_bit_cnt)
 69             bit_cnt <= 0; 
 70         else
 71             bit_cnt <= bit_cnt+1 ;
 72    end
 73 end
 74 assign add_bit_cnt = (state_c == SEND && end_div_cnt);
 75 assign end_bit_cnt = add_bit_cnt  && bit_cnt == (8)-1 ;
 76 
 77 //控制状态机
 78 always @(posedge clk or negedge rst_n) begin 
 79     if (rst_n==0) begin
 80         state_c <= IDLE ;
 81     end
 82     else begin
 83         state_c <= state_n;
 84    end
 85 end
 86 
 87 always @(*) begin 
 88     case(state_c)  
 89         IDLE :begin
 90             if(idle2send ) 
 91                 state_n = SEND ;
 92             else 
 93                 state_n = state_c ;
 94         end
 95         SEND :begin
 96             if(send2done ) 
 97                 state_n = DONE ;
 98             else 
 99                 state_n = state_c ;
100         end
101         DONE :begin
102             if(done2idle ) 
103                 state_n = IDLE ;
104             else 
105                 state_n = state_c ;
106         end
107         default : state_n = IDLE ;
108     endcase
109 end
110 
111 assign idle2send  = state_c==IDLE && (end_div_cnt && data_tmp[10-1 -:2] != 0);
112 assign send2done  = state_c==SEND && (end_bit_cnt);
113 assign done2idle  = state_c==DONE && (end_div_cnt);
114 
115 
116 //输入命令/数据寄存
117 always  @(posedge clk or negedge rst_n)begin
118     if(rst_n==1'b0)begin
119         data_tmp <= 0;
120     end
121     else if(din_vld)begin
122         data_tmp <= {com,din};
123     end
124     else if(done2idle)begin
125         data_tmp <= 0;
126     end
127 end
128 
129 assign din_vld = busy_flag == 1'b0 && com != 2'd0;
130 
131 //SPI输出信号
132 always  @(posedge clk or negedge rst_n)begin
133     if(rst_n==1'b0)begin
134         sdin <= 0;
135     end
136     else if(add_bit_cnt)begin
137         sdin <= data_tmp[8-1-bit_cnt];
138     end
139 end
140 
141 always  @(posedge clk or negedge rst_n)begin
142     if(rst_n==1'b0)begin
143         cs <= 1'b1;
144     end
145     else if(start_send)begin
146         cs <= 0;
147     end
148     else if(done2idle)begin
149         cs <= 1'b1;
150     end
151 end
152 
153 assign start_send = add_bit_cnt && bit_cnt == 0;
154 
155 always  @(posedge clk or negedge rst_n)begin
156     if(rst_n==1'b0)begin
157         dc <= 1'b0;
158     
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小舞很执着/article/detail/738538
推荐阅读
相关标签
  

闽ICP备14008679号