最近在测试AXI Quad SPI这个IP核的端口时序,搭建BD后导出到硬件,在SDK中导入xspi_intr_example.c的源文件,在师兄的帮助下,浅浅研究了一下代码。
首先,需要修改源程序中的错误,参照CSDN文章:ZYNQ中断示例修改 做出以下修改:
#define INTC_DEVICE_ID XPAR_INTC_0_DEVICE_ID
#define SPI_IRPT_INTR XPAR_INTC_0_SPI_0_VEC_ID
// 修改为:
#define INTC_DEVICE_ID XPAR_SCUGIC_0_DEVICE_ID
#define SPI_IRPT_INTR XPAR_FABRIC_SPI_0_VEC_ID
此外,还要对中断驱动实例名称进行修改:
static INTC Intc; /* The instance of the Interrupt Controller */
// 修改为:
static INTC IntcInstance; /* The instance of the Interrupt Controller */
以下是对程序中函数的理解。
XSpi_LookupConfig()函数
ConfigPtr = XSpi_LookupConfig(XPAR_SPI_0_DEVICE_ID);
参数XPAR_SPI_0_DEVICE_ID为SPI的设备ID。该函数的作用是从系统中查询是否有这个设备的定义,该函数的原型为:
#define XPAR_XSPI_NUM_INSTANCES 1U
/*****************************************************************************/
/**
*
* Looks up the device configuration based on the unique device ID. A table
* contains the configuration info for each device in the system.
*
* @param DeviceId contains the ID of the device to look up the
* configuration for.
*
* @return
*
* A pointer to the configuration found or NULL if the specified device ID was
* not found. See xspi.h for the definition of XSpi_Config.
*
* @note None.
*
******************************************************************************/
XSpi_Config *XSpi_LookupConfig(u16 DeviceId)
{
XSpi_Config *CfgPtr = NULL;
u32 Index;
for (Index = 0; Index < XPAR_XSPI_NUM_INSTANCES; Index++) {
if (XSpi_ConfigTable[Index].DeviceId == DeviceId) {
CfgPtr = &XSpi_ConfigTable[Index];
break;
}
}
return CfgPtr;
}
该函数实现的是:查表看是否有相应的设备描述,如果有,就会将该设备在ZYNQ地址空间中的地址赋值给CfgPtr这个设备指针。其中XSpi_ConfigTable是一个结构体列表,有两层大括号,第一层由Index索引,第二层由‘.’来定位。定义如下:
/*
* The configuration table for devices
*/
XSpi_Config XSpi_ConfigTable[] =
{
{
XPAR_SPI_0_DEVICE_ID,
XPAR_SPI_0_BASEADDR,
XPAR_SPI_0_FIFO_EXIST,
XPAR_SPI_0_SPI_SLAVE_ONLY,
XPAR_SPI_0_NUM_SS_BITS,
XPAR_SPI_0_NUM_TRANSFER_BITS,
XPAR_SPI_0_SPI_MODE,
XPAR_SPI_0_TYPE_OF_AXI4_INTERFACE,
XPAR_SPI_0_AXI4_BASEADDR,
XPAR_SPI_0_XIP_MODE,
XPAR_SPI_0_USE_STARTUP
}
};
XSpi_CfgInitialize()函数
Status = XSpi_CfgInitialize(SpiInstancePtr, ConfigPtr,
ConfigPtr->BaseAddress);
参数SpiInstancePtr为SPI驱动实例指针,ConfigPtr为LookupConfig函数找到的SPI设备地址指针。该函数的作用是:根据前面找到的设备地址指针,对设备的寄存器进行配置。该函数的原型为:
/*****************************************************************************/
/**
*
* Initializes a specific XSpi instance such that the driver is ready to use.
*
* The state of the device after initialization is:
* - Device is disabled
* - Slave mode
* - Active high clock polarity
* - Clock phase 0
*
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
* @param Config is a reference to a structure containing information
* about a specific SPI device. This function initializes an
* InstancePtr object for a specific device specified by the
* contents of Config. This function can initialize multiple
* instance objects with the use of multiple calls giving
different Config information on each call.
* @param EffectiveAddr is the device base address in the virtual memory
* address space. The caller is responsible for keeping the
* address mapping from EffectiveAddr to the device physical base
* address unchanged once this function is invoked. Unexpected
* errors may occur if the address mapping changes after this
* function is called. If address translation is not used, use
* Config->BaseAddress for this parameters, passing the physical
* address instead.
*
* @return
* - XST_SUCCESS if successful.
* - XST_DEVICE_IS_STARTED if the device is started. It must be
* stopped to re-initialize.
*
* @note None.
*
******************************************************************************/
int XSpi_CfgInitialize(XSpi *InstancePtr, XSpi_Config *Config,
UINTPTR EffectiveAddr)
{
u8 Buffer[3];
u32 ControlReg;
u32 StatusReg;
Xil_AssertNonvoid(InstancePtr != NULL);
/*
* If the device is started, disallow the initialize and return a status
* indicating it is started. This allows the user to stop the device
* and reinitialize, but prevents a user from inadvertently
* initializing.
*/
if (InstancePtr->IsStarted == XIL_COMPONENT_IS_STARTED) {
return XST_DEVICE_IS_STARTED;
}
/*
* Set some default values.
*/
InstancePtr->IsStarted = 0;
InstancePtr->IsBusy = FALSE;
InstancePtr->StatusHandler = StubStatusHandler;
InstancePtr->SendBufferPtr = NULL;
InstancePtr->RecvBufferPtr = NULL;
InstancePtr->RequestedBytes = 0;
InstancePtr->RemainingBytes = 0;
InstancePtr->BaseAddr = EffectiveAddr;
InstancePtr->HasFifos = Config->HasFifos;
InstancePtr->SlaveOnly = Config->SlaveOnly;
InstancePtr->NumSlaveBits = Config->NumSlaveBits;
if (Config->DataWidth == 0) {
InstancePtr->DataWidth = XSP_DATAWIDTH_BYTE;
} else {
InstancePtr->DataWidth = Config->DataWidth;
}
InstancePtr->SpiMode = Config->SpiMode;
InstancePtr->FlashBaseAddr = Config->AxiFullBaseAddress;
InstancePtr->XipMode = Config->XipMode;
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
/*
* Create a slave select mask based on the number of bits that can
* be used to deselect all slaves, initialize the value to put into
* the slave select register to this value.
*/
InstancePtr->SlaveSelectMask = (1 << InstancePtr->NumSlaveBits) - 1;
InstancePtr->SlaveSelectReg = InstancePtr->SlaveSelectMask;
/*
* Clear the statistics for this driver.
*/
InstancePtr->Stats.ModeFaults = 0;
InstancePtr->Stats.XmitUnderruns = 0;
InstancePtr->Stats.RecvOverruns = 0;
InstancePtr->Stats.SlaveModeFaults = 0;
InstancePtr->Stats.BytesTransferred = 0;
InstancePtr->Stats.NumInterrupts = 0;
if(Config->Use_Startup == 1) {
/*
* Perform a dummy read this is used when startup block is
* enabled in the hardware to fix CR #721229.
*/
ControlReg = XSpi_GetControlReg(InstancePtr);
ControlReg |= XSP_CR_TXFIFO_RESET_MASK | XSP_CR_RXFIFO_RESET_MASK |
XSP_CR_ENABLE_MASK | XSP_CR_MASTER_MODE_MASK ;
XSpi_SetControlReg(InstancePtr, ControlReg);
/*
* Initiate Read command to get the ID. This Read command is for
* Numonyx flash.
*
* NOTE: If user interfaces different flash to the SPI controller
* this command need to be changed according to target flash Read
* command.
*/
Buffer[0] = 0x9F;
Buffer[1] = 0x00;
Buffer[2] = 0x00;
/* Write dummy ReadId to the DTR register */
XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET, Buffer[0]);
XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET, Buffer[1]);
XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET, Buffer[2]);
/* Master Inhibit enable in the CR */
ControlReg = XSpi_GetControlReg(InstancePtr);
ControlReg &= ~XSP_CR_TRANS_INHIBIT_MASK;
XSpi_SetControlReg(InstancePtr, ControlReg);
/* Master Inhibit disable in the CR */
ControlReg = XSpi_GetControlReg(InstancePtr);
ControlReg |= XSP_CR_TRANS_INHIBIT_MASK;
XSpi_SetControlReg(InstancePtr, ControlReg);
/* Read the Rx Data Register */
StatusReg = XSpi_GetStatusReg(InstancePtr);
if ((StatusReg & XSP_SR_RX_EMPTY_MASK) == 0) {
XSpi_ReadReg(InstancePtr->BaseAddr, XSP_DRR_OFFSET);
}
StatusReg = XSpi_GetStatusReg(InstancePtr);
if ((StatusReg & XSP_SR_RX_EMPTY_MASK) == 0) {
XSpi_ReadReg(InstancePtr->BaseAddr, XSP_DRR_OFFSET);
}
}
/*
* Reset the SPI device to get it into its initial state. It is expected
* that device configuration will take place after this initialization
* is done, but before the device is started.
*/
XSpi_Reset(InstancePtr);
return XST_SUCCESS;
}
里面涉及到了几个比较重要的函数:Xil_AssertNonvoid(),XSpi_GetControlReg(),XSpi_SetControlReg(),XSpi_WriteReg(),XSpi_ReadReg()和XSpi_Reset()。
Xil_AssertNonvoid()
Xil_AssertNonvoid(InstancePtr != NULL)
该函数作用为:检查输入是否合法。
函数原型为:
/*****************************************************************************/
/**
* @brief This assert macro is to be used for functions that do return a
* value. This in conjunction with the Xil_AssertWait boolean can be
* used to accomodate tests so that asserts which fail allow execution
* to continue.
*
* @param Expression: expression to be evaluated. If it evaluates to false,
* the assert occurs.
*
* @return Returns 0 unless the Xil_AssertWait variable is true, in which
* case no return is made and an infinite loop is entered.
*
******************************************************************************/
#define Xil_AssertNonvoid(Expression) \
{ \
if (Expression) { \
Xil_AssertStatus = XIL_ASSERT_NONE; \
} else { \
Xil_Assert(__FILE__, __LINE__); \
Xil_AssertStatus = XIL_ASSERT_OCCURRED; \
return 0; \
} \
}
XSpi_ReadReg()
XSpi_ReadReg(InstancePtr->BaseAddr, XSP_DRR_OFFSET);
该函数作用为:根据某寄存器地址(基地址+地址偏移)读出该寄存器的值。
函数的原型为:
/****************************************************************************/
/**
*
* Read from the specified Spi device register.
*
* @param BaseAddress contains the base address of the device.
* @param RegOffset contains the offset from the 1st register of the
* device to select the specific register.
*
* @return The value read from the register.
*
* @note C-Style signature:
* u32 XSpi_ReadReg(u32 BaseAddress, u32 RegOffset);
*
******************************************************************************/
#define XSpi_ReadReg(BaseAddress, RegOffset) \
XSpi_In32((BaseAddress) + (RegOffset))
/***************************************************************************/
有关XSpi_In32()函数,将在后面补充。
XSpi_WriteReg()
Buffer[0] = 0x9F;
XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET, Buffer[0]);
该函数的作用为:将一个值(此处为Buffer[0])写入某寄存器地址(基地址+地址偏移)指向的寄存器中。
函数原型为:
/***************************************************************************/
/**
*
* Write to the specified Spi device register.
*
* @param BaseAddress contains the base address of the device.
* @param RegOffset contains the offset from the 1st register of the
* device to select the specific register.
* @param RegisterValue is the value to be written to the register.
*
* @return None.
*
* @note C-Style signature:
* void XSpi_WriteReg(u32 BaseAddress, u32 RegOffset,
* u32 RegisterValue);
******************************************************************************/
#define XSpi_WriteReg(BaseAddress, RegOffset, RegisterValue) \
XSpi_Out32((BaseAddress) + (RegOffset), (RegisterValue))
有关XSpi_Out32()函数,将在后面补充。
XSpi_GetControlReg()
ControlReg = XSpi_GetControlReg(InstancePtr);
该函数作用为:读取控制寄存器的值。
函数原型为:
/****************************************************************************/
/**
*
* Get the contents of the control register. Use the XSP_CR_* constants defined
* above to interpret the bit-mask returned.
*
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
*
* @return A 32-bit value representing the contents of the control
* register.
*
* @note C-Style signature:
* u32 XSpi_GetControlReg(XSpi *InstancePtr);
*
*****************************************************************************/
#define XSpi_GetControlReg(InstancePtr) \
XSpi_ReadReg(((InstancePtr)->BaseAddr), XSP_CR_OFFSET)
/***************************************************************************/
XSpi_SetControlReg()
ControlReg |= XSP_CR_TXFIFO_RESET_MASK | XSP_CR_RXFIFO_RESET_MASK |
XSP_CR_ENABLE_MASK | XSP_CR_MASTER_MODE_MASK ;
XSpi_SetControlReg(InstancePtr, ControlReg);
该函数的作用是,设置控制寄存器,向控制寄存器中写入一些掩码以设定一些模式,不设置的模式默认为禁用。
函数原型为:
/****************************************************************************/
/**
*
* Set the contents of the control register. Use the XSP_CR_* constants defined
* above to create the bit-mask to be written to the register.
*
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
* @param Mask is the 32-bit value to write to the control register.
*
* @return None.
*
* @note C-Style signature:
* void XSpi_SetControlReg(XSpi *InstancePtr, u32 Mask);
*
*****************************************************************************/
#define XSpi_SetControlReg(InstancePtr, Mask) \
XSpi_WriteReg(((InstancePtr)->BaseAddr), XSP_CR_OFFSET, (Mask))
XSpi_Reset()
XSpi_Reset(InstancePtr)
该函数的作用是复位。
函数原型为:
/*****************************************************************************/
/**
*
* Resets the SPI device by writing to the Software Reset register. Reset must
* only be called after the driver has been initialized. The configuration of the
* device after reset is the same as its configuration after initialization.
* Refer to the XSpi_Initialize function for more details. This is a hard reset
* of the device. Any data transfer that is in progress is aborted.
*
* The upper layer software is responsible for re-configuring (if necessary)
* and restarting the SPI device after the reset.
*
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
*
* @return None.
*
* @note None.
*
******************************************************************************/
void XSpi_Reset(XSpi *InstancePtr)
{
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/*
* Abort any transfer that is in progress.
*/
XSpi_Abort(InstancePtr);
/*
* Reset any values that are not reset by the hardware reset such that
* the software state matches the hardware device.
*/
InstancePtr->IsStarted = 0;
InstancePtr->SlaveSelectReg = InstancePtr->SlaveSelectMask;
/*
* Reset the device.
*/
XSpi_WriteReg(InstancePtr->BaseAddr, XSP_SRR_OFFSET,
XSP_SRR_RESET_MASK);
}
补充内容:
XSpi_In32()
函数原型为:
#define INLINE __inline
#define XSpi_In32 Xil_In32 // 进一步包装
/*****************************************************************************/
/**
*
* @brief Performs an input operation for a memory location by
* reading from the specified address and returning the 32 bit Value
* read from that address.
*
* @param Addr: contains the address to perform the input operation
*
* @return The 32 bit Value read from the specified input address.
*
******************************************************************************/
static INLINE u32 Xil_In32(UINTPTR Addr)
{
return *(volatile u32 *) Addr;
}
该函数的作用是从传入的地址读取一个32bit的值并返回。
另外,__inline通知编译器将该函数的内容拷贝一份放在调用函数的地方,这称之为内联。内联减少了函数调用的开销,但却增加了代码量。(参考文献:C中__inline__的含义及作用)
XSpi_Out32()
函数原型为:
#define INLINE __inline
#define XSpi_Out32 Xil_Out32 // 进一步包装
/*****************************************************************************/
/**
*
* @brief Performs an output operation for a memory location by writing the
* 32 bit Value to the the specified address.
*
* @param Addr contains the address to perform the output operation
* @param Value contains the 32 bit Value to be written at the specified
* address.
*
* @return None.
*
******************************************************************************/
static INLINE void Xil_Out32(UINTPTR Addr, u32 Value)
{
#ifndef ENABLE_SAFETY
volatile u32 *LocalAddr = (volatile u32 *)Addr;
*LocalAddr = Value;
#else
XStl_RegUpdate(Addr, Value);
#endif
}
该函数的作用是,将一个32bit的值写入给定地址指向的寄存器。
XSpi_SelfTest()函数
Status = XSpi_SelfTest(SpiInstancePtr);
参数SpiInstancePtr为SPI驱动实例指针。该函数的作用是:测试硬件电路是否搭建正确。
函数原型为:
/*****************************************************************************/
/**
*
* Runs a self-test on the driver/device. The self-test is destructive in that
* a reset of the device is performed in order to check the reset values of
* the registers and to get the device into a known state. A simple loopback
* test is also performed to verify that transmit and receive are working
* properly. The device is changed to master mode for the loopback test, since
* only a master can initiate a data transfer.
*
* Upon successful return from the self-test, the device is reset.
*
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
*
* @return
* - XST_SUCCESS if successful.
* - XST_REGISTER_ERROR indicates a register did not read or write
* correctly.
* - XST_LOOPBACK_ERROR if a loopback error occurred.
*
* @note None.
*
******************************************************************************/
int XSpi_SelfTest(XSpi *InstancePtr)
{
int Result;
u32 Register;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/* Return Success if XIP Mode */
if((InstancePtr->XipMode) == 1) {
return XST_SUCCESS;
}
/*
* Reset the SPI device to leave it in a known good state.
*/
XSpi_Reset(InstancePtr);
if(InstancePtr->XipMode)
{
Register = XSpi_GetControlReg(InstancePtr);
if (Register != XSP_CR_RESET_STATE) {
return XST_REGISTER_ERROR;
}
Register = XSpi_GetStatusReg(InstancePtr);
if ((Register & XSP_SR_RESET_STATE) != XSP_SR_RESET_STATE) {
return XST_REGISTER_ERROR;
}
}
/*
* All the SPI registers should be in their default state right now.
*/
Register = XSpi_GetControlReg(InstancePtr);
if (Register != XSP_CR_RESET_STATE) {
return XST_REGISTER_ERROR;
}
Register = XSpi_GetStatusReg(InstancePtr);
if ((Register & XSP_SR_RESET_STATE) != XSP_SR_RESET_STATE) {
return XST_REGISTER_ERROR;
}
/*
* Each supported slave select bit should be set to 1.
*/
Register = XSpi_GetSlaveSelectReg(InstancePtr);
if (Register != InstancePtr->SlaveSelectMask) {
return XST_REGISTER_ERROR;
}
/*
* If configured with FIFOs, the occupancy values should be 0.
*/
if (InstancePtr->HasFifos) {
Register = XSpi_ReadReg(InstancePtr->BaseAddr,
XSP_TFO_OFFSET);
if (Register != 0) {
return XST_REGISTER_ERROR;
}
Register = XSpi_ReadReg(InstancePtr->BaseAddr,
XSP_RFO_OFFSET);
if (Register != 0) {
return XST_REGISTER_ERROR;
}
}
/*
* Run loopback test only in case of standard SPI mode.
*/
if (InstancePtr->SpiMode != XSP_STANDARD_MODE) {
return XST_SUCCESS;
}
/*
* Run an internal loopback test on the SPI.
*/
Result = LoopbackTest(InstancePtr);
if (Result != XST_SUCCESS) {
return Result;
}
/*
* Reset the SPI device to leave it in a known good state.
*/
XSpi_Reset(InstancePtr);
return XST_SUCCESS;
}
里面涉及到几个函数,都是形似Get_XxxReg()/Set_XxxReg(),即读取某特定寄存器的值或者向某特定寄存器写入值。这里涉及多个寄存器的偏移值,列在下面:
/**
* XSPI register offsets
*/
/** @name Register Map
*
* Register offsets for the XSpi device.
* @{
*/
#define XSP_DGIER_OFFSET 0x1C /**< Global Intr Enable Reg */
#define XSP_IISR_OFFSET 0x20 /**< Interrupt status Reg */
#define XSP_IIER_OFFSET 0x28 /**< Interrupt Enable Reg */
#define XSP_SRR_OFFSET 0x40 /**< Software Reset register */
#define XSP_CR_OFFSET 0x60 /**< Control register */
#define XSP_SR_OFFSET 0x64 /**< Status Register */
#define XSP_DTR_OFFSET 0x68 /**< Data transmit */
#define XSP_DRR_OFFSET 0x6C /**< Data receive */
#define XSP_SSR_OFFSET 0x70 /**< 32-bit slave select */
#define XSP_TFO_OFFSET 0x74 /**< Tx FIFO occupancy */
#define XSP_RFO_OFFSET 0x78 /**< Rx FIFO occupancy */
挖个坑:具体向某个寄存器中写入什么值会产生什么样的配置,请参考:SPI各寄存器配置。
(未来某个时间更新,时间待定)
另外,该自测函数中调用了一个回环测试,测试完成后,测试过程中产生的数据不会保留,寄存器配置将恢复到测试之前的状态(猜的,待验证)。
函数原型为:
/*****************************************************************************/
/*
*
* Runs an internal loopback test on the SPI device. This is done as a master
* with a enough data to fill the FIFOs if FIFOs are present. If the device is
* configured as a slave-only, this function returns successfully even though
* no loopback test is performed.
*
* This function does not restore the device context after performing the test
* as it assumes the device will be reset after the call.
*
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
*
* @return
* - XST_SUCCESS if loopback was performed successfully or not
* performed at all if device is slave-only.
* - XST_LOOPBACK_ERROR if loopback failed.
*
* @note None.
*
******************************************************************************/
static int LoopbackTest(XSpi *InstancePtr)
{
u32 StatusReg;
u32 ControlReg;
u32 Index;
u32 Data;
u32 RxData;
u32 NumSent = 0;
u32 NumRecvd = 0;
u8 DataWidth;
/*
* Cannot run as a slave-only because we need to be master in order to
* initiate a transfer. Still return success, though.
*/
if (InstancePtr->SlaveOnly) {
return XST_SUCCESS;
}
/*
* Setup the control register to enable master mode and the loopback so
* that data can be sent and received.
*/
ControlReg = XSpi_GetControlReg(InstancePtr);
XSpi_SetControlReg(InstancePtr, ControlReg |
XSP_CR_LOOPBACK_MASK | XSP_CR_MASTER_MODE_MASK);
/*
* We do not need interrupts for this loopback test.
*/
XSpi_IntrGlobalDisable(InstancePtr);
DataWidth = InstancePtr->DataWidth;
/*
* Send data up to the maximum size of the transmit register, which is
* one byte without FIFOs. We send data 4 times just to exercise the
* device through more than one iteration.
*/
for (Index = 0; Index < 4; Index++) {
Data = 0;
/*
* Fill the transmit register.
*/
StatusReg = XSpi_GetStatusReg(InstancePtr);
while ((StatusReg & XSP_SR_TX_FULL_MASK) == 0) {
if (DataWidth == XSP_DATAWIDTH_BYTE) {
/*
* Data Transfer Width is Byte (8 bit).
*/
Data = 0;
} else if (DataWidth == XSP_DATAWIDTH_HALF_WORD) {
/*
* Data Transfer Width is Half Word (16 bit).
*/
Data = XSP_HALF_WORD_TESTBYTE;
} else if (DataWidth == XSP_DATAWIDTH_WORD){
/*
* Data Transfer Width is Word (32 bit).
*/
Data = XSP_WORD_TESTBYTE;
}
XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET,
Data + Index);
NumSent += (DataWidth >> 3);
StatusReg = XSpi_GetStatusReg(InstancePtr);
}
/*
* Start the transfer by not inhibiting the transmitter and
* enabling the device.
*/
ControlReg = XSpi_GetControlReg(InstancePtr) &
(~XSP_CR_TRANS_INHIBIT_MASK);
XSpi_SetControlReg(InstancePtr, ControlReg |
XSP_CR_ENABLE_MASK);
/*
* Wait for the transfer to be done by polling the transmit
* empty status bit.
*/
do {
StatusReg = XSpi_IntrGetStatus(InstancePtr);
} while ((StatusReg & XSP_INTR_TX_EMPTY_MASK) == 0);
XSpi_IntrClear(InstancePtr, XSP_INTR_TX_EMPTY_MASK);
/*
* Receive and verify the data just transmitted.
*/
StatusReg = XSpi_GetStatusReg(InstancePtr);
while ((StatusReg & XSP_SR_RX_EMPTY_MASK) == 0) {
RxData = XSpi_ReadReg(InstancePtr->BaseAddr,
XSP_DRR_OFFSET);
if (DataWidth == XSP_DATAWIDTH_BYTE) {
if((u8)RxData != Index) {
return XST_LOOPBACK_ERROR;
}
} else if (DataWidth ==
XSP_DATAWIDTH_HALF_WORD) {
if((u16)RxData != (u16)(Index +
XSP_HALF_WORD_TESTBYTE)) {
return XST_LOOPBACK_ERROR;
}
} else if (DataWidth == XSP_DATAWIDTH_WORD) {
if(RxData != (u32)(Index + XSP_WORD_TESTBYTE)) {
return XST_LOOPBACK_ERROR;
}
}
NumRecvd += (DataWidth >> 3);
StatusReg = XSpi_GetStatusReg(InstancePtr);
}
/*
* Stop the transfer (hold off automatic sending) by inhibiting
* the transmitter and disabling the device.
*/
ControlReg |= XSP_CR_TRANS_INHIBIT_MASK;
XSpi_SetControlReg(InstancePtr ,
ControlReg & ~ XSP_CR_ENABLE_MASK);
}
/*
* One final check to make sure the total number of bytes sent equals
* the total number of bytes received.
*/
if (NumSent != NumRecvd) {
return XST_LOOPBACK_ERROR;
}
return XST_SUCCESS;
}
SpiSetupIntrSystem()函数
Status = SpiSetupIntrSystem(IntcInstancePtr, SpiInstancePtr, SpiIntrId);
参数IntcInstancePtr为中断驱动实例指针,SpiIntrId为SPI的中断ID。该函数的作用是,关联SPI设备与中断子系统,使中断发生。
中断配置内容先不详细介绍了,或许可以挖个新坑,详见:嵌入式开发SDK中的中断配置。
XSpi_SetStatusHandler()函数
XSpi_SetStatusHandler(SpiInstancePtr, SpiInstancePtr,
(XSpi_StatusHandler) SpiIntrHandler);
也是一个中断相关的函数,先不做介绍,后续补充的话应该也在上面函数的坑里。
XSpi_SetOptions()函数
#define XSP_MASTER_OPTION 0x1
#define XSP_CLK_ACTIVE_LOW_OPTION 0x2
#define XSP_CLK_PHASE_1_OPTION 0x4
#define XSP_LOOPBACK_OPTION 0x8
#define XSP_MANUAL_SSELECT_OPTION 0x10
Status = XSpi_SetOptions(SpiInstancePtr, XSP_MASTER_OPTION |
XSP_CLK_PHASE_1_OPTION | XSP_CLK_ACTIVE_LOW_OPTION |
XSP_MANUAL_SSELECT_OPTION);
该函数的作用为,对SPI的可选的工作模式做一些配置。这里设置了主模式,手动片选,还有一个时钟相位和极性的组合(ChatGPT说这种组合是设置MSB?待验证)
该函数的原型为:
/***************************** Include Files *********************************/
#include "xspi.h"
#include "xspi_i.h"
/************************** Constant Definitions *****************************/
/**************************** Type Definitions *******************************/
/***************** Macros (Inline Functions) Definitions *********************/
/************************** Function Prototypes ******************************/
/************************** Variable Definitions *****************************/
/*
* Create the table of options which are processed to get/set the device
* options. These options are table driven to allow easy maintenance and
* expansion of the options.
*/
typedef struct {
u32 Option;
u32 Mask;
} OptionsMap;
static OptionsMap OptionsTable[] = {
{XSP_LOOPBACK_OPTION, XSP_CR_LOOPBACK_MASK},
{XSP_CLK_ACTIVE_LOW_OPTION, XSP_CR_CLK_POLARITY_MASK},
{XSP_CLK_PHASE_1_OPTION, XSP_CR_CLK_PHASE_MASK},
{XSP_MASTER_OPTION, XSP_CR_MASTER_MODE_MASK},
{XSP_MANUAL_SSELECT_OPTION, XSP_CR_MANUAL_SS_MASK}
};
#define XSP_NUM_OPTIONS (sizeof(OptionsTable) / sizeof(OptionsMap))
/*****************************************************************************/
/**
*
* This function sets the options for the SPI device driver. The options control
* how the device behaves relative to the SPI bus. The device must be idle
* rather than busy transferring data before setting these device options.
*
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
* @param Options contains the specified options to be set. This is a bit
* mask where a 1 means to turn the option on, and a 0 means to
* turn the option off. One or more bit values may be contained in
* the mask.
* See the bit definitions named XSP_*_OPTIONS in the file xspi.h.
*
* @return
* -XST_SUCCESS if options are successfully set.
* - XST_DEVICE_BUSY if the device is currently transferring data.
* The transfer must complete or be aborted before setting options.
* - XST_SPI_SLAVE_ONLY if the caller attempted to configure a
* slave-only device as a master.
*
* @note
*
* This function makes use of internal resources that are shared between the
* XSpi_Stop() and XSpi_SetOptions() functions. So if one task might be setting
* device options while another is trying to stop the device, the user is
* required to provide protection of this shared data (typically using a
* semaphore).
*
******************************************************************************/
int XSpi_SetOptions(XSpi *InstancePtr, u32 Options)
{
u32 ControlReg;
u32 Index;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/*
* Do not allow the slave select to change while a transfer is in
* progress.
* No need to worry about a critical section here since even if the Isr
* changes the busy flag just after we read it, the function will return
* busy and the caller can retry when notified that their current
* transfer is done.
*/
if (InstancePtr->IsBusy) {
return XST_DEVICE_BUSY;
}
/*
* Do not allow master option to be set if the device is slave only.
*/
if ((Options & XSP_MASTER_OPTION) && (InstancePtr->SlaveOnly)) {
return XST_SPI_SLAVE_ONLY;
}
ControlReg = XSpi_GetControlReg(InstancePtr);
/*
* Loop through the options table, turning the option on or off
* depending on whether the bit is set in the incoming options flag.
*/
for (Index = 0; Index < XSP_NUM_OPTIONS; Index++) {
if (Options & OptionsTable[Index].Option) {
/*
*Turn it ON.
*/
ControlReg |= OptionsTable[Index].Mask;
}
else {
/*
*Turn it OFF.
*/
ControlReg &= ~OptionsTable[Index].Mask;
}
}
/*
* Now write the control register. Leave it to the upper layers
* to restart the device.
*/
XSpi_SetControlReg(InstancePtr, ControlReg);
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* This function gets the options for the SPI device. The options control how
* the device behaves relative to the SPI bus.
*
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
*
* @return
*
* Options contains the specified options to be set. This is a bit mask where a
* 1 means to turn the option on, and a 0 means to turn the option off. One or
* more bit values may be contained in the mask. See the bit definitions named
* XSP_*_OPTIONS in the file xspi.h.
*
* @note None.
*
******************************************************************************/
u32 XSpi_GetOptions(XSpi *InstancePtr)
{
u32 OptionsFlag = 0;
u32 ControlReg;
u32 Index;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/*
* Get the control register to determine which options are currently
* set.
*/
ControlReg = XSpi_GetControlReg(InstancePtr);
/*
* Loop through the options table to determine which options are set.
*/
for (Index = 0; Index < XSP_NUM_OPTIONS; Index++) {
if (ControlReg & OptionsTable[Index].Mask) {
OptionsFlag |= OptionsTable[Index].Option;
}
}
return OptionsFlag;
}
刚好是一个文件xspi_options.c,就直接贴过来了哈哈哈。估计是主程序的某个头文件中包含了这个.c文件,才能进入xspi_options.c这个文件调用XSpi_SetOptions()这个函数。
XSpi_Start()函数
XSpi_Start(SpiInstancePtr);
该函数作用是,开启SPI驱动以使中断能够发生。
函数原型为:
/*****************************************************************************/
/**
*
* This function enables interrupts for the SPI device. If the Spi driver is used
* in interrupt mode, it is up to the user to connect the SPI interrupt handler
* to the interrupt controller before this function is called. If the Spi driver
* is used in polled mode the user has to disable the Global Interrupts after
* this function is called. If the device is configured with FIFOs, the FIFOs are
* reset at this time.
*
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
*
* @return
* - XST_SUCCESS if the device is successfully started
* - XST_DEVICE_IS_STARTED if the device was already started.
*
* @note None.
*
******************************************************************************/
int XSpi_Start(XSpi *InstancePtr)
{
u32 ControlReg;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/*
* If it is already started, return a status indicating so.
*/
if (InstancePtr->IsStarted == XIL_COMPONENT_IS_STARTED) {
return XST_DEVICE_IS_STARTED;
}
/*
* Enable the interrupts.
*/
XSpi_IntrEnable(InstancePtr, XSP_INTR_DFT_MASK);
/*
* Indicate that the device is started before we enable the transmitter
* or receiver or interrupts.
*/
InstancePtr->IsStarted = XIL_COMPONENT_IS_STARTED;
/*
* Reset the transmit and receive FIFOs if present. There is a critical
* section here since this register is also modified during interrupt
* context. So we wait until after the r/m/w of the control register to
* enable the Global Interrupt Enable.
*/
ControlReg = XSpi_GetControlReg(InstancePtr);
ControlReg |= XSP_CR_TXFIFO_RESET_MASK | XSP_CR_RXFIFO_RESET_MASK |
XSP_CR_ENABLE_MASK;
XSpi_SetControlReg(InstancePtr, ControlReg);
/*
* Enable the Global Interrupt Enable just after we start.
*/
XSpi_IntrGlobalEnable(InstancePtr);
return XST_SUCCESS;
}
XSpi_SetSlaveSelect()函数
XSpi_SetSlaveSelect(SpiInstancePtr, 0x01);
官方示例程序中没有的这一行代码,是我参考CSDN上一篇文章:AXI quad SPI没有输出 加上的,用以配合设定的手动片选模式。
函数原型为:
/*****************************************************************************/
/**
*
* Selects or deselect the slave with which the master communicates. Each slave
* that can be selected is represented in the slave select register by a bit.
* The argument passed to this function is the bit mask with a 1 in the bit
* position of the slave being selected. Only one slave can be selected.
*
* The user is not allowed to deselect the slave while a transfer is in progress.
* If no transfer is in progress, the user can select a new slave, which
* implicitly deselects the current slave. In order to explicitly deselect the
* current slave, a zero can be passed in as the argument to the function.
*
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
* @param SlaveMask is a 32-bit mask with a 1 in the bit position of the
* slave being selected. Only one slave can be selected. The
* SlaveMask can be zero if the slave is being deselected.
*
* @return
* - XST_SUCCESS if the slave is selected or deselected
* successfully.
* - XST_DEVICE_BUSY if a transfer is in progress, slave cannot be
* changed
* - XST_SPI_TOO_MANY_SLAVES if more than one slave is being
* selected.
*
* @note
*
* This function only sets the slave which will be selected when a transfer
* occurs. The slave is not selected when the SPI is idle. The slave select
* has no affect when the device is configured as a slave.
*
******************************************************************************/
int XSpi_SetSlaveSelect(XSpi *InstancePtr, u32 SlaveMask)
{
int NumAsserted;
int Index;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/*
* Do not allow the slave select to change while a transfer is in
* progress.
* No need to worry about a critical section here since even if the Isr
* changes the busy flag just after we read it, the function will return
* busy and the caller can retry when notified that their current
* transfer is done.
*/
if (InstancePtr->IsBusy) {
return XST_DEVICE_BUSY;
}
/*
* Verify that only one bit in the incoming slave mask is set.
*/
NumAsserted = 0;
for (Index = (InstancePtr->NumSlaveBits - 1); Index >= 0; Index--) {
if ((SlaveMask >> Index) & 0x1) {
/* this bit is asserted */
NumAsserted++;
}
}
/*
* Return an error if more than one slave is selected.
*/
if (NumAsserted > 1) {
return XST_SPI_TOO_MANY_SLAVES;
}
/*
* A single slave is either being selected or the incoming SlaveMask is
* zero, which means the slave is being deselected. Setup the value to
* be written to the slave select register as the inverse of the slave
* mask.
*/
InstancePtr->SlaveSelectReg = ~SlaveMask;
return XST_SUCCESS;
}
该函数做了些什么事情呢?它计算了一下选择了多少个从设备,如果SlaveMask中有多个1,则意味着从设备不止一个,就会报错输出“SPI的从设备太多了”的文字。否则,将SlaveMask取反后赋值给从选寄存器,实现片选,低电平有效。
写数据(发送数据)初始化
Test = 0x01;
for (Count = 0; Count < BUFFER_SIZE; Count++) {
WriteBuffer[Count] = (u8)(Count + Test);
ReadBuffer[Count] = 0;
}
这段代码是写缓存寄存器组WriteBuffer和读缓存寄存器组ReadBuffer的初始化。WriteBuffer和ReadBuffer是8*BUFFER_SIZE的寄存器组,BUFFER_SIZE在程序中设置为16。以Test = 8‘b0000_0001’为起始,步长为1,生成了16个8位二进制数,WriteBuffer和ReadBuffer的结构如下图:
XSpi_Transfer()函数
XSpi_Transfer(SpiInstancePtr, WriteBuffer, ReadBuffer, BUFFER_SIZE);
该函数的作用是,实现数据的传送,这是整个程序中最重要的函数之一。
函数原型为:
/*****************************************************************************/
/**
*
* Transfers the specified data on the SPI bus. If the SPI device is configured
* to be a master, this function initiates bus communication and sends/receives
* the data to/from the selected SPI slave. If the SPI device is configured to
* be a slave, this function prepares the data to be sent/received when selected
* by a master. For every byte sent, a byte is received.
*
* This function/driver operates in interrupt mode and polled mode.
* - In interrupt mode this function is non-blocking and the transfer is
* initiated by this function and completed by the interrupt service routine.
* - In polled mode this function is blocking and the control exits this
* function only after all the requested data is transferred.
*
* The caller has the option of providing two different buffers for send and
* receive, or one buffer for both send and receive, or no buffer for receive.
* The receive buffer must be at least as big as the send buffer to prevent
* unwanted memory writes. This implies that the byte count passed in as an
* argument must be the smaller of the two buffers if they differ in size.
* Here are some sample usages:
* <pre>
* XSpi_Transfer(InstancePtr, SendBuf, RecvBuf, ByteCount)
* The caller wishes to send and receive, and provides two different
* buffers for send and receive.
*
* XSpi_Transfer(InstancePtr, SendBuf, NULL, ByteCount)
* The caller wishes only to send and does not care about the received
* data. The driver ignores the received data in this case.
*
* XSpi_Transfer(InstancePtr, SendBuf, SendBuf, ByteCount)
* The caller wishes to send and receive, but provides the same buffer
* for doing both. The driver sends the data and overwrites the send
* buffer with received data as it transfers the data.
*
* XSpi_Transfer(InstancePtr, RecvBuf, RecvBuf, ByteCount)
* The caller wishes to only receive and does not care about sending
* data. In this case, the caller must still provide a send buffer, but
* it can be the same as the receive buffer if the caller does not care
* what it sends. The device must send N bytes of data if it wishes to
* receive N bytes of data.
* </pre>
* In interrupt mode, though this function takes a buffer as an argument, the
* driver can only transfer a limited number of bytes at time. It transfers only
* one byte at a time if there are no FIFOs, or it can transfer the number of
* bytes up to the size of the FIFO if FIFOs exist.
* - In interrupt mode a call to this function only starts the transfer, the
* subsequent transfer of the data is performed by the interrupt service
* routine until the entire buffer has been transferred.The status callback
* function is called when the entire buffer has been sent/received.
* - In polled mode this function is blocking and the control exits this
* function only after all the requested data is transferred.
*
* As a master, the SetSlaveSelect function must be called prior to this
* function.
*
* @param InstancePtr is a pointer to the XSpi instance to be worked on.
* @param SendBufPtr is a pointer to a buffer of data which is to be sent.
* This buffer must not be NULL.
* @param RecvBufPtr is a pointer to a buffer which will be filled with
* received data. This argument can be NULL if the caller does not
* wish to receive data.
* @param ByteCount contains the number of bytes to send/receive. The
* number of bytes received always equals the number of bytes sent.
*
* @return
* -XST_SUCCESS if the buffers are successfully handed off to the
* driver for transfer. Otherwise, returns:
* - XST_DEVICE_IS_STOPPED if the device must be started before
* transferring data.
* - XST_DEVICE_BUSY indicates that a data transfer is already in
* progress. This is determined by the driver.
* - XST_SPI_NO_SLAVE indicates the device is configured as a
* master and a slave has not yet been selected.
*
* @notes
*
* This function is not thread-safe. The higher layer software must ensure that
* no two threads are transferring data on the SPI bus at the same time.
*
******************************************************************************/
int XSpi_Transfer(XSpi *InstancePtr, u8 *SendBufPtr,
u8 *RecvBufPtr, unsigned int ByteCount)
{
u32 ControlReg;
u32 GlobalIntrReg;
u32 StatusReg;
u32 Data = 0;
u8 DataWidth;
/*
* The RecvBufPtr argument can be NULL.
*/
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(SendBufPtr != NULL);
Xil_AssertNonvoid(ByteCount > 0);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
if (InstancePtr->IsStarted != XIL_COMPONENT_IS_STARTED) {
return XST_DEVICE_IS_STOPPED;
}
/*
* Make sure there is not a transfer already in progress. No need to
* worry about a critical section here. Even if the Isr changes the bus
* flag just after we read it, a busy error is returned and the caller
* can retry when it gets the status handler callback indicating the
* transfer is done.
*/
if (InstancePtr->IsBusy) {
return XST_DEVICE_BUSY;
}
/*
* Save the Global Interrupt Enable Register.
*/
GlobalIntrReg = XSpi_IsIntrGlobalEnabled(InstancePtr);
/*
* Enter a critical section from here to the end of the function since
* state is modified, an interrupt is enabled, and the control register
* is modified (r/m/w).
*/
XSpi_IntrGlobalDisable(InstancePtr);
ControlReg = XSpi_GetControlReg(InstancePtr);
/*
* If configured as a master, be sure there is a slave select bit set
* in the slave select register. If no slaves have been selected, the
* value of the register will equal the mask. When the device is in
* loopback mode, however, no slave selects need be set.
*/
if (ControlReg & XSP_CR_MASTER_MODE_MASK) {
if ((ControlReg & XSP_CR_LOOPBACK_MASK) == 0) {
if (InstancePtr->SlaveSelectReg ==
InstancePtr->SlaveSelectMask) {
if (GlobalIntrReg == TRUE) {
/* Interrupt Mode of operation */
XSpi_IntrGlobalEnable(InstancePtr);
}
return XST_SPI_NO_SLAVE;
}
}
}
/*
* Set the busy flag, which will be cleared when the transfer
* is completely done.
*/
InstancePtr->IsBusy = TRUE;
/*
* Set up buffer pointers.
*/
InstancePtr->SendBufferPtr = SendBufPtr;
InstancePtr->RecvBufferPtr = RecvBufPtr;
InstancePtr->RequestedBytes = ByteCount;
InstancePtr->RemainingBytes = ByteCount;
DataWidth = InstancePtr->DataWidth;
/*
* Fill the DTR/FIFO with as many bytes as it will take (or as many as
* we have to send). We use the tx full status bit to know if the device
* can take more data. By doing this, the driver does not need to know
* the size of the FIFO or that there even is a FIFO. The downside is
* that the status register must be read each loop iteration.
*/
StatusReg = XSpi_GetStatusReg(InstancePtr);
while (((StatusReg & XSP_SR_TX_FULL_MASK) == 0) &&
(InstancePtr->RemainingBytes > 0)) {
if (DataWidth == XSP_DATAWIDTH_BYTE) {
/*
* Data Transfer Width is Byte (8 bit).
*/
Data = *InstancePtr->SendBufferPtr;
} else if (DataWidth == XSP_DATAWIDTH_HALF_WORD) {
/*
* Data Transfer Width is Half Word (16 bit).
*/
Data = *(u16 *)InstancePtr->SendBufferPtr;
} else if (DataWidth == XSP_DATAWIDTH_WORD){
/*
* Data Transfer Width is Word (32 bit).
*/
Data = *(u32 *)InstancePtr->SendBufferPtr;
}
XSpi_WriteReg(InstancePtr->BaseAddr, XSP_DTR_OFFSET, Data);
InstancePtr->SendBufferPtr += (DataWidth >> 3);
InstancePtr->RemainingBytes -= (DataWidth >> 3);
StatusReg = XSpi_GetStatusReg(InstancePtr);
}
/*
* Set the slave select register to select the device on the SPI before
* starting the transfer of data.
*/
XSpi_SetSlaveSelectReg(InstancePtr,
InstancePtr->SlaveSelectReg);
/*
* Start the transfer by no longer inhibiting the transmitter and
* enabling the device. For a master, this will in fact start the
* transfer, but for a slave it only prepares the device for a transfer
* that must be initiated by a master.
*/
ControlReg = XSpi_GetControlReg(InstancePtr);
ControlReg &= ~XSP_CR_TRANS_INHIBIT_MASK;
XSpi_SetControlReg(InstancePtr, ControlReg);
/*
* If the interrupts are enabled as indicated by Global Interrupt
* Enable Register, then enable the transmit empty interrupt to operate
* in Interrupt mode of operation.
*/
if (GlobalIntrReg == TRUE) { /* Interrupt Mode of operation */
/*
* Enable the transmit empty interrupt, which we use to
* determine progress on the transmission.
*/
XSpi_IntrEnable(InstancePtr, XSP_INTR_TX_EMPTY_MASK);
/*
* End critical section.
*/
XSpi_IntrGlobalEnable(InstancePtr);
} else { /* Polled mode of operation */
/*
* If interrupts are not enabled, poll the status register to
* Transmit/Receive SPI data.
*/
while(ByteCount > 0) {
/*
* Wait for the transfer to be done by polling the
* Transmit empty status bit
*/
do {
StatusReg = XSpi_IntrGetStatus(InstancePtr);
} while ((StatusReg & XSP_INTR_TX_EMPTY_MASK) == 0);
XSpi_IntrClear(InstancePtr,XSP_INTR_TX_EMPTY_MASK);
/*
* A transmit has just completed. Process received data
* and check for more data to transmit. Always inhibit
* the transmitter while the transmit register/FIFO is
* being filled, or make sure it is stopped if we're
* done.
*/
ControlReg = XSpi_GetControlReg(InstancePtr);
XSpi_SetControlReg(InstancePtr, ControlReg |
XSP_CR_TRANS_INHIBIT_MASK);
/*
* First get the data received as a result of the
* transmit that just completed. We get all the data
* available by reading the status register to determine
* when the Receive register/FIFO is empty. Always get
* the received data, but only fill the receive
* buffer if it points to something (the upper layer
* software may not care to receive data).
*/
StatusReg = XSpi_GetStatusReg(InstancePtr);
while ((StatusReg & XSP_SR_RX_EMPTY_MASK) == 0) {
Data = XSpi_ReadReg(InstancePtr->BaseAddr,
XSP_DRR_OFFSET);
if (DataWidth == XSP_DATAWIDTH_BYTE) {
/*
* Data Transfer Width is Byte (8 bit).
*/
if(InstancePtr->RecvBufferPtr != NULL) {
*InstancePtr->RecvBufferPtr++ =
(u8)Data;
}
} else if (DataWidth ==
XSP_DATAWIDTH_HALF_WORD) {
/*
* Data Transfer Width is Half Word
* (16 bit).
*/
if (InstancePtr->RecvBufferPtr != NULL){
*(u16 *)InstancePtr->RecvBufferPtr =
(u16)Data;
InstancePtr->RecvBufferPtr += 2;
}
} else if (DataWidth == XSP_DATAWIDTH_WORD) {
/*
* Data Transfer Width is Word (32 bit).
*/
if (InstancePtr->RecvBufferPtr != NULL){
*(u32 *)InstancePtr->RecvBufferPtr =
Data;
InstancePtr->RecvBufferPtr += 4;
}
}
InstancePtr->Stats.BytesTransferred +=
(DataWidth >> 3);
ByteCount -= (DataWidth >> 3);
StatusReg = XSpi_GetStatusReg(InstancePtr);
}
if (InstancePtr->RemainingBytes > 0) {
/*
* Fill the DTR/FIFO with as many bytes as it
* will take (or as many as we have to send).
* We use the Tx full status bit to know if the
* device can take more data.
* By doing this, the driver does not need to
* know the size of the FIFO or that there even
* is a FIFO.
* The downside is that the status must be read
* each loop iteration.
*/
StatusReg = XSpi_GetStatusReg(InstancePtr);
while(((StatusReg & XSP_SR_TX_FULL_MASK)== 0) &&
(InstancePtr->RemainingBytes > 0)) {
if (DataWidth == XSP_DATAWIDTH_BYTE) {
/*
* Data Transfer Width is Byte
* (8 bit).
*/
Data = *InstancePtr->
SendBufferPtr;
} else if (DataWidth ==
XSP_DATAWIDTH_HALF_WORD) {
/*
* Data Transfer Width is Half
* Word (16 bit).
*/
Data = *(u16 *)InstancePtr->
SendBufferPtr;
} else if (DataWidth ==
XSP_DATAWIDTH_WORD) {
/*
* Data Transfer Width is Word
* (32 bit).
*/
Data = *(u32 *)InstancePtr->
SendBufferPtr;
}
XSpi_WriteReg(InstancePtr->BaseAddr,
XSP_DTR_OFFSET, Data);
InstancePtr->SendBufferPtr +=
(DataWidth >> 3);
InstancePtr->RemainingBytes -=
(DataWidth >> 3);
StatusReg = XSpi_GetStatusReg(
InstancePtr);
}
/*
* Start the transfer by not inhibiting the
* transmitter any longer.
*/
ControlReg = XSpi_GetControlReg(InstancePtr);
ControlReg &= ~XSP_CR_TRANS_INHIBIT_MASK;
XSpi_SetControlReg(InstancePtr, ControlReg);
}
}
/*
* Stop the transfer (hold off automatic sending) by inhibiting
* the transmitter.
*/
ControlReg = XSpi_GetControlReg(InstancePtr);
XSpi_SetControlReg(InstancePtr,
ControlReg | XSP_CR_TRANS_INHIBIT_MASK);
/*
* Select the slave on the SPI bus when the transfer is
* complete, this is necessary for some SPI devices,
* such as serial EEPROMs work correctly as chip enable
* may be connected to slave select
*/
XSpi_SetSlaveSelectReg(InstancePtr,
InstancePtr->SlaveSelectMask);
InstancePtr->IsBusy = FALSE;
}
return XST_SUCCESS;
}
实际上,那本程序中的中断关联函数基本没做什么事情,只是一个形式,具体的内容全部是在这个数据传输函数XSpi_Transfer()中实现的。
数据比较程序
for (Count = 0; Count < BUFFER_SIZE; Count++) {
if (WriteBuffer[Count] != ReadBuffer[Count]) {
return XST_FAILURE;
}
}
这段程序实现最后的数据比较,通过将发送出去的的数据和接收回来的数据相比较,判断发送接收过程是否正确。
值得注意的是,在测试该程序的时候,需要使用杜邦线将映射到ZYNQ上的MOSI和MISO两个引脚短接,从而完成一个数据的环回。这样既可以使用物理存在的示波器、也可以在BD中使用ILA在线逻辑分析器,观测端口的波形。
波形图中,MOSI和MISO波形是一样的,都是对应于每个SCK周期的0/1bit。
修改Test值为0xff,再次运行,在波形图中手动算出传输的数据,得到如下图。
证明数据通过SPI端口传输正确。文章来源:https://www.toymoban.com/news/detail-660200.html
完。文章来源地址https://www.toymoban.com/news/detail-660200.html
到了这里,关于FPGA嵌入式开发一些Xilinx SDK库函数的理解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!