介紹
Pikascript 是 RT-Thread 軟件包中心 - 編程語言 中的一個包,是一個對單片機友好的輕量級 python 腳本支持工具,類似 micropython。
在 Pikascript 中,架構如下:
對于不同的平臺,我們需要手動為平臺適配 pika_hal 的設備抽象層接口。今天以 packages/pikascript-latest/pikaRTDevice/pika_hal_RTT_GPIO.c 為例,講解 Pikascript GPIO 接口如何基于 RT-Thread Pin 設備 rt-thread/components/misc/pin.c 實現。
講解
模型如下:
所有設備均遵循類 linux 文件的編程模型,所有類型的設備均使用 pika_dev 結構體來作為設備句柄。
pika_dev 類型定義:
typedef struct {
PIKA_HAL_DEV_TYPE type;
PIKA_BOOL is_enabled;
void* ioctl_config;
void* platform_data;
} pika_dev;
在 RT-Thread 的文檔中可以得知,應用程序通過 RT-Thread 提供的 PIN 設備管理接口來訪問 GPIO,相關接口如下所示:
// 通過設備名,返回 pin num
rt_base_t rt_pin_get(const char *name);
// 通過 pin num,返回該 pin 的數據
int rt_pin_read(rt_base_t pin);
// 把 value 電平信息寫到對應 pin 上
void rt_pin_write(rt_base_t pin, rt_base_t value);
// 把 pin 的模式設置為 mode
void rt_pin_mode(rt_base_t pin, rt_base_t mode)
所以經過分析,不難看出我們在 open 中要通過 rt_pin_get() 獲取引腳編號,獲取設備信息;在 read 中要通過 rt_pin_read() 讀取引腳電平;在 write 中要通過 rt_pin_write() 設置引腳電平。
在 pika_hal_RTT_GPIO.c 中,我們一共有 7 個接口要實現:
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);
下面我們依次進行講解。
dev->platform_io 中存儲的數據
首先定義一個結構體 platform_data_GPIO 用來存在 dev->platform_data 中,由上面對 rtt pin 接口的簡單調用分析可知,只需要 pin 的數據:
typedef struct platform_data_GPIO {
uint32_t pin_num;
} platform_data_GPIO;
pika_hal_platform_GPIO_open
函數的原型為: int pika_hal_platform_GPIO_open(pika_dev* dev, char* name);
參數:
pika_dev* dev: 要操作的設備句柄
char* name: GPIO 設備名
函數功能:
根據 GPIO 設備名,找到對應的 GPIO 設備的 pin
把設備 pin 數據存在 dev->platform_data 里面
實現:
int pika_hal_platform_GPIO_open(pika_dev* dev, char* name) {
// 打印當前信息
rt_kprintf("rn=%s==%s=%d=name:%s==rn", FILE , FUNCTION , LINE ,name);
// 打印一下日志信息,當前正在打開哪個設備
__platform_printf("Open: %s rn", name);
// 調用 pikaMalloc 分配內存,創建一個 platform_data_GPIO 結構體,用來存放這個 GPIO 設備的信息
platform_data_GPIO* data = pikaMalloc(sizeof(platform_data_GPIO));
// 在 RT_USING_PIN 這個宏定義存在時,通過 rt_pin_get 函數獲取這個 GPIO 設備的引腳號,存放在 platform_data_GPIO 結構體的 pin_num 成員中。
#ifdef RT_USING_PIN
data->pin_num = rt_pin_get(name) ;
#endif
// 將創建的 platform_data_GPIO 結構體賦值給 dev->platform_data
dev->platform_data = data;
return 0;
}
pika_hal_platform_GPIO_close
函數的原型為:int pika_hal_platform_GPIO_close(pika_dev* dev);
參數:
pika_dev* dev: 要操作的設備句柄
函數功能:
清除這個 GPIO 設備的信息,即清空 dev->platform_data 中的數據
實現:
int pika_hal_platform_GPIO_close(pika_dev* dev) {
rt_kprintf("rn=%s==%s=%d===rn", FILE , FUNCTION , LINE );
// 如果現在有 GPIO 設備數據,就清空
if (NULL != dev->platform_data) {
pikaFree(dev->platform_data, sizeof(platform_data_GPIO));
dev->platform_data = NULL;
}
return 0;
}
pika_hal_platform_GPIO_read
函數的原型為:int pika_hal_platform_GPIO_write(pika_dev* dev, void* buf, size_t count);
參數:
pika_dev dev:要操作的設備句柄
void buf:讀取緩沖區
size_t count:讀取數據長度,對于 GPIO、ADC 這樣只能讀取單個數據的設備,長度為 sizeof(uint32_t)
函數功能:
根據之前存到 dev->platform_data 中的 pin num 數據,調用 rt_pin_read() 函數來獲取該 pin 的數據
把讀取到的數據存到 buf 緩沖區中
實現:
int pika_hal_platform_GPIO_read(pika_dev* dev, void* buf, size_t count) {
// 獲取之前存放的platform_data_GPIO結構體指針
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
// 根據 pin num 讀取電平
level = rt_pin_read(data->pin_num);
#endif
// 只有可能是 0 或 1
if (level != 1 && level != 0) {
return -1;
}
// 把 &level 處的 count 個(sizeof(uint32_t) 個)數據拷貝到 buf 緩存區,memcpy 函數不關心 buf 和 src 指向的內存是什么類型,它只根據 count 拷貝內存
memcpy(buf, &level, count);
return 0;
}
注意:
在文檔中指出,GPIO 設備 read 時讀取的數據 count 應該為 sizeof(uin32_t),在 pika_hal_platform_GPIO_read 中,level 的類型設置為 uint32_t,這是和文檔要求一致的。而給 level 賦值的 rt_pin_read() 函數的返回類型為 int,這里進行了一個隱式類型轉換。
pika_hal_platform_GPIO_write
函數的原型為:int pika_hal_platform_GPIO_write(pika_dev* dev, void* buf, size_t count);
參數:
pika_dev dev:要操作的設備句柄
oid buf:寫入緩沖區
size_t count:寫入數據長度,對于 GPIO、ADC 這樣只能讀取單個數據的設備,長度為 sizeof(uint32_t)
函數功能:
1.根據之前存儲的 dev->platform_data 信息獲取 pin num
2.獲取之前 buf 中存儲的電平信息
3.把電平信息寫到對應 pin 上
實現:
int pika_hal_platform_GPIO_write(pika_dev* dev, void* buf, size_t count) {
// 獲取之前 platform_data 數據
platform_data_GPIO* data = dev->platform_data;
// 獲取 buf 緩存區存儲的高低電平信息
uint32_t level = 0;
memcpy(&level, buf, count);
// 把電平寫到對應 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
函數的原型為:int pika_hal_platform_GPIO_ioctl_enable(pika_dev* dev);
參數:
pika_dev* dev:被操作的設備句柄
函數功能:
這個函數其實對應的是 pika_hal_ioctl(pika_dev* dev, PIKA_HAL_IOCTL_ENABLE) 這里的情況,使能了這個配置函數
所以要初始化一下 GPIO 的 pin num、輸入輸出模式、推挽模式、波特率等數據
實現:
目前只實現了打印日志
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
函數的原型為:int pika_hal_platform_GPIO_ioctl_disable(pika_dev dev);
參數:
pika_dev* dev:被操作的設備句柄
函數功能:
這個函數其實對應的是 pika_hal_ioctl(pika_dev* dev, PIKA_HAL_IOCTL_DISABLE) 這里的情況
實現:
同 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
函數的原型為:int pika_hal_platform_GPIO_ioctl_config(pika_dev* dev, pika_hal_GPIO_config* cfg);
參數:
pika_dev dev:被操作的設備句柄
pika_hal_GPIO_config cfg:GPIO 配置,具體定義如下:
typedef struct {
PIKA_HAL_GPIO_DIR dir;//輸入輸出
PIKA_HAL_GPIO_PULL pull;//推挽模式
PIKA_HAL_GPIO_SPEED speed;//數據傳輸速率
void (event_callback)(pika_dev dev, PIKA_HAL_GPIO_EVENT_SIGNAL signal);//事件回調函數
PIKA_HAL_GPIO_EVENT_SIGNAL event_callback_filter;//上升沿還是下降沿
//事件回調是否使能
PIKA_HAL_EVENT_CALLBACK_ENA event_callback_ena;
} pika_hal_GPIO_config;
函數功能:
1.對輸入輸出進行討論,分為 PIKA_HAL_GPIO_DIR_OUT、PIKA_HAL_GPIO_DIR_IN 兩種
2.對推挽模式進行討論,分為 PIKA_HAL_GPIO_PULL_NONE、PIKA_HAL_GPIO_PULL_UP、PIKA_HAL_GPIO_PULL_DOWN 三種
3.討論事件回調是否使能、是否設置了回調函數
4.討論回調函數是上升沿觸發還是下降沿觸發(PIKA_HAL_GPIO_EVENT_SIGNAL_RISING 以及 PIKA_HAL_GPIO_EVENT_SIGNAL_FALLING)
實現:
現有實現中并沒有管回調函數的部分,所以相對簡單
RT-Thread 文檔中關于 void rt_pin_mode(rt_base_t pin, rt_base_t mode); 函數的 mode 可選項為:
#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 / 開漏輸出 */
所以最外層討論輸入輸出,內層討論上拉下拉即可。
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;
// 對 cfg 中各項分類討論,從而確定 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 的模式設置為 pinMode
#ifdef RT_USING_PIN
rt_pin_mode(data->pin_num, pinMode);
#endif
return 0;
}
Todo
作者也在閱讀源碼的過程中發現了一些問題:
1.對失敗的情況有時候沒有做討論,如 pika_hal_platform_GPIO_open 函數中,顯然 rt_pin_get() 是可能獲取不到的,此時應該返回 -1 表示出錯并打印有關日志信息,但現有代碼中沒有這部分處理
2.pika_hal_platform_GPIO_ioctl_config 中沒有配置為開漏模式的情況,這意味著無法使用 pikascript 腳本將 GPIO 模式設置為開漏模式。
-
存儲器
+關注
關注
38文章
7454瀏覽量
163613 -
緩沖器
+關注
關注
6文章
1920瀏覽量
45450 -
GPIO
+關注
關注
16文章
1196瀏覽量
51924 -
python
+關注
關注
56文章
4782瀏覽量
84468 -
RTThread
+關注
關注
8文章
132瀏覽量
40811
發布評論請先 登錄
相關推薦
評論