不枉初心,砥砺前行

皮皮祥的博客

欢迎留言,评论

导航

linux省去驱动直接从应用层寄存器操作

这个外设的功能已经在zynq sdk裸机下验证通过,如果这个外设没有用到中断,我们可以通过linux下映射寄存器的方法,将这个裸机下的程序直接移植到linux下使用,这样就可以省去硬件开发驱动的工作。

一,linux内存映射

二,原理图

三,linux应用层代码解析

1,pl_gpio.h

  1.  
    #ifndef PL_GPIO_H
  2.  
    #define PL_GPIO_H
  3.  
    #include <stdint.h>
  4.  
     
  5.  
    typedef uint8_t u8;
  6.  
    typedef uint16_t u16;
  7.  
    typedef uint32_t u32;
  8.  
    typedef uint64_t u64;
  9.  
    #define XGPIO_CHAN_OFFSET 8
  10.  
    #define XGPIO_DATA_OFFSET 0x0 /**< Data register for 1st channel */
  11.  
    #define XGPIO_TRI_OFFSET 0x4 /**< I/O direction reg for 1st channel */
  12.  
    #define XGPIO_DATA2_OFFSET 0x8 /**< Data register for 2nd channel */
  13.  
    #define XGPIO_TRI2_OFFSET 0xC /**< I/O direction reg for 2nd channel */
  14.  
    #define XGPIO_GIE_OFFSET 0x11C /**< Glogal interrupt enable register */
  15.  
    #define XGPIO_ISR_OFFSET 0x120 /**< Interrupt status register */
  16.  
    #define XGPIO_IER_OFFSET 0x128 /**< Interrupt enable register */
  17.  
     
  18.  
     
  19.  
    typedef uintptr_t UINTPTR;
  20.  
     
  21.  
    typedef struct
  22.  
    {
  23.  
    UINTPTR BaseAddress; /* Device base address */
  24.  
    u32 IsReady; /* Device is initialized and ready */
  25.  
    int InterruptPresent; /* Are interrupts supported in h/w */
  26.  
    int IsDual; /* Are 2 channels supported in h/w */
  27.  
    } XGpio;
  28.  
     
  29.  
    #define XGpio_Out32 Xil_Out32
  30.  
    #define XGpio_In32 Xil_In32
  31.  
     
  32.  
    #define XGpio_WriteReg(BaseAddress, RegOffset, Data) \
  33.  
    XGpio_Out32((BaseAddress) + (RegOffset), (u32)(Data))
  34.  
     
  35.  
    #define XGpio_ReadReg(BaseAddress, RegOffset) \
  36.  
    XGpio_In32((BaseAddress) + (RegOffset))
  37.  
     
  38.  
    #define XIL_COMPONENT_IS_READY 0x11111111U
  39.  
    #define XPAR_AXI_GPIO_1_BASEADDR 0x800A0000
  40.  
     
  41.  
    /*The following constant is used to determine which channel of the GPIO is
  42.  
    * used for the LED if there are 2 channels supported.*/
  43.  
    #define LED_CHANNEL 1
  44.  
     
  45.  
    #define LED 0x01 /* Assumes bit 0 of GPIO is connected to an LED */
  46.  
     
  47.  
    void XGpio_SetDataDirection(XGpio *InstancePtr, unsigned Channel,u32 DirectionMask);
  48.  
    void XGpio_DiscreteWrite(XGpio * InstancePtr, unsigned Channel, u32 Data);
  49.  
    void XGpio_DiscreteClear(XGpio * InstancePtr, unsigned Channel, u32 Mask);
  50.  
    #endif

  pl_gpio.c 

  1.  
    #include "pl_gpio.h"
  2.  
    static inline u32 Xil_In32(UINTPTR Addr)
  3.  
    {
  4.  
    return *(volatile u32 *) Addr;
  5.  
    }
  6.  
    static inline void Xil_Out32(UINTPTR Addr, u32 Value)
  7.  
    {
  8.  
    #ifndef ENABLE_SAFETY
  9.  
    volatile u32 *LocalAddr = (volatile u32 *)Addr;
  10.  
    *LocalAddr = Value;
  11.  
    #else
  12.  
    XStl_RegUpdate(Addr, Value);
  13.  
    #endif
  14.  
    }
  15.  
     
  16.  
    void XGpio_SetDataDirection(XGpio *InstancePtr, unsigned Channel,u32 DirectionMask)
  17.  
    {
  18.  
    XGpio_WriteReg(InstancePtr->BaseAddress,
  19.  
    ((Channel - 1) * XGPIO_CHAN_OFFSET) + XGPIO_TRI_OFFSET,DirectionMask);
  20.  
    }
  21.  
     
  22.  
    void XGpio_DiscreteWrite(XGpio * InstancePtr, unsigned Channel, u32 Data)
  23.  
    {
  24.  
    XGpio_WriteReg(InstancePtr->BaseAddress,
  25.  
    ((Channel - 1) * XGPIO_CHAN_OFFSET) + XGPIO_DATA_OFFSET,Data);
  26.  
    }
  27.  
     
  28.  
    void XGpio_DiscreteClear(XGpio * InstancePtr, unsigned Channel, u32 Mask)
  29.  
    {
  30.  
    u32 Current;
  31.  
    unsigned DataOffset;
  32.  
     
  33.  
    //Calculate the offset to the data register of the GPIO */
  34.  
    DataOffset = ((Channel - 1) * XGPIO_CHAN_OFFSET) + XGPIO_DATA_OFFSET;
  35.  
    //Read the contents of the data register, merge in Mask and write back results
  36.  
    Current = XGpio_ReadReg(InstancePtr->BaseAddress, DataOffset);
  37.  
    Current &= ~Mask;
  38.  
    XGpio_WriteReg(InstancePtr->BaseAddress, DataOffset, Current);
  39.  
    }

