1 本文的目的和結(jié)構(gòu)
1.1 本文的目的 和背景
為了給用戶提供操作GPIO的通用API,方便應(yīng)用程序開發(fā),RT-Thread中引入了通用GPIO設(shè)備驅(qū)動(dòng)。并提供類似Arduino風(fēng)格的API用于操作GPIO,如設(shè)置GPIO模式和輸出電平、讀取GPIO輸入電平、配置GPIO外部中斷。本文說明了如何使用RT-Thread的通用GPIO設(shè)備驅(qū)動(dòng)。
1.2 本文的結(jié)構(gòu)
本文首先描述了RT-Thread 通用GPIO設(shè)備驅(qū)動(dòng)的基本情況,接下來給出了在正點(diǎn)原子STM32F4探索者開發(fā)板上驗(yàn)證的代碼示例,最后詳細(xì)描述了通用GPIO設(shè)備驅(qū)動(dòng)API的參數(shù)取值和注意事項(xiàng)。
2 問題闡述
RT-Thread提供了一套簡(jiǎn)單的I/O設(shè)備管理框架,它把I/O設(shè)備分成了三層進(jìn)行處理:應(yīng)用層、I/O設(shè)備管理層、硬件驅(qū)動(dòng)層。應(yīng)用程序通過RT-Thread的設(shè)備操作接口獲得正確的設(shè)備驅(qū)動(dòng),然后通過這個(gè)設(shè)備驅(qū)動(dòng)與底層I/O硬件設(shè)備進(jìn)行數(shù)據(jù)(或控制)交互。RT-Thread提供給上層應(yīng)用的是一個(gè)抽象的設(shè)備操作接口,給下層設(shè)備提供的是底層驅(qū)動(dòng)框架。對(duì)于通用GPIO設(shè)備,應(yīng)用程序既可以通過設(shè)備操作接口訪問,又可以直接通過通用GPIO設(shè)備驅(qū)動(dòng)來訪問。一般來說,我們都是使用第二種方式,那么如何在RT-Thread中使用通用GPIO設(shè)備驅(qū)動(dòng)從而操作GPIO呢?
圖A. 1 RT-Thread設(shè)備管理框架
3 問題的解決
本文基于正點(diǎn)原子STM32F4探索者開發(fā)板,給出了通用GPIO設(shè)備的具體應(yīng)用示例代碼,包含管腳輸入、輸出和外部中斷的使用方法。由于RT-Thread上層應(yīng)用API的通用性,因此這些代碼不局限于具體的硬件平臺(tái),用戶可以輕松將它移植到其它平臺(tái)上。
正點(diǎn)原子 STM32F4 探索者開發(fā)板使用的MCU是 STM32F407ZET6,板載2顆LED和4個(gè)獨(dú)立按鍵。LED分別連接到MCU的GPIOF9、GPIOF10,KEY0按鍵連接到GPIOE4,KEY1按鍵連接到GPIOE3,KEY2按鍵連接到GPIOE2,WK_UP按鍵連接到GPIOA0,2顆LED均為低電平點(diǎn)亮,獨(dú)立按鍵KEY0、KEY1、KEY2按下為低電平;WK_UP按下為高電平。
圖A. 2 實(shí)驗(yàn)用正點(diǎn)原子開發(fā)板
3.1 準(zhǔn)備和配置工程
1. 下載 RT-Thread 源碼 https://github.com/RT-Thread/rt-thread
2. 進(jìn)入 rt-threadspstm32f4xx-HAL 目錄,在 env 命令行中輸入menuconfig,進(jìn)入配置界面,使用 menuconfig 工具(學(xué)習(xí)如何使用)配置工程。
1) 在menuconfig配置界面依次選擇RT-Thread Components ---》 Device Drivers ---》 Using generic GPIO device drivers,如圖所示:
圖A. 3 menuconfig中開啟GPIO驅(qū)動(dòng)
2) 輸入scons --target=mdk5 -s
命令生成mdk5工程。將本應(yīng)用筆記附帶的main.c替換掉bsp中的main.c,如圖所示:
圖A. 4 加入測(cè)試代碼
3) 編譯,下載程序,在終端輸入list_device命令可以看到pin device、類型是Miscellaneous Device就說明通用GPIO設(shè)備驅(qū)動(dòng)添加成功了。
圖A. 5 查看pin設(shè)備
下面是3個(gè)通用GPIO設(shè)備驅(qū)動(dòng)API應(yīng)用示例,分別是:GPIO輸出、GPIO輸入、GPIO外部中斷,這些代碼在正點(diǎn)原子STM32F4探索者開發(fā)板上驗(yàn)證通過。
3.2 GPIO輸出配置
示例1:配置GPIO為輸出,點(diǎn)亮LED。根據(jù)原理圖,GPIOF9連接到了板載紅色LED,絲印為DS0;GPIOF10連接到了板載綠色LED,絲印為DS1。GPIOF9輸出低電平則點(diǎn)亮DS0,GPIOF9輸出高電平則DS0不亮;GPIOF10輸出低電平則點(diǎn)亮DS1,GPIOF10輸出高電平則DS1不亮。
圖A. 6 LED原理圖
#define LED0 21 //PF9--21,在 drv_gpio.c 文件 pin_index pins[]中查到 PF9 編號(hào)為 21
#define LED1 22 //PF10--21,在 drv_gpio.c 文件 pin_index pins[]中查到 PF10 編號(hào)為 22
void led_thread_entry(void* parameter)
{
//設(shè)置管腳為輸出模式
rt_pin_mode(LED0, PIN_MODE_OUTPUT);
//設(shè)置管腳為輸出模式
rt_pin_mode(LED1, PIN_MODE_OUTPUT);
while (1)
{
//輸出低電平,LED0 亮
rt_pin_write(LED0, PIN_LOW);
//輸出低電平,LED1 亮
rt_pin_write(LED1, PIN_LOW);
//掛起 500ms
rt_thread_delay(rt_tick_from_millisecond(500));
//輸出高電平,LED0 滅
rt_pin_write(LED0, PIN_HIGH);
//輸出高電平,LED1 滅
rt_pin_write(LED1, PIN_HIGH);
//掛起 500ms
rt_thread_delay(rt_tick_from_millisecond(500));
}
}
在線程入口函數(shù)led_thread_entry里首先調(diào)用rt_pin_mode設(shè)置管腳模式為輸出模式,然后就進(jìn)入while(1)循環(huán),間隔500ms調(diào)用rt_pin_write來改變GPIO輸出電平。
下面是創(chuàng)建線程的代碼:
rt_thread_t tid;//線程句柄
/* 創(chuàng)建led線程 */
tid = rt_thread_create(“l(fā)ed”,
led_thread_entry,
RT_NULL,
1024,
3,
10);
/* 創(chuàng)建成功則啟動(dòng)線程 */
if (tid != RT_NULL)
rt_thread_startup(tid);
編譯、下載程序,我們將看到LED間隔500ms閃爍的現(xiàn)象。
3.3 GPIO輸入配置
示例2:配置GPIOE3、GPIOE2為上拉輸入,GPIOA0為下拉輸入,檢測(cè)按鍵信號(hào)。根據(jù)原理圖,GPIOE3連接到按鍵KEY1,按鍵被按下時(shí)GPIOE3應(yīng)讀取到低電平,按鍵沒有被按下時(shí)GPIOE3應(yīng)讀取到高電平;GPIOE2連接到按鍵KEY2,按鍵被按下時(shí)GPIOE2應(yīng)讀取到低電平,按鍵沒有被按下時(shí)GPIOE2應(yīng)讀取到高電平;GPIOA0連接到按鍵WK_UP,按鍵被按下時(shí)GPIOA0應(yīng)讀取到高電平,按鍵沒有被按下時(shí)GPIOA0應(yīng)讀取到低電平。
圖A. 7 按鍵原理圖
#define KEY1 2 //PE3--2,在 drv_gpio.c 文件 pin_index pins[]中查到 PE3 編號(hào)為 2
#define KEY2 1 //PE2--1,在 drv_gpio.c 文件 pin_index pins[]中查到 PE2 編號(hào)為 1
#define WK_UP 34 //PA0--34,在 drv_gpio.c 文件 pin_index pins[]中查到 PA0 編號(hào)為 34
void key_thread_entry(void* parameter)
{
//PE2、PE3設(shè)置上拉輸入
rt_pin_mode(KEY1, PIN_MODE_INPUT_PULLUP);
rt_pin_mode(KEY2, PIN_MODE_INPUT_PULLUP);
//PA0設(shè)置為下拉輸入
rt_pin_mode(WK_UP, PIN_MODE_INPUT_PULLDOWN);
while (1)
{
//檢測(cè)到低電平,即按鍵1按下了
if (rt_pin_read(KEY1) == PIN_LOW)
{
rt_kprintf(“key1 pressed! ”);
}
//檢測(cè)到低電平,即按鍵2按下了
if (rt_pin_read(KEY2) == PIN_LOW)
{
rt_kprintf(“key2 pressed! ”);
}
//檢測(cè)到高電平,即按鍵wp按下了
if (rt_pin_read(WK_UP) == PIN_HIGH)
{
rt_kprintf(“WK_UP pressed! ”);
}
//掛起10ms
rt_thread_delay(rt_tick_from_millisecond(10));
}
}
在線程入口函數(shù)key_thread_entry里首先調(diào)用rt_pin_mode設(shè)置管腳GPIOE3為上拉輸入模式。這樣當(dāng)用戶按下按鍵KEY1時(shí),GPIOE3讀取到的電平是低電平;按鍵未被按下時(shí),GPIOE3讀取到的電平是高電平。然后進(jìn)入while(1)循環(huán),調(diào)用rt_pin_read讀取管腳GPIOE3電平,如果讀取到低電平則表示按鍵KEY1被按下,就在終端打印字符串“key1 pressed!”。每隔10ms檢測(cè)一次按鍵輸入情況。
下面是創(chuàng)建線程的代碼:
rt_thread_t tid;
/* 創(chuàng)建key線程 */
tid = rt_thread_create(“key”,
key_thread_entry,
RT_NULL,
1024,
2,
10);
/* 創(chuàng)建成功則啟動(dòng)線程 */
if (tid != RT_NULL)
rt_thread_startup(tid);
編譯、下載程序,我們按下開發(fā)板上的用戶按鍵,終端將打印提示字符。
3.4 GPIO中斷配置
示例3:配置GPIO為外部中斷模式、下降沿觸發(fā),檢測(cè)按鍵信號(hào)。根據(jù)原理圖,GPIOE4連接到按鍵KEY0,按鍵被按下時(shí)MCU應(yīng)探測(cè)到電平下降沿。
#define KEY0 3 //PE4--3,在gpio.c文件pin_index pins[]中查到PE4編號(hào)為3
void hdr_callback(void *args)//回調(diào)函數(shù)
{
char *a = args;//獲取參數(shù)
rt_kprintf(“key0 down! %s ”,a);
}
void irq_thread_entry(void* parameter)
{
//上拉輸入
rt_pin_mode(KEY0, PIN_MODE_INPUT_PULLUP);
//綁定中斷,下降沿模式,回調(diào)函數(shù)名為hdr_callback
rt_pin_attach_irq(KEY0, PIN_IRQ_MODE_FALLING, hdr_callback, (void*)“callback
args”);
//使能中斷
rt_pin_irq_enable(KEY0, PIN_IRQ_ENABLE);
}
在線程入口函數(shù)irq_thread_entry里首先調(diào)用rt_pin_attach_irq設(shè)置管腳GPIOE4為下降沿中斷模式,并綁定了中斷回調(diào)函數(shù),還傳入了字符串“callback args”。然后調(diào)用rt_pin_irq_enable使能中斷,這樣按鍵KEY0被按下時(shí)MCU會(huì)檢測(cè)到電平下降沿,觸發(fā)外部中斷,在中斷服務(wù)程序中會(huì)調(diào)用回調(diào)函數(shù)hdr_callback,在回調(diào)函數(shù)中打印傳入的參數(shù)和提示信息。
下面是創(chuàng)建線程的代碼:
rt_thread_t tid;//線程句柄
/* 創(chuàng)建irq線程 */
tid = rt_thread_create(“exirq”,
irq_thread_entry,
RT_NULL,
1024,
4,
10);
/* 創(chuàng)建成功則啟動(dòng)線程 */
if (tid != RT_NULL)
rt_thread_startup(tid);
編譯、下載程序,我們按下按鍵KEY0,終端將打印提示字符。
3.5 I/O設(shè)備管理框架和通用GPIO設(shè)備聯(lián)系
RT-Thread自動(dòng)初始化功能依次調(diào)用rt_hw_pin_init ===》 rt_device_pin_register ===》 rt_device_register完成了GPIO硬件初始化。rt_device_register注冊(cè)設(shè)備類型為RT_Device_Class_Miscellaneous,即雜類設(shè)備,從而我們就可以使用統(tǒng)一的API操作GPIO。
圖A. 8 通用GPIO驅(qū)動(dòng)和設(shè)備管理框架聯(lián)系
更多關(guān)于I/O設(shè)備管理框架的說明,請(qǐng)參考《RT-Thread編程手冊(cè)》第 6 章 I/O設(shè)備管理,在線查看地址:https://www.rt-thread.org/document/site/zh/1chapters/06-chapter_device/?
4 參考
4.1 本文所有相關(guān)的API
要使用這些API需引用頭文件
#include
4.1.1 API 列表(Summary)
評(píng)論