ZYNQ中的SPI控制器使用

一、SPI控制器简介

SPI 接口是 Motorola 首先提出的全双工三线同步串行外围接口, 采用主从模式(MasterSlave) 架构; 支持多 slave模式应用, 一般仅支持单 Master。 时钟由 Master 控制, 在时钟移位脉冲下, 数据按位传输, 高位在前, 低位在后(MSBfirst) ; SPI 接口有 2 根单向数据线, 为全双工通信。


1.SPI 接口

共有 4 根信号线, 分别是: 设备选择线、 时钟线、 串行输出数据线、 串行输入数据线。

(1) MOSI: 主器件数据输出, 从器件数据输入

(2) MISO: 主器件数据输入, 从器件数据输出

(3) SCLK: 时钟信号, 由主器件产生

(4) /SS: 从器件使能信号, 由主器件控制

2.时钟极性和时钟相位

时钟的极性(CPOL)。决定空闲时时钟时低电平还是高电平。当时钟极性为 0 时(CPOL=0) , SCK 信号线在空闲时为低电平; 当时钟极性为 1 时(CPOL=1) , SCK 信号线在空闲时为高电平;
时钟的相位(CPHA)。当时钟相位为 1 时(CPHA=1) , 在 SCK 信号线的第二个跳变沿进行采样。为0的时候是在第一个跳变沿采样

3. ZYNQ的SPI控制器

        ZYNQ中有两个SPI控制器(SPI0和SPI1),每个控制器有三个片选信号,也可3-8译码获得八个片选信号。既可以做从机又可以做主机。他的引脚:SS/SCLK/MOSI和MISO,可以映射到MIO引脚上,也可以映射到EMIO引脚上(就可以根据需要调试)

二、在ZYNQ中使用SPI控制器的方法

1.在vivado中配置

        在vivado中程配置IP核时,勾选SPI0。同时可以选择映射到MIO还是EMIO上。这里选择EMIO,就可以把SPI控制器的线引到特定的管脚,方便调试。

        设置完IP核之后要连线,检查是否有错误,然后保存——生成OUTPUT文件——生成HDL文件。映射到EMIO的时候,要进行管脚约束,确定映射到哪一个EMIO上。设置管脚约束的方法可以在正点原子的嵌入式开发指南中的EMIO教程视频当中看到。

设置完以后要生成比特流文件。然后导出XSA文件。注意要记录下来SPI控制器的哪个脚,引射到了哪个EMIO上面。当使用了更多EMIO的时候,也要记录下来EMIO[0]对应了哪个引脚,EMIO[1]对应了哪个引脚等等……方便后面编程的时候能正确对应起来

2.在vitis中编程

将刚才生成的xsa文件导入,在vitis中编写程序

(1)添加spi控制器的相关代码

介绍轮询方式:

首先添加文件命名为spi_ps.c文件


#include "spi_ps.h"

XSpiPs SpiInstance;

/* 发送和接收的缓冲区 */
u8 *ReadBuf = (void*) 0x08000000 ;
u8 *SendBuf = (void*) 0x08100000 ;


/* -----------------spi初始化函数--------------------------- */

int SpiPs_Init(XSpiPs *SpiInstancePtr,u16 SpiDeviceId )
{
	int Status;
	XSpiPs_Config *SpiConfig;

	//根据器件ID查找配置信息
	SpiConfig = XSpiPs_LookupConfig(SpiDeviceId);
	if (NULL == SpiConfig) {
		return XST_FAILURE;
	}
	/* 初始化函数 */
	Status = XSpiPs_CfgInitialize((SpiInstancePtr), SpiConfig,
					SpiConfig->BaseAddress);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/* 配置SPI的工作模式
	 * 第一个参数是XSpiPs 实例
	 * 第二个参数设置为主机模式, */
	Status = XSpiPs_SetOptions(SpiInstancePtr, XSPIPS_MASTER_OPTION | XSPIPS_FORCE_SSELECT_OPTION );

	/* 配置SPI时钟的分频参数 */
	Status = XSpiPs_SetClkPrescaler(SpiInstancePtr, XSPIPS_CLK_PRESCALE_256);

	/* 配置被选择的从设备 */
	XSpiPs_SetSlaveSelect(SpiInstancePtr, 0x00);

	return XST_SUCCESS;
}

