书接上文:
I2C控制器练习(1)_NoNoUnknow的博客-CSDN博客
SPI协议与FPGA的自动升级和多启动-CSDN博客
本文主要做一些基本知识的补充和工程参考。
写IIC需要注意的事情:
1.查询芯片手册获得slave地址,以及寄存器地址(或者叫寄存器命令)。
2.约定好每次读/写的数据长度。
对于这类芯片而言,读取一般都使用了顺序读,即读完一个地址自动读下一个地址,所以约定好长度通过no ack来打断很重要。
(SPI设备是通过停止输入时钟和拉高CS_N来打断的)
FLASH使用笔记~M25P64_m25p16_NoNoUnknow的博客-CSDN博客
操作时序:写(单字节写入)
随机读
顺序读
页写:(不是每个设备都有)
所有 I2C 设备均支持单字节数据写入操作,但只有部分 I2C 设备支持页写操作; 且支持页写操作的设备,一次页写操作写入的字节数不能超过设备单页包含的存储单元
本章节使用的 AT24CXX 系列的 EEPROM 存储芯片,单页存储单元个数为 32 个,一 次页写操作只能写入 32 字节数据。
PS端的API可以在板载支持包里找到。
SDK可以直接在简历平台的界面找到,Vitis则是被整合到了spr里。
BSP板级支持包(board support package)-CSDN博客
一些API。
主模式下中断驱动的接收:XIicPs_MasterRecv()
* 该函数在主模式下启动中断驱动的接收。
* 它设置传输大小寄存器,以便从机可以向我们发送数据。
* 其余工作由中断处理程序管理。
/*****************************************************************************/
/**
* @brief
* This function initiates an interrupt-driven receive in master mode.
*
* It sets the transfer size register so the slave can send data to us.
* The rest of the work is managed by interrupt handler.
*
* @param InstancePtr is a pointer to the XIicPs instance.
* @param MsgPtr is the pointer to the receive buffer.
* @param ByteCount is the number of bytes to be received.
* @param SlaveAddr is the address of the slave we are receiving from.
*
* @return None.
*
* @note This receive routine is for interrupt-driven transfer only.
*
****************************************************************************/
void XIicPs_MasterRecv(XIicPs *InstancePtr, u8 *MsgPtr, s32 ByteCount,
u16 SlaveAddr)
{
UINTPTR BaseAddr;
/*
* Assert validates the input arguments.
*/
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(MsgPtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY);
Xil_AssertVoid((u16)XIICPS_ADDR_MASK >= SlaveAddr);
BaseAddr = InstancePtr->Config.BaseAddress;
InstancePtr->RecvBufferPtr = MsgPtr;
InstancePtr->RecvByteCount = ByteCount;
InstancePtr->SendBufferPtr = NULL;
InstancePtr->IsSend = 0;
#if defined (XCLOCKING)
if (InstancePtr->IsClkEnabled == 0) {
Xil_ClockEnable(InstancePtr->Config.RefClk);
InstancePtr->IsClkEnabled = 1;
}
#endif
if ((ByteCount > XIICPS_FIFO_DEPTH) ||
((InstancePtr->IsRepeatedStart) !=0))
{
XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET,
XIicPs_ReadReg(BaseAddr, (u32)XIICPS_CR_OFFSET) |
(u32)XIICPS_CR_HOLD_MASK);
}
/*
* Initialize for a master receiving role.
*/
(void)XIicPs_SetupMaster(InstancePtr, RECVING_ROLE);
/*
* Setup the transfer size register so the slave knows how much
* to send to us.
*/
if (ByteCount > (s32)XIICPS_MAX_TRANSFER_SIZE) {
XIicPs_WriteReg(BaseAddr, XIICPS_TRANS_SIZE_OFFSET,
XIICPS_MAX_TRANSFER_SIZE);
InstancePtr->CurrByteCount = (s32)XIICPS_MAX_TRANSFER_SIZE;
InstancePtr->UpdateTxSize = 1;
}else {
InstancePtr->CurrByteCount = ByteCount;
XIicPs_WriteReg(BaseAddr, (u32)(XIICPS_TRANS_SIZE_OFFSET),
(u32)ByteCount);
InstancePtr->UpdateTxSize = 0;
}
/*
* Clear the interrupt status register before use it to monitor.
*/
XIicPs_WriteReg(BaseAddr, XIICPS_ISR_OFFSET, XIICPS_IXR_ALL_INTR_MASK);
XIicPs_EnableInterrupts(BaseAddr,
(u32)XIICPS_IXR_NACK_MASK | (u32)XIICPS_IXR_DATA_MASK |
(u32)XIICPS_IXR_RX_OVR_MASK | (u32)XIICPS_IXR_COMP_MASK |
(u32)XIICPS_IXR_ARB_LOST_MASK | (u32)XIICPS_IXR_TO_MASK);
/*
* Do the address transfer to signal the slave.
*/
XIicPs_WriteReg(BaseAddr, XIICPS_ADDR_OFFSET, (u32)SlaveAddr);
}
主模式下中断驱动的发送:XIicPs_MasterSend()
* 该函数在主模式下启动中断驱动的发送。
* 它尝试发送第一个 FIFO 满的数据,
* 然后让中断处理程序处理其余数据(如果有)。
#define TX_MAX_LOOPCNT 1000000U /**< Used to wait in polled function */
/************************** Function Prototypes ******************************/
/************************* Variable Definitions *****************************/
/*****************************************************************************/
/**
* @brief
* This function initiates an interrupt-driven send in master mode.
*
* It tries to send the first FIFO-full of data, then lets the interrupt
* handler to handle the rest of the data if there is any.
*
* @param InstancePtr is a pointer to the XIicPs instance.
* @param MsgPtr is the pointer to the send buffer.
* @param ByteCount is the number of bytes to be sent.
* @param SlaveAddr is the address of the slave we are sending to.
*
* @return None.
*
* @note This send routine is for interrupt-driven transfer only.
*
****************************************************************************/
void XIicPs_MasterSend(XIicPs *InstancePtr, u8 *MsgPtr, s32 ByteCount,
u16 SlaveAddr)
{
UINTPTR BaseAddr;
u32 Platform = XGetPlatform_Info();
/*
* Assert validates the input arguments.
*/
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(MsgPtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY);
Xil_AssertVoid((u16)XIICPS_ADDR_MASK >= SlaveAddr);
BaseAddr = InstancePtr->Config.BaseAddress;
InstancePtr->SendBufferPtr = MsgPtr;
InstancePtr->SendByteCount = ByteCount;
InstancePtr->RecvBufferPtr = NULL;
InstancePtr->IsSend = 1;
#if defined (XCLOCKING)
if (InstancePtr->IsClkEnabled == 0) {
Xil_ClockEnable(InstancePtr->Config.RefClk);
InstancePtr->IsClkEnabled = 1;
}
#endif
/*
* Set repeated start if sending more than FIFO of data.
*/
if (((InstancePtr->IsRepeatedStart) != 0)||
(ByteCount > XIICPS_FIFO_DEPTH)) {
XIicPs_WriteReg(BaseAddr, (u32)XIICPS_CR_OFFSET,
XIicPs_ReadReg(BaseAddr, (u32)XIICPS_CR_OFFSET) |
(u32)XIICPS_CR_HOLD_MASK);
}
/*
* Setup as a master sending role.
*/
(void)XIicPs_SetupMaster(InstancePtr, SENDING_ROLE);
(void)TransmitFifoFill(InstancePtr);
/*
* Clear the interrupt status register before use it to monitor.
*/
XIicPs_WriteReg(BaseAddr, XIICPS_ISR_OFFSET, XIICPS_IXR_ALL_INTR_MASK);
XIicPs_EnableInterrupts(BaseAddr,
(u32)XIICPS_IXR_NACK_MASK | (u32)XIICPS_IXR_COMP_MASK |
(u32)XIICPS_IXR_ARB_LOST_MASK | (u32)XIICPS_IXR_TO_MASK);
/*
* Do the address transfer to notify the slave.
*/
XIicPs_WriteReg(BaseAddr, XIICPS_ADDR_OFFSET, (u32)SlaveAddr);
/* Clear the Hold bit in ZYNQ if receive byte count is less than
* the FIFO depth to get the completion interrupt properly.
*/
if ((ByteCount < XIICPS_FIFO_DEPTH) && (Platform == (u32)XPLAT_ZYNQ))
{
XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET,
XIicPs_ReadReg(BaseAddr, (u32)XIICPS_CR_OFFSET) &
(~(u32)XIICPS_CR_HOLD_MASK));
}
}
参考资料:
Zynq SDK开发之外设信息描述 - 简书 (jianshu.com)
zynq7000系列PS端GPIO初始化函数XGpioPs_LookupConfig()和XGpioPs_CfgInitialize()详解-CSDN博客
Xilinx sdk 学习笔记 之 IIC(二)-CSDN博客
配置查找函数:XIicPs_LookupConfig(DeviceId)
* 根据唯一设备ID查找设备配置表
* 表中包含系统中每个设备的配置信息。
DeviceId需要在xparameters.h中找到。
/************************** Constant Definitions *****************************/
/**************************** Type Definitions *******************************/
/***************** Macros (Inline Functions) Definitions *********************/
/************************** Function Prototypes ******************************/
/************************** Variable Definitions *****************************/
/*****************************************************************************/
/**
*
* @brief
* 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 xiicps.h for the definition of
* XIicPs_Config.
*
* @note None.
*
******************************************************************************/
XIicPs_Config *XIicPs_LookupConfig(u16 DeviceId)
{
XIicPs_Config *CfgPtr = NULL;
s32 Index;
for (Index = 0; Index < XPAR_XIICPS_NUM_INSTANCES; Index++) {
if (XIicPs_ConfigTable[Index].DeviceId == DeviceId) {
CfgPtr = &XIicPs_ConfigTable[Index];
break;
}
}
return (XIicPs_Config *)CfgPtr;
}
/** @} */
初始化函数:XIicPs_CfgInitialize
* 初始化特定的 XIicPs 实例,以便驱动程序可供使用。
* 初始化后设备的状态为:
* - 设备已禁用
* - 从机模式
ConfigPtr即Lookup函数的返回值。
/************************** Variable Definitions *****************************/
/*****************************************************************************/
/**
*
* @brief
* Initializes a specific XIicPs instance such that the driver is ready to use.
*
* The state of the device after initialization is:
* - Device is disabled
* - Slave mode
*
* @param InstancePtr is a pointer to the XIicPs instance.
* @param ConfigPtr is a reference to a structure containing information
* about a specific IIC device. This function initializes an
* InstancePtr object for a specific device specified by the
* contents of Config.
* @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
* ConfigPtr->BaseAddress for this parameter, passing the physical
* address instead.
*
* @return The return value is XST_SUCCESS if successful.
*
* @note None.
*
******************************************************************************/
s32 XIicPs_CfgInitialize(XIicPs *InstancePtr, XIicPs_Config *ConfigPtr,
u32 EffectiveAddr)
{
/*
* Assert validates the input arguments.
*/
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(ConfigPtr != NULL);
/*
* Set some default values.
*/
InstancePtr->Config.DeviceId = ConfigPtr->DeviceId;
InstancePtr->Config.BaseAddress = EffectiveAddr;
InstancePtr->Config.InputClockHz = ConfigPtr->InputClockHz;
#if defined (XCLOCKING)
InstancePtr->Config.RefClk = ConfigPtr->RefClk;
InstancePtr->IsClkEnabled = 0;
#endif
InstancePtr->StatusHandler = StubHandler;
InstancePtr->CallBackRef = NULL;
InstancePtr->IsReady = (u32)XIL_COMPONENT_IS_READY;
/*
* Reset the IIC 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.
*/
XIicPs_Reset(InstancePtr);
/*
* Keep a copy of what options this instance has.
*/
InstancePtr->Options = XIicPs_GetOptions(InstancePtr);
/* Initialize repeated start flag to 0 */
InstancePtr->IsRepeatedStart = 0;
return (s32)XST_SUCCESS;
}
速率配置函数 XIicPs_SetSClk
* 该函数设置IIC 器件的串行时钟速率。
* 在设置这些设备选项之前,设备必须处于空闲状态而不是忙于传输数据。
* 数据速率由控制寄存器中的值设置。
* 确定正确寄存器值的公式为:
* Fscl = Fpclk/(22 x (divisor_a+1) x (divisor_b+1))
* 有关设置串行时钟速率的完整说明,请参阅硬件数据表。
/*****************************************************************************/
/**
*
* @brief
* This function sets the serial clock rate for the IIC device. The device
* must be idle rather than busy transferring data before setting these device
* options.
*
* The data rate is set by values in the control register. The formula for
* determining the correct register values is:
* Fscl = Fpclk/(22 x (divisor_a+1) x (divisor_b+1))
* See the hardware data sheet for a full explanation of setting the serial
* clock rate.
*
* @param InstancePtr is a pointer to the XIicPs instance.
* @param FsclHz is the clock frequency in Hz. The two most common clock
* rates are 100KHz and 400KHz.
*
* @return
* - XST_SUCCESS if options are successfully set.
* - XST_DEVICE_IS_STARTED if the device is currently transferring
* data. The transfer must complete or be aborted before setting
* options.
* - XST_FAILURE if the Fscl frequency can not be set.
*
* @note The clock can not be faster than the input clock divide by 22.
*
******************************************************************************/
s32 XIicPs_SetSClk(XIicPs *InstancePtr, u32 FsclHz)
{
u32 Div_a;
u32 Div_b;
u32 ActualFscl;
u32 Temp;
u32 TempLimit;
u32 LastError;
u32 BestError;
u32 CurrentError;
u32 ControlReg;
u32 CalcDivA;
u32 CalcDivB;
u32 BestDivA;
u32 BestDivB;
u32 FsclHzVar = FsclHz;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY);
Xil_AssertNonvoid(FsclHzVar > 0U);
if (0U != XIicPs_In32((InstancePtr->Config.BaseAddress) +
XIICPS_TRANS_SIZE_OFFSET)) {
return (s32)XST_DEVICE_IS_STARTED;
}
/*
* Assume Div_a is 0 and calculate (divisor_a+1) x (divisor_b+1).
*/
Temp = (InstancePtr->Config.InputClockHz) / ((u32)22U * FsclHzVar);
/*
* If the answer is negative or 0, the Fscl input is out of range.
*/
if ((u32)(0U) == Temp) {
return (s32)XST_FAILURE;
}
/*
* If frequency 400KHz is selected, 384.6KHz should be set.
* If frequency 100KHz is selected, 90KHz should be set.
* This is due to a hardware limitation.
*/
if(FsclHzVar > (u32)384600U) {
FsclHzVar = (u32)384600U;
}
if((FsclHzVar <= (u32)100000U) && (FsclHzVar > (u32)90000U)) {
FsclHzVar = (u32)90000U;
}
/*
* TempLimit helps in iterating over the consecutive value of Temp to
* find the closest clock rate achievable with divisors.
* Iterate over the next value only if fractional part is involved.
*/
TempLimit = (((InstancePtr->Config.InputClockHz) %
((u32)22 * FsclHzVar)) != (u32)0x0U) ?
(Temp + (u32)1U) : Temp;
BestError = FsclHzVar;
BestDivA = 0U;
BestDivB = 0U;
for ( ; Temp <= TempLimit ; Temp++)
{
LastError = FsclHzVar;
CalcDivA = 0U;
CalcDivB = 0U;
for (Div_b = 0U; Div_b < 64U; Div_b++) {
Div_a = Temp / (Div_b + 1U);
if (Div_a != 0U){
Div_a = Div_a - (u32)1U;
}
if (Div_a > 3U){
continue;
}
ActualFscl = (InstancePtr->Config.InputClockHz) /
(22U * (Div_a + 1U) * (Div_b + 1U));
if (ActualFscl > FsclHzVar){
CurrentError = (ActualFscl - FsclHzVar);}
else{
CurrentError = (FsclHzVar - ActualFscl);}
if (LastError > CurrentError) {
CalcDivA = Div_a;
CalcDivB = Div_b;
LastError = CurrentError;
}
}
/*
* Used to capture the best divisors.
*/
if (LastError < BestError) {
BestError = LastError;
BestDivA = CalcDivA;
BestDivB = CalcDivB;
}
}
/*
* Read the control register and mask the Divisors.
*/
ControlReg = XIicPs_ReadReg(InstancePtr->Config.BaseAddress,
(u32)XIICPS_CR_OFFSET);
ControlReg &= ~((u32)XIICPS_CR_DIV_A_MASK | (u32)XIICPS_CR_DIV_B_MASK);
ControlReg |= (BestDivA << XIICPS_CR_DIV_A_SHIFT) |
(BestDivB << XIICPS_CR_DIV_B_SHIFT);
XIicPs_WriteReg(InstancePtr->Config.BaseAddress, (u32)XIICPS_CR_OFFSET,
ControlReg);
return (s32)XST_SUCCESS;
}
/*****************************************************************************/
轮询模式接收:XIicPs_MasterRecvPolled
* 该函数在主模式下启动轮询模式接收。
* 它重复设置传输大小寄存器,以便从机可以
* 向我们发送数据。它轮询数据寄存器以获取要输入的数据。
* 如果主机由于仲裁失败而无法读取数据,将返回 :仲裁失败状态。
* 如果从机无法向我们发送数据,则会因超时而失败。
/*****************************************************************************/
/**
* @brief
* This function initiates a polled mode receive in master mode.
*
* It repeatedly sets the transfer size register so the slave can
* send data to us. It polls the data register for data to come in.
* If master fails to read data due arbitration lost, will return
* with arbitration lost status.
* If slave fails to send us data, it fails with time out.
*
* @param InstancePtr is a pointer to the XIicPs instance.
* @param MsgPtr is the pointer to the receive buffer.
* @param ByteCount is the number of bytes to be received.
* @param SlaveAddr is the address of the slave we are receiving from.
*
* @return
* - XST_SUCCESS if everything went well.
* - XST_FAILURE if timed out.
* - XST_IIC_ARB_LOST if arbitration lost
*
* @note This receive routine is for polled mode transfer only.
*
****************************************************************************/
s32 XIicPs_MasterRecvPolled(XIicPs *InstancePtr, u8 *MsgPtr,
s32 ByteCount, u16 SlaveAddr)
{
u32 IntrStatusReg;
u32 Intrs;
UINTPTR BaseAddr;
s32 Result;
s32 IsHold;
s32 UpdateTxSize = 0;
s32 ByteCountVar = ByteCount;
u32 Platform;
/*
* Assert validates the input arguments.
*/
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(MsgPtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY);
Xil_AssertNonvoid((u16)XIICPS_ADDR_MASK >= SlaveAddr);
BaseAddr = InstancePtr->Config.BaseAddress;
InstancePtr->RecvBufferPtr = MsgPtr;
InstancePtr->RecvByteCount = ByteCountVar;
#if defined (XCLOCKING)
if (InstancePtr->IsClkEnabled == 0) {
Xil_ClockEnable(InstancePtr->Config.RefClk);
InstancePtr->IsClkEnabled = 1;
}
#endif
Platform = XGetPlatform_Info();
if((ByteCountVar > XIICPS_FIFO_DEPTH) ||
((InstancePtr->IsRepeatedStart) !=0))
{
XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET,
XIicPs_ReadReg(BaseAddr, (u32)XIICPS_CR_OFFSET) |
(u32)XIICPS_CR_HOLD_MASK);
IsHold = 1;
} else {
IsHold = 0;
}
(void)XIicPs_SetupMaster(InstancePtr, RECVING_ROLE);
/*
* Clear the interrupt status register before use it to monitor.
*/
IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET);
XIicPs_WriteReg(BaseAddr, XIICPS_ISR_OFFSET, IntrStatusReg);
/*
* Set up the transfer size register so the slave knows how much
* to send to us.
*/
if (ByteCountVar > (s32)XIICPS_MAX_TRANSFER_SIZE) {
XIicPs_WriteReg(BaseAddr, XIICPS_TRANS_SIZE_OFFSET,
XIICPS_MAX_TRANSFER_SIZE);
ByteCountVar = (s32)XIICPS_MAX_TRANSFER_SIZE;
UpdateTxSize = 1;
}else {
XIicPs_WriteReg(BaseAddr, XIICPS_TRANS_SIZE_OFFSET,
ByteCountVar);
}
XIicPs_WriteReg(BaseAddr, XIICPS_ADDR_OFFSET, SlaveAddr);
/*
* Intrs keeps all the error-related interrupts.
*/
Intrs = (u32)XIICPS_IXR_ARB_LOST_MASK | (u32)XIICPS_IXR_RX_OVR_MASK |
(u32)XIICPS_IXR_RX_UNF_MASK | (u32)XIICPS_IXR_NACK_MASK;
/*
* Poll the interrupt status register to find the errors.
*/
IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET);
while ((InstancePtr->RecvByteCount > 0) &&
((IntrStatusReg & Intrs) == 0U)) {
while ((XIicPs_RxDataValid(InstancePtr)) != 0U) {
if ((InstancePtr->RecvByteCount <
XIICPS_DATA_INTR_DEPTH) && (IsHold != 0) &&
(InstancePtr->IsRepeatedStart == 0) &&
(UpdateTxSize == 0)) {
IsHold = 0;
XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET,
XIicPs_ReadReg(BaseAddr,
XIICPS_CR_OFFSET) &
(~XIICPS_CR_HOLD_MASK));
}
XIicPs_RecvByte(InstancePtr);
ByteCountVar --;
if (Platform == (u32)XPLAT_ZYNQ) {
if ((UpdateTxSize != 0) &&
(ByteCountVar == (XIICPS_FIFO_DEPTH + 1))) {
break;
}
}
}
if (Platform == (u32)XPLAT_ZYNQ) {
if ((UpdateTxSize != 0) &&
(ByteCountVar == (XIICPS_FIFO_DEPTH + 1))) {
/* wait while fifo is full */
while (XIicPs_RxFIFOFull(InstancePtr, ByteCountVar) != 0U) { ;
}
if ((InstancePtr->RecvByteCount - XIICPS_FIFO_DEPTH) >
(s32)XIICPS_MAX_TRANSFER_SIZE) {
XIicPs_WriteReg(BaseAddr,
XIICPS_TRANS_SIZE_OFFSET,
XIICPS_MAX_TRANSFER_SIZE);
ByteCountVar = (s32)XIICPS_MAX_TRANSFER_SIZE +
XIICPS_FIFO_DEPTH;
} else {
XIicPs_WriteReg(BaseAddr,
XIICPS_TRANS_SIZE_OFFSET,
InstancePtr->RecvByteCount -
XIICPS_FIFO_DEPTH);
UpdateTxSize = 0;
ByteCountVar = InstancePtr->RecvByteCount;
}
}
} else {
if ((InstancePtr->RecvByteCount > 0) && (ByteCountVar == 0)) {
/*
* Clear the interrupt status register before use it to
* monitor.
*/
IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET);
XIicPs_WriteReg(BaseAddr, XIICPS_ISR_OFFSET, IntrStatusReg);
XIicPs_WriteReg(BaseAddr, XIICPS_ADDR_OFFSET, SlaveAddr);
if ((InstancePtr->RecvByteCount) >
(s32)XIICPS_MAX_TRANSFER_SIZE) {
XIicPs_WriteReg(BaseAddr,
XIICPS_TRANS_SIZE_OFFSET,
XIICPS_MAX_TRANSFER_SIZE);
ByteCountVar = (s32)XIICPS_MAX_TRANSFER_SIZE;
} else {
XIicPs_WriteReg(BaseAddr,
XIICPS_TRANS_SIZE_OFFSET,
InstancePtr->RecvByteCount);
UpdateTxSize = 0;
ByteCountVar = InstancePtr->RecvByteCount;
}
}
}
IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET);
}
if (InstancePtr->IsRepeatedStart == 0) {
XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET,
XIicPs_ReadReg(BaseAddr,XIICPS_CR_OFFSET) &
(~XIICPS_CR_HOLD_MASK));
}
if ((IntrStatusReg & Intrs) != 0U) {
if ((IntrStatusReg & XIICPS_IXR_ARB_LOST_MASK) != 0U) {
Result = (s32) XST_IIC_ARB_LOST;
} else {
Result = (s32)XST_FAILURE;
}
} else {
Result = (s32)XST_SUCCESS;
}
return Result;
}
轮询模式发送:XIicPs_MasterSendPolled
* 该函数在主模式下启动轮询模式发送。
* 它将数据发送到 FIFO 并等待从机接收它们。
* 如果主设备由于仲裁丢失而无法发送数据,将停止传输
* 并处于仲裁丢失状态
* 如果从设备无法从 FIFO 中删除数据,则发送失败并
* 超时。
/*****************************************************************************/
/**
* @brief
* This function initiates a polled mode send in master mode.
*
* It sends data to the FIFO and waits for the slave to pick them up.
* If master fails to send data due arbitration lost, will stop transfer
* and with arbitration lost status
* If slave fails to remove data from FIFO, the send fails with
* time out.
*
* @param InstancePtr is a pointer to the XIicPs instance.
* @param MsgPtr is the pointer to the send buffer.
* @param ByteCount is the number of bytes to be sent.
* @param SlaveAddr is the address of the slave we are sending to.
*
* @return
* - XST_SUCCESS if everything went well.
* - XST_FAILURE if timed out.
* - XST_IIC_ARB_LOST if arbitration lost
*
* @note This send routine is for polled mode transfer only.
*
****************************************************************************/
s32 XIicPs_MasterSendPolled(XIicPs *InstancePtr, u8 *MsgPtr,
s32 ByteCount, u16 SlaveAddr)
{
u32 IntrStatusReg;
u32 StatusReg;
UINTPTR BaseAddr;
u32 Intrs;
s32 Status = (s32)XST_FAILURE;
u32 timeout = 0;
_Bool Value;
/*
* Assert validates the input arguments.
*/
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(MsgPtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == (u32)XIL_COMPONENT_IS_READY);
Xil_AssertNonvoid((u16)XIICPS_ADDR_MASK >= SlaveAddr);
#if defined (XCLOCKING)
if (InstancePtr->IsClkEnabled == 0) {
Xil_ClockEnable(InstancePtr->Config.RefClk);
InstancePtr->IsClkEnabled = 1;
}
#endif
BaseAddr = InstancePtr->Config.BaseAddress;
InstancePtr->SendBufferPtr = MsgPtr;
InstancePtr->SendByteCount = ByteCount;
if (((InstancePtr->IsRepeatedStart) != 0) ||
(ByteCount > XIICPS_FIFO_DEPTH)) {
XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET,
XIicPs_ReadReg(BaseAddr, (u32)XIICPS_CR_OFFSET) |
(u32)XIICPS_CR_HOLD_MASK);
}
(void)XIicPs_SetupMaster(InstancePtr, SENDING_ROLE);
/*
* Intrs keeps all the error-related interrupts.
*/
Intrs = (u32)XIICPS_IXR_ARB_LOST_MASK | (u32)XIICPS_IXR_TX_OVR_MASK |
(u32)XIICPS_IXR_NACK_MASK;
/*
* Clear the interrupt status register before use it to monitor.
*/
IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET);
XIicPs_WriteReg(BaseAddr, XIICPS_ISR_OFFSET, IntrStatusReg);
/*
* Transmit first FIFO full of data.
*/
(void)TransmitFifoFill(InstancePtr);
XIicPs_WriteReg(BaseAddr, XIICPS_ADDR_OFFSET, (u32)SlaveAddr);
IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET);
/*
* Continue sending as long as there is more data and
* there are no errors.
*/
Value = ((InstancePtr->SendByteCount > (s32)0) &&
((IntrStatusReg & Intrs) == (u32)0U));
while (Value != FALSE) {
StatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_SR_OFFSET);
/*
* Wait until transmit FIFO is empty.
*/
if ((StatusReg & XIICPS_SR_TXDV_MASK) != 0U) {
IntrStatusReg = XIicPs_ReadReg(BaseAddr,
XIICPS_ISR_OFFSET);
Value = ((InstancePtr->SendByteCount > (s32)0) &&
((IntrStatusReg & Intrs) == (u32)0U));
continue;
}
/*
* Send more data out through transmit FIFO.
*/
(void)TransmitFifoFill(InstancePtr);
Value = ((InstancePtr->SendByteCount > (s32)0) &&
((IntrStatusReg & Intrs) == (u32)0U));
}
/*
* Check for completion of transfer.
*/
while ((IntrStatusReg & XIICPS_IXR_COMP_MASK) != XIICPS_IXR_COMP_MASK){
IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET);
/*
* If there is an error, tell the caller.
*/
if ((IntrStatusReg & Intrs) != 0U) {
if ((IntrStatusReg & XIICPS_IXR_ARB_LOST_MASK) != 0U) {
Status = (s32) XST_IIC_ARB_LOST;
}
break;
}
/*
* Timeout if stuck for more than 1 second
*/
usleep(1);
timeout++;
if (timeout == TX_MAX_LOOPCNT) {
break;
}
}
if (InstancePtr->IsRepeatedStart == 0) {
XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET,
XIicPs_ReadReg(BaseAddr,XIICPS_CR_OFFSET) &
(~XIICPS_CR_HOLD_MASK));
}
/* Set the Status for XST_SUCCESS */
if (((IntrStatusReg & Intrs) == 0U) && (timeout != TX_MAX_LOOPCNT)) {
Status = (s32)XST_SUCCESS;
}
return Status;
}
头文件导入:
#include "xiicps.h"
The xiicps.h header file is an implementation of IIC driver in the PS block.
#include "xparameters.h"
里面列出了各个外设对应的地址、Device_ID、中断号,等等。
运行步骤:
1.通过XIicPs_LookupConfig查找到设备的配置列表;
2.通过配置列表使用XIicPs_CfgInitialize进行初始化配置;
3.配置完成后使用XIicPs_SetSClk配置速率;
4.使用XIicPs_MasterSendPolled向指定的实例、从机发送寄存器地址
(MsgPtr is the pointer to the send buffer.)部分设备可能会将此项作为操作命令使用。
5.假如是读指令则还需要使用XIicPs_MasterRecvPolled来接收数据。
其中XIicPs_MasterRecvPolled API的MsgPtr是用以配置接收缓存的,可以定义合适的数组(寄存器)来存储它。