PS利用EMIO控制LED灯

  昨天做了一个PS端利用EMIO对LED灯进行控制的实验,在XPS进行完系统的配置导出到SDK中后,发现EMIO口的映射地址为:0xe000a000,如下图

    

  此时想起sheldon此前说过的xmd命令,可以利用命令行的模式,直接向地址中写入数据,从而对外设进行控制,于是,在烧写了FPGA后,没有编写应用程序,利用命令

  mwr 0xe000a000 0xFF 进行LED灯的点亮,但是结果却并没有变化,此时考虑可能是没有对PS端应用程序编写的原因。由于本工程内还添加了一个自定义的IP核同样用于LED灯的控制,即上图中的axi_gpio_0,于是利用命令 mwr 0x41200000 0xFF进行测试,结果同样灯没亮,这样断定应该为没有进行应用程序编写的原因,于是,根据教程,敲入代码。具体代码如下:

/*
 * Copyright (c) 2009-2012 Xilinx, Inc.  All rights reserved.
 *
 * Xilinx, Inc.
 * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A
 * COURTESY TO YOU.  BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS
 * ONE POSSIBLE   IMPLEMENTATION OF THIS FEATURE, APPLICATION OR
 * STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION
 * IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE
 * FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION.
 * XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO
 * THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO
 * ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE
 * FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 */

/*
 * helloworld.c: simple test application
 *
 * This application configures UART 16550 to baud rate 9600.
 * PS7 UART (Zynq) is not initialized by this application, since
 * bootrom/bsp configures it to baud rate 115200
 *
 * ------------------------------------------------
 * | UART TYPE   BAUD RATE                        |
 * ------------------------------------------------
 *   uartns550   9600
 *   uartlite    Configurable only in HW design
 *   ps7_uart    115200 (configured by bootrom/bsp)
 */

#include <stdio.h>
#include "platform.h"
#include "xparameters.h"
#include "xuartps.h"
#include "xgpio.h"
#include "xgpiops.h"

XUartPs Uart_Ps;

int main()
{
    XUartPs_Config* Config;
    int Status;
    int SentCount = 0;
    int RecvCount = 0;
    u8 HelloZynq[] = "Hello Zynq!\n";
    u8 RecvBuf[3];

    XGpio Gpio;        /* 通过axi总线控制LED */
    XGpioPs Gpiops;    /* 通过EMIO控制LED */
    XGpioPs_Config* ConfigPtr;
    u8 axi_Gpio_Data, ps_Gpio_Data;


    init_platform();

    /* Look up the configuration in the config table and the initialize it*/
    Config = XUartPs_LookupConfig(XPAR_PS7_UART_1_DEVICE_ID);
    if(NULL == Config)
    {
        return XST_FAILURE;
    }

    Status = XUartPs_CfgInitialize(&Uart_Ps, Config, Config->BaseAddress);
    if(Status != XST_SUCCESS)
    {
        return XST_FAILURE;
    }

    /* Send Hello Zynq*/

    while(SentCount < (sizeof(HelloZynq) - 1))
    {
        SentCount += XUartPs_Send(&Uart_Ps, &HelloZynq[SentCount], 1);
    }

    /* initialize the GPIO driver */
    Status = XGpio_Initialize(&Gpio, XPAR_AXI_GPIO_0_DEVICE_ID);
    if(Status != XST_SUCCESS)
    {
        return XST_FAILURE;
    }

    /* Set the direction for all signals to be output */
    XGpio_SetDataDirection(&Gpio, 1, 0x0);

    /* Initialize the PS GPIO driver */
    ConfigPtr = XGpioPs_LookupConfig(XPAR_XGPIOPS_0_DEVICE_ID);
    Status = XGpioPs_CfgInitialize(&Gpiops, ConfigPtr, ConfigPtr->BaseAddr);
    if(Status != XST_SUCCESS)
    {
        return XST_FAILURE;
    }

    /* Set the direction for all signals to be outputs and Enable the Output enable for the LED pins */

    XGpioPs_SetDirection(&Gpiops, 2, 0xF);
    XGpioPs_SetOutputEnable(&Gpiops, 2, 0xF);

    while(1)
    {
        /* Receive data and display data */
        RecvCount = 0;
        while(RecvCount < 3)
        {
            while(!XUartPs_IsReceiveData(Config->BaseAddress));
            XUartPs_Recv(&Uart_Ps, &RecvBuf[RecvCount], 1);
            if(RecvBuf[RecvCount] == 'r')
            {
                RecvBuf[RecvCount] = '\n';
                XUartPs_Send(&Uart_Ps, &RecvBuf[RecvCount], 1);
                break;
            }
            XUartPs_Send(&Uart_Ps, &RecvBuf[RecvCount++], 1);
        }
        /* Change ASCII char to number */
            if(RecvBuf[0] >= '0' && RecvBuf[0] <= '9')
                axi_Gpio_Data = RecvBuf[0] - '0';

            if(RecvBuf[0] >= 'a' && RecvBuf[0] <= 'z')
                axi_Gpio_Data = RecvBuf[0] - 'a' + 10;

            if(RecvBuf[0] >= 'A' && RecvBuf[0] <= 'Z')
                axi_Gpio_Data = RecvBuf[0] - 'A' + 10;

            XGpio_DiscreteWrite(&Gpio, 1, axi_Gpio_Data);

            if(RecvBuf[1] >= '0' && RecvBuf[1] <= '9')
                ps_Gpio_Data = RecvBuf[1] - '0';

            if(RecvBuf[1] >= 'a' && RecvBuf[1] <= 'z')
                ps_Gpio_Data = RecvBuf[1] - 'a' + 10;

            if(RecvBuf[1] >= 'A' && RecvBuf[1] <= 'Z')
                ps_Gpio_Data = RecvBuf[1] - 'A' + 10;

            XGpioPs_Write(&Gpiops, 2, ps_Gpio_Data);

    }

    cleanup_platform();
    return 0;
}
View Code

  而后运行应用程序,根据应用程序进行操作,可以很好的通过EMIO和自定义IP核对LED灯进行控制,此时再利用XMD命令行向自定义IP核写命令,发现可以控制LED灯,但在向EMIO写入命令时,依然没有作用。此时可以确定工程中并没有错误,唯一可能出错的地方就是XMD命令中的地址可能错误,于是去应用程序代码中进行寻找,看应用程序中是如何把数据写入的,根据应用程序中的写入程序,寻找地址。

  应用程序中,进行写入数据的代码为:

  

            if(RecvBuf[0] >= '0' && RecvBuf[0] <= '9')
                axi_Gpio_Data = RecvBuf[0] - '0';

            if(RecvBuf[0] >= 'a' && RecvBuf[0] <= 'z')
                axi_Gpio_Data = RecvBuf[0] - 'a' + 10;

            if(RecvBuf[0] >= 'A' && RecvBuf[0] <= 'Z')
                axi_Gpio_Data = RecvBuf[0] - 'A' + 10;

            XGpio_DiscreteWrite(&Gpio, 1, axi_Gpio_Data);

            if(RecvBuf[1] >= '0' && RecvBuf[1] <= '9')
                ps_Gpio_Data = RecvBuf[1] - '0';

            if(RecvBuf[1] >= 'a' && RecvBuf[1] <= 'z')
                ps_Gpio_Data = RecvBuf[1] - 'a' + 10;

            if(RecvBuf[1] >= 'A' && RecvBuf[1] <= 'Z')
                ps_Gpio_Data = RecvBuf[1] - 'A' + 10;

            XGpioPs_Write(&Gpiops, 2, ps_Gpio_Data);
