最近在测试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端口传输正确。
完。