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 givingdifferent 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_SAFETYvolatile u32 *LocalAddr = (volatile u32 *)Addr;*LocalAddr = Value;
#elseXStl_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	0x10Status = 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端口传输正确。

完。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/15080.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt文本编辑器开发------纯代码实现

目录 前言 项目分析 项目效果 项目流程 1. 先设置好文本编辑框的大小与名称 2.创建输入处理框 3.创建菜单栏 4.创建菜单按钮下的动作按钮 5.创建与动作按钮对应的槽函数 6.附件功能的实现 7.槽函数的实现 7.1文件处理读取 7.2关闭文本编辑框 7.3保存文本编辑框 …

Pytorch从零开始实现Transformer (from scratch)

Pytorch从零开始实现Transformer 前言一、Transformer架构介绍1. Embedding2. Multi-Head AttentionQuery&#xff0c;Key&#xff0c;Value 3. Transformer BlockLayerNormFeed Forward 4. Decoder Block 二、Transformer代码实现0. 导入库1. Word Embedding2. Positional Enc…

一文入门BMS(电池管理系统)

1.前言 本文章是基于曾师兄的文稿《具有组网扩展功能的新能源汽车电池管理系统》而写的文章。该文章主要是对BMS&#xff08;Battery Management System&#xff0c;电池管理系统&#xff09;做一些简要的介绍&#xff0c;借此助各位也助我入门BMS。 有纰漏请指出&#xff0c…

Redis数据结构

字节青训营后端打卡笔记&#xff0c;主题结构参照文章&#xff0c;以及网络上其它很多的资料所记录下来的笔记。 Redis数据结构一览 SDS(Simple Dynamic String) C语言字符串的缺陷 获取字符串长度函数strlen()时间复杂度为O(N) 字符串以\0结尾&#xff0c;意味着字符串里的…

手抖把Python2.7卸载了,导致了自己的yum不可用以及yum因python版本无法使用的问题

摘要: 从标题就能看到我有多心如死灰了,简单介绍下我是如何自残的过程. ①首先因为需要部署爬虫程序,然后安装Python3. ②Python3系列和Python2系列版本不向下兼容,所以我就卸载了机器自带的Python2.7,删的干干净净. ③然后我下载了Python3.8的包. ④我开始使用yum命令安装…

LangChain+LLM大模型问答能力搭建与思考

1. 背景 最近&#xff0c;大模型&#xff08;LLMs&#xff0c;Large Language Models&#xff09;可谓是NLP领域&#xff0c;甚至整个科技领域最火热的技术了。凑巧的是&#xff0c;我本人恰好就是NLP算法工程师&#xff0c;面临着被LLMs浪潮淘汰的窘境&#xff0c;决定在焦虑…

给AI挖坑 | 实测New Bing能否回答员工那些刁钻的问题?

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了人工智能中文站https://ai.weoknow.com 每天给大家更新可用的国内可用chatGPT资源 之前玩ChatGPT&#xff0c;发现这玩意很擅长胡说八道&#xff0c;比如你让它写一段发言稿&#xff0c;并引用鲁迅名言&#xff0…

如何用Rosetta全家桶设计一个抗体?

新冠肆虐无药可医&#xff0c; 医护冒险奋战在前线。 实验室里抗体设计&#xff0c; 试管里混合液波光粼粼&#xff0c; 分子结构、细胞实验频频。 日以继夜&#xff0c;孜孜不倦&#xff0c; 只为破解疫情的难题。 我们紧密团结&#xff0c;努力前行&#xff0c; 心中不灭的信…

【ChatGPT 】《ChatGPT 后续:我开发了一个超级阅读器,免费分享给大家》- 知识点目录

《ChatGPT 后续&#xff1a;我开发了一个超级阅读器&#xff0c;免费分享给大家》 00:00 我们开发了超级阅读器 01:37 思路和开发过程 03:00 使用方式 03:43 AI 工具加持开发效率 04:14 更多可能性 04:57 局限性 1. 介绍&#xff1a;PandaGPT 上传文献聊天窗口提问 2. DALL…

搭建正版GPT4.0!不用ChatGPT账号,不要API!

手把手教你免费搭建正版GPT4.0&#xff01;不用ChatGPT账号&#xff0c;不要API&#xff01; 项目简介 项目地址&#xff1a;https://github.com/ramonvc/freegpt-webui优点&#xff1a; 完全免费且不需要任何 API 密钥 ❌&#x1f511; 该项目的特点是使用G4F API 的WebUI …

他做了一个「ChatGPT 杀手」,a16z 抢着投