View Code

  代码中的前半部分为利用自定义IP核进行的写入,后半部分为利用EMIO接口进行的写入,进入函数XGpio_DiscreteWrite和XGpioPs_Write,去寻找地址

  XGpio_DiscreteWrite:

  

void XGpio_DiscreteWrite(XGpio * InstancePtr, unsigned Channel, u32 Data)
{
    Xil_AssertVoid(InstancePtr != NULL);
    Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
    Xil_AssertVoid((Channel == 1) ||
             ((Channel == 2) && (InstancePtr->IsDual == TRUE)));

    XGpio_WriteReg(InstancePtr->BaseAddress,
            ((Channel - 1) * XGPIO_CHAN_OFFSET) + XGPIO_DATA_OFFSET,
            Data);
}
View Code

  发现在此函数中真正的数据写入由函数XGpio_WriteReg进行,此函数的第一个参数为基地址,根据第二个参数和偏移量进行计算具体的地址(此工程中偏移量为0,channel为1),可以看出此时数据的写入地址就为基地址,第三个参数为写入的数据,因此可以确定通过自定义IP核进行LED灯控制的外设地址就为基地址,即system.xml中显示的地址

 

  另外分析XGpioPs_Write:

  

 1 void XGpioPs_Write(XGpioPs *InstancePtr, u8 Bank, u32 Data)
 2 {
 3     Xil_AssertVoid(InstancePtr != NULL);
 4     Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
 5     Xil_AssertVoid(Bank < XGPIOPS_MAX_BANKS);
 6 
 7     XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
 8               ((Bank) * XGPIOPS_DATA_BANK_OFFSET) +
 9               XGPIOPS_DATA_OFFSET, Data);
10 }
View Code

  同样数据的写入也由函数XGpioPs_WriteReg执行,其参数与上类似,但通过单步调试和分析可以发现,此时的地址偏移量并不为0,通过单步调试发现地址偏移量为0x48,因此在通过EMIO对LED灯进行控制时,写入数据的地址并不为system.xml中显示的基地址,而是基地址加上地址偏移量0x48,故通过XMD命令向地址0xe000a048写入数据,即

  mwr 0xe000a048 0xFF, 执行命令后发现LED灯被电量,此时证明我们的猜测正确。

 

 

    通过此例程发现在PS利用EMIO和PL进行通讯时,将PL端的配置当作外设进行映射到PS端时,外设的地址相对于基地址有偏移量,这里我猜测不同的地址偏移量跟ps端的system7总线有关系,但具体关系还不太清楚,现在地址的具体计算还不太清楚,有待进一步的了解,干巴蝶!!

posted @ 2013-07-20 09:55  菜鸟也搞FPGA  阅读(1860)  评论(0)    收藏  举报