/* -------------------SPI的发送函数------------- */
void SpiPs_Send(XSpiPs *SpiInstancePtr,u8 *SendBuffer, int ByteCount)
{

	/* 原函数形式
	* s32 XSpiPs_PolledTransfer(XSpiPs *InstancePtr, u8 *SendBufPtr,u8 *RecvBufPtr, u32 ByteCount)
	* 其中 第二个参数 SendBufPtr 是发送的数据的指针,指向要发送数据的地址了
	* 第三个参数 RecvBufPtr 是接收的数据,
	* ByteCount 是之要发送的字节数
	* 这个属于轮询模式    */
	XSpiPs_PolledTransfer(SpiInstancePtr, SendBuffer, ReadBuf,ByteCount);

}

/* -------------------SPI的读取函数------------- */
void SpiPs_Read(XSpiPs *SpiInstancePtr,u8 *ReadBuffer,int ByteCount)
{
	XSpiPs_PolledTransfer(SpiInstancePtr, SendBuf, ReadBuffer,ByteCount);
}


其中,这一句配置SPI的工作模式,包括主机模式、手动片选还是自动片选、手动开始传输还是自动开始传输、时钟相位和时钟极性。要配置哪一个,就把对应的写到第二个参数的位置上。

Status = XSpiPs_SetOptions(SpiInstancePtr, XSPIPS_MASTER_OPTION | XSPIPS_FORCE_SSELECT_OPTION );

 可以跳转进这个函数,能看到这个配置表,需要加哪个就把哪个复制进去。

typedef struct {
	u32 Option;
	u32 Mask;
} OptionsMap;

static OptionsMap OptionsTable[] = {
	{XSPIPS_MASTER_OPTION, XSPIPS_CR_MSTREN_MASK},
	{XSPIPS_CLK_ACTIVE_LOW_OPTION, XSPIPS_CR_CPOL_MASK},
	{XSPIPS_CLK_PHASE_1_OPTION, XSPIPS_CR_CPHA_MASK},
	{XSPIPS_DECODE_SSELECT_OPTION, XSPIPS_CR_SSDECEN_MASK},
	{XSPIPS_FORCE_SSELECT_OPTION, XSPIPS_CR_SSFORCE_MASK},
	{XSPIPS_MANUAL_START_OPTION, XSPIPS_CR_MANSTRTEN_MASK}
};

比如,要想设置时钟极性为1,就把这个函数改写成:

Status = XSpiPs_SetOptions(SpiInstancePtr, XSPIPS_MASTER_OPTION | XSPIPS_FORCE_SSELECT_OPTION | XSPIPS_CLK_ACTIVE_LOW_OPTION);

第二个分频参数:SPI的时钟是166.66MHz,分频后的时钟就是SPI控制器做主机时的时钟频率。比如最大256分频,这时候时钟频率就是大概是651KHz左右。

然后设置片选的从设备。这个功能我没有使用,但是应该是用哪个片选就写哪个,从0到2编号

然后后面的是轮询模式下发送和接收的函数。可以直接调用。

然后再添加spi_ps.h的文件:


#ifndef SRC_SPIPS_H_
#define SRC_SPIPS_H_

#include "xparameters.h"	/* EDK generated parameters */
#include "xspips.h"		/* SPI device driver */
#include "xscugic.h"		/* Interrupt controller device driver */
#include "xil_exception.h"
#include "xil_printf.h"


void SpiPs_Read(XSpiPs *SpiInstancePtr,u8 *ReadBuffer,int ByteCount);

void SpiPs_Send(XSpiPs *SpiInstancePtr,u8 *SendBuffer, int ByteCount);

int SpiPs_Init(XSpiPs *SpiInstancePtr,u16 SpiDeviceId );

#define SPI_DEVICE_ID		XPAR_XSPIPS_0_DEVICE_ID
#define MAX_DATA		100



#endif /* SRC_SPIPS_H_ */

具体要用SPI发送数据的时候,第一步先把要发的数据写到sendbuf缓冲区里面,然后调用send函数即可。比如这样:

SendBuf[0] = RegAddr;
for(t=0;t<Len;t++)
	{
		SendBuf[t+1] = Data[t];
	}
SpiPs_Send(&SpiInstance,SendBuf,Len+1);

(部分内容来自米联客开发指南)

  • 38
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值