2,ps_gpio.h

  1.  
    #ifndef PS_GPIO_H
  2.  
    #define PS_GPIO_H
  3.  
    #include <stdint.h>
  4.  
    typedef uint8_t u8;
  5.  
    typedef uint16_t u16;
  6.  
    typedef uint32_t u32;
  7.  
    typedef uint64_t u64;
  8.  
    #define XGPIOPS_SIX 6U
  9.  
    #define XPAR_PSU_GPIO_0_BASEADDR 0xFF0A0000
  10.  
    extern u64 config_baseaddr;
  11.  
    void XGpioPs_SetDirectionPin(u32 Pin, u32 Direction);
  12.  
    #endif

   ps_gpio.c

  1.  
    #include "ps_gpio.h"
  2.  
    u64 config_baseaddr;
  3.  
    #define XGPIOPS_DATA_LSW_OFFSET 0x00000000U /* Mask and Data Register LSW, WO */
  4.  
    #define XGPIOPS_DATA_MSW_OFFSET 0x00000004U /* Mask and Data Register MSW, WO */
  5.  
    #define XGPIOPS_DATA_OFFSET 0x00000040U /* Data Register, RW */
  6.  
    #define XGPIOPS_DATA_RO_OFFSET 0x00000060U /* Data Register - Input, RO */
  7.  
    #define XGPIOPS_DIRM_OFFSET 0x00000204U /* Direction Mode Register, RW */
  8.  
    #define XGPIOPS_OUTEN_OFFSET 0x00000208U /* Output Enable Register, RW */
  9.  
     
  10.  
    #define XGPIOPS_DATA_MASK_OFFSET 0x00000008U /* Data/Mask Registers offset */
  11.  
    #define XGPIOPS_DATA_BANK_OFFSET 0x00000004U /* Data Registers offset */
  12.  
    #define XGPIOPS_REG_MASK_OFFSET 0x00000040U /* Registers offset */
  13.  
     
  14.  
    static inline u32 Xil_In32(u64 Addr)
  15.  
    {
  16.  
    return *(volatile u32 *) Addr;
  17.  
    }
  18.  
     
  19.  
    static inline void Xil_Out32(u64 Addr, u32 Value)
  20.  
    {
  21.  
    volatile u32 *LocalAddr = (volatile u32 *)Addr;
  22.  
    *LocalAddr = Value;
  23.  
    }
  24.  
     
  25.  
    #define XGpioPs_ReadReg(BaseAddr, RegOffset) \
  26.  
    Xil_In32((BaseAddr) + (u64)(RegOffset))
  27.  
     
  28.  
    #define XGpioPs_WriteReg(BaseAddr, RegOffset, Data) \
  29.  
    Xil_Out32((BaseAddr) + (u64)(RegOffset), (u32)(Data))
  30.  
     
  31.  
    void XGpioPs_GetBankPin(u8 PinNumber, u8 *BankNumber, u8 *PinNumberInBank)
  32.  
    {
  33.  
    u32 XGpioPsPinTable[XGPIOPS_SIX] = {0};
  34.  
    /*This structure defines the mapping of the pin numbers to the banks when
  35.  
    * the driver APIs are used for working on the individual pins.*/
  36.  
    XGpioPsPinTable[0] = (u32)25; /* 0 - 25, Bank 0 */
  37.  
    XGpioPsPinTable[1] = (u32)51; /* 26 - 51, Bank 1 */
  38.  
    XGpioPsPinTable[2] = (u32)77; /* 52 - 77, Bank 2 */
  39.  
    XGpioPsPinTable[3] = (u32)109; /* 78 - 109, Bank 3 */
  40.  
    XGpioPsPinTable[4] = (u32)141; /* 110 - 141, Bank 4 */
  41.  
    XGpioPsPinTable[5] = (u32)173; /* 142 - 173 Bank 5 */
  42.  
     
  43.  
    *BankNumber = 0U;
  44.  
    while (*BankNumber < XGPIOPS_SIX)
  45.  
    {
  46.  
    if (PinNumber <= XGpioPsPinTable[*BankNumber])
  47.  
    {
  48.  
    break;
  49.  
    }
  50.  
    (*BankNumber)++;
  51.  
    }
  52.  
    if (*BankNumber == (u8)0)
  53.  
    {
  54.  
    *PinNumberInBank = PinNumber;
  55.  
    }
  56.  
    else
  57.  
    {
  58.  
    *PinNumberInBank=(u8)((u32)PinNumber%(XGpioPsPinTable[*BankNumber-(u8)1]+ (u32)1));
  59.  
    }
  60.  
    }
  61.  
     
  62.  
    void XGpioPs_SetDirectionPin(u32 Pin, u32 Direction)
  63.  
    {
  64.  
    u8 Bank;
  65.  
    u8 PinNumber;
  66.  
    u32 DirModeReg;
  67.  
     
  68.  
    // Get the Bank number and Pin number within the bank
  69.  
    XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber);
  70.  
    DirModeReg = XGpioPs_ReadReg(config_baseaddr,
  71.  
    ((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) + XGPIOPS_DIRM_OFFSET);
  72.  
     
  73.  
    if (Direction!=(u32)0) // Output Direction
  74.  
    {
  75.  
    DirModeReg |= ((u32)1 << (u32)PinNumber);
  76.  
    }
  77.  
    else //Input Direction
  78.  
    {
  79.  
    DirModeReg &= ~ ((u32)1 << (u32)PinNumber);
  80.  
    }
  81.  
     
  82.  
    XGpioPs_WriteReg(config_baseaddr,
  83.  
    ((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +XGPIOPS_DIRM_OFFSET, DirModeReg);
  84.  
    }
  85.  
     
  86.  
    void XGpioPs_SetOutputEnablePin(u32 Pin, u32 OpEnable)
  87.  
    {
  88.  
    u8 Bank;
  89.  
    u8 PinNumber;
  90.  
    u32 OpEnableReg;
  91.  
     
  92.  
    // Get the Bank number and Pin number within the bank
  93.  
    XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber);
  94.  
    OpEnableReg = XGpioPs_ReadReg(config_baseaddr,
  95.  
    ((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) + XGPIOPS_OUTEN_OFFSET);
  96.  
     
  97.  
    if (OpEnable != (u32)0) //Enable Output Enable
  98.  
    {
  99.  
    OpEnableReg |= ((u32)1 << (u32)PinNumber);
  100.  
    }
  101.  
    else //Disable Output Enable
  102.  
    {
  103.  
    OpEnableReg &= ~ ((u32)1 << (u32)PinNumber);
  104.  
    }
  105.  
     
  106.  
    XGpioPs_WriteReg(config_baseaddr,
  107.  
    ((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) + XGPIOPS_OUTEN_OFFSET, OpEnableReg);
  108.  
    }
  109.  
     
  110.  
    void XGpioPs_WritePin(u32 Pin, u32 Data)
  111.  
    {
  112.  
    u32 RegOffset;
  113.  
    u32 Value;
  114.  
    u8 Bank;
  115.  
    u8 PinNumber;
  116.  
    u32 DataVar = Data;
  117.  
     
  118.  
    // Get the Bank number and Pin number within the bank
  119.  
    XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber);
  120.  
    if (PinNumber > 15U)
  121.  
    {
  122.  
    // There are only 16 data bits in bit maskable register
  123.  
    PinNumber -= (u8)16;
  124.  
    RegOffset = XGPIOPS_DATA_MSW_OFFSET;
  125.  
    }
  126.  
    else
  127.  
    {
  128.  
    RegOffset = XGPIOPS_DATA_LSW_OFFSET;
  129.  
    }
  130.  
     
  131.  
    /*Get the 32 bit value to be written to the Mask/Data register where
  132.  
    * the upper 16 bits is the mask and lower 16 bits is the data.*/
  133.  
    DataVar &= (u32)0x01;
  134.  
    Value = ~((u32)1 << (PinNumber + 16U)) & ((DataVar << PinNumber) | 0xFFFF0000U);
  135.  
    XGpioPs_WriteReg(config_baseaddr,
  136.  
    ((u32)(Bank) * XGPIOPS_DATA_MASK_OFFSET) +RegOffset, Value);
  137.  
    }
  138.  
     

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中的数据,而不是外设的最新状态,此时读到的可能是一个错误的值。

  1.  
    #include <stdio.h>
  2.  
    #include <stdint.h>
  3.  
    #include <unistd.h>
  4.  
    #include <fcntl.h>
  5.  
    #include <unistd.h>
  6.  
    #include <sys/mman.h>
  7.  
    #include "ps_gpio.h"
  8.  
    #include "pl_gpio.h"
  9.  
    int main(void)
  10.  
    {
  11.  
    int mem_fd;
  12.  
    XGpio Gpio;
  13.  
    mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
  14.  
    if (mem_fd < 0)
  15.  
    {
  16.  
    printf("open /dev/mem failed\r\n");
  17.  
    return 0;
  18.  
    }
  19.  
    Gpio.BaseAddress = (u64)mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, XPAR_AXI_GPIO_1_BASEADDR );
  20.  
    if((u64)MAP_FAILED == Gpio.BaseAddress)
  21.  
    {
  22.  
    printf("mmap fail\r\n");
  23.  
    }
  24.  
    XGpio_SetDataDirection(&Gpio, LED_CHANNEL, ~LED);
  25.  
    while(1)
  26.  
    {
  27.  
    /* Set the GPIO output to be low. */
  28.  
    XGpio_DiscreteWrite(&Gpio, LED_CHANNEL, LED);
  29.  
    usleep(500000);
  30.  
    /* Set the GPIO Output to High. */
  31.  
    XGpio_DiscreteClear(&Gpio, LED_CHANNEL, LED);
  32.  
    msync((void *)Gpio.BaseAddress, 1024, MS_ASYNC);
  33.  
    usleep(500000);
  34.  
    }
  35.  
    return 0;
  36.  
    }

 

posted on 2022-11-15 15:41  皮皮祥  阅读(457)  评论(0编辑  收藏  举报