FPGA学习笔记(九)SPI学习总结及stm32的HAL库下SPI配置
创始人
2024-03-22 02:24:27

系列文章目录

一、FPGA学习笔记(一)入门背景、软件及时钟约束

二、FPGA学习笔记(二)Verilog语法初步学习(语法篇1)

三、FPGA学习笔记(三) 流水灯入门FPGA设计流程

四、FPGA学习笔记(四)通过数码管学习顶层模块和例化的编写

五、FPGA学习笔记(五)Testbench(测试平台)文件编写进行Modelsim仿真

六、FPGA学习笔记(六)Modelsim单独仿真和Quartus联合仿真

七、FPGA学习笔记(七)verilog的深入学习之任务与函数(语法篇3)

八、FPGA学习笔记(八)同步/异步信号的打拍分析及处理


参考文章

FPGA实现的SPI协议(一)----SPI驱动

文章目录

  • 系列文章目录
  • 参考文章
  • SPI协议概念
    • 概念
    • 工作方式
    • 协议
  • stm32的HAL库下的SPI
    • 配置SPI工作模式
    • 配置SPI工作参数
    • 程序代码
  • FPGA下的SPI


SPI协议概念

概念

SPI(Serial Peripheral Interface,串行外围设备接口)通讯协议,是一个重要的低速协议。支持全双工通信,且传输速度相对较快,缺点是没有指定的流控制,没有应答机制,在数据可靠性上有一定缺陷。也有人说SPI就是数据交换,想要接收一个数据就必须发送一个数据。一般的实现通常能达到甚至超过10 Mbps。

工作方式

在这里插入图片描述
SCK (Serial Clock):时钟信号线,用于同步通讯数据。

MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。

MISO (Master Input,Slave Output):主设备输入/从设备输出引脚。

CS (Chip Select):片选信号线。当有多个 SPI 从 设备与 SPI 主机相连时,每个从设备都有独立的片选信号线,通过CS片选信号来决定通信的从机设备是哪一台。通信期间低电平有效,表示对应从机被选中。
有的也把CS叫SS( Slave Select),NSS就是(Negetive Slave Select:低电平开始传输)

协议

SPI协议中,时钟上升沿或下降沿读取、空闲时是高电平还是低电平(时钟信号)都是可以选择的。
时钟极性(CPOL,Clock Polarity):规定了SCK时钟信号空闲状态的电平。0低电平,1高电平。
时钟相位(CPHA,Clock Phase):0上升沿,1下降沿。(通常用下降沿采样的少)

在这里插入图片描述
SPI 每次传输的单位数不受限制,一般是八个单位,也就是一个字节。在传输过程中,没有规定LSB(Least Significant Bit最低有效位)和MSB(Most Significant Bit最高有效位),一般采用的MSB高位先传输。

stm32的HAL库下的SPI

配置SPI工作模式

在这里插入图片描述

这里的主机接收模式相当下图:(后四个相当是单工模式,只支持单方向的传输)
在这里插入图片描述
半双工模式(数据传输上支持双方向传输,但是不能同时进行双向传输):
stm32支持半双工模式,可以(通过使能BIDIMODE reg位来)选择单线双向通信模式或单线单向模式。
在这里插入图片描述
又在网上找了找,因为还没有实际接触SPI的单线、双线、四线模式,所以只能先简单概括一下:(不一定对)
1.单线模式:就是普通的最常见的SPI接线方式,MOSI主机发从机接,MISO:主机接收从机发送
2.双线模式:MOSI和MISO都用来发送数据,同时单方向传输(感觉变成了IIC方式)
在这里插入图片描述
3.四线模式:QSPI是Queued SPI的简写,是Motorola公司推出的SPI接口的扩展,比SPI应用更加广泛。
同时使用MOSI、MISO、WP、HOLD作为数据传输,使得SPI传输带宽增加四倍;
改掉了原来引脚的功能:WP(Write Protect)是防止QSPI Flash的状态寄存器被写入错误的数据,WP信号低电平有效;HOLD信号的作用是暂停QSPI Flash的操作。当HOLD信号为低,并且CS也为低时,串行输出信号DO将处于高阻态,串行输入信号DI与串行时钟信号SCLK将被QSPI Flash忽略。当HOLD拉高以后,QSPI Flash的读写操作能继续进行。当多个SPI设备共享同一组SPI总线相同的信号的时候,可以通过HOLD来切换信号的流向。
在这里插入图片描述

在这里插入图片描述

配置SPI工作参数

在这里插入图片描述

配置NSS 引脚的使用模式,可以选择为硬件模式(SPI_NSS_HARD )与软件模式( SPI_NSS_SOFT ),在硬件模式中的SPI 片选信号由SPI 硬件自动产生,而软件模式则需要我们亲自把相应的GPIO 端口拉高或置低产生非片选和片选信号。实际中软件模式应用比较多。
在这里插入图片描述
CRC Calculation:指定是否启用CRC 计算,若我们使用CRC 校验时,就使用这个成员的参数(多项式),来计算CRC 的值。
Baud Rate是计算出的sck速率
在这里插入图片描述
摩托罗拉协议就是一般的SPI协议,TI协议的一般指的SSP协议。

程序代码

HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size,uint32_t Timeout);
HAL_StatusTypeDef HAL_SPI_Transmit_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_Receive_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_TransmitReceive_IT(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,uint16_t Size);
HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,uint16_t Size);
HAL_StatusTypeDef HAL_SPI_DMAPause(SPI_HandleTypeDef *hspi);
HAL_StatusTypeDef HAL_SPI_DMAResume(SPI_HandleTypeDef *hspi);
HAL_StatusTypeDef HAL_SPI_DMAStop(SPI_HandleTypeDef *hspi);

