SPI协议 一.SPI协议简介
SPI(Serial Peripheral interface)定义的一种串行外围设备接口,是一种全双工、同步的通信总线,只需四根信号线即可,节约引脚,同时有利于PCB的布局。正是出于这种简单易用的特性,现在越来越多的芯片集成了SPI通信协议,如FLASH、AD转换器等。
SPI通信协议的优点是支持全双工通信,通讯方式比较简单,且相对数据传输速率较快(相比UART、I2C),灵活的数据传输方式,不限于8位,可以是任意大小的字;缺点是没有应答机制,在数据可靠性上有一定缺陷
二.SPI协议通信原理 1.SPI协议物理层
2.SPI协议协议层
SPI总线传输一共有4种模式,这4种模式分别由时钟极性(CPOL,Clock Polarity)和时钟相位(CPHA,Clock Phase)来定义,其中CPOL参数规定了SCK时钟信号空闲状态的电平,CPHA规定了数据实在SCK时钟的上升沿被采样还是下降沿被采样。
SPI总线的极性-时钟极性 时钟极性决定SPI总线空闲时的时钟信号高电平还是低电平。 CPOL=1:表示空闲时是高电平; CPOL=0: 表示空闲时是低电平。
SPI总线的相位-时钟相位 时钟相位决定SPI总线从哪个跳变沿开始采样数据。 CPHA=0:在时钟信号SCK的第一个跳变沿采样,第二个跳变沿输出; CPHA=1:在时钟信号SCK的第一个跳变沿输出,第二个跳变沿采样。
模式0:CPOL= 0,CPHA=0。SCK串行时钟线空闲是为低电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换
模式1:CPOL= 0,CPHA=1。SCK串行时钟线空闲是为低电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿切换
模式2:CPOL= 1,CPHA=0。SCK串行时钟线空闲是为高电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿切换
模式3:CPOL= 1,CPHA=1。SCK串行时钟线空闲是为高电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换
3.SPI协议通信过程 SCK,CS_N,MOSI信号为主机控制,且MOSI和MISO信号在CS_N信号为低时才有效。当CS_N信号拉低时,表示某一从机被主机选中,且可以开始通信。MOSI和MISO在每个SCK的时钟周期传输一位数据(一般为MSB,高位先行),且数据的输入输出是同时进行的。数据采集和数据变化是按上述SPI通信模式进行。 SPI每次传输数据可以8位或16位为单位,每次传输的单位数不受限制。
4.SPI协议端口
4.SPI协议程序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 module spi_interface ( input sys_clk , input rst_n , input req , input [7 :0 ]din , output [7 :0 ]dout , output done , input miso , output mosi , output sclk , output cs_n ); assign cs_n = !req; reg [2 :0 ] state_c; reg [2 :0 ] state_n; localparam SCLK_PERIOD = 16 , SCLK_FALL = 4 , SCLK_RISE = 12 ; localparam IDLE = 3'b001 , READY= 3'b010 , TRANS= 3'b100 ; reg [4 :0 ]cnt; wire add_cnt; wire end_cnt; reg [3 :0 ]cnt_bit; wire add_cnt_bit; wire end_cnt_bit; wire idle2ready ; wire ready2trans; wire trans2idle ; assign idle2ready = (state_c == IDLE ) && (req) ; assign ready2trans= (state_c == READY) && (1'b1 ); assign trans2idle = (state_c == TRANS) && (end_cnt_bit); always @(posedge sys_clk or negedge rst_n) begin if (!rst_n) begin state_c <= IDLE; end else state_c <= state_n; end always @(*) begin case (state_c) IDLE : if (idle2ready) begin state_n <= READY; end else begin state_n <= state_c; end READY: if (ready2trans) begin state_n <= TRANS; end else begin state_n <= state_c; end TRANS: if (trans2idle) begin state_n <= IDLE; end else begin state_n <= state_c; end default : state_n <= IDLE; endcase end reg add_cnt_flag; always @(posedge sys_clk or negedge rst_n) begin if (!rst_n) begin add_cnt_flag <= 1'd0 ; end else if (ready2trans) begin add_cnt_flag <= 1'd1 ; end else if (trans2idle) begin add_cnt_flag <= 1'd0 ; end end assign add_cnt = add_cnt_flag; assign end_cnt = add_cnt && (cnt == SCLK_PERIOD - 1'd1 ); always @(posedge sys_clk or negedge rst_n) begin if (!rst_n) begin cnt <= 5'd0 ; end else if (add_cnt) begin if (end_cnt) begin cnt <= 5'd0 ; end else cnt <= cnt +5'd1 ; end end reg sclk_r ; assign sclk = sclk_r; always @(posedge sys_clk or negedge rst_n) begin if (!rst_n) begin sclk_r <= 1'd1 ; end else if (add_cnt && (cnt == SCLK_FALL-1'd1 )) begin sclk_r <= 1'd0 ; end else if (add_cnt && (cnt == SCLK_RISE-1'd1 )) begin sclk_r <= 1'd1 ; end end localparam CNT_BIT_MAX = 8 ; assign add_cnt_bit = end_cnt; assign end_cnt_bit = add_cnt_bit && (cnt_bit == CNT_BIT_MAX - 3'd1 ); always @(posedge sys_clk or negedge rst_n) begin if (!rst_n) begin cnt_bit <= 3'd0 ; end else if (add_cnt_bit) begin if (end_cnt_bit) begin cnt_bit <= 3'd0 ; end else cnt_bit <= cnt_bit + 3'd1 ; end end reg [7 :0 ] tx_data; always @(posedge sys_clk or negedge rst_n) begin if (!rst_n) begin tx_data <= 0 ; end else if (state_c == READY) begin tx_data <= din; end end assign mosi = tx_data[7 -cnt_bit]; reg [7 :0 ]rx_data; assign dout = rx_data ; always @(posedge sys_clk or negedge rst_n) begin if (!rst_n) begin rx_data <= 0 ; end else if (add_cnt && (cnt == SCLK_RISE - 1 )) begin rx_data <= {rx_data[6 :0 ],miso}; end end assign done = trans2idle; endmodule
Copyright Notice: 此文章版权归Violet所有,如有转载,请注明来自原作者