FPGA嵌入式开发一些Xilinx SDK库函数的理解

最近在测试AXI Quad SPI这个IP核的端口时序,搭建BD后导出到硬件,在SDK中导入xspi_intr_example.c的源文件,在师兄的帮助下,浅浅研究了一下代码。

首先,需要修改源程序中的错误,参照CSDN文章:ZYNQ中断示例修改 做出以下修改:

#define INTC_DEVICE_ID		XPAR_INTC_0_DEVICE_ID
#define SPI_IRPT_INTR		XPAR_INTC_0_SPI_0_VEC_ID

// 修改为:
#define INTC_DEVICE_ID		XPAR_SCUGIC_0_DEVICE_ID
#define SPI_IRPT_INTR		XPAR_FABRIC_SPI_0_VEC_ID

此外,还要对中断驱动实例名称进行修改:

static INTC Intc;	 /* The instance of the Interrupt Controller */

// 修改为:
static INTC IntcInstance;	 /* The instance of the Interrupt Controller */

以下是对程序中函数的理解。

XSpi_LookupConfig()函数

ConfigPtr = XSpi_LookupConfig(XPAR_SPI_0_DEVICE_ID);

参数XPAR_SPI_0_DEVICE_ID为SPI的设备ID。该函数的作用是从系统中查询是否有这个设备的定义,该函数的原型为:

#define XPAR_XSPI_NUM_INSTANCES 1U

/*****************************************************************************/
/**
*
* Looks up the device configuration based on the unique device ID. A table
* contains the configuration info for each device in the system.
*
* @param	DeviceId contains the ID of the device to look up the
*		configuration for.
*
* @return
*
* A pointer to the configuration found or NULL if the specified device ID was
* not found. See xspi.h for the definition of XSpi_Config.
*
* @note		None.
*
******************************************************************************/

XSpi_Config *XSpi_LookupConfig(u16 DeviceId)
{
	XSpi_Config *CfgPtr = NULL;
	u32 Index;

	for (Index = 0; Index < XPAR_XSPI_NUM_INSTANCES; Index++) {
		if (XSpi_ConfigTable[Index].DeviceId == DeviceId) {
			CfgPtr = &XSpi_ConfigTable[Index];
			break;
		}
	}

	return CfgPtr;
}

该函数实现的是:查表看是否有相应的设备描述,如果有,就会将该设备在ZYNQ地址空间中的地址赋值给CfgPtr这个设备指针。其中XSpi_ConfigTable是一个结构体列表,有两层大括号,第一层由Index索引,第二层由‘.’来定位。定义如下:

/*
* The configuration table for devices
*/

XSpi_Config XSpi_ConfigTable[] =
{
	{
		XPAR_SPI_0_DEVICE_ID,
		XPAR_SPI_0_BASEADDR,
		XPAR_SPI_0_FIFO_EXIST,
		XPAR_SPI_0_SPI_SLAVE_ONLY,
		XPAR_SPI_0_NUM_SS_BITS,
		XPAR_SPI_0_NUM_TRANSFER_BITS,
		XPAR_SPI_0_SPI_MODE,
		XPAR_SPI_0_TYPE_OF_AXI4_INTERFACE,
		XPAR_SPI_0_AXI4_BASEADDR,
		XPAR_SPI_0_XIP_MODE,
		XPAR_SPI_0_USE_STARTUP
	}
};

XSpi_CfgInitialize()函数

Status = XSpi_CfgInitialize(SpiInstancePtr, ConfigPtr,
                  ConfigPtr->BaseAddress);

参数SpiInstancePtr为SPI驱动实例指针,ConfigPtr为LookupConfig函数找到的SPI设备地址指针。该函数的作用是:根据前面找到的设备地址指针,对设备的寄存器进行配置。该函数的原型为:

/*****************************************************************************/
/**
*
* Initializes a specific XSpi instance such that the driver is ready to use.
*
* The state of the device after initialization is:
*	- Device is disabled
*	- Slave mode
*	- Active high clock polarity
*	- Clock phase 0
*
* @param	InstancePtr is a pointer to the XSpi instance to be worked on.
* @param	Config is a reference to a structure containing information
*		about a specific SPI device. This function initializes an
*		InstancePtr object for a specific device specified by the
*		contents of Config. This function can initialize multiple
*		instance objects with the use of multiple calls giving
		different Config information on each call.
* @param	EffectiveAddr is the device base address in the virtual memory
*		address space. The caller is responsible for keeping the
*		address mapping from EffectiveAddr to the device physical base
*		address unchanged once this function is invoked. Unexpected
*		errors may occur if the address mapping changes after this
*		function is called. If address translation is not used, use
*		Config->BaseAddress for this parameters, passing the physical
*		address instead.
*
* @return
*		- XST_SUCCESS if successful.
*		- XST_DEVICE_IS_STARTED if the device is started. It must be
*		  stopped to re-initialize.
*
* @note		None.
*
******************************************************************************/
int XSpi_CfgInitialize(XSpi *InstancePtr, XSpi_Config *Config,
			UINTPTR EffectiveAddr)
{
	u8  Buffer[3];
	u32 ControlReg;
	u32 StatusReg;
	
	Xil_AssertNonvoid(InstancePtr != NULL);

	/*
	 * If the device is started, disallow the initialize and return a status
	 * indicating it is started.  This allows the user to stop the device
	 * and reinitialize, but prevents a user from inadvertently
	 * initializing.
	 */
	if (InstancePtr->IsStarted == XIL_COMPONENT_IS_STARTED) {
		return XST_DEVICE_IS_STARTED;
	}

	/*
	 * Set some default values.
	 */
	InstancePtr->IsStarted = 0;
	InstancePtr->IsBusy = FALSE;

	InstancePtr->StatusHandler = StubStatusHandler;

	InstancePtr->SendBufferPtr = NULL;
	InstancePtr->RecvBufferPtr = NULL;
	InstancePtr->RequestedBytes = 0;
	InstancePtr->RemainingBytes = 0;
	InstancePtr->BaseAddr = EffectiveAddr;
	InstancePtr->HasFifos = Config->HasFifos;
	InstancePtr->SlaveOnly = Config->SlaveOnly;
	InstancePtr->NumSlaveBits = Config->NumSlaveBits;
	if (Config->DataWidth == 0) {
		InstancePtr->DataWidth = XSP_DATAWIDTH_BYTE;
	} else {
		InstancePtr->DataWidth = Config->DataWidth;
	}

	InstancePtr->SpiMode = Config->SpiMode;

	InstancePtr->FlashBaseAddr = Config->AxiFullBaseAddress;
	InstancePtr->XipMode = Config->XipMode;

	InstancePtr->IsReady = XIL_COMPONENT_IS_READY;

	/*
	 * Create a slave select mask based on the number of bits that can
	 * be used to deselect all slaves, initialize the value to put into
	 * the slave select register to this value.
	 */
	InstancePtr->SlaveSelectMask = (1 << InstancePtr->NumSlaveBits) - 1;
	InstancePtr->SlaveSelectReg = InstancePtr->SlaveSelectMask;

	/*
	 * Clear the statistics for this driver.
	 */
	InstancePtr->Stats.ModeFaults = 0;
	InstancePtr->Stats.XmitUnderruns = 0;
	InstancePtr->Stats.RecvOverruns = 0;
	InstancePtr->Stats.SlaveModeFaults = 0;
	InstancePtr->Stats.BytesTransferred = 0;
	InstancePtr->Stats.NumInterrupts = 0;
	
        if(Config->Use_Startup == 1) {
		/*  
		 * Perform a dummy read this is used when startup block is
		 * enabled in the hardware to fix CR #721229.
		 */
		ControlReg = XSpi_GetControlReg(InstancePtr);
		ControlReg |= XSP_CR_TXFIFO_RESET_MASK | XSP_CR_RXFIFO_RESET_MASK |
				XSP_CR_ENABLE_MASK | XSP_CR_MASTER_MODE_MASK ;
		XSpi_SetControlReg(InstancePtr, ControlReg);
		
		/* 
		 * Initiate Read command to get the ID. This Read command is for
		 * Numonyx flash.
		 *
		 * NOTE: If user interfaces different flash to the SPI controller 
		 * this command need to be changed according to target flash Read
		 * command.
		 */
		Buffer[0] = 0x9F;
		Buffer[1] = 0x00;
		Buffer[2] = 0x00;
	
		/* Write dummy ReadId to the DTR register */
		XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET, Buffer[0]);
		XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET, Buffer[1]);
		XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET, Buffer[2]);
	
		/* Master Inhibit enable in the CR */
		ControlReg = XSpi_GetControlReg(InstancePtr);
		ControlReg &= ~XSP_CR_TRANS_INHIBIT_MASK;
		XSpi_SetControlReg(InstancePtr, ControlReg);
	
		/* Master Inhibit disable in the CR */
		ControlReg = XSpi_GetControlReg(InstancePtr);
		ControlReg |= XSP_CR_TRANS_INHIBIT_MASK;
		XSpi_SetControlReg(InstancePtr, ControlReg);
		
		/* Read the Rx Data Register */
		StatusReg = XSpi_GetStatusReg(InstancePtr);
		if ((StatusReg & XSP_SR_RX_EMPTY_MASK) == 0) {
			XSpi_ReadReg(InstancePtr->BaseAddr, XSP_DRR_OFFSET);
		}
		StatusReg = XSpi_GetStatusReg(InstancePtr);
		if ((StatusReg & XSP_SR_RX_EMPTY_MASK) == 0) {
			XSpi_ReadReg(InstancePtr->BaseAddr, XSP_DRR_OFFSET);
		}
	}
	
	/*
	 * Reset the SPI device to get it into its initial state. It is expected
	 * that device configuration will take place after this initialization
	 * is done, but before the device is started.
	 */
	XSpi_Reset(InstancePtr);

	return XST_SUCCESS;
}