这里有一个问题,就是从机的SCK是input,那么主机如何接收从机数据呢,我想还是通过主机发送数据,从机与此同时接收数据并发送数据,主机才能接收,这也是为什么网上有人说SPI是数据交换。

FPGA下的SPI

以下是FPGA作为主机的时候,是参考文章里面的代码


module spi_drive
(
// 系统接口input               sys_clk		, 			// 全局时钟50MHzinput               sys_rst_n	, 			// 复位信号,低电平有效
// 用户接口	input               spi_start	,			// 发送传输开始信号,一个高电平input              	spi_end		,			// 发送传输结束信号,一个高电平input        [7:0]  data_send   , 			// 要发送的数据output  reg  [7:0]  data_rec  	, 			// 接收到的数据output  reg         send_done	, 			// 主机发送一个字节完毕标志位    output  reg         rec_done	, 			// 主机接收一个字节完毕标志位    
// SPI物理接口input               spi_miso	, 			// SPI串行输入,用来接收从机的数据output  reg         spi_sclk	, 			// SPI时钟output  reg         spi_cs    	, 			// SPI片选信号,低电平有效output  reg         spi_mosi				// SPI输出,用来给从机发送数据          
);reg	[1:0]	cnt;								//4分频计数器
reg	[3:0]	bit_cnt_send;						//发送计数器
reg	[3:0]	bit_cnt_rec;						//接收计数器
reg			spi_end_req;						//结束请求//4分频计数器 
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)cnt <= 2'd0;						else if(!spi_cs)beginif(cnt == 2'd3)cnt <= 2'd0;elsecnt <= cnt + 1'b1;		endelse cnt <= 2'd0;	
end
// 生成spi_sclk时钟  
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)spi_sclk <= 1'b0;			//模式0默认为低电平,CPOL=0				else if(!spi_cs)begin			//在SPI传输过程中if(cnt == 2'd0 )spi_sclk <= 1'b0;else if (cnt == 2'd2)spi_sclk <= 1'b1;else spi_sclk <= spi_sclk;	endelse spi_sclk <= 1'b0;			//模式0默认为低电平		
end
// 生成片选信号spi_cs
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)spi_cs <= 1'b1;				//默认为高电平,低电平选中						else if(spi_start)				//开始SPI准备传输,拉低片选信号spi_cs <= 1'b0;//收到了SPI结束信号,且结束了最近的一个BYTEelse if(spi_end_req && (cnt == 2'd1 && bit_cnt_rec == 4'd0))spi_cs <= 1'b1;				//拉高片选信号,结束SPI传输
end
// 生成结束请求信号(捕捉spi_end信号)
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)spi_end_req <= 1'b0;		//默认不使能					else if(spi_cs)					spi_end_req <= 1'b0;		//结束SPI传输后拉低请求else if(spi_end)				spi_end_req <= 1'b1;		//接收到SPI结束信号后就把结束请求拉高
end
// 发送数据过程--------------------------------------------------------------------// 发送数据
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beginspi_mosi <= 1'b0;						//模式0空闲bit_cnt_send <= 4'd0;endelse if(cnt == 2'd0 && !spi_cs)begin		//模式0的上升沿spi_mosi <= data_send[7-bit_cnt_send];	//发送数据移位if(bit_cnt_send == 4'd7)				//发送完8bitbit_cnt_send <= 4'd0;elsebit_cnt_send <= bit_cnt_send + 1'b1;	endelse if(spi_cs)begin						//非传输时间段spi_mosi <= 1'b0;						//模式0空闲bit_cnt_send <= 4'd0;endelse beginspi_mosi <= spi_mosi;bit_cnt_send <= bit_cnt_send;end
end
// 发送数据标志
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)send_done <= 1'b0;			else if(cnt == 2'd0 && bit_cnt_send == 4'd7)		//发送完了8bit数据send_done <= 1'b1;								//拉高一个周期,表示发送完成	else send_done <= 1'b0;			
end// 接收数据过程--------------------------------------------------------------------// 接收数据spi_miso
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)begindata_rec <= 8'd0;		bit_cnt_rec <= 4'd0;endelse if(cnt == 2'd2 && !spi_cs)begin				//模式0的上升沿data_rec[7-bit_cnt_rec] <= 	spi_miso;			//移位接收if(bit_cnt_rec == 4'd7)							//接收完了8bitbit_cnt_rec <= 4'd0;elsebit_cnt_rec <= bit_cnt_rec + 1'b1;	endelse if(spi_cs)begin								bit_cnt_rec <= 4'd0;endelse begindata_rec <= data_rec;bit_cnt_rec <= bit_cnt_rec;end
end
// 接收数据标志
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)rec_done <= 1'b0;									else if(cnt == 2'd2 && bit_cnt_rec == 4'd7)			//接收完了8bitrec_done <= 1'b1;								//拉高一个周期,表示接收完成			else rec_done <= 1'b0;					
endendmodule

上面程序的结构就是4分频发出去一个SCK信号,然后分频产生的cnt 信号来发送和接收数据,最后有传输开始和传输结束信号,不过这两个信号感觉…,主机不需要别的输入控制,想发的时候发送?那么从机跟不上咋办,就像stm32是串行的,不像FPGA是并行的,所以stm32接受前发送一个起始信号和结束信号

想想FPGA作为从机的时候:
打拍收集SCK的信号就行了,然后在对应的主机的设置,在SCK的上升沿或者下降沿收集信号就行。FPGA是并行的,主机发什么,它都能跟上

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...