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

本文介绍了如何通过自定义AXI SPI接口控制OLED显示屏,重点涉及SPI时序的RTL设计和AXI-Lite总线分析。通过设计SPI接口模块和AXI总线封装,实现了数据传输。仿真测试验证了接口和SPI时序逻辑的正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

  最近花费很多精力在算法仿真和实现上,外设接口的调试略有生疏。本文以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     end
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值