里面涉及到了几个比较重要的函数:Xil_AssertNonvoid()XSpi_GetControlReg()XSpi_SetControlReg()XSpi_WriteReg()XSpi_ReadReg()XSpi_Reset()

Xil_AssertNonvoid()

Xil_AssertNonvoid(InstancePtr != NULL)

该函数作用为:检查输入是否合法。

函数原型为:

/*****************************************************************************/
/**
* @brief    This assert macro is to be used for functions that do return a
*           value. This in conjunction with the Xil_AssertWait boolean can be
*           used to accomodate tests so that asserts which fail allow execution
*           to continue.
*
* @param    Expression: expression to be evaluated. If it evaluates to false,
*           the assert occurs.
*
* @return   Returns 0 unless the Xil_AssertWait variable is true, in which
* 	        case no return is made and an infinite loop is entered.
*
******************************************************************************/
#define Xil_AssertNonvoid(Expression)             \
{                                                  \
    if (Expression) {                              \
        Xil_AssertStatus = XIL_ASSERT_NONE;       \
    } else {                                       \
        Xil_Assert(__FILE__, __LINE__);            \
        Xil_AssertStatus = XIL_ASSERT_OCCURRED;   \
        return 0;                                  \
    }                                              \
}

XSpi_ReadReg()

XSpi_ReadReg(InstancePtr->BaseAddr, XSP_DRR_OFFSET);

该函数作用为:根据某寄存器地址(基地址+地址偏移)读出该寄存器的值。

函数的原型为:

/****************************************************************************/
/**
*
* Read from the specified Spi device register.
*
* @param	BaseAddress contains the base address of the device.
* @param	RegOffset contains the offset from the 1st register of the
*		device to select the specific register.
*
* @return	The value read from the register.
*
* @note		C-Style signature:
*		u32 XSpi_ReadReg(u32 BaseAddress, u32 RegOffset);
*
******************************************************************************/
#define XSpi_ReadReg(BaseAddress, RegOffset) \
	XSpi_In32((BaseAddress) + (RegOffset))

/***************************************************************************/

有关XSpi_In32()函数,将在后面补充。

XSpi_WriteReg()

Buffer[0] = 0x9F;
XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET, Buffer[0]);

该函数的作用为:将一个值(此处为Buffer[0])写入某寄存器地址(基地址+地址偏移)指向的寄存器中。

函数原型为:

/***************************************************************************/
/**
*
* Write to the specified Spi device register.
*
* @param	BaseAddress contains the base address of the device.
* @param	RegOffset contains the offset from the 1st register of the
*		device to select the specific register.
* @param	RegisterValue is the value to be written to the register.
*
* @return	None.
*
* @note		C-Style signature:
*		void XSpi_WriteReg(u32 BaseAddress, u32 RegOffset,
*					u32 RegisterValue);
******************************************************************************/
#define XSpi_WriteReg(BaseAddress, RegOffset, RegisterValue) \
	XSpi_Out32((BaseAddress) + (RegOffset), (RegisterValue))

有关XSpi_Out32()函数,将在后面补充。

XSpi_GetControlReg()

ControlReg = XSpi_GetControlReg(InstancePtr);

该函数作用为:读取控制寄存器的值。

函数原型为:

/****************************************************************************/
/**
*
* Get the contents of the control register. Use the XSP_CR_* constants defined
* above to interpret the bit-mask returned.
*
* @param	InstancePtr is a pointer to the XSpi instance to be worked on.
*
* @return	A 32-bit value representing the contents of the control
*		register.
*
* @note		C-Style signature:
* 		u32 XSpi_GetControlReg(XSpi *InstancePtr);
*
*****************************************************************************/
#define XSpi_GetControlReg(InstancePtr) \
	XSpi_ReadReg(((InstancePtr)->BaseAddr), XSP_CR_OFFSET)

/***************************************************************************/

XSpi_SetControlReg()

ControlReg |= XSP_CR_TXFIFO_RESET_MASK | XSP_CR_RXFIFO_RESET_MASK |
				XSP_CR_ENABLE_MASK | XSP_CR_MASTER_MODE_MASK ;
XSpi_SetControlReg(InstancePtr, ControlReg);

该函数的作用是,设置控制寄存器,向控制寄存器中写入一些掩码以设定一些模式,不设置的模式默认为禁用。

