Device Tree
dts/binding/gpio/gpio-controller.yaml
描述了设备树中支持的属性。
gpio-controller节点:
其中gpio-controller
和gpio-cells
两个属性是必须的。
前者表示gpio controller节点,后者表示其他节点使用gpio specifier需要几个item来描述。
gpio: gpio@0x400ff000 {
compatible = "nxp, kinetis-gpio";
status = "disabled";
reg = <0x400ff000 0x40>;
interrupts = <59 2>;
gpio-controller;
#gpio-cells = <2>;
ngpios = <32>; // optional, default 32
gpio-reserved-ranges = <3 2>, <10, 1>; // optional, <index size>表示第<index>起<size>个gpio不能使用。
};
consumer节点:
xxx-gpios/gpios = <&<gpio-controller节点> <gpio_number> <配置属性>>
xxx-gpios = <&gpio 5 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>,
<&gpio 6 (GPIO_PULL_DOWN | GPIO_ACTIVE_LOW)>;
GPIO_ACTIVE_LOW
表示逻辑电平和实际电平相反,逻辑1代表低电平。
配置属性可在include/zephyr/dt-bindings/gpio/gpio.h
中查找。
Interrupt相关flags查阅gpio.h,是多个bit的组合,上图没有完全列出。
Consumer
方法一: 设备树API
从设备树获取的gpio信息会保存到gpio_dt_spec
结构体。包括gpio controller, pin number, config flag。对应上面device tree中consumer节点的node。
struct gpio_dt_spec {
/** GPIO device controlling the pin */
const struct device *port;
/** The pin's number on the device */
gpio_pin_t pin;
/** The pin's configuration flags as specified in devicetree */
gpio_dt_flags_t dt_flags;
};
可以通过GPIO_DT_SPEC_GET_BY_IDX()
或其他一系列变种函数解析dts获取gpio_dt_spec
结构体。node_id
对应consumer的node,prop
为xxx-gpios
, idx
为xxx-gpios
下的第x条属性。
#define GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx) \
{ \
.port = DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(node_id, prop, idx)),\
.pin = DT_GPIO_PIN_BY_IDX(node_id, prop, idx), \
.dt_flags = DT_GPIO_FLAGS_BY_IDX(node_id, prop, idx), \
}
得到gpio_dt_spec
结构体后,就可以调用一系列设备树函数来进行GPIO操作了。
// 判断gpio controller是否ready
static inline bool gpio_is_ready_dt(const struct gpio_dt_spec *spec);
/** 设置GPIO中断, 可传入的gpio_flags_t有, 具体含义参考drivers/gpio.h:
* GPIO_INT_EDGE_RISING
* GPIO_INT_EDGE_FALLING
* GPIO_INT_EDGE_BOTH
* GPIO_INT_LEVEL_LOW
* GPIO_INT_LEVEL_HIGH
* ...
**/
static inline int gpio_pin_interrupt_configure_dt(const struct gpio_dt_spec *spec,
gpio_flags_t flags);
// 配置一个pin的config, 结果为spec->dt_flags | extra_flags
static inline int gpio_pin_configure_dt(const struct gpio_dt_spec *spec,
gpio_flags_t extra_flags)
// 判断pin是否为input
static inline int gpio_pin_is_input_dt(const struct gpio_dt_spec *spec)
// 判断pin是否为output
static inline int gpio_pin_is_output_dt(const struct gpio_dt_spec *spec)
// 获取pin的config保存到flags中
static inline int gpio_pin_get_config_dt(const struct gpio_dt_spec *spec,
gpio_flags_t *flags)
// 获取input pin value
static inline int gpio_pin_get_dt(const struct gpio_dt_spec *spec)
// 设置ouput pin value
static inline int gpio_pin_set_dt(const struct gpio_dt_spec *spec, int value)
// toggle gpio
static inline int gpio_pin_toggle_dt(const struct gpio_dt_spec *spec)
// 增加gpio中断的callback函数
static inline int gpio_add_callback_dt(const struct gpio_dt_spec *spec,
struct gpio_callback *callback)
方法二: 直接传入device
结构体,不需要从设备树获取数据。
可以通过DEVICE_DT_GET
宏得到device
结构体。include/zephyr/devicetree/gpio.h
中封装了许多gpio操作devicetree的宏,用来获取gpio pin flag等属性。
和上面的device tree APIs的底层实现是相同的,不过这些APIs需要指定pin, flags等参数传入。
// 配置gpio中断相关config, interrupt edge/level...
__syscall int gpio_pin_interrupt_configure(const struct device *port,
gpio_pin_t pin,
gpio_flags_t flags);
// 配置gpio相关config, output/input, pull up/down...
__syscall int gpio_pin_configure(const struct device *port,
gpio_pin_t pin,
gpio_flags_t flags);
__syscall int gpio_port_get_direction(const struct device *port, gpio_port_pins_t map,
gpio_port_pins_t *inputs, gpio_port_pins_t *outputs);
__syscall int gpio_pin_get_config(const struct device *port, gpio_pin_t pin,
gpio_flags_t *flags);
// 获取input value
__syscall int gpio_port_get_raw(const struct device *port,
gpio_port_value_t *value);
// 设置output value
__syscall int gpio_port_set_masked_raw(const struct device *port,
gpio_port_pins_t mask,
gpio_port_value_t value);
// 把gpio_port_pins_t(U32) pins中对应置1的output high
__syscall int gpio_port_set_bits_raw(const struct device *port,
gpio_port_pins_t pins);
// 与上面相反,output low
__syscall int gpio_port_clear_bits_raw(const struct device *port,
gpio_port_pins_t pins);
__syscall int gpio_port_toggle_bits(const struct device *port,
gpio_port_pins_t pins);
__syscall int gpio_get_pending_int(const struct device *dev);
注意设备树的API获取的都是单个pin的信息,而方法二的API例如gpio_port_get_raw等都是获取一整个gpio port的信息。
Driver Example
i2c_ite_enhance.c
在dev->config
中保存了两个gpio_dt_spec
结构体:
struct i2c_enhance_config {
//...
/* SCL GPIO cells */
struct gpio_dt_spec scl_gpios;
/* SDA GPIO cells */
struct gpio_dt_spec sda_gpios;
//...
};
初始化时通过GPIO_DT_SPEC_INST_GET
宏从设备树获取数据填充gpio_dt_spec
:
i2c0: i2c@f04300 {
compatible = "ite,enhance-i2c";
scl-gpios = <&gpiob 3 GPIO_ACTIVE_HIGH>;
sda-gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>;
};
static const struct i2c_enhance_config i2c_enhance_cfg_##inst = { \
//...
.scl_gpios = GPIO_DT_SPEC_INST_GET(inst, scl_gpios), \
.sda_gpios = GPIO_DT_SPEC_INST_GET(inst, sda_gpios), \
//...
};
在i2c_enhance_recover_bus
函数中获取dev->config
, 就可以调用gpio相关api了。
const struct i2c_enhance_config *config = dev->config;
/* Set SCL of I2C as GPIO pin */
gpio_pin_configure_dt(&config->scl_gpios, GPIO_OUTPUT);
/* Set SDA of I2C as GPIO pin */
gpio_pin_configure_dt(&config->sda_gpios, GPIO_OUTPUT);
/* Pull SCL and SDA pin to high */
gpio_pin_set_dt(&config->scl_gpios, 1);
gpio_pin_set_dt(&config->sda_gpios, 1);
GPIO Interrupt
增加gpio中断的callback处理函数:
static inline void gpio_init_callback(struct gpio_callback *callback,
gpio_callback_handler_t handler,
gpio_port_pins_t pin_mask)
static inline int gpio_add_callback(const struct device *port,
struct gpio_callback *callback)
Test Example
tests/drivers/gpio/
目录下有许多gpio相关的测试程序。
Provider
可以实现的API:
__subsystem struct gpio_driver_api {
// 配置output/input, pull up/down,
int (*pin_configure)(const struct device *port, gpio_pin_t pin,
gpio_flags_t flags);
#ifdef CONFIG_GPIO_GET_CONFIG
int (*pin_get_config)(const struct device *port, gpio_pin_t pin,
gpio_flags_t *flags);
#endif
int (*port_get_raw)(const struct device *port,
gpio_port_value_t *value);
int (*port_set_masked_raw)(const struct device *port,
gpio_port_pins_t mask,
gpio_port_value_t value);
int (*port_set_bits_raw)(const struct device *port,
gpio_port_pins_t pins);
int (*port_clear_bits_raw)(const struct device *port,
gpio_port_pins_t pins);
int (*port_toggle_bits)(const struct device *port,
gpio_port_pins_t pins);
int (*pin_interrupt_configure)(const struct device *port,
gpio_pin_t pin,
enum gpio_int_mode, enum gpio_int_trig);
int (*manage_callback)(const struct device *port,
struct gpio_callback *cb,
bool set);
uint32_t (*get_pending_int)(const struct device *dev);
#ifdef CONFIG_GPIO_GET_DIRECTION
int (*port_get_direction)(const struct device *port, gpio_port_pins_t map,
gpio_port_pins_t *inputs, gpio_port_pins_t *outputs);
#endif /* CONFIG_GPIO_GET_DIRECTION */
};
Driver需要定义一个device实例,其中dev->config
和dev->data
两个自定义结构体,需要首先包含struct gpio_driver_config
和struct gpio_driver_data
两个通用结构体,以gpio_dw.h
为例:
struct gpio_dw_config {
/* gpio_driver_config needs to be first */
struct gpio_driver_config common;
uint32_t ngpios;
uint32_t irq_num; /* set to 0 if GPIO port cannot interrupt */
gpio_config_irq_t config_func;
};
struct gpio_dw_runtime {
/* gpio_driver_data needs to be first */
struct gpio_driver_data common;
uint32_t base_addr;
sys_slist_t callbacks;
};
struct gpio_driver_config {
/** Mask identifying pins supported by the controller.
*
* Initialization of this mask is the responsibility of device
* instance generation in the driver.
*/
gpio_port_pins_t port_pin_mask;
};
struct gpio_driver_data {
/** Mask identifying pins that are configured as active low.
*
* Management of this mask is the responsibility of the
* wrapper functions in this header.
*/
gpio_port_pins_t invert;
};
// port_pin_mask需要在gpio controller driver中从设备树gpio-reserved-ranges获取,表示需要跳过的gpios。以gpio_dw.c为例:
static const struct gpio_dw_config gpio_dw_config_##n = { \
.common = { \
.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n), \
}, \
.irq_num = COND_CODE_1(DT_INST_IRQ_HAS_IDX(n, 0), (DT_INST_IRQN(n)), (0)), \
.ngpios = DT_INST_PROP(n, ngpios), \
.config_func = gpio_config_##n##_irq, \
};
// invert会在抽象层gpio.h定义,不需要driver来实现,从设备树GPIO_ACTIVE_HIGH/LOW获取。
GPIO SHELL
Zephyr 在shell中提供了一个gpio命令来操作pins, 具体是现在gpio_shell.c
中。
uart:~$ gpio
gpio - GPIO commands
Subcommands:
conf : Configure GPIO pin
Usage: gpio conf <device> <pin> <configuration <i|o>[u|d][h|l][0|1]>
[vendor specific]
<i|o> - input|output
[u|d] - pull up|pull down, otherwise open
[h|l] - active high|active low, otherwise defaults to active high
[0|1] - initialise to logic 0|logic 1, otherwise defaults to logic 0
[vendor specific] - configuration flags within the mask 0xFF00
see include/zephyr/dt-bindings/gpio/
get : Get GPIO pin value
Usage: gpio get <device> <pin>
set : Set GPIO pin value
Usage: gpio set <device> <pin> <level 0|1>
blink : Blink GPIO pin
Usage: gpio blink <device> <pin>
info : GPIO Information
Usage: gpio info [device]
uart:~$ gpio info
Line Reserved Device Pin
gpio@40110180 0
* gpio@40110180 1
* gpio@40110180 2
* gpio@40110180 3
gpio@40110180 4
gpio@40110180 5
gpio@40110180 6
gpio@40110180 7
gpio@40110180 8
gpio@40110180 9
uart:~$ gpio conf gpio@40110180 20 o1 # 配置pin
uart:~$ gpio get gpio@40110180 20 # 获取pin input value