芯路恒电子技术论坛

 找回密码
 立即注册
热搜: 合集
查看: 12850|回复: 11

【zynq课程笔记】【裸机】【第7课 】【硬件编程原理】

[复制链接]
  • TA的每日心情
    慵懒
    2021-2-24 10:16
  • 428

    主题

    811

    帖子

    1万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    16109
    QQ
    发表于 2022-1-18 18:19:53 | 显示全部楼层 |阅读模式
    更多课程笔记请查看:【zynq裸机编程课程笔记合集】
    http://www.corecourse.cn/forum.php?mod=viewthread&tid=29095



    1、什么是硬件编程

    所谓硬件编程,完整说法应该是对硬件进行编程以实现对该硬件的控制,在Zynq开发中,就是指通过对Zynq的各个硬件外设控制器(GPIO、UART、I2C、SPI等)以及在FPGA(PL)中添加的各个外设控制器IP的寄存器进行读写,以配置其工作模式,并完成数据的传输。


    2、为啥要学习硬件编程

    要控制外部设备执行相应的操作,如在LCD上显示字符,控制继电器断开/闭合,通过串口打印各种工作信息。或者获取外部设备的相关信息,例如读取温湿度传感器(I2C)采集的温度和湿度信息,控制并读取模数转换器(SPI)采样的模拟电压值,检测按键的按下与释放状态等。CPU要想控制或者获得某些外部设备的状态或信息,需要通过各种硬件外设控制器来与这些外部设备进行信息交互。如何通过硬件外设控制器来完成信息交互呢,就是对这些外设提供的相关寄存器进行读写,也就是我们说的硬件编程。

    1.png


    了解了所谓的硬件编程的定义和意义之后,相信大家也都了解了硬件编程的重要性,掌握硬件编程属于开发Zynq SoC FPGA的必备技能。不同的硬件外设,功能作用不同,其寄存器设定和对应的物理意义也都不一样,很难找到一个通用的程序或者方法,来对任意一个外设进行编程。所以只能具体硬件具体分析,具体功能具体编程实现。
    GPIO,属于是Zynq SoC中最简单的外设控制器,也是所有基于CPU结构的芯片中都会有的一类基础外设,因此,我们本节课程,就使用GPIO这个基础且简单的外设控制器来带领大家入手Zynq SoC FPGA的PS硬件编程。


    3、典型GPIO结构

    要了解GPIO的结构,我们先来说说我们一般希望使用GPIO做什么。还是前面说的:
    我们可能会使用GPIO来驱动一个继电器,或者LED灯亮灭。这类应用,只需要GPIO能够输出高电平或者低电平就可以了。
    或者使用GPIO来检测一个按键是否按下,这类应用,只需要GPIO能够检测管脚的高低电平即可。也就是说:
            GPIO需要有一个寄存器来存储CPU告知它的希望其输出的电平状态(高/低)
            GPIO需要有一个寄存器能够存储外部管脚当前的电平状态(高/低)
    同时,作为GPIO,还需要设置其方向,因为一个GPIO在不同的场景下需要工作在不同的方向,检测按键状态,则需要其工作在输入检测模式,而驱动继电器或者LED,则需要其工作在输出模式。处于输入状态的时候无法做输出,因此:
            GPIO需要有一个寄存器来存储并控制管脚的方向
    典型的就是下面这张图。

    2.png

    R1:方向/输出使能控制寄存器
    R2:输出状态/数据寄存器
    R3:输入状态/数据寄存器

    用更加形象的图示描述,就是下面在Quartus软件中绘制的逻辑图。
    3.png

    上面这个图所描述的GPIO,严格意义上来说,只是CPU GPIO的成员之一,我们常称为1位,在CPU系统中,由多位这样的成员组合在一起,构成的才是一个完整的GPIO。例如对于大家最熟悉的STC89C52单片机,其总共有4个GPIO(P0、P1、P2、P3),每个GPIO包含8位,每个GPIO由一组寄存器共同管理其功能。寄存器的每一位对应GPIO中对应引脚位,例如下图中,如此,有3个8位的寄存器,分别用来对应存储这个8位GPIO的输出状态(OE[7:0])、输出值(WDATA[7:0])、输入值(RDATA[7:0]),则CPU编程时,只需要读写这三个寄存器的值,就能设置和查询GPIO的输出状态,输出值、输入值了。
    4.png


    4、增强的GPIO结构

    除了这些最最基础的功能,根据不同的应用场景或者需求,各芯片厂家一般都会在基本功能的基础上,加上一些其他功能,比如置位/清零寄存器、中断屏蔽寄存器


    置位和清零寄存器

    对于一个8位的GPIO,当需要仅对其中1位的输出值进行更改,而不影响其他位的功能时,则需要先读取这个8位寄存器WDATA的值,然后将这个值的对应位修改为希望设置的状态,然后再写入WDATA寄存器。
    例如设置bit2位为1,如下所示:
    uint8_t value;
    value = (uint8_t *)WDATA;
    value = value | (1<<2);
    (uint8_t *)WDATA = value;
    再如设置bit3位为0,如下所示:
    uint8_t value;
    value = (uint8_t *)WDATA;
    value = value & (~(1<<3));
    (uint8_t *)WDATA = value;

    这样的操作方式,从表层看,需要3条语句才能实现一次操作,增加了编程的复杂性。而实际上,最终编译得到的指令更多。执行效率也有所下降。
    为了解决这个问题,现在的MCU基本都配备了专门用来设置GPIO_SET和清零GPIO_CLR某特定bit的寄存器,只需要往该寄存器的对应位写入1,就能实现对该GPIO输出值的设置(输出1)或清零(输出0)操作,写入0的位输出则不受影响。这样,通过增加GPIO硬件的复杂度,来降低CPU编程的复杂性,提升GPIO的控制效率。


    中断屏蔽寄存器

    在一些对外部信号变化要求及时响应的应用中,需要CPU能够及时知道外部信号已经发生了变化,以便于迅速做出反应。MCU采用中断的方式来应对这样的场景。而对于GPIO来说,是MCU与外界信号交换的最直接的接口。所以我们希望当GPIO检测到对应信号变化之后能够及时的通知CPU去处理,这也意味着要求GPIO有发出中断请求的能力。
    由于在实际应用中,仅有GPIO中的部分bit需要发出中断请求,其他bit不需要或者说不得发出中断请求,因此需要有一个开关,来分别控制GPIO的每个位是否被允许发出中断请求,这个寄存器一般被称为中断屏蔽寄存器(INTMASK),如果该寄存器中对应位为1,则对应的GPIO的bit被允许发起中断请求,为0则不允许发起中断请求。

    当然,不同的处理器系统,所使用的GPIO的功能和复杂度也不一样。这里只是简单的为大家介绍了最常见的一些功能及对应的寄存器。当我们编程时,需要查阅该器件datasheet中相应的信息,已获得这些寄存器的分布、功能以及使用方法。


    Zynq7000 GPIO结构
    UG585的第390页贴出了Xilinx Zynq 7000 SoC FPGA的GPIO的功能框图。
    5.png

    5、GPIO控制的编程思路

    初始化

    根据GPIO对应位的工作场景,设置其方向、中断屏蔽位、中断检测类型。
    工作
    输入:若开启了中断,则编写中断处理函数,若不开启中断,则在需要的时候直接读取输入寄存器的值;

    输出,则通过写数据寄存器或输出置位/清零寄存器来修改该位的输出值。


    6、GPIO控制的编程方法

    单输出型应用场景
    使用GPIO控制LED
    在ACZ702开发板上,设计了一个LED灯,连接到了Zynq芯片的MIO7脚上。MIO7输出高电平,则点亮LED灯,MIO7输出低电平,则LED熄灭。
    6.png
    场景分析:该场景下只需要简单的控制GPIO的对应位输出高低电平即可,用不到中断功能。
    初始化
    关闭中断
    INT_DIS/ INT_EN:
    这是一组作用于同一个功能的2个独立的寄存器,一个负责使能GPIO的每一位的中断,另一个负责禁止GPIO的每一位的中断。
    本应用中,对应GPIO无需开启中断,所以设计时针对INT_DIS/ INT_EN寄存器,需要明确:
    关闭某位中断该操作哪个寄存器,往该寄存器写0关闭中断,还是写1关闭中断。
    根据UG585中的描述,对INT_DIS寄存器的对应位写1就可以禁止该位对应的IO产生中断。所以本例中初始化时,理论上需要对INT_DIS寄存器进行操作。设置MIO7对应的中断控制位不打开。
    Data = (1<<7);
    Xil_Out32(XPAR_PS7_GPIO_0_BASEADDR + XGPIOPS_INTDIS_OFFSET, Data);



    INT_MASK:用来指示GPIO中哪些位使能了中断,某位为1则代表该位对应的GPIO中断被屏蔽了,无法产生中断。此为指示型的只读寄存器,因此无需设置和修改,只在需要确认某个GPIO的中断是否已经使能的时候读取。

    因为不使能中断,因此与中断相关的各个模式设置寄存器以及状态寄存器都不需要在初始化中再处理。因此更多相关寄存器本节暂不介绍,再后续讲解中断课程时再来详细介绍。
    设置方向和输出使能
    OUTEN、DIRM寄存器
    由于是点亮LED,属于输出型GPIO,因此,根据GPIO的结构图知道,需要使output enable信号为高电平,以使能IO Pin上的三态缓冲器输出。而output enable为1的条件则是OUTEN和DIRM两个寄存器对应的位都为1,。
    所以需要在初始化时,向这两个寄存器中对应位均写入1。由于这两个寄存器均是32位同时写入型,所以为了不干扰寄存器中其他位的值,需要采用read-modify-write的操作顺序,也就是先读出,再修改,最后再写回。
    //设置IO方向,bit7的方向为输出
    reg_val = Xil_In32(XPAR_PS7_GPIO_0_BASEADDR + XGPIOPS_DIRM_OFFSET);
    Data = reg_val | (1<<7);
    Xil_Out32(XPAR_PS7_GPIO_0_BASEADDR + XGPIOPS_DIRM_OFFSET, Data);

    //设置输出使能,bit7输出使能
    reg_val = Xil_In32(XPAR_PS7_GPIO_0_BASEADDR + XGPIOPS_OUTEN_OFFSET);
    Data = reg_val | (1<<7);
    Xil_Out32(XPAR_PS7_GPIO_0_BASEADDR + XGPIOPS_OUTEN_OFFSET, Data);

    虽然这种操作相对繁琐,但是这些操作在整个程序运行周期内,往往只需要执行一次,所以稍微繁琐一点也关系不大。
    设置GPIO的状态
    操作DATA寄存器
    写DATA寄存器时,是32位同时写入的,所以,为了不干扰其他位的值,需要先将该寄存器的值读回来,修改其中想要修改的位后,再写回该寄存器,也就是上面提到的read-modify-write操作顺序,这种操作相对繁琐。

    操作MASK_DATA寄存器
    由于设置GPIO输出状态的操作在整个程序的运行周期内可能需要执行很多次,直接使用写DATA寄存器的方法一定程度上会影响IO翻转效率。所以Zynq的GPIO提供了单独操作指定位的快速操作方法。也就是MASK_DATA寄存器。

    写MASK_DATA寄存器时需要注意,对于每一个GPIO Bank,由MASK_DATA_LSW和MASK_DATA_MSW两个寄存器组成,其中MASK_DATA_LSW控制该组GPIO中低16位的状态,MASK_DATA_MSW控制该组GPIO中高16位的状态。
    而每个寄存器又分为高16位和低16位2部分,两部分的功能不同。高16位对应描述当前的写入操作对哪些位生效,低16位对应要写入的值。
                            
    GPIO[31:0]        MASK_DATA_MSW
    控制GPIO[31:16]        MASK_DATA_MSW[31:16]        控制对应位值是否更新
                    MASK_DATA_MSW[15:0]        指定对应位要更新的值
            MASK_DATA_LSW
    控制GPIO[15:0]        MASK_DATA_LSW[31:16]        控制对应位值是否更新
                    MASK_DATA_LSW[15:0]        指定对应位要更新的值

    MASK_DATA的高16位中,哪些位为0,这些位对应的数据寄存器的值才允许被更新,更新的值由MASK_DATA寄存器的低16位来指定。

    //设置bit7输出1
    Data = ((~(1<<7)) << 16) | (1<<7);
    Xil_Out32(XPAR_PS7_GPIO_0_BASEADDR + XGPIOPS_DATA_LSW_OFFSET,  Data);



    //设置bit7输出0
    Data = ((~(1<<7)) << 16) & (~(1<<7));
    Xil_Out32(XPAR_PS7_GPIO_0_BASEADDR + XGPIOPS_DATA_LSW_OFFSET,  Data);


    7、思考和练习
    在上述内容中,我们讲解了使用Zynq的GPIO驱动一个LED亮灭的编程思路和方法,该场景下,是使用GPIO作为单独输出功能使用的。除了输出,GPIO还有一个很典型的应用场景是输入,比如连接按键开关。
    在ACZ702开发板上,设置了3个按键,其中S3是复位按键,不作为用户功能按键使用,我们无法对其编程操作,S1则接到了PS的MIO47管脚上。
    7.png

    所以,请大家根据本节课讲解的思路和方法,实现以下功能。
    读取S1按键的电平,
    当S1按键为按下状态时,驱动PS_LED以1S的频率闪烁(注意理解1S的频率闪烁和1S的时间翻转两种描述之间的差别),
    当S1释放后,停止闪烁,
    确保S1释放后PS_LED处于熄灭状态。

    通俗点讲,就是S1被按下后PS_LED就以1S的频率闪烁,释放后就熄灭且不再闪烁,
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2021-2-24 10:16
  • 428

    主题

    811

    帖子

    1万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    16109
    QQ
     楼主| 发表于 2022-1-18 18:20:29 | 显示全部楼层
    本楼存放课程附件,暂未生成附件,待生成后补充上传
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    71

    主题

    100

    帖子

    1525

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    1525
    发表于 2022-1-18 18:22:18 | 显示全部楼层
    冲冲冲冲冲,跟着小梅哥学zynq
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    慵懒
    2021-2-24 10:16
  • 428

    主题

    811

    帖子

    1万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    16109
    QQ
     楼主| 发表于 2022-1-18 18:31:24 | 显示全部楼层
    本楼层作者占位,主要用来统一解答大家学习本节课程过程中遇到的常见问题,后续会再次编辑完善。学习者有疑问的,请在本楼层之后开始提问和讨论。
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    2024-3-26 13:58
  • 3

    主题

    30

    帖子

    319

    积分

    中级会员

    Rank: 4

    积分
    319
    发表于 2022-8-12 22:51:40 | 显示全部楼层
    我有一个疑问?当我使用的是EMIO时,在库函数编程是直接定义一个54写进入,而在寄存器编程中,我54又该怎么定义呢,又该写哪个寄存器里去呢?这样就不能像                Data = reg_val | (1<<7); 这样来移位了,不知道思路了,求解惑
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    2024-3-26 13:58
  • 3

    主题

    30

    帖子

    319

    积分

    中级会员

    Rank: 4

    积分
    319
    发表于 2022-8-13 23:26:16 | 显示全部楼层
    #include "stdio.h"
    #include "sleep.h"
    #include "xgpiops_hw.h"
    #include "xgpiops.h"
    #include "xil_io.h"
    #include "xparameters.h"


    #define EMIO_LED0                                 78


    uint8_t gpio_get_bank(uint8_t pin);
    uint8_t gpio_get_pin_number(uint8_t PinNumber,uint8_t bank);

    int main(void)
    {
            uint8_t bank = 0;
            uint8_t pin_number = 0;
            uint8_t reg_val = 0;
            uint8_t data = 0;
            uint32_t reg_offset;
    //        uint8_t data_value;

            /* 得到哪个bank */
            bank = gpio_get_bank(EMIO_LED0);
            /* 得到哪个bank哪个引脚 */
            pin_number = gpio_get_pin_number(EMIO_LED0, bank);

            if (pin_number > 15U) {
                    /* 这里仅对写数据有效,因为数据寄存器只能设置16bit */
    //                data_value = pin_number - (uint8_t)16;
                    reg_offset = XGPIOPS_DATA_MSW_OFFSET;
            } else {
                    reg_offset = XGPIOPS_DATA_LSW_OFFSET;
            }
            /* 设置方向为输出 */
            reg_val = Xil_In32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_REG_MASK_OFFSET
                                                            +XGPIOPS_DIRM_OFFSET);
            data = reg_val | (1 << pin_number);
            Xil_Out32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_REG_MASK_OFFSET
                                                                    +XGPIOPS_DIRM_OFFSET,data);

            /* 设置输出使能 */
            reg_val = Xil_In32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_REG_MASK_OFFSET
                                                    + XGPIOPS_OUTEN_OFFSET);
            data = reg_val | (1 << pin_number);
            Xil_Out32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_REG_MASK_OFFSET
                                                                    +XGPIOPS_OUTEN_OFFSET,data);
            printf("hello zynq\r\n");

            while(1)
            {
                    /* 设置高16位允许更新低16位改变值 */
    //                data = (~(1 << 16) | (1 << 0));
                    data = ~((uint32_t)1 << (pin_number + 16U)) & ((1 << pin_number) | 0xFFFF0000U);
                    Xil_Out32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_DATA_MASK_OFFSET
                                                    + reg_offset, 0X00000001);
                    sleep(1);
    //                data = (~(1 << 16) | ~(1 << 0));
                    data = ~((uint32_t)1 << (pin_number + 16U)) & ((~(1 << pin_number)) | 0xFFFF0000U);
                    Xil_Out32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_DATA_MASK_OFFSET
                                                    + reg_offset, 0X00000000);
                    sleep(1);
            }

            return 0;

    }

    uint8_t gpio_get_pin_number(uint8_t PinNumber,uint8_t bank)
    {
            uint8_t pin_number = 0;
            uint32_t XGpioPsPinTable[6] = {0};
            XGpioPsPinTable[0] = (u32)25; /* 0 - 25, Bank 0 */
            XGpioPsPinTable[1] = (u32)51; /* 26 - 51, Bank 1 */
            XGpioPsPinTable[2] = (u32)77; /* 52 - 77, Bank 2 */
            XGpioPsPinTable[3] = (u32)109; /* 78 - 109, Bank 3 */
            XGpioPsPinTable[4] = (u32)141; /* 110 - 141, Bank 4 */
            XGpioPsPinTable[5] = (u32)173; /* 142 - 173 Bank 5 */

            pin_number = (uint8_t)((uint32_t)PinNumber %
                                                    (XGpioPsPinTable[bank - (uint8_t)1] + (uint32_t)1));

            return pin_number;
    }

    uint8_t gpio_get_bank(uint8_t pin)
    {
            uint8_t bank;

            if (EMIO_LED0 >0 && EMIO_LED0 <=25)
            {
                    bank = 0;
            }
            else if (EMIO_LED0 >= 26 && EMIO_LED0 <= 51)
            {
                    bank = 1;
            }
            else if (EMIO_LED0 >= 52 && EMIO_LED0 <= 77)
            {
                    bank = 2;
            }
            else if (EMIO_LED0 >= 78 && EMIO_LED0 <= 109)
            {
                    bank = 3;
            }
            else if (EMIO_LED0 >= 110 && EMIO_LED0 <= 141)
            {
                    bank = 4;
            }
            else if (EMIO_LED0 >= 142 && EMIO_LED0 <= 173)
            {
                    bank = 5;
            }

            return bank;
    }

    以78为例,寄存器驱动EMIO
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    2024-3-26 13:58
  • 3

    主题

    30

    帖子

    319

    积分

    中级会员

    Rank: 4

    积分
    319
    发表于 2022-8-14 14:53:25 | 显示全部楼层
    本帖最后由 CJC 于 2022-8-15 17:13 编辑
    CJC 发表于 2022-8-12 22:51
    我有一个疑问?当我使用的是EMIO时,在库函数编程是直接定义一个54写进入,而在寄存器编程中,我54又该怎么 ...

    #define EMIO_LED0                                 78


    uint8_t gpio_get_bank(uint8_t pin);
    uint8_t gpio_get_pin_number(uint8_t PinNumber,uint8_t bank);

    int main(void)
    {
            uint8_t bank = 0;
            uint8_t pin_number = 0;
            uint8_t reg_val = 0;
            uint8_t data = 0;
            uint32_t reg_offset;
    //        uint8_t data_value;

            /* 得到哪个bank */
            bank = gpio_get_bank(EMIO_LED0);
            /* 得到哪个bank哪个引脚 */
            pin_number = gpio_get_pin_number(EMIO_LED0, bank);

            if (pin_number > 15U) {
                    /* 这里仅对写数据有效,因为数据寄存器只能设置16bit */
    //                data_value = pin_number - (uint8_t)16;
                    reg_offset = XGPIOPS_DATA_MSW_OFFSET;
            } else {
                    reg_offset = XGPIOPS_DATA_LSW_OFFSET;
            }
            /* 设置方向为输出 */
            reg_val = Xil_In32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_REG_MASK_OFFSET
                                                            +XGPIOPS_DIRM_OFFSET);
            data = reg_val | (1 << pin_number);
            Xil_Out32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_REG_MASK_OFFSET
                                                                    +XGPIOPS_DIRM_OFFSET,data);

            /* 设置输出使能 */
            reg_val = Xil_In32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_REG_MASK_OFFSET
                                                    + XGPIOPS_OUTEN_OFFSET);
            data = reg_val | (1 << pin_number);
            Xil_Out32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_REG_MASK_OFFSET
                                                                    +XGPIOPS_OUTEN_OFFSET,data);
            printf("hello zynq\r\n");

            while(1)
            {
                    /* 设置高16位允许更新低16位改变值 */
    //                data = (~(1 << 16) | (1 << 0));
                    data = ~((uint32_t)1 << (pin_number + 16U)) & ((1 << pin_number) | 0xFFFF0000U);
                    Xil_Out32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_DATA_MASK_OFFSET
                                                    + reg_offset, 0X00000001);
                    sleep(1);
    //                data = (~(1 << 16) | ~(1 << 0));
                    data = ~((uint32_t)1 << (pin_number + 16U)) & ((~(1 << pin_number)) | 0xFFFF0000U);
                    Xil_Out32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_DATA_MASK_OFFSET
                                                    + reg_offset, 0X00000000);
                    sleep(1);
            }

            return 0;

    }

    uint8_t gpio_get_pin_number(uint8_t PinNumber,uint8_t bank)
    {
            uint8_t pin_number = 0;
            uint32_t XGpioPsPinTable[6] = {0};
            XGpioPsPinTable[0] = (u32)25;         /* 0 - 25, Bank 0 */
            XGpioPsPinTable[1] = (u32)51;         /* 26 - 51, Bank 1 */
            XGpioPsPinTable[2] = (u32)77;         /* 52 - 77, Bank 2 */
            XGpioPsPinTable[3] = (u32)109;         /* 78 - 109, Bank 3 */
            XGpioPsPinTable[4] = (u32)141;         /* 110 - 141, Bank 4 */
            XGpioPsPinTable[5] = (u32)173;         /* 142 - 173 Bank 5 */

            pin_number = (uint8_t)((uint32_t)PinNumber %
                                                    (XGpioPsPinTable[bank - (uint8_t)1] + (uint32_t)1));

            return pin_number;
    }

    uint8_t gpio_get_bank(uint8_t pin)
    {
            uint8_t bank;

            if (pin >0 && pin <=25)
            {
                    bank = 0;
            }
            else if (pin >= 26 && pin <= 51)
            {
                    bank = 1;
            }
            else if (pin >= 52 && pin <= 77)
            {
                    bank = 2;
            }
            else if (pin >= 78 && pin <= 109)
            {
                    bank = 3;
            }
            else if (pin >= 110 && pin <= 141)
            {
                    bank = 4;
            }
            else if (pin >= 142 && pin <= 173)
            {
                    bank = 5;
            }

            return bank;
    }
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    2024-3-26 13:58
  • 3

    主题

    30

    帖子

    319

    积分

    中级会员

    Rank: 4

    积分
    319
    发表于 2022-8-14 15:07:15 | 显示全部楼层
    CJC 发表于 2022-8-14 14:53
    #define EMIO_LED0                                 78

    可算是逼着自己写出来了,参考库函数里面的实现
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    2024-3-26 13:58
  • 3

    主题

    30

    帖子

    319

    积分

    中级会员

    Rank: 4

    积分
    319
    发表于 2022-8-14 21:22:32 | 显示全部楼层
    本帖最后由 CJC 于 2022-8-15 17:13 编辑

    #include "stdio.h"
    #include "sleep.h"
    #include "xgpiops_hw.h"
    #include "xgpiops.h"
    #include "xil_io.h"
    #include "xparameters.h"


    #define EMIO_LED0                                 78
    #define EMIO_KEY0                                 79

    uint8_t gpio_get_bank(uint8_t pin);
    uint8_t gpio_get_pin_number(uint8_t PinNumber,uint8_t bank);

    #define KEY0                        ((Xil_In32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(key_bank) * XGPIOPS_DATA_BANK_OFFSET \
                                                    + XGPIOPS_DATA_RO_OFFSET) >> (uint32_t)key_pin_number) & (uint32_t)1)

    int main(void)
    {
            uint8_t bank = 0;
            uint8_t pin_number = 0;
            uint8_t reg_val = 0;
            uint8_t data = 0;
            uint32_t reg_offset;
    //        uint8_t data_value;
            uint8_t key_bank;
            uint8_t key_pin_number;
            /* 得到哪个bank */
            bank = gpio_get_bank(EMIO_LED0);
            key_bank = gpio_get_bank(EMIO_KEY0);
            /* 得到哪个bank哪个引脚 */
            pin_number = gpio_get_pin_number(EMIO_LED0, bank);
            key_pin_number = gpio_get_pin_number(EMIO_KEY0, key_bank);
            if (pin_number > 15U) {
                    /* 这里仅对写数据有效,因为数据寄存器只能设置16bit */
    //                data_value = pin_number - (uint8_t)16;
                    reg_offset = XGPIOPS_DATA_MSW_OFFSET;
            } else {
                    reg_offset = XGPIOPS_DATA_LSW_OFFSET;
            }
            /* 设置方向为输出 */
            reg_val = Xil_In32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_REG_MASK_OFFSET
                                                            +XGPIOPS_DIRM_OFFSET);
            data = reg_val | (1 << pin_number);
            Xil_Out32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_REG_MASK_OFFSET
                                                                    +XGPIOPS_DIRM_OFFSET,data);
            /* 设置方向为输入 */
            reg_val = Xil_In32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(key_bank) * XGPIOPS_REG_MASK_OFFSET
                                                            +XGPIOPS_DIRM_OFFSET);
            data = reg_val | (1 << key_pin_number);
            Xil_Out32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(key_bank) * XGPIOPS_REG_MASK_OFFSET
                                                                    +XGPIOPS_DIRM_OFFSET,data);
            /* 设置输出使能 */
            reg_val = Xil_In32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_REG_MASK_OFFSET
                                                    + XGPIOPS_OUTEN_OFFSET);
            data = reg_val | (1 << pin_number);
            Xil_Out32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_REG_MASK_OFFSET
                                                                    +XGPIOPS_OUTEN_OFFSET,data);
            printf("hello zynq\r\n");

            while(1)
            {
                    if(KEY0 == 0)
                    {
                            /* 设置高16位允许更新低16位改变值 */
            //                data = (~(1 << 16) | (1 << 0));
                            data = ~((uint32_t)1 << (pin_number + 16U)) & ((1 << pin_number) | 0xFFFF0000U);
                            Xil_Out32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_DATA_MASK_OFFSET
                                                            + reg_offset, data);
                            usleep(500000);
            //                data = (~(1 << 16) | ~(1 << 0));
                            data = ~((uint32_t)1 << (pin_number + 16U)) & ((~(1 << pin_number)) | 0xFFFF0000U);
                            Xil_Out32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_DATA_MASK_OFFSET
                                                            + reg_offset, data);
                            usleep(500000);
                    }
                    else
                    {
                            data = ~((uint32_t)1 << (pin_number + 16U)) & ((1 << pin_number) | 0xFFFF0000U);
                            Xil_Out32(XPAR_XGPIOPS_0_BASEADDR + (uint32_t)(bank) * XGPIOPS_DATA_MASK_OFFSET
                                                            + reg_offset, data);
                    }

            }

            return 0;

    }

    uint8_t gpio_get_pin_number(uint8_t PinNumber,uint8_t bank)
    {
            uint8_t pin_number = 0;
            uint32_t XGpioPsPinTable[6] = {0};
            XGpioPsPinTable[0] = (u32)25;         /* 0 - 25, Bank 0 */
            XGpioPsPinTable[1] = (u32)51;         /* 26 - 51, Bank 1 */
            XGpioPsPinTable[2] = (u32)77;         /* 52 - 77, Bank 2 */
            XGpioPsPinTable[3] = (u32)109;         /* 78 - 109, Bank 3 */
            XGpioPsPinTable[4] = (u32)141;         /* 110 - 141, Bank 4 */
            XGpioPsPinTable[5] = (u32)173;         /* 142 - 173 Bank 5 */

            pin_number = (uint8_t)((uint32_t)PinNumber %
                                                    (XGpioPsPinTable[bank - (uint8_t)1] + (uint32_t)1));

            return pin_number;
    }

    uint8_t gpio_get_bank(uint8_t pin)
    {
            uint8_t bank;

            if (pin >0 && pin <=25)
            {
                    bank = 0;
            }
            else if (pin >= 26 && pin <= 51)
            {
                    bank = 1;
            }
            else if (pin >= 52 && pin <= 77)
            {
                    bank = 2;
            }
            else if (pin >= 78 && pin <= 109)
            {
                    bank = 3;
            }
            else if (pin >= 110 && pin <= 141)
            {
                    bank = 4;
            }
            else if (pin >= 142 && pin <= 173)
            {
                    bank = 5;
            }

            return bank;
    }
    交作业啦
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    慵懒
    2021-2-24 10:16
  • 428

    主题

    811

    帖子

    1万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    16109
    QQ
     楼主| 发表于 2022-8-15 16:33:59 | 显示全部楼层
    CJC 发表于 2022-8-14 21:22
    #include "stdio.h"
    #include "sleep.h"
    #include "xgpiops_hw.h"

    不错不错,大部分场景下,我们用库函数就行了,非要使用寄存器的场合,也可以和你的思路一样,通过库函数去查找应该怎么操作寄存器
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|小黑屋|Archiver|芯路恒电子技术论坛 |鄂ICP备2021003648号

    GMT+8, 2024-11-24 11:19 , Processed in 0.131642 second(s), 35 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc. Template By 【未来科技】【 www.wekei.cn 】

    快速回复 返回顶部 返回列表