函数原型为:

/****************************************************************************/
/**
*
* Set the contents of the control register. Use the XSP_CR_* constants defined
* above to create the bit-mask to be written to the register.
*
* @param	InstancePtr is a pointer to the XSpi instance to be worked on.
* @param	Mask is the 32-bit value to write to the control register.
*
* @return	None.
*
* @note		C-Style signature:
* 		void XSpi_SetControlReg(XSpi *InstancePtr, u32 Mask);
*
*****************************************************************************/
#define XSpi_SetControlReg(InstancePtr, Mask) \
	XSpi_WriteReg(((InstancePtr)->BaseAddr), XSP_CR_OFFSET, (Mask))

XSpi_Reset()

XSpi_Reset(InstancePtr)

该函数的作用是复位。

函数原型为:

/*****************************************************************************/
/**
*
* Resets the SPI device by writing to the Software Reset register. Reset must
* only be called after the driver has been initialized. The configuration of the
* device after reset is the same as its configuration after initialization.
* Refer to the XSpi_Initialize function for more details. This is a hard reset
* of the device. Any data transfer that is in progress is aborted.
*
* The upper layer software is responsible for re-configuring (if necessary)
* and restarting the SPI device after the reset.
*
* @param	InstancePtr is a pointer to the XSpi instance to be worked on.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
void XSpi_Reset(XSpi *InstancePtr)
{
	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * Abort any transfer that is in progress.
	 */
	XSpi_Abort(InstancePtr);

	/*
	 * Reset any values that are not reset by the hardware reset such that
	 * the software state matches the hardware device.
	 */
	InstancePtr->IsStarted = 0;
	InstancePtr->SlaveSelectReg = InstancePtr->SlaveSelectMask;

	/*
	 * Reset the device.
	 */
	XSpi_WriteReg(InstancePtr->BaseAddr, XSP_SRR_OFFSET,
			XSP_SRR_RESET_MASK);
}

补充内容:

XSpi_In32()

函数原型为:

#define INLINE __inline
#define XSpi_In32	Xil_In32     // 进一步包装

/*****************************************************************************/
/**
*
* @brief    Performs an input operation for a memory location by
*           reading from the specified address and returning the 32 bit Value
*           read  from that address.
*
* @param	Addr: contains the address to perform the input operation
*
* @return	The 32 bit Value read from the specified input address.
*
******************************************************************************/
static INLINE u32 Xil_In32(UINTPTR Addr)
{
	return *(volatile u32 *) Addr;
}

该函数的作用是从传入的地址读取一个32bit的值并返回。

另外,__inline通知编译器将该函数的内容拷贝一份放在调用函数的地方,这称之为内联。内联减少了函数调用的开销,但却增加了代码量。(参考文献:C中__inline__的含义及作用

XSpi_Out32()

函数原型为:

#define INLINE __inline
#define XSpi_Out32	Xil_Out32     // 进一步包装

/*****************************************************************************/
/**
*
* @brief    Performs an output operation for a memory location by writing the
*           32 bit Value to the the specified address.
*
* @param	Addr contains the address to perform the output operation
* @param	Value contains the 32 bit Value to be written at the specified
*           address.
*
* @return	None.
*
******************************************************************************/
static INLINE void Xil_Out32(UINTPTR Addr, u32 Value)
{
#ifndef ENABLE_SAFETY
	volatile u32 *LocalAddr = (volatile u32 *)Addr;
	*LocalAddr = Value;
#else
	XStl_RegUpdate(Addr, Value);
#endif
}

该函数的作用是,将一个32bit的值写入给定地址指向的寄存器。

XSpi_SelfTest()函数

Status = XSpi_SelfTest(SpiInstancePtr);

参数SpiInstancePtr为SPI驱动实例指针。该函数的作用是:测试硬件电路是否搭建正确。

函数原型为:

/*****************************************************************************/
/**
*
* Runs a self-test on the driver/device. The self-test is destructive in that
* a reset of the device is performed in order to check the reset values of
* the registers and to get the device into a known state. A simple loopback
* test is also performed to verify that transmit and receive are working
* properly. The device is changed to master mode for the loopback test, since
* only a master can initiate a data transfer.
*
* Upon successful return from the self-test, the device is reset.
*
* @param	InstancePtr is a pointer to the XSpi instance to be worked on.
*
* @return
* 		- XST_SUCCESS if successful.
*		- XST_REGISTER_ERROR indicates a register did not read or write
*		  correctly.
* 		- XST_LOOPBACK_ERROR if a loopback error occurred.
*
* @note		None.
*
******************************************************************************/
int XSpi_SelfTest(XSpi *InstancePtr)
{
	int Result;
	u32 Register;

	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);


	/* Return Success if XIP Mode */
	if((InstancePtr->XipMode) == 1) {
		return XST_SUCCESS;
	}

	/*
	 * Reset the SPI device to leave it in a known good state.
	 */
	XSpi_Reset(InstancePtr);

	if(InstancePtr->XipMode)
	{
		Register = XSpi_GetControlReg(InstancePtr);
		if (Register != XSP_CR_RESET_STATE) {
			return XST_REGISTER_ERROR;
		}

		Register = XSpi_GetStatusReg(InstancePtr);
		if ((Register & XSP_SR_RESET_STATE) != XSP_SR_RESET_STATE) {
			return XST_REGISTER_ERROR;
		}
	}




	/*
	 * All the SPI registers should be in their default state right now.
	 */
	Register = XSpi_GetControlReg(InstancePtr);
	if (Register != XSP_CR_RESET_STATE) {
		return XST_REGISTER_ERROR;
	}

	Register = XSpi_GetStatusReg(InstancePtr);
	if ((Register & XSP_SR_RESET_STATE) != XSP_SR_RESET_STATE) {
		return XST_REGISTER_ERROR;
	}

	/*
	 * Each supported slave select bit should be set to 1.
	 */
	Register = XSpi_GetSlaveSelectReg(InstancePtr);
	if (Register != InstancePtr->SlaveSelectMask) {
		return XST_REGISTER_ERROR;
	}

	/*
	 * If configured with FIFOs, the occupancy values should be 0.
	 */
	if (InstancePtr->HasFifos) {
		Register = XSpi_ReadReg(InstancePtr->BaseAddr,
					 XSP_TFO_OFFSET);
		if (Register != 0) {
			return XST_REGISTER_ERROR;
		}
		Register = XSpi_ReadReg(InstancePtr->BaseAddr,
					 XSP_RFO_OFFSET);
		if (Register != 0) {
			return XST_REGISTER_ERROR;
		}
	}

	/*
	 * Run loopback test only in case of standard SPI mode.
	 */
	if (InstancePtr->SpiMode != XSP_STANDARD_MODE) {
		return XST_SUCCESS;
	}

	/*
	 * Run an internal loopback test on the SPI.
	 */
	Result = LoopbackTest(InstancePtr);
	if (Result != XST_SUCCESS) {
		return Result;
	}

	/*
	 * Reset the SPI device to leave it in a known good state.
	 */
	XSpi_Reset(InstancePtr);

	return XST_SUCCESS;
}

