Linux下的gpio,gpiod
GPIO 應該是每個嵌入式設備都避免不了的。最近在做項目的時候,也遇到這方面的問題,所以簡單總結一下
現在內核里面多了gpiod的來控制gpio口,相對于原來的形式,使用gpiod的好處是我們申請后不進行free也沒有什么問題。但是你要是使用原來的方式后,一定要記得釋放。不釋放的話可能會有問題。
#舊的GPIO使用實例
DTS文件
det-gpios = <&gpio3 RK_PA6 IRQ_TYPE_EDGE_BOTH>;驅動文件調用
gc5025->det_pin?=?of_get_named_gpio_flags(node,?"det-gpios",?0,?&det_flags);camera_det_irq?=?gpio_to_irq(gc5025->det_pin);gc5025->det_value?=?gpio_get_value(gc5025->det_pin);/*判斷注冊終端*/if(camera_det_irq){if?(gpio_request(gc5025->det_pin,?"camera-irq-gpio"))?{printk("gpio?%d?request?failed!\n",?gc5025->det_pin);gpio_free(gc5025->det_pin);return?IRQ_NONE;}ret?=?request_irq(camera_det_irq,?camera_det_irq_handler,?IRQ_TYPE_EDGE_BOTH,?"det-gpio",?NULL);if?(ret?!=?0)?{free_irq(camera_det_irq,?NULL);dev_err(dev,?"Failed?to?request?IRQ:?%d\n",?ret);return?ret;}}# 新的GPIOD文檔
Linux 內核文檔
https://www.kernel.org/doc/Documentation/gpio/consumer.txt
#頭文件
我們需要包含頭文件
#include <linux/gpio/consumer.h>看頭文件里面包含的函數列表
desc_to_gpio devm_get_gpiod_from_chi devm_gpiod_get devm_gpiod_get_array devm_gpiod_get_array_op devm_gpiod_get_index devm_gpiod_get_index_op devm_gpiod_get_optional devm_gpiod_put devm_gpiod_put_array fwnode_get_named_gpiod gpio_to_desc gpiod_cansleep gpiod_count gpiod_direction_input gpiod_direction_output gpiod_direction_output_ gpiod_export gpiod_export_link gpiod_get gpiod_get_array gpiod_get_array_optiona gpiod_get_direction gpiod_get_index gpiod_get_index_optiona gpiod_get_optional gpiod_get_raw_value gpiod_get_raw_value_can gpiod_get_value gpiod_get_value_canslee gpiod_is_active_low gpiod_put gpiod_put_array gpiod_set_array_value gpiod_set_array_value_c gpiod_set_debounce gpiod_set_raw_array_val gpiod_set_raw_array_val gpiod_set_raw_value gpiod_set_raw_value_can gpiod_set_value gpiod_set_value_canslee gpiod_to_irq gpiod_unexport#獲取gpio描述符和釋放
使用一下兩個函數獲取GPIO設備,多個設備時需要附帶index參數。函數返回一個GPIO描述符,或一個錯誤編碼,可以使用IS_ERR()進行檢查:
struct gpio_desc *gpiod_get(struct device *dev, const char *con_id,enum gpiod_flags flags)struct gpio_desc *gpiod_get_index(struct device *dev,const char *con_id, unsigned int idx,enum gpiod_flags flags)或者也可以使用如下兩個函數獲取可用設備:
struct gpio_desc *gpiod_get_optional(struct device *dev,const char *con_id,enum gpiod_flags flags)struct gpio_desc *gpiod_get_index_optional(struct device *dev,const char *con_id,unsigned int index,enum gpiod_flags flags)使用如下函數同時獲取多個設備:
struct gpio_descs *gpiod_get_array(struct device *dev,const char *con_id,enum gpiod_flags flags)該函數返回一個GPIO描述結構體:
struct gpio_descs {unsigned int ndescs;struct gpio_desc *desc[]; }一個GPIO描述符可以使用如下函數釋放:
void gpiod_put(struct gpio_desc *desc) void gpiod_put_array(struct gpio_descs *descs)需要注意GPIO描述符被釋放后不可再使用,而且不允許使用第一個函數來釋放通過序列獲取得到GPIO描述符。
#舉個例子
#dts文件
gc5025: gc5025@37 {status = "okay";compatible = "galaxycore,gc5025";reg = <0x37>;clock-frequency = <400000>;pinctrl-names = "default";pinctrl-0 = <&cif_clkout_m0>;clocks = <&cru SCLK_CIF_OUT>;clock-names = "xvclk";avdd-supply = <&vcc2v8_dvp>;dovdd-supply = <&vcc1v8_dvp>;dvdd-supply = <&vdd1v2_dvp>;reset-gpios = <&gpio3 RK_PA3 GPIO_ACTIVE_LOW>;pwdn-gpios = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>;det-gpios = <&gpio3 RK_PA6 IRQ_TYPE_EDGE_BOTH>;rockchip,camera-module-index = <0>;rockchip,camera-module-facing = "front";rockchip,camera-module-name = "CMK-CW4191-FG1";rockchip,camera-module-lens-name = "CK5502";port {ucam_out: endpoint {remote-endpoint = <&mipi_in_ucam>;data-lanes = <1 2>;};};};驅動文件調用:
????gc5025->reset_gpio?=?devm_gpiod_get(dev,?"reset",?GPIOD_OUT_LOW);if?(IS_ERR(gc5025->reset_gpio))dev_warn(dev,?"Failed?to?get?reset-gpios\n");gc5025->pwdn_gpio?=?devm_gpiod_get(dev,?"pwdn",?GPIOD_OUT_LOW);if?(IS_ERR(gc5025->pwdn_gpio))dev_warn(dev,?"Failed?to?get?pwdn-gpios\n");/*新的GPIO子系統方式,這種方式不需要手動釋放資源*/gc5025->det_gpio?=?devm_gpiod_get(dev,?"det",?GPIOD_OUT_LOW);if?(IS_ERR(gc5025->det_gpio))#GPIO使用
#設置GPIO口方向
int gpiod_direction_input(struct gpio_desc *desc) int gpiod_direction_output(struct gpio_desc *desc, int value)#檢查GPIO口是方向
int gpiod_get_direction(const struct gpio_desc *desc)函數返回GPIOF_DIR_IN或者GPIOF_DIR_OUT
#讀取GPIO口電平
訪問分為兩種,一種是通過儲存器讀寫實現的,這種操作屬于原子操作,不需要等待,所以可以在中斷處理程序中使用:
int gpiod_get_value(const struct gpio_desc *desc); void gpiod_set_value(struct gpio_desc *desc, int value);還有一種訪問必須通過消息總線比如I2C或者SPI,這種訪問需要在總線訪問隊列中等待,所以可能進入睡眠,此類訪問不能出現在IRQ handler。可以使用如下函數分辨這些設備:
int gpiod_cansleep(const struct gpio_desc *desc)使用如下函數讀寫:
int gpiod_get_value_cansleep(const struct gpio_desc *desc) void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)#active-low和raw-value
active-low & raw value有些設備采用低電平有效的方式輸出邏輯信號。此時低電平輸出1,高電平輸出0。此時可以通過訪問raw_value的方式來訪問實際電路上的值,與邏輯處理無關:假設我們在DTS里面這樣設置
reset-gpios = <&gpio3 RK_PA3 GPIO_ACTIVE_LOW>;然后我們這樣調用
gpiod_set_value_cansleep(gc5025->reset_gpio, 1);因為DTS里面的active 狀態是 GPIO_ACTIVE_LOW,所以這個代碼輸出的是 低電平
gpiod_set_value_cansleep(gc5025->reset_gpio, 0);輸出的是高電平
這幾個函數如下:
int gpiod_get_raw_value(const struct gpio_desc *desc) void gpiod_set_raw_value(struct gpio_desc *desc, int value) int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) int gpiod_direction_output_raw(struct gpio_desc *desc, int value)raw-value 的意思就是不在乎DTS里面的ACTIVE,我set 高電平,就是高電平。邏輯關系匯總如下:
Function (example) active-low property physical line gpiod_set_raw_value(desc, 0); don’t care low gpiod_set_raw_value(desc, 1); don’t care high gpiod_set_value(desc, 0); default (active-high) low gpiod_set_value(desc, 1); default (active-high) high gpiod_set_value(desc, 0); active-low high gpiod_set_value(desc, 1); active-low low可以使用如下函數判斷一個設備是否是低電平有效的設備。
int gpiod_is_active_low(const struct gpio_desc *desc)#設置多個輸出
這個沒使用過 使用如下函數設置一組設備的輸出值
void gpiod_set_array_value(unsigned int array_size, struct gpio_desc **desc_array, int *value_array) void gpiod_set_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, int *value_array) void gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, int *value_array) void gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, int *value_array)#兼容舊版本
舊的GPIO系統使用基于標號的結構而不是基于描述符。可以使用如下兩個函數進行相互轉換:
int desc_to_gpio(const struct gpio_desc *desc) struct gpio_desc *gpio_to_desc(unsigned gpio)注意不能使用一套API的方法釋放另一套API獲取的設備
#和中斷IRQ相關
使用如下函數獲取一個GPIO設備對應的IRQ中斷號
int gpiod_to_irq(const struct gpio_desc *desc)返回值時一個IRQ number,或者一個負數的錯誤代碼。得到的中斷號可以傳遞給函數request_irq(),free_irq().
#舉例子
/*新的GPIO子系統方式,這種方式不需要手動釋放資源*/gc5025->det_gpio = devm_gpiod_get(dev, "det", GPIOD_OUT_LOW);if (IS_ERR(gc5025->det_gpio))dev_warn(dev, "Failed to get det-gpios\n");camera_det_irq = gpiod_to_irq(gc5025->det_gpio);/*新gpio子系統轉成舊gpio子系統*/gc5025->det_pin = desc_to_gpio(gc5025->det_gpio);/*讀取上電gpio電平*/gc5025->det_value = gpio_get_value(gc5025->det_pin);/*判斷注冊終端*/if(camera_det_irq){ret = request_irq(camera_det_irq, camera_det_irq_handler, IRQ_TYPE_EDGE_BOTH, "det-gpio", NULL);if (ret != 0) {free_irq(camera_det_irq, NULL);dev_err(dev, "Failed to request IRQ: %d\n", ret);return ret;}}#調試
移植驅動階段或者調試階段的工程中,難免想知道當前gpio的電平狀態。當然很easy。萬用表戳上去不就行了。是啊!硬件工程師的思維。作為軟件工程師自然是要軟件的方法。下面介紹兩個api接口。自己摸索使用吧。點到為止。
static inline int gpio_export(unsigned gpio, bool direction_may_change); static inline int gpio_export_link(struct device *dev, const char *name, unsigned gpio);在你的driver中調用以上api后,編譯下載。去/sys/class/gpio目錄看看有什么發現。
? 回復「?籃球的大肚子」進入技術群聊
回復「1024」獲取1000G學習資料
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的Linux下的gpio,gpiod的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 模块式工业机器人柔性自动化生产线实训系统
- 下一篇: linux 其他常用命令