这个外设的功能已经在zynq sdk裸机下验证通过,如果这个外设没有用到中断,我们可以通过linux下映射寄存器的方法,将这个裸机下的程序直接移植到linux下使用,这样就可以省去硬件开发驱动的工作。
一,linux内存映射
二,原理图
三,linux应用层代码解析
1,pl_gpio.h
#ifndef PL_GPIO_H #define PL_GPIO_H #includetypedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; #define XGPIO_CHAN_OFFSET 8 #define XGPIO_DATA_OFFSET 0x0 #define XGPIO_TRI_OFFSET 0x4 #define XGPIO_DATA2_OFFSET 0x8 #define XGPIO_TRI2_OFFSET 0xC #define XGPIO_GIE_OFFSET 0x11C #define XGPIO_ISR_OFFSET 0x120 #define XGPIO_IER_OFFSET 0x128 typedef uintptr_t UINTPTR; typedef struct { UINTPTR BaseAddress; u32 IsReady; int InterruptPresent; int IsDual; } XGpio; #define XGpio_Out32 Xil_Out32 #define XGpio_In32 Xil_In32 #define XGpio_WriteReg(BaseAddress, RegOffset, Data) XGpio_Out32((BaseAddress) + (RegOffset), (u32)(Data)) #define XGpio_ReadReg(BaseAddress, RegOffset) XGpio_In32((BaseAddress) + (RegOffset)) #define XIL_COMPONENT_IS_READY 0x11111111U #define XPAR_AXI_GPIO_1_BASEADDR 0x800A0000 #define LED_CHANNEL 1 #define LED 0x01 void XGpio_SetDataDirection(XGpio *InstancePtr, unsigned Channel,u32 DirectionMask); void XGpio_DiscreteWrite(XGpio * InstancePtr, unsigned Channel, u32 Data); void XGpio_DiscreteClear(XGpio * InstancePtr, unsigned Channel, u32 Mask); #endif
pl_gpio.c
#include "pl_gpio.h"
static inline u32 Xil_In32(UINTPTR Addr)
{
return *(volatile u32 *) Addr;
}
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
}
void XGpio_SetDataDirection(XGpio *InstancePtr, unsigned Channel,u32 DirectionMask)
{
XGpio_WriteReg(InstancePtr->BaseAddress,
((Channel - 1) * XGPIO_CHAN_OFFSET) + XGPIO_TRI_OFFSET,DirectionMask);
}
void XGpio_DiscreteWrite(XGpio * InstancePtr, unsigned Channel, u32 Data)
{
XGpio_WriteReg(InstancePtr->BaseAddress,
((Channel - 1) * XGPIO_CHAN_OFFSET) + XGPIO_DATA_OFFSET,Data);
}
void XGpio_DiscreteClear(XGpio * InstancePtr, unsigned Channel, u32 Mask)
{
u32 Current;
unsigned DataOffset;
//Calculate the offset to the data register of the GPIO */
DataOffset = ((Channel - 1) * XGPIO_CHAN_OFFSET) + XGPIO_DATA_OFFSET;
//Read the contents of the data register, merge in Mask and write back results
Current = XGpio_ReadReg(InstancePtr->BaseAddress, DataOffset);
Current &= ~Mask;
XGpio_WriteReg(InstancePtr->BaseAddress, DataOffset, Current);
}
2,ps_gpio.h
#ifndef PS_GPIO_H #define PS_GPIO_H #includetypedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; #define XGPIOPS_SIX 6U #define XPAR_PSU_GPIO_0_BASEADDR 0xFF0A0000 extern u64 config_baseaddr; void XGpioPs_SetDirectionPin(u32 Pin, u32 Direction); #endif
ps_gpio.c
#include "ps_gpio.h"
u64 config_baseaddr;
#define XGPIOPS_DATA_LSW_OFFSET 0x00000000U
#define XGPIOPS_DATA_MSW_OFFSET 0x00000004U
#define XGPIOPS_DATA_OFFSET 0x00000040U
#define XGPIOPS_DATA_RO_OFFSET 0x00000060U
#define XGPIOPS_DIRM_OFFSET 0x00000204U
#define XGPIOPS_OUTEN_OFFSET 0x00000208U
#define XGPIOPS_DATA_MASK_OFFSET 0x00000008U
#define XGPIOPS_DATA_BANK_OFFSET 0x00000004U
#define XGPIOPS_REG_MASK_OFFSET 0x00000040U
static inline u32 Xil_In32(u64 Addr)
{
return *(volatile u32 *) Addr;
}
static inline void Xil_Out32(u64 Addr, u32 Value)
{
volatile u32 *LocalAddr = (volatile u32 *)Addr;
*LocalAddr = Value;
}
#define XGpioPs_ReadReg(BaseAddr, RegOffset)
Xil_In32((BaseAddr) + (u64)(RegOffset))
#define XGpioPs_WriteReg(BaseAddr, RegOffset, Data)
Xil_Out32((BaseAddr) + (u64)(RegOffset), (u32)(Data))
void XGpioPs_GetBankPin(u8 PinNumber, u8 *BankNumber, u8 *PinNumberInBank)
{
u32 XGpioPsPinTable[XGPIOPS_SIX] = {0};
XGpioPsPinTable[0] = (u32)25;
XGpioPsPinTable[1] = (u32)51;
XGpioPsPinTable[2] = (u32)77;
XGpioPsPinTable[3] = (u32)109;
XGpioPsPinTable[4] = (u32)141;
XGpioPsPinTable[5] = (u32)173;
*BankNumber = 0U;
while (*BankNumber < XGPIOPS_SIX)
{
if (PinNumber <= XGpioPsPinTable[*BankNumber])
{
break;
}
(*BankNumber)++;
}
if (*BankNumber == (u8)0)
{
*PinNumberInBank = PinNumber;
}
else
{
*PinNumberInBank=(u8)((u32)PinNumber%(XGpioPsPinTable[*BankNumber-(u8)1]+ (u32)1));
}
}
void XGpioPs_SetDirectionPin(u32 Pin, u32 Direction)
{
u8 Bank;
u8 PinNumber;
u32 DirModeReg;
// Get the Bank number and Pin number within the bank
XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber);
DirModeReg = XGpioPs_ReadReg(config_baseaddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) + XGPIOPS_DIRM_OFFSET);
if (Direction!=(u32)0) // Output Direction
{
DirModeReg |= ((u32)1 << (u32)PinNumber);
}
else //Input Direction
{
DirModeReg &= ~ ((u32)1 << (u32)PinNumber);
}
XGpioPs_WriteReg(config_baseaddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +XGPIOPS_DIRM_OFFSET, DirModeReg);
}
void XGpioPs_SetOutputEnablePin(u32 Pin, u32 OpEnable)
{
u8 Bank;
u8 PinNumber;
u32 OpEnableReg;
// Get the Bank number and Pin number within the bank
XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber);
OpEnableReg = XGpioPs_ReadReg(config_baseaddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) + XGPIOPS_OUTEN_OFFSET);
if (OpEnable != (u32)0) //Enable Output Enable
{
OpEnableReg |= ((u32)1 << (u32)PinNumber);
}
else //Disable Output Enable
{
OpEnableReg &= ~ ((u32)1 << (u32)PinNumber);
}
XGpioPs_WriteReg(config_baseaddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) + XGPIOPS_OUTEN_OFFSET, OpEnableReg);
}
void XGpioPs_WritePin(u32 Pin, u32 Data)
{
u32 RegOffset;
u32 Value;
u8 Bank;
u8 PinNumber;
u32 DataVar = Data;
// Get the Bank number and Pin number within the bank
XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber);
if (PinNumber > 15U)
{
// There are only 16 data bits in bit maskable register
PinNumber -= (u8)16;
RegOffset = XGPIOPS_DATA_MSW_OFFSET;
}
else
{
RegOffset = XGPIOPS_DATA_LSW_OFFSET;
}
DataVar &= (u32)0x01;
Value = ~((u32)1 << (PinNumber + 16U)) & ((DataVar << PinNumber) | 0xFFFF0000U);
XGpioPs_WriteReg(config_baseaddr,
((u32)(Bank) * XGPIOPS_DATA_MASK_OFFSET) +RegOffset, Value);
}
3,main.c
<1>关于gpio寄存器地址,都是从vivado导出后的sdk gpio例程中复制出来,开发思路也应该是这样,先用sdk裸机程序验证。很多外设和 PL端的 IP,sdk会生成好操作的方法和操作的地址,这样就不需要我们再去找对应关系。
<2>打开/dev/mem,使用选项O_SYNC向外部写入数据通常数据是写入到cache缓冲,O_SYNC将确保数据写入至外设才返回,需要注意这里的O_SYNC,只会影响写操作,对读无影响。
<3>msync的调用:如果需要向外设一次写入比较多的数据,此时如果调用O_SYNC,将会严重影响系统的性能,此时如果不使用O_SYNC,而是在写完数据后,调用msync,这样会提升写的性能。
<4>读操作一致性问题:如果需要读外设的数据,因为cache的存在,应用中取到的数据是cache中的数据,而不是外设的最新状态,此时读到的可能是一个错误的值。
#include#include #include #include #include #include #include "ps_gpio.h" #include "pl_gpio.h" int main(void) { int mem_fd; XGpio Gpio; mem_fd = open("/dev/mem", O_RDWR | O_SYNC); if (mem_fd < 0) { printf("open /dev/mem failedrn"); return 0; } Gpio.BaseAddress = (u64)mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, XPAR_AXI_GPIO_1_BASEADDR ); if((u64)MAP_FAILED == Gpio.BaseAddress) { printf("mmap failrn"); } XGpio_SetDataDirection(&Gpio, LED_CHANNEL, ~LED); while(1) { XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, LED); usleep(500000); XGpio_DiscreteClear(&Gpio, LED_CHANNEL, LED); msync((void *)Gpio.BaseAddress, 1024, MS_ASYNC); usleep(500000); } return 0; }