里面涉及到几个函数,都是形似Get_XxxReg()/Set_XxxReg(),即读取某特定寄存器的值或者向某特定寄存器写入值。这里涉及多个寄存器的偏移值,列在下面:

/**
 * XSPI register offsets
 */
/** @name Register Map
 *
 * Register offsets for the XSpi device.
 * @{
 */
#define XSP_DGIER_OFFSET	0x1C	/**< Global Intr Enable Reg */
#define XSP_IISR_OFFSET		0x20	/**< Interrupt status Reg */
#define XSP_IIER_OFFSET		0x28	/**< Interrupt Enable Reg */
#define XSP_SRR_OFFSET	 	0x40	/**< Software Reset register */
#define XSP_CR_OFFSET		0x60	/**< Control register */
#define XSP_SR_OFFSET		0x64	/**< Status Register */
#define XSP_DTR_OFFSET		0x68	/**< Data transmit */
#define XSP_DRR_OFFSET		0x6C	/**< Data receive */
#define XSP_SSR_OFFSET		0x70	/**< 32-bit slave select */
#define XSP_TFO_OFFSET		0x74	/**< Tx FIFO occupancy */
#define XSP_RFO_OFFSET		0x78	/**< Rx FIFO occupancy */

挖个坑:具体向某个寄存器中写入什么值会产生什么样的配置,请参考:SPI各寄存器配置。

(未来某个时间更新,时间待定)

另外,该自测函数中调用了一个回环测试,测试完成后,测试过程中产生的数据不会保留,寄存器配置将恢复到测试之前的状态猜的,待验证)。

函数原型为:

/*****************************************************************************/
/*
*
* Runs an internal loopback test on the SPI device. This is done as a master
* with a enough data to fill the FIFOs if FIFOs are present. If the device is
* configured as a slave-only, this function returns successfully even though
* no loopback test is performed.
*
* This function does not restore the device context after performing the test
* as it assumes the device will be reset after the call.
*
* @param	InstancePtr is a pointer to the XSpi instance to be worked on.
*
* @return
* 		- XST_SUCCESS if loopback was performed successfully or not
*		  performed at all if device is slave-only.
*		- XST_LOOPBACK_ERROR if loopback failed.
*
* @note		None.
*
******************************************************************************/
static int LoopbackTest(XSpi *InstancePtr)
{
	u32 StatusReg;
	u32 ControlReg;
	u32 Index;
	u32 Data;
	u32 RxData;
	u32 NumSent = 0;
	u32 NumRecvd = 0;
	u8  DataWidth;

	/*
	 * Cannot run as a slave-only because we need to be master in order to
	 * initiate a transfer. Still return success, though.
	 */
	if (InstancePtr->SlaveOnly) {
		return XST_SUCCESS;
	}

	/*
	 * Setup the control register to enable master mode and the loopback so
	 * that data can be sent and received.
	 */
	ControlReg = XSpi_GetControlReg(InstancePtr);
	XSpi_SetControlReg(InstancePtr, ControlReg |
			    XSP_CR_LOOPBACK_MASK | XSP_CR_MASTER_MODE_MASK);
	/*
	 * We do not need interrupts for this loopback test.
	 */
	XSpi_IntrGlobalDisable(InstancePtr);

	DataWidth = InstancePtr->DataWidth;
	/*
	 * Send data up to the maximum size of the transmit register, which is
	 * one byte without FIFOs.  We send data 4 times just to exercise the
	 * device through more than one iteration.
	 */
	for (Index = 0; Index < 4; Index++) {
		Data = 0;

		/*
		 * Fill the transmit register.
		 */
		StatusReg = XSpi_GetStatusReg(InstancePtr);
		while ((StatusReg & XSP_SR_TX_FULL_MASK) == 0) {
			if (DataWidth == XSP_DATAWIDTH_BYTE) {
				/*
				 * Data Transfer Width is Byte (8 bit).
				 */
				Data = 0;
			} else if (DataWidth == XSP_DATAWIDTH_HALF_WORD) {
				/*
				 * Data Transfer Width is Half Word (16 bit).
				 */
				Data = XSP_HALF_WORD_TESTBYTE;
			} else if (DataWidth == XSP_DATAWIDTH_WORD){
				/*
				 * Data Transfer Width is Word (32 bit).
				 */
				Data = XSP_WORD_TESTBYTE;
			}

			XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET,
					Data + Index);
			NumSent += (DataWidth >> 3);
			StatusReg = XSpi_GetStatusReg(InstancePtr);
		}

		/*
		 * Start the transfer by not inhibiting the transmitter and
		 * enabling the device.
		 */
		ControlReg = XSpi_GetControlReg(InstancePtr) &
						 (~XSP_CR_TRANS_INHIBIT_MASK);
		XSpi_SetControlReg(InstancePtr, ControlReg |
				    XSP_CR_ENABLE_MASK);

		/*
		 * Wait for the transfer to be done by polling the transmit
		 * empty status bit.
		 */
		do {
			StatusReg = XSpi_IntrGetStatus(InstancePtr);
		} while ((StatusReg & XSP_INTR_TX_EMPTY_MASK) == 0);

		XSpi_IntrClear(InstancePtr, XSP_INTR_TX_EMPTY_MASK);

		/*
		 * Receive and verify the data just transmitted.
		 */
		StatusReg = XSpi_GetStatusReg(InstancePtr);
		while ((StatusReg & XSP_SR_RX_EMPTY_MASK) == 0) {

			RxData = XSpi_ReadReg(InstancePtr->BaseAddr,
						XSP_DRR_OFFSET);

			if (DataWidth == XSP_DATAWIDTH_BYTE) {
				if((u8)RxData != Index) {
					return XST_LOOPBACK_ERROR;
				}
			} else if (DataWidth ==
					XSP_DATAWIDTH_HALF_WORD) {
				if((u16)RxData != (u16)(Index +
						   XSP_HALF_WORD_TESTBYTE)) {
					return XST_LOOPBACK_ERROR;
				}
			} else if (DataWidth == XSP_DATAWIDTH_WORD) {
				if(RxData != (u32)(Index + XSP_WORD_TESTBYTE)) {
					return XST_LOOPBACK_ERROR;
				}
			}

			NumRecvd += (DataWidth >> 3);
			StatusReg = XSpi_GetStatusReg(InstancePtr);
		}

		/*
		 * Stop the transfer (hold off automatic sending) by inhibiting
		 * the transmitter and disabling the device.
		 */
		ControlReg |= XSP_CR_TRANS_INHIBIT_MASK;
		XSpi_SetControlReg(InstancePtr ,
				    ControlReg & ~ XSP_CR_ENABLE_MASK);
	}

	/*
	 * One final check to make sure the total number of bytes sent equals
	 * the total number of bytes received.
	 */
	if (NumSent != NumRecvd) {
		return XST_LOOPBACK_ERROR;
	}

	return XST_SUCCESS;
}

SpiSetupIntrSystem()函数

Status = SpiSetupIntrSystem(IntcInstancePtr, SpiInstancePtr, SpiIntrId);

参数IntcInstancePtr为中断驱动实例指针,SpiIntrId为SPI的中断ID。该函数的作用是,关联SPI设备与中断子系统,使中断发生。

中断配置内容先不详细介绍了,或许可以挖个新坑,详见:嵌入式开发SDK中的中断配置。

XSpi_SetStatusHandler()函数

XSpi_SetStatusHandler(SpiInstancePtr, SpiInstancePtr,
		 		(XSpi_StatusHandler) SpiIntrHandler);

也是一个中断相关的函数,先不做介绍,后续补充的话应该也在上面函数的坑里

XSpi_SetOptions()函数

#define XSP_MASTER_OPTION	    	0x1
#define XSP_CLK_ACTIVE_LOW_OPTION	0x2
#define XSP_CLK_PHASE_1_OPTION		0x4
#define XSP_LOOPBACK_OPTION		    0x8
#define XSP_MANUAL_SSELECT_OPTION	0x10

Status = XSpi_SetOptions(SpiInstancePtr, XSP_MASTER_OPTION |
				 XSP_CLK_PHASE_1_OPTION | XSP_CLK_ACTIVE_LOW_OPTION |
                 XSP_MANUAL_SSELECT_OPTION);

该函数的作用为,对SPI的可选的工作模式做一些配置。这里设置了主模式,手动片选,还有一个时钟相位和极性的组合(ChatGPT说这种组合是设置MSB?待验证

该函数的原型为:

/***************************** Include Files *********************************/

#include "xspi.h"
#include "xspi_i.h"

/************************** Constant Definitions *****************************/


/**************************** Type Definitions *******************************/


/***************** Macros (Inline Functions) Definitions *********************/


/************************** Function Prototypes ******************************/


/************************** Variable Definitions *****************************/

/*
 * Create the table of options which are processed to get/set the device
 * options. These options are table driven to allow easy maintenance and
 * expansion of the options.
 */
typedef struct {
	u32 Option;
	u32 Mask;
} OptionsMap;

static OptionsMap OptionsTable[] = {
	{XSP_LOOPBACK_OPTION, XSP_CR_LOOPBACK_MASK},
	{XSP_CLK_ACTIVE_LOW_OPTION, XSP_CR_CLK_POLARITY_MASK},
	{XSP_CLK_PHASE_1_OPTION, XSP_CR_CLK_PHASE_MASK},
	{XSP_MASTER_OPTION, XSP_CR_MASTER_MODE_MASK},
	{XSP_MANUAL_SSELECT_OPTION, XSP_CR_MANUAL_SS_MASK}
};

#define XSP_NUM_OPTIONS		(sizeof(OptionsTable) / sizeof(OptionsMap))

/*****************************************************************************/
/**
*
* This function sets the options for the SPI device driver. The options control
* how the device behaves relative to the SPI bus. The device must be idle
* rather than busy transferring data before setting these device options.
*
* @param	InstancePtr is a pointer to the XSpi instance to be worked on.
* @param	Options contains the specified options to be set. This is a bit
*		mask where a 1 means to turn the option on, and a 0 means to
*		turn the option off. One or more bit values may be contained in
*		the mask.
*		See the bit definitions named XSP_*_OPTIONS in the file xspi.h.
*
* @return
*		-XST_SUCCESS if options are successfully set.
*		- XST_DEVICE_BUSY if the device is currently transferring data.
*		The transfer must complete or be aborted before setting options.
*		- XST_SPI_SLAVE_ONLY if the caller attempted to configure a
*		slave-only device as a master.
*
* @note
*
* This function makes use of internal resources that are shared between the
* XSpi_Stop() and XSpi_SetOptions() functions. So if one task might be setting
* device options while another is trying to stop the device, the user is
* required to provide protection of this shared data (typically using a
* semaphore).
*
******************************************************************************/
int XSpi_SetOptions(XSpi *InstancePtr, u32 Options)
{
	u32 ControlReg;
	u32 Index;

	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * Do not allow the slave select to change while a transfer is in
	 * progress.
	 * No need to worry about a critical section here since even if the Isr
	 * changes the busy flag just after we read it, the function will return
	 * busy and the caller can retry when notified that their current
	 * transfer is done.
	 */
	if (InstancePtr->IsBusy) {
		return XST_DEVICE_BUSY;
	}
	/*
	 * Do not allow master option to be set if the device is slave only.
	 */
	if ((Options & XSP_MASTER_OPTION) && (InstancePtr->SlaveOnly)) {
		return XST_SPI_SLAVE_ONLY;
	}

	ControlReg = XSpi_GetControlReg(InstancePtr);

	/*
	 * Loop through the options table, turning the option on or off
	 * depending on whether the bit is set in the incoming options flag.
	 */
	for (Index = 0; Index < XSP_NUM_OPTIONS; Index++) {
		if (Options & OptionsTable[Index].Option) {
			/*
			 *Turn it ON.
			 */
			ControlReg |= OptionsTable[Index].Mask;
		}
		else {
			/*
			 *Turn it OFF.
			 */
			ControlReg &= ~OptionsTable[Index].Mask;
		}
	}

	/*
	 * Now write the control register. Leave it to the upper layers
	 * to restart the device.
	 */
	XSpi_SetControlReg(InstancePtr, ControlReg);

	return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* This function gets the options for the SPI device. The options control how
* the device behaves relative to the SPI bus.
*
* @param	InstancePtr is a pointer to the XSpi instance to be worked on.
*
* @return
*
* Options contains the specified options to be set. This is a bit mask where a
* 1 means to turn the option on, and a 0 means to turn the option off. One or
* more bit values may be contained in the mask. See the bit definitions named
* XSP_*_OPTIONS in the file xspi.h.
*
* @note		None.
*
******************************************************************************/
u32 XSpi_GetOptions(XSpi *InstancePtr)
{
	u32 OptionsFlag = 0;
	u32 ControlReg;
	u32 Index;

	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * Get the control register to determine which options are currently
	 * set.
	 */
	ControlReg = XSpi_GetControlReg(InstancePtr);

	/*
	 * Loop through the options table to determine which options are set.
	 */
	for (Index = 0; Index < XSP_NUM_OPTIONS; Index++) {
		if (ControlReg & OptionsTable[Index].Mask) {
			OptionsFlag |= OptionsTable[Index].Option;
		}
	}

	return OptionsFlag;
}

刚好是一个文件xspi_options.c,就直接贴过来了哈哈哈。估计是主程序的某个头文件中包含了这个.c文件,才能进入xspi_options.c这个文件调用XSpi_SetOptions()这个函数。

XSpi_Start()函数

XSpi_Start(SpiInstancePtr);

该函数作用是,开启SPI驱动以使中断能够发生。

函数原型为:

/*****************************************************************************/
/**
*
* This function enables interrupts for the SPI device. If the Spi driver is used
* in interrupt mode, it is up to the user to connect the SPI interrupt handler
* to the interrupt controller before this function is called. If the Spi driver
* is used in polled mode the user has to disable the Global Interrupts after
* this function is called. If the device is configured with FIFOs, the FIFOs are
* reset at this time.
*
* @param	InstancePtr is a pointer to the XSpi instance to be worked on.
*
* @return
*		- XST_SUCCESS if the device is successfully started
*		- XST_DEVICE_IS_STARTED if the device was already started.
*
* @note		None.
*
******************************************************************************/
int XSpi_Start(XSpi *InstancePtr)
{
	u32 ControlReg;

	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * If it is already started, return a status indicating so.
	 */
	if (InstancePtr->IsStarted == XIL_COMPONENT_IS_STARTED) {
		return XST_DEVICE_IS_STARTED;
	}

	/*
	 * Enable the interrupts.
	 */
	XSpi_IntrEnable(InstancePtr, XSP_INTR_DFT_MASK);

	/*
	 * Indicate that the device is started before we enable the transmitter
	 * or receiver or interrupts.
	 */
	InstancePtr->IsStarted = XIL_COMPONENT_IS_STARTED;

	/*
	 * Reset the transmit and receive FIFOs if present. There is a critical
	 * section here since this register is also modified during interrupt
	 * context. So we wait until after the r/m/w of the control register to
	 * enable the Global Interrupt Enable.
	 */
	ControlReg = XSpi_GetControlReg(InstancePtr);
	ControlReg |= XSP_CR_TXFIFO_RESET_MASK | XSP_CR_RXFIFO_RESET_MASK |
			XSP_CR_ENABLE_MASK;
	XSpi_SetControlReg(InstancePtr, ControlReg);

	/*
	 * Enable the Global Interrupt Enable just after we start.
	 */
	XSpi_IntrGlobalEnable(InstancePtr);

	return XST_SUCCESS;
}

XSpi_SetSlaveSelect()函数

XSpi_SetSlaveSelect(SpiInstancePtr, 0x01);

官方示例程序中没有的这一行代码,是我参考CSDN上一篇文章:AXI quad SPI没有输出 加上的,用以配合设定的手动片选模式。

函数原型为:

/*****************************************************************************/
/**
*
* Selects or deselect the slave with which the master communicates. Each slave
* that can be selected is represented in the slave select register by a bit.
* The argument passed to this function is the bit mask with a 1 in the bit
* position of the slave being selected. Only one slave can be selected.
*
* The user is not allowed to deselect the slave while a transfer is in progress.
* If no transfer is in progress, the user can select a new slave, which
* implicitly deselects the current slave. In order to explicitly deselect the
* current slave, a zero can be passed in as the argument to the function.
*
* @param	InstancePtr is a pointer to the XSpi instance to be worked on.
* @param	SlaveMask is a 32-bit mask with a 1 in the bit position of the
*		slave being selected. Only one slave can be selected. The
*		SlaveMask can be zero if the slave is being deselected.
*
* @return
* 		- XST_SUCCESS if the slave is selected or deselected
*		successfully.
*		- XST_DEVICE_BUSY if a transfer is in progress, slave cannot be
*		changed
*		- XST_SPI_TOO_MANY_SLAVES if more than one slave is being
*		selected.
*
* @note
*
* This function only sets the slave which will be selected when a transfer
* occurs. The slave is not selected when the SPI is idle. The slave select
* has no affect when the device is configured as a slave.
*
******************************************************************************/
int XSpi_SetSlaveSelect(XSpi *InstancePtr, u32 SlaveMask)
{
	int NumAsserted;
	int Index;

	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * Do not allow the slave select to change while a transfer is in
	 * progress.
	 * No need to worry about a critical section here since even if the Isr
	 * changes the busy flag just after we read it, the function will return
	 * busy and the caller can retry when notified that their current
	 * transfer is done.
	 */
	if (InstancePtr->IsBusy) {
		return XST_DEVICE_BUSY;
	}

	/*
	 * Verify that only one bit in the incoming slave mask is set.
	 */
	NumAsserted = 0;
	for (Index = (InstancePtr->NumSlaveBits - 1); Index >= 0; Index--) {
		if ((SlaveMask >> Index) & 0x1) {
			/* this bit is asserted */
			NumAsserted++;
		}
	}

	/*
	 * Return an error if more than one slave is selected.
	 */
	if (NumAsserted > 1) {
		return XST_SPI_TOO_MANY_SLAVES;
	}

	/*
	 * A single slave is either being selected or the incoming SlaveMask is
	 * zero, which means the slave is being deselected. Setup the value to
	 * be  written to the slave select register as the inverse of the slave
	 * mask.
	 */
	InstancePtr->SlaveSelectReg = ~SlaveMask;

	return XST_SUCCESS;
}

该函数做了些什么事情呢?它计算了一下选择了多少个从设备,如果SlaveMask中有多个1,则意味着从设备不止一个,就会报错输出“SPI的从设备太多了”的文字。否则,将SlaveMask取反后赋值给从选寄存器,实现片选,低电平有效。

写数据(发送数据)初始化

Test = 0x01;
for (Count = 0; Count < BUFFER_SIZE; Count++) {
	WriteBuffer[Count] = (u8)(Count + Test);
	ReadBuffer[Count] = 0;
}

这段代码是写缓存寄存器组WriteBuffer和读缓存寄存器组ReadBuffer的初始化。WriteBuffer和ReadBuffer是8*BUFFER_SIZE的寄存器组,BUFFER_SIZE在程序中设置为16。以Test = 8‘b0000_0001’为起始,步长为1,生成了16个8位二进制数,WriteBuffer和ReadBuffer的结构如下图:

 XSpi_Transfer()函数

XSpi_Transfer(SpiInstancePtr, WriteBuffer, ReadBuffer, BUFFER_SIZE);

该函数的作用是,实现数据的传送,这是整个程序中最重要的函数之一。

函数原型为:

/*****************************************************************************/
/**
*
* Transfers the specified data on the SPI bus. If the SPI device is configured
* to be a master, this function initiates bus communication and sends/receives
* the data to/from the selected SPI slave. If the SPI device is configured to
* be a slave, this function prepares the data to be sent/received when selected
* by a master. For every byte sent, a byte is received.
*
* This function/driver operates in interrupt mode and polled mode.
*  - In interrupt mode this function is non-blocking and the transfer is
*    initiated by this function and completed by the interrupt service routine.
*  - In polled mode this function is blocking and the control exits this
*    function only after all the requested data is transferred.
*
* The caller has the option of providing two different buffers for send and
* receive, or one buffer for both send and receive, or no buffer for receive.
* The receive buffer must be at least as big as the send buffer to prevent
* unwanted memory writes. This implies that the byte count passed in as an
* argument must be the smaller of the two buffers if they differ in size.
* Here are some sample usages:
* <pre>
*	XSpi_Transfer(InstancePtr, SendBuf, RecvBuf, ByteCount)
*	The caller wishes to send and receive, and provides two different
*	buffers for send and receive.
*
*	XSpi_Transfer(InstancePtr, SendBuf, NULL, ByteCount)
*	The caller wishes only to send and does not care about the received
*	data. The driver ignores the received data in this case.
*
*	XSpi_Transfer(InstancePtr, SendBuf, SendBuf, ByteCount)
*	The caller wishes to send and receive, but provides the same buffer
*	for doing both. The driver sends the data and overwrites the send
*	buffer with received data as it transfers the data.
*
*	XSpi_Transfer(InstancePtr, RecvBuf, RecvBuf, ByteCount)
*	The caller wishes to only receive and does not care about sending
*	data.  In this case, the caller must still provide a send buffer, but
*	it can be the same as the receive buffer if the caller does not care
*	what it sends. The device must send N bytes of data if it wishes to
*	receive N bytes of data.
* </pre>
* In interrupt mode, though this function takes a buffer as an argument, the
* driver can only transfer a limited number of bytes at time. It transfers only
* one byte at a time if there are no FIFOs, or it can transfer the number of
* bytes up to the size of the FIFO if FIFOs exist.
*  - In interrupt mode a call to this function only starts the transfer, the
*    subsequent transfer of the data is performed by the interrupt service
*    routine until the entire buffer has been transferred.The status callback
*    function is called when the entire buffer has been sent/received.
*  - In polled mode this function is blocking and the control exits this
*    function only after all the requested data is transferred.
*
* As a master, the SetSlaveSelect function must be called prior to this
* function.
*
* @param	InstancePtr is a pointer to the XSpi instance to be worked on.
* @param	SendBufPtr is a pointer to a buffer of data which is to be sent.
*		This buffer must not be NULL.
* @param	RecvBufPtr is a pointer to a buffer which will be filled with
*		received data. This argument can be NULL if the caller does not
*		wish to receive data.
* @param	ByteCount contains the number of bytes to send/receive. The
*		number of bytes received always equals the number of bytes sent.
*
* @return
*		-XST_SUCCESS if the buffers are successfully handed off to the
*		driver for transfer. Otherwise, returns:
*		- XST_DEVICE_IS_STOPPED if the device must be started before
*		transferring data.
*		- XST_DEVICE_BUSY indicates that a data transfer is already in
*		progress. This is determined by the driver.
*		- XST_SPI_NO_SLAVE indicates the device is configured as a
*		master and a slave has not yet been selected.
*
* @notes
*
* This function is not thread-safe.  The higher layer software must ensure that
* no two threads are transferring data on the SPI bus at the same time.
*
******************************************************************************/
int XSpi_Transfer(XSpi *InstancePtr, u8 *SendBufPtr,
		  u8 *RecvBufPtr, unsigned int ByteCount)
{
	u32 ControlReg;
	u32 GlobalIntrReg;
	u32 StatusReg;
	u32 Data = 0;
	u8  DataWidth;

	/*
	 * The RecvBufPtr argument can be NULL.
	 */
	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(SendBufPtr != NULL);
	Xil_AssertNonvoid(ByteCount > 0);
	Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	if (InstancePtr->IsStarted != XIL_COMPONENT_IS_STARTED) {
		return XST_DEVICE_IS_STOPPED;
	}

	/*
	 * Make sure there is not a transfer already in progress. No need to
	 * worry about a critical section here. Even if the Isr changes the bus
	 * flag just after we read it, a busy error is returned and the caller
	 * can retry when it gets the status handler callback indicating the
	 * transfer is done.
	 */
	if (InstancePtr->IsBusy) {
		return XST_DEVICE_BUSY;
	}

	/*
	 * Save the Global Interrupt Enable Register.
	 */
	GlobalIntrReg = XSpi_IsIntrGlobalEnabled(InstancePtr);

	/*
	 * Enter a critical section from here to the end of the function since
	 * state is modified, an interrupt is enabled, and the control register
	 * is modified (r/m/w).
	 */
	XSpi_IntrGlobalDisable(InstancePtr);

	ControlReg = XSpi_GetControlReg(InstancePtr);

	/*
	 * If configured as a master, be sure there is a slave select bit set
	 * in the slave select register. If no slaves have been selected, the
	 * value of the register will equal the mask.  When the device is in
	 * loopback mode, however, no slave selects need be set.
	 */
	if (ControlReg & XSP_CR_MASTER_MODE_MASK) {
		if ((ControlReg & XSP_CR_LOOPBACK_MASK) == 0) {
			if (InstancePtr->SlaveSelectReg ==
				InstancePtr->SlaveSelectMask) {
				if (GlobalIntrReg == TRUE) {
					/* Interrupt Mode of operation */
					XSpi_IntrGlobalEnable(InstancePtr);
				}
				return XST_SPI_NO_SLAVE;
			}
		}
	}

	/*
	 * Set the busy flag, which will be cleared when the transfer
	 * is completely done.
	 */
	InstancePtr->IsBusy = TRUE;

	/*
	 * Set up buffer pointers.
	 */
	InstancePtr->SendBufferPtr = SendBufPtr;
	InstancePtr->RecvBufferPtr = RecvBufPtr;

	InstancePtr->RequestedBytes = ByteCount;
	InstancePtr->RemainingBytes = ByteCount;

	DataWidth = InstancePtr->DataWidth;

	/*
	 * Fill the DTR/FIFO with as many bytes as it will take (or as many as
	 * we have to send). We use the tx full status bit to know if the device
	 * can take more data. By doing this, the driver does not need to know
	 * the size of the FIFO or that there even is a FIFO. The downside is
	 * that the status register must be read each loop iteration.
	 */
	StatusReg = XSpi_GetStatusReg(InstancePtr);

	while (((StatusReg & XSP_SR_TX_FULL_MASK) == 0) &&
		(InstancePtr->RemainingBytes > 0)) {
		if (DataWidth == XSP_DATAWIDTH_BYTE) {
			/*
			 * Data Transfer Width is Byte (8 bit).
			 */
			Data = *InstancePtr->SendBufferPtr;
		} else if (DataWidth == XSP_DATAWIDTH_HALF_WORD) {
			/*
			 * Data Transfer Width is Half Word (16 bit).
			 */
			Data = *(u16 *)InstancePtr->SendBufferPtr;
		} else if (DataWidth == XSP_DATAWIDTH_WORD){
			/*
			 * Data Transfer Width is Word (32 bit).
			 */
			Data = *(u32 *)InstancePtr->SendBufferPtr;
		}

		XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET, Data);
		InstancePtr->SendBufferPtr += (DataWidth >> 3);
		InstancePtr->RemainingBytes -= (DataWidth >> 3);
		StatusReg = XSpi_GetStatusReg(InstancePtr);
	}

	
	/*
	 * Set the slave select register to select the device on the SPI before
	 * starting the transfer of data.
	 */
	XSpi_SetSlaveSelectReg(InstancePtr,
				InstancePtr->SlaveSelectReg);
				
	/*
	 * Start the transfer by no longer inhibiting the transmitter and
	 * enabling the device. For a master, this will in fact start the
	 * transfer, but for a slave it only prepares the device for a transfer
	 * that must be initiated by a master.
	 */
	ControlReg = XSpi_GetControlReg(InstancePtr);
	ControlReg &= ~XSP_CR_TRANS_INHIBIT_MASK;
	XSpi_SetControlReg(InstancePtr, ControlReg);

	/*
	 * If the interrupts are enabled as indicated by Global Interrupt
	 * Enable Register, then enable the transmit empty interrupt to operate
	 * in Interrupt mode of operation.
	 */
	if (GlobalIntrReg == TRUE) { /* Interrupt Mode of operation */

		/*
		 * Enable the transmit empty interrupt, which we use to
		 * determine progress on the transmission.
		 */
		XSpi_IntrEnable(InstancePtr, XSP_INTR_TX_EMPTY_MASK);

		/*
		 * End critical section.
		 */
		XSpi_IntrGlobalEnable(InstancePtr);

	} else { /* Polled mode of operation */

		/*
		 * If interrupts are not enabled, poll the status register to
		 * Transmit/Receive SPI data.
		 */
		while(ByteCount > 0) {

			/*
			 * Wait for the transfer to be done by polling the
			 * Transmit empty status bit
			 */
			do {
				StatusReg = XSpi_IntrGetStatus(InstancePtr);
			} while ((StatusReg & XSP_INTR_TX_EMPTY_MASK) == 0);

			XSpi_IntrClear(InstancePtr,XSP_INTR_TX_EMPTY_MASK);

			/*
			 * A transmit has just completed. Process received data
			 * and check for more data to transmit. Always inhibit
			 * the transmitter while the transmit register/FIFO is
			 * being filled, or make sure it is stopped if we're
			 * done.
			 */
			ControlReg = XSpi_GetControlReg(InstancePtr);
			XSpi_SetControlReg(InstancePtr, ControlReg |
						XSP_CR_TRANS_INHIBIT_MASK);

			/*
			 * First get the data received as a result of the
			 * transmit that just completed. We get all the data
			 * available by reading the status register to determine
			 * when the Receive register/FIFO is empty. Always get
			 * the received data, but only fill the receive
			 * buffer if it points to something (the upper layer
			 * software may not care to receive data).
			 */
			StatusReg = XSpi_GetStatusReg(InstancePtr);

			while ((StatusReg & XSP_SR_RX_EMPTY_MASK) == 0) {

				Data = XSpi_ReadReg(InstancePtr->BaseAddr,
								XSP_DRR_OFFSET);
				if (DataWidth == XSP_DATAWIDTH_BYTE) {
					/*
					 * Data Transfer Width is Byte (8 bit).
					 */
					if(InstancePtr->RecvBufferPtr != NULL) {
						*InstancePtr->RecvBufferPtr++ =
							(u8)Data;
					}
				} else if (DataWidth ==
						XSP_DATAWIDTH_HALF_WORD) {
					/*
					 * Data Transfer Width is Half Word
					 * (16 bit).
					 */
					if (InstancePtr->RecvBufferPtr != NULL){
					    *(u16 *)InstancePtr->RecvBufferPtr =
							(u16)Data;
						InstancePtr->RecvBufferPtr += 2;
					}
				} else if (DataWidth == XSP_DATAWIDTH_WORD) {
					/*
					 * Data Transfer Width is Word (32 bit).
					 */
					if (InstancePtr->RecvBufferPtr != NULL){
					    *(u32 *)InstancePtr->RecvBufferPtr =
							Data;
						InstancePtr->RecvBufferPtr += 4;
					}
				}
				InstancePtr->Stats.BytesTransferred +=
						(DataWidth >> 3);
				ByteCount -= (DataWidth >> 3);
				StatusReg = XSpi_GetStatusReg(InstancePtr);
			}

			if (InstancePtr->RemainingBytes > 0) {

				/*
				 * Fill the DTR/FIFO with as many bytes as it
				 * will take (or as many as we have to send).
				 * We use the Tx full status bit to know if the
				 * device can take more data.
				 * By doing this, the driver does not need to
				 * know the size of the FIFO or that there even
				 * is a FIFO.
				 * The downside is that the status must be read
				 * each loop iteration.
				 */
				StatusReg = XSpi_GetStatusReg(InstancePtr);

				while(((StatusReg & XSP_SR_TX_FULL_MASK)== 0) &&
					(InstancePtr->RemainingBytes > 0)) {
					if (DataWidth == XSP_DATAWIDTH_BYTE) {
						/*
						 * Data Transfer Width is Byte
						 * (8 bit).
						 */
						Data = *InstancePtr->
								SendBufferPtr;

					} else if (DataWidth ==
						XSP_DATAWIDTH_HALF_WORD) {

						/*
						 * Data Transfer Width is Half
						 * Word (16 bit).
			 			 */
						Data = *(u16 *)InstancePtr->
								SendBufferPtr;
					} else if (DataWidth ==
							XSP_DATAWIDTH_WORD) {
						/*
						 * Data Transfer Width is Word
						 * (32 bit).
			 			 */
						Data = *(u32 *)InstancePtr->
								SendBufferPtr;
					}
					XSpi_WriteReg(InstancePtr->BaseAddr,
							XSP_DTR_OFFSET, Data);
					InstancePtr->SendBufferPtr +=
							(DataWidth >> 3);
					InstancePtr->RemainingBytes -=
							(DataWidth >> 3);
					StatusReg = XSpi_GetStatusReg(
							InstancePtr);
				}

				/*
				 * Start the transfer by not inhibiting the
				 * transmitter any longer.
				 */
				ControlReg = XSpi_GetControlReg(InstancePtr);
				ControlReg &= ~XSP_CR_TRANS_INHIBIT_MASK;
				XSpi_SetControlReg(InstancePtr, ControlReg);
			}
		}

		/*
		 * Stop the transfer (hold off automatic sending) by inhibiting
		 * the transmitter.
		 */
		ControlReg = XSpi_GetControlReg(InstancePtr);
		XSpi_SetControlReg(InstancePtr,
				    ControlReg | XSP_CR_TRANS_INHIBIT_MASK);

		/*
		 * Select the slave on the SPI bus when the transfer is
		 * complete, this is necessary for some SPI devices,
		 * such as serial EEPROMs work correctly as chip enable
		 * may be connected to slave select
		 */
		XSpi_SetSlaveSelectReg(InstancePtr,
					InstancePtr->SlaveSelectMask);
		InstancePtr->IsBusy = FALSE;
	}

	return XST_SUCCESS;
}

实际上,那本程序中的中断关联函数基本没做什么事情,只是一个形式,具体的内容全部是在这个数据传输函数XSpi_Transfer()中实现的。

数据比较程序

for (Count = 0; Count < BUFFER_SIZE; Count++) {
		if (WriteBuffer[Count] != ReadBuffer[Count]) {
			return XST_FAILURE;
		}
	}

这段程序实现最后的数据比较,通过将发送出去的的数据和接收回来的数据相比较,判断发送接收过程是否正确。

值得注意的是,在测试该程序的时候,需要使用杜邦线将映射到ZYNQ上的MOSI和MISO两个引脚短接,从而完成一个数据的环回。这样既可以使用物理存在的示波器、也可以在BD中使用ILA在线逻辑分析器,观测端口的波形。

 波形图中,MOSI和MISO波形是一样的,都是对应于每个SCK周期的0/1bit。

修改Test值为0xff,再次运行,在波形图中手动算出传输的数据,得到如下图。

证明数据通过SPI端口传输正确。

完。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值