如何使用 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/