介紹
Pikascript 是 RT-Thread 軟件包中心 - 編程語言 中的一個(gè)包,是一個(gè)對(duì)單片機(jī)友好的輕量級(jí) python 腳本支持工具,類似 micropython。
在 Pikascript 中,架構(gòu)如下:
對(duì)于不同的平臺(tái),我們需要手動(dòng)為平臺(tái)適配 pika_hal 的設(shè)備抽象層接口。今天以 packages/pikascript-latest/pikaRTDevice/pika_hal_RTT_GPIO.c 為例,講解 Pikascript GPIO 接口如何基于 RT-Thread Pin 設(shè)備 rt-thread/components/misc/pin.c 實(shí)現(xiàn)。
講解
模型如下:
所有設(shè)備均遵循類 linux 文件的編程模型,所有類型的設(shè)備均使用 pika_dev 結(jié)構(gòu)體來作為設(shè)備句柄。
pika_dev 類型定義:
typedef struct {
PIKA_HAL_DEV_TYPE type;
PIKA_BOOL is_enabled;
void* ioctl_config;
void* platform_data;
} pika_dev;
在 RT-Thread 的文檔中可以得知,應(yīng)用程序通過 RT-Thread 提供的 PIN 設(shè)備管理接口來訪問 GPIO,相關(guān)接口如下所示:
// 通過設(shè)備名,返回 pin num
rt_base_t rt_pin_get(const char *name);
// 通過 pin num,返回該 pin 的數(shù)據(jù)
int rt_pin_read(rt_base_t pin);
// 把 value 電平信息寫到對(duì)應(yīng) pin 上
void rt_pin_write(rt_base_t pin, rt_base_t value);
// 把 pin 的模式設(shè)置為 mode
void rt_pin_mode(rt_base_t pin, rt_base_t mode)
所以經(jīng)過分析,不難看出我們?cè)?open 中要通過 rt_pin_get() 獲取引腳編號(hào),獲取設(shè)備信息;在 read 中要通過 rt_pin_read() 讀取引腳電平;在 write 中要通過 rt_pin_write() 設(shè)置引腳電平。
在 pika_hal_RTT_GPIO.c 中,我們一共有 7 個(gè)接口要實(shí)現(xiàn):
int pika_hal_platform_GPIO_open(pika_dev* dev, char* name);
int pika_hal_platform_GPIO_close(pika_dev* dev);
int pika_hal_platform_GPIO_read(pika_dev* dev, void* buf, size_t count);
int pika_hal_platform_GPIO_write(pika_dev* dev, void* buf, size_t count);
int pika_hal_platform_GPIO_ioctl_enable(pika_dev* dev);
int pika_hal_platform_GPIO_ioctl_disable(pika_dev* dev);
int pika_hal_platform_GPIO_ioctl_config(pika_dev* dev, pika_hal_GPIO_config* cfg);
下面我們依次進(jìn)行講解。
dev->platform_io 中存儲(chǔ)的數(shù)據(jù)
首先定義一個(gè)結(jié)構(gòu)體 platform_data_GPIO 用來存在 dev->platform_data 中,由上面對(duì) rtt pin 接口的簡單調(diào)用分析可知,只需要 pin 的數(shù)據(jù):
typedef struct platform_data_GPIO {
uint32_t pin_num;
} platform_data_GPIO;
pika_hal_platform_GPIO_open
函數(shù)的原型為: int pika_hal_platform_GPIO_open(pika_dev* dev, char* name);
參數(shù):
pika_dev* dev: 要操作的設(shè)備句柄
char* name: GPIO 設(shè)備名
函數(shù)功能:
根據(jù) GPIO 設(shè)備名,找到對(duì)應(yīng)的 GPIO 設(shè)備的 pin
把設(shè)備 pin 數(shù)據(jù)存在 dev->platform_data 里面
實(shí)現(xiàn):
int pika_hal_platform_GPIO_open(pika_dev* dev, char* name) {
// 打印當(dāng)前信息
rt_kprintf("rn=%s==%s=%d=name:%s==rn", FILE , FUNCTION , LINE ,name);
// 打印一下日志信息,當(dāng)前正在打開哪個(gè)設(shè)備
__platform_printf("Open: %s rn", name);
// 調(diào)用 pikaMalloc 分配內(nèi)存,創(chuàng)建一個(gè) platform_data_GPIO 結(jié)構(gòu)體,用來存放這個(gè) GPIO 設(shè)備的信息
platform_data_GPIO* data = pikaMalloc(sizeof(platform_data_GPIO));
// 在 RT_USING_PIN 這個(gè)宏定義存在時(shí),通過 rt_pin_get 函數(shù)獲取這個(gè) GPIO 設(shè)備的引腳號(hào),存放在 platform_data_GPIO 結(jié)構(gòu)體的 pin_num 成員中。
#ifdef RT_USING_PIN
data->pin_num = rt_pin_get(name) ;
#endif
// 將創(chuàng)建的 platform_data_GPIO 結(jié)構(gòu)體賦值給 dev->platform_data
dev->platform_data = data;
return 0;
}
pika_hal_platform_GPIO_close
函數(shù)的原型為:int pika_hal_platform_GPIO_close(pika_dev* dev);
參數(shù):
pika_dev* dev: 要操作的設(shè)備句柄
函數(shù)功能:
清除這個(gè) GPIO 設(shè)備的信息,即清空 dev->platform_data 中的數(shù)據(jù)
實(shí)現(xiàn):
int pika_hal_platform_GPIO_close(pika_dev* dev) {
rt_kprintf("rn=%s==%s=%d===rn", FILE , FUNCTION , LINE );
// 如果現(xiàn)在有 GPIO 設(shè)備數(shù)據(jù),就清空
if (NULL != dev->platform_data) {
pikaFree(dev->platform_data, sizeof(platform_data_GPIO));
dev->platform_data = NULL;
}
return 0;
}
pika_hal_platform_GPIO_read
函數(shù)的原型為:int pika_hal_platform_GPIO_write(pika_dev* dev, void* buf, size_t count);
參數(shù):
pika_dev dev:要操作的設(shè)備句柄
void buf:讀取緩沖區(qū)
size_t count:讀取數(shù)據(jù)長度,對(duì)于 GPIO、ADC 這樣只能讀取單個(gè)數(shù)據(jù)的設(shè)備,長度為 sizeof(uint32_t)
函數(shù)功能:
根據(jù)之前存到 dev->platform_data 中的 pin num 數(shù)據(jù),調(diào)用 rt_pin_read() 函數(shù)來獲取該 pin 的數(shù)據(jù)
把讀取到的數(shù)據(jù)存到 buf 緩沖區(qū)中
實(shí)現(xiàn):
int pika_hal_platform_GPIO_read(pika_dev* dev, void* buf, size_t count) {
// 獲取之前存放的platform_data_GPIO結(jié)構(gòu)體指針
platform_data_GPIO* data = dev->platform_data;
uint32_t level;
rt_kprintf("rn=%s==%s=%d=gpio:%d==rn", FILE , FUNCTION , LINE ,data->pin_num);
#ifdef RT_USING_PIN
// 根據(jù) pin num 讀取電平
level = rt_pin_read(data->pin_num);
#endif
// 只有可能是 0 或 1
if (level != 1 && level != 0) {
return -1;
}
// 把 &level 處的 count 個(gè)(sizeof(uint32_t) 個(gè))數(shù)據(jù)拷貝到 buf 緩存區(qū),memcpy 函數(shù)不關(guān)心 buf 和 src 指向的內(nèi)存是什么類型,它只根據(jù) count 拷貝內(nèi)存
memcpy(buf, &level, count);
return 0;
}
注意:
在文檔中指出,GPIO 設(shè)備 read 時(shí)讀取的數(shù)據(jù) count 應(yīng)該為 sizeof(uin32_t),在 pika_hal_platform_GPIO_read 中,level 的類型設(shè)置為 uint32_t,這是和文檔要求一致的。而給 level 賦值的 rt_pin_read() 函數(shù)的返回類型為 int,這里進(jìn)行了一個(gè)隱式類型轉(zhuǎn)換。
pika_hal_platform_GPIO_write
函數(shù)的原型為:int pika_hal_platform_GPIO_write(pika_dev* dev, void* buf, size_t count);
參數(shù):
pika_dev dev:要操作的設(shè)備句柄
oid buf:寫入緩沖區(qū)
size_t count:寫入數(shù)據(jù)長度,對(duì)于 GPIO、ADC 這樣只能讀取單個(gè)數(shù)據(jù)的設(shè)備,長度為 sizeof(uint32_t)
函數(shù)功能:
1.根據(jù)之前存儲(chǔ)的 dev->platform_data 信息獲取 pin num
2.獲取之前 buf 中存儲(chǔ)的電平信息
3.把電平信息寫到對(duì)應(yīng) pin 上
實(shí)現(xiàn):
int pika_hal_platform_GPIO_write(pika_dev* dev, void* buf, size_t count) {
// 獲取之前 platform_data 數(shù)據(jù)
platform_data_GPIO* data = dev->platform_data;
// 獲取 buf 緩存區(qū)存儲(chǔ)的高低電平信息
uint32_t level = 0;
memcpy(&level, buf, count);
// 把電平寫到對(duì)應(yīng) pin 上
#ifdef RT_USING_PIN
if (level == 0) {
rt_pin_write(data->pin_num, PIN_LOW);
return 0;
}
if (level == 1) {
rt_pin_write(data->pin_num, PIN_HIGH);
return 0;
}
#endif
return 0;
}
pika_hal_platform_GPIO_ioctl_enable
函數(shù)的原型為:int pika_hal_platform_GPIO_ioctl_enable(pika_dev* dev);
參數(shù):
pika_dev* dev:被操作的設(shè)備句柄
函數(shù)功能:
這個(gè)函數(shù)其實(shí)對(duì)應(yīng)的是 pika_hal_ioctl(pika_dev* dev, PIKA_HAL_IOCTL_ENABLE) 這里的情況,使能了這個(gè)配置函數(shù)
所以要初始化一下 GPIO 的 pin num、輸入輸出模式、推挽模式、波特率等數(shù)據(jù)
實(shí)現(xiàn):
目前只實(shí)現(xiàn)了打印日志
int pika_hal_platform_GPIO_ioctl_enable(pika_dev* dev) {
platform_data_GPIO* data = dev->platform_data;
rt_kprintf("rn=%s==%s=%d=pin_num:%x==rn", FILE , FUNCTION , LINE ,data->pin_num);
/* TODO /
return 0;
}
pika_hal_platform_GPIO_ioctl_disable
函數(shù)的原型為:int pika_hal_platform_GPIO_ioctl_disable(pika_dev dev);
參數(shù):
pika_dev* dev:被操作的設(shè)備句柄
函數(shù)功能:
這個(gè)函數(shù)其實(shí)對(duì)應(yīng)的是 pika_hal_ioctl(pika_dev* dev, PIKA_HAL_IOCTL_DISABLE) 這里的情況
實(shí)現(xiàn):
同 enable 部分,disable 也只是打印了日志
int pika_hal_platform_GPIO_ioctl_disable(pika_dev* dev) {
rt_kprintf("rn=%s==%s=%d===rn", FILE , FUNCTION , LINE );
platform_data_GPIO* data = dev->platform_data;
return -1;
}
pika_hal_platform_GPIO_ioctl_config
函數(shù)的原型為:int pika_hal_platform_GPIO_ioctl_config(pika_dev* dev, pika_hal_GPIO_config* cfg);
參數(shù):
pika_dev dev:被操作的設(shè)備句柄
pika_hal_GPIO_config cfg:GPIO 配置,具體定義如下:
typedef struct {
PIKA_HAL_GPIO_DIR dir;//輸入輸出
PIKA_HAL_GPIO_PULL pull;//推挽模式
PIKA_HAL_GPIO_SPEED speed;//數(shù)據(jù)傳輸速率
void (event_callback)(pika_dev dev, PIKA_HAL_GPIO_EVENT_SIGNAL signal);//事件回調(diào)函數(shù)
PIKA_HAL_GPIO_EVENT_SIGNAL event_callback_filter;//上升沿還是下降沿
//事件回調(diào)是否使能
PIKA_HAL_EVENT_CALLBACK_ENA event_callback_ena;
} pika_hal_GPIO_config;
函數(shù)功能:
1.對(duì)輸入輸出進(jìn)行討論,分為 PIKA_HAL_GPIO_DIR_OUT、PIKA_HAL_GPIO_DIR_IN 兩種
2.對(duì)推挽模式進(jìn)行討論,分為 PIKA_HAL_GPIO_PULL_NONE、PIKA_HAL_GPIO_PULL_UP、PIKA_HAL_GPIO_PULL_DOWN 三種
3.討論事件回調(diào)是否使能、是否設(shè)置了回調(diào)函數(shù)
4.討論回調(diào)函數(shù)是上升沿觸發(fā)還是下降沿觸發(fā)(PIKA_HAL_GPIO_EVENT_SIGNAL_RISING 以及 PIKA_HAL_GPIO_EVENT_SIGNAL_FALLING)
實(shí)現(xiàn):
現(xiàn)有實(shí)現(xiàn)中并沒有管回調(diào)函數(shù)的部分,所以相對(duì)簡單
RT-Thread 文檔中關(guān)于 void rt_pin_mode(rt_base_t pin, rt_base_t mode); 函數(shù)的 mode 可選項(xiàng)為:
#define PIN_MODE_OUTPUT 0x00 /* 輸出 /
#define PIN_MODE_INPUT 0x01 / 輸入 /
#define PIN_MODE_INPUT_PULLUP 0x02 / 上拉輸入 /
#define PIN_MODE_INPUT_PULLDOWN 0x03 / 下拉輸入 /
#define PIN_MODE_OUTPUT_OD 0x04 / 開漏輸出 */
所以最外層討論輸入輸出,內(nèi)層討論上拉下拉即可。
int pika_hal_platform_GPIO_ioctl_config(pika_dev* dev,
pika_hal_GPIO_config* cfg) {
rt_kprintf("rn=%s==%s=%d=dir:%d==rn", FILE , FUNCTION , LINE ,cfg->dir);
platform_data_GPIO* data = dev->platform_data;
uint8_t pinMode = 0;
// 對(duì) cfg 中各項(xiàng)分類討論,從而確定 pinMode
switch (cfg->dir) {
case PIKA_HAL_GPIO_DIR_IN:
switch(cfg->pull)
{
case PIKA_HAL_GPIO_PULL_UP:
pinMode = PIN_MODE_INPUT_PULLUP;
break;
case PIKA_HAL_GPIO_PULL_DOWN:
pinMode = PIN_MODE_INPUT_PULLDOWN;
break;
default:
pinMode = PIN_MODE_INPUT;
}
break;
case PIKA_HAL_GPIO_DIR_OUT:
pinMode = PIN_MODE_OUTPUT;
break;
default:
pinMode = PIN_MODE_OUTPUT;
}
// 將 pin 的模式設(shè)置為 pinMode
#ifdef RT_USING_PIN
rt_pin_mode(data->pin_num, pinMode);
#endif
return 0;
}
Todo
作者也在閱讀源碼的過程中發(fā)現(xiàn)了一些問題:
1.對(duì)失敗的情況有時(shí)候沒有做討論,如 pika_hal_platform_GPIO_open 函數(shù)中,顯然 rt_pin_get() 是可能獲取不到的,此時(shí)應(yīng)該返回 -1 表示出錯(cuò)并打印有關(guān)日志信息,但現(xiàn)有代碼中沒有這部分處理
2.pika_hal_platform_GPIO_ioctl_config 中沒有配置為開漏模式的情況,這意味著無法使用 pikascript 腳本將 GPIO 模式設(shè)置為開漏模式。
-
存儲(chǔ)器
+關(guān)注
關(guān)注
38文章
7637瀏覽量
166528 -
緩沖器
+關(guān)注
關(guān)注
6文章
2037瀏覽量
46687 -
GPIO
+關(guān)注
關(guān)注
16文章
1276瀏覽量
53628 -
python
+關(guān)注
關(guān)注
56文章
4825瀏覽量
86268 -
RTThread
+關(guān)注
關(guān)注
8文章
132瀏覽量
41564
發(fā)布評(píng)論請(qǐng)先 登錄
如何通過RT-Thred提供的pin設(shè)備管理接口來操作GPIO
RT-Thread PIN設(shè)備學(xué)習(xí)資料分享
RT Thread PIN組件的物理操作
怎樣通過RT-Thread提供的PIN設(shè)備管理接口來訪問GPIO呢
RTT系統(tǒng)初始化后_hw_pin指針總是為空這是為什么呢
GPIO設(shè)備接口主要有哪些功能呢
使用RTT的設(shè)備框架PIN設(shè)備去設(shè)置邊沿觸發(fā)無法關(guān)閉這是什么原因
使用RT-Thread與N32G457實(shí)現(xiàn)SPI接口的驅(qū)動(dòng)設(shè)計(jì)
pikascript移植報(bào)錯(cuò)不知道是哪里的問題?
STM32 通用GPIO模擬I2C實(shí)現(xiàn)

RTT_Draco的外置uart接口(TXD,RXD)怎么配置和使用呢?
gpio接口是干什么的 gpio四種輸入輸出模式怎么選擇
GPIO使用教程 GPIO接口應(yīng)用
RT-Thread PIN驅(qū)動(dòng)添加

評(píng)論