比「GPT 侦探」更重要的是&#xff0c;AI 生成内容在不同行业的「容忍度」。 图片来源&#xff1a;由无界版图AI工具生成 作者 | 美漪编辑 | 靖宇 最近两个月&#xff0c;科技圈最热的话题&#xff0c;无疑是 OpenAI 推出的对话式 AI 应用 ChatGPT&#xff0c;不仅可以让它给你…

巴比特 | 元宇宙每日必读:ChatGPT「代码解释器」正式解禁,它补齐了ChatGPT的哪些短板?用户该如何使用?...

摘要&#xff1a;7月9日&#xff0c;OpenAI 的聊天机器人 ChatGPT 推出了新功能&#xff1a;代码解释器&#xff08;Code Interpreter&#xff09;。这个新功能已经对所有 Plus 订阅用户开放&#xff0c;其扩展了 ChatGPT 的功能&#xff0c;为用户带来了更好的交互式编程体验和…

ChatGPT应用组队学习来了!

Datawhale学习 联合主办&#xff1a;Datawhale、百度文心 Datawhale联合百度文心&#xff0c;五月为大家带来AIGC应用专题&#xff1a;大模型从入门到应用&#xff0c;学习大纲如下&#xff08;文末整理了这次学习的所有资料&#xff09;&#xff1a; 参与学习 ▶ 活动时间&am…

阿尔法狗咬向ChatGPT七寸

图片来源&#xff1a;由无界AI生成 瞄准ChatGPT&#xff0c;谷歌的下一枚炮弹已经准备好&#xff0c;只待发射。而担负起发射任务的&#xff0c;是谷歌DeepMind。 昨天&#xff0c;谷歌DeepMind的CEO德米斯哈萨比斯&#xff08;Demis Hassabis&#xff09;在采访中放出豪言&…

谷歌版ChatGPT突然公测!上手实测结果在此,体验申请通过飞快

杨净 金磊 发自 凹非寺量子位 | 公众号 QbitAI 谷歌吃了大亏之后&#xff0c;这次一声不吭&#xff0c;放了大招&#xff1a; 对标ChatGPT的Bard测试版&#xff0c;刚刚正式对外发布。 而且这次用户在申请候补名单之后&#xff0c;无需经历漫长的等待时间。 没错&#xff0c;量…

对抗 ChatGPT 的创业武器:专注和紧密的反馈循环

ChatGPT 超越谷歌主导地位 在我的上一篇文章中,我探讨了 ChatGPT 超越谷歌主导地位的可能牛市案例。但我也对我认为是熊市的情况表示赞赏。正如我提到的,ChatGPT 的无界界面有点像,而不是 DoorDash 的重点推出策略,DoorDash 在美国所有城市和商品类别中同时推出,当你订购…

chatgpt赋能Python-python_queque

Python Queue模块实现队列的介绍 Python语言是一种通俗易懂、功能丰富的编程语言。它的标准库还包括许多有用的模块&#xff0c;用于实现各种数据结构和算法。其中&#xff0c;Queue模块是一种实现队列的模块。这个模块实现了多线程编程时所必需的队列数据结构。 什么是队列&…

ChatGPT已能操控机器人,工程师连代码都不用写,网友:微软在搞天网?

Alex 发自 凹非寺量子位 | 公众号 QbitAI 当我还在跟ChatGPT吹牛尬聊时&#xff0c;有人已经在拿它操控机器人了。 不是别人&#xff0c;正是OpenAI的金主爸爸、不久前刚拿ChatGPT“重新发明搜索引擎”的微软。 到目前为止&#xff0c;开发者调教机器人不仅技术门槛高&#xff…

火爆外网的ChatGPT,改Bug,敲代码不在话下

目录 前言 一、ChatGPT 是什么&#xff1f; 二、ChatGPT到底有什么用 1.可以回答问题 2.帮你创作文章和标题 3.调试代码和修复代码 4.检测安全漏洞&#xff0c;也许还能创建PoC 总结 前言 这几天ChatGPT AI 可谓是火的一塌糊涂&#xff0c;那么它到底是什么&#xff1f…

Python使用itchat库+图灵机器人(新手上路)

前不久有个朋友说&#xff0c;谁谁的男朋友写个机器人&#xff0c;然后聊天很嗨的样子&#xff0c;看下面图&#xff0c;然后今天下午闲着&#xff0c;就把整理了下思路&#xff0c;采用Python进行如下开发&#xff0c;具体步骤如下&#xff1a; 1、第一步&#xff0c;因为我是…