当前位置 博文首页 > 哈拎:NUC980 运行 RT-Thread 时使用 GPIO

    哈拎:NUC980 运行 RT-Thread 时使用 GPIO

    作者:哈拎 时间:2021-06-30 18:32

    如何使用 GPIO?

    NuMaker-RTU-NUC980 板子引出的 IO 有:

    分别有一个 I2C1、GPIO、SPI0、UART4,RT-Thread 中 NuMaker-RTU-NUC980 的默认工程也分别使能了这些外设:

    我在想一个问题,板子上就只引出了一个 GPIO,如果我要用多几个GPIO,在这个工程的基础上,需要做些什么,需要几步?

    测试工具 & 方法

    这里测试硬件是这个:

    板子上有 8 个LED,正极都接在一起,既共阳接法,负极接 IO 口,IO口拉低是 LED亮,拉高时 LED 灭,跟 NuMaker-RTU-NUC980 连接如下:

    猜想1,一步就行了

    直接当作 GPIO 使用,参考给出的代码,我写了个简单的测试程序,把板子上的 PB6、PB4、PC3、PC4、PC5、PC6、PC8、PC9直接设为输出,然后每隔一段时间翻转这些 IO,反复点亮、关闭 LED灯,查看这些 IO 是否能都被控制,这些 IO 正好覆盖了 I2C1、SPI0、UART4、GPIO,代码如下:

    #include <rtconfig.h>
    #include <rtdevice.h>
    #include <rtthread.h>
    #include <drv_gpio.h>
    
    #define LED0    NU_GET_PININDEX(NU_PB, 6)
    #define LED1   NU_GET_PININDEX(NU_PB, 4)
    #define LED2   NU_GET_PININDEX(NU_PC, 3)
    #define LED3   NU_GET_PININDEX(NU_PC, 4)
    #define LED4   NU_GET_PININDEX(NU_PC, 5)
    #define LED5   NU_GET_PININDEX(NU_PC, 6)
    #define LED6   NU_GET_PININDEX(NU_PC, 8)
    #define LED7   NU_GET_PININDEX(NU_PC, 9)
    
    
    int test_io(int argc, char **argv)
    {
        uint8_t i=0;
        rt_pin_mode(LED0, PIN_MODE_OUTPUT);
        rt_pin_mode(LED1, PIN_MODE_OUTPUT);
        rt_pin_mode(LED2, PIN_MODE_OUTPUT);
        rt_pin_mode(LED3, PIN_MODE_OUTPUT);
        rt_pin_mode(LED4, PIN_MODE_OUTPUT);
        rt_pin_mode(LED5, PIN_MODE_OUTPUT);
        rt_pin_mode(LED6, PIN_MODE_OUTPUT);
        rt_pin_mode(LED7, PIN_MODE_OUTPUT);
    
        for(i=0;i<100;i++)
        {
            rt_pin_write(LED0, PIN_HIGH);
            rt_pin_write(LED1, PIN_HIGH);
            rt_pin_write(LED2, PIN_HIGH);
            rt_pin_write(LED3, PIN_HIGH);
            rt_pin_write(LED4, PIN_HIGH);
            rt_pin_write(LED5, PIN_HIGH);
            rt_pin_write(LED6, PIN_HIGH);
            rt_pin_write(LED7, PIN_HIGH);
            rt_thread_mdelay(200);
            rt_pin_write(LED0, PIN_LOW);
            rt_pin_write(LED1, PIN_LOW);
            rt_pin_write(LED2, PIN_LOW);
            rt_pin_write(LED3, PIN_LOW);
            rt_pin_write(LED4, PIN_LOW);
            rt_pin_write(LED5, PIN_LOW);
            rt_pin_write(LED6, PIN_LOW);
            rt_pin_write(LED7, PIN_LOW);
            rt_thread_mdelay(200);
        }
        
        return 0;
    }
    
    MSH_CMD_EXPORT(test_io, io test app);
    

    编译运行,结果只有 GPIO PC3 对于的灯才有闪烁:

    证明在目前的工程代码中是无法把这些 IO 当作 GPIO 来使用

    猜想2,需要2步

    因为工程中启用了跟这些 IO 的外设,把这些相关外设去掉试下,首先在配置中不启用 I2C1、SPI0、UART4,相关配置如下:

    然后实现相关代码,跟第一个猜想一样,然后编译,运行,跟上一种情况一样,还是无法把其他 IO 用起来。

    猜想3,需要3步

    后来看了下代码,发现工程 borad目录下有这个文件 nu_pin_init.c,里面有些跟 IO 设置相关的函数,有跟 I2C1、SPI0、UART4相关部分,如下:

    static void nu_pin_uart_init(void)
    {
        /* UART0: GPF11, GPF12 */
        outpw(REG_SYS_GPF_MFPH, (inpw(REG_SYS_GPF_MFPH) & 0xFFF00FFF) | 0x00011000);
    
        /* UART4: GPC9, GPC10 */
        outpw(REG_SYS_GPC_MFPH, (inpw(REG_SYS_GPC_MFPH) & 0xFFFFF00F) | 0x00000770);
    
        /* UART8: GPC12, GPC13, GPC14 */
        outpw(REG_SYS_GPC_MFPH, (inpw(REG_SYS_GPC_MFPH) & 0xF000FFFF) | 0x07770000);
    }
    
    static void nu_pin_spi_init(void)
    {
        /* SPI0: PC[4, 8]  */
        outpw(REG_SYS_GPC_MFPL, (inpw(REG_SYS_GPC_MFPL) & 0xF000FFFF) | 0x05560000);
        outpw(REG_SYS_GPC_MFPH, (inpw(REG_SYS_GPC_MFPH) & 0xFFFFFFF0) | 0x00000005);
    }
    
    static void nu_pin_i2c_init(void)
    {
        /* I2C1: PB4, PB6 */
        outpw(REG_SYS_GPB_MFPL, (inpw(REG_SYS_GPB_MFPL) & 0xF0F0FFFF) | 0x02020000);
    }
    
    void nu_pin_init(void)
    {
        nu_pin_uart_init();
        nu_pin_emac_init();
        nu_pin_qspi_init();
        nu_pin_spi_init();
        nu_pin_i2c_init();
        nu_pin_can_init();
    
        nu_pin_usbd_init();
        nu_pin_usbh_init();
    }
    

    首先把跟 I2C1、SPI0、UART4相关部分 屏蔽了,还有两个步骤跟猜想2一样,编译运行:

    其中缘由

    其中缘由是,nuc980 IO 复用的问题。比如 uart4 对应得 IO口:

    一个 IO 可以复用为很多功能,比如 PC9可以用作 GPIO、USRT_TXD 等,可是同一时刻只能用于一种功能,上述代码中 uart4 相关的:

    outpw(REG_SYS_GPC_MFPH, (inpw(REG_SYS_GPC_MFPH) & 0xFFFFF00F) | 0x00000770);
    

    看下宏定义:

    /*!< GPIOC Low Byte Multiple Function Control Register */
    #define    REG_SYS_GPC_MFPL     (SYS_BA+0x080) 
    /*!< GPIOC High Byte Multiple Function Control Register */
    #define    REG_SYS_GPC_MFPH     (SYS_BA+0x084) 
    

    REG_SYS_GPC_MFPH 就是 IO 多功能设置寄存器:

    上述代码把 REG_SYS_GPC_MFPH 中 PC9、PC10 对应的部分设置为 7,正好是设置为 uart 功能,从参考手册上得知,NUC980 的 IO 上电默认是作为 GPIO的,如果设置了其他功能就不能用做 GPIO,也就无法直接拉高拉低。如果要把这些 IO 用作 GPIO,只能把这些 IO 复用设置相关的代码去掉。

    改进

    RT-Thread 工程是使用宏来进行条件编译的,改进下代码,对这些 IO 设置相关的代码也加些宏,如下:

    static void nu_pin_uart_init(void)
    {
        #if defined(BSP_USING_UART0)
        /* UART0: GPF11, GPF12 */
        outpw(REG_SYS_GPF_MFPH, (inpw(REG_SYS_GPF_MFPH) & 0xFFF00FFF) | 0x00011000);
        #endif
        #if defined(BSP_USING_UART4)
        /* UART4: GPC9, GPC10 */
        outpw(REG_SYS_GPC_MFPH, (inpw(REG_SYS_GPC_MFPH) & 0xFFFFF00F) | 0x00000770);
        #endif    
        #if defined(BSP_USING_UART8)
        /* UART8: GPC12, GPC13, GPC14 */
        outpw(REG_SYS_GPC_MFPH, (inpw(REG_SYS_GPC_MFPH) & 0xF000FFFF) | 0x07770000);
        #endif  
    }
    
    static void nu_pin_spi_init(void)
    {
    #if defined(BSP_USING_SPI0)
        /* SPI0: PC[4, 8]  */
        outpw(REG_SYS_GPC_MFPL, (inpw(REG_SYS_GPC_MFPL) & 0xF000FFFF) | 0x05560000);
        outpw(REG_SYS_GPC_MFPH, (inpw(REG_SYS_GPC_MFPH) & 0xFFFFFFF0) | 0x00000005);
    #endif    
    
    }
    
    static void nu_pin_i2c_init(void)
    {
    #if defined(BSP_USING_I2C1)
        /* I2C1: PB4, PB6 */
        outpw(REG_SYS_GPB_MFPL, (inpw(REG_SYS_GPB_MFPL) & 0xF0F0FFFF) | 0x02020000);
    #endif    
    }
    

    在原来的基础上做了这个改进,就可以实现 2 步使用 GPIO了:

    • 实现相关程序
    • 如果所使用 IO 对应的外设有使能,则关闭所使用 IO 对应的外设

    转载请注明出处:https://www.cnblogs.com/halin/

    bk