首先学习头文件:./zephyr/include/drivers/gpio.h。
该文件指导驱动中应该包含哪些接口,同时也提供了用户可以调用的系统调用。头文件的内容可以分为一下四个部分:
一、一些flags的定义区,这些flags在应用进程,驱动中均可以被使用。
/**
* @name GPIO input/output configuration flags
* @{
*/
/** Enables pin as input. */
#define GPIO_INPUT (1U << 8)
/** Enables pin as output, no change to the output state. */
#define GPIO_OUTPUT (1U << 9)
/** Disables pin for both input and output. */
#define GPIO_DISCONNECTED 0
/** @cond INTERNAL_HIDDEN */
/****************************/
/** so many other defines/
/****************************/
/** Default drive strength when GPIO pin output is high.
*/
#define GPIO_DS_DFLT_HIGH (0x0U << GPIO_DS_HIGH_POS)
/** Alternative drive strength when GPIO pin output is high.
* For hardware that does not support configurable drive strengths
* use the default drive strength.
*/
#define GPIO_DS_ALT_HIGH (0x1U << GPIO_DS_HIGH_POS)
/** @} */
/** @cond INTERNAL_HIDDEN */
#define GPIO_DIR_MASK (GPIO_INPUT | GPIO_OUTPUT)
/** @endcond */
二、数据类型定义
/* 提供一个数据结构gpio_port_pins_t
用于表示一个port中有哪些pin,每bit代表一个pin,通过(1 << n)来表示第n个pin
*/
typedef uint32_t gpio_port_pins_t;
/**
* @brief Provides values for a set of pins associated with a port.
*
* The value for a pin with index n is high (physical mode) or active
* (logical mode) if and only if the bit identified by (1U << n) is set.
* Otherwise the value for the pin is low (physical mode) or inactive
* (logical mode).
*
* Values of this type are often paired with a `gpio_port_pins_t` value
* that specifies which encoded pin values are valid for the operation.
*/
/*
提供一个数据结构,用于表示一个port的value,
当port的n pin为高时(包含逻辑高、电平高),value的n bit置位,value |= 1<<n;
当port的n pin为低时(包含逻辑低、电平低),value的n bit清零,value &= ~(1<<n);
*/
typedef uint32_t gpio_port_value_t;
/**
* @brief Provides a type to hold a GPIO pin index.
*
* This reduced-size type is sufficient to record a pin number,
* e.g. from a devicetree GPIOS property.
*/
/* 提供一个数据类型,记录pin的序号n*/
typedef uint8_t gpio_pin_t;
/**
* @brief Provides a type to hold GPIO devicetree flags.
*
* All GPIO flags that can be expressed in devicetree fit in the low 8
* bits of the full flags field, so use a reduced-size type to record
* that part of a GPIOS property.
*/
typedef uint8_t gpio_dt_flags_t;
/**
* @brief Provides a type to hold GPIO configuration flags.
*
* This type is sufficient to hold all flags used to control GPIO
* configuration, whether pin or interrupt.
*/
typedef uint32_t gpio_flags_t;
/**
* @brief Maximum number of pins that are supported by `gpio_port_pins_t`.
*/
#define GPIO_MAX_PINS_PER_PORT (sizeof(gpio_port_pins_t) * __CHAR_BIT__)
/**
* This structure is common to all GPIO drivers and is expected to be
* the first element in the object pointed to by the config field
* in the device structure.
*/
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;
};
/**
* This structure is common to all GPIO drivers and is expected to be the first
* element in the driver's struct driver_data declaration.
*/
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;
};
struct gpio_callback;
/**
* @typedef gpio_callback_handler_t
* @brief Define the application callback handler function signature
*
* @param port Device struct for the GPIO device.
* @param cb Original struct gpio_callback owning this handler
* @param pins Mask of pins that triggers the callback handler
*
* Note: cb pointer can be used to retrieve private data through
* CONTAINER_OF() if original struct gpio_callback is stored in
* another private structure.
*/
typedef void (*gpio_callback_handler_t)(const struct device *port,
struct gpio_callback *cb,
gpio_port_pins_t pins);
/**
* @brief GPIO callback structure
*
* Used to register a callback in the driver instance callback list.
* As many callbacks as needed can be added as long as each of them
* are unique pointers of struct gpio_callback.
* Beware such structure should not be allocated on stack.
*
* Note: To help setting it, see gpio_init_callback() below
*/
struct gpio_callback {
/** This is meant to be used in the driver and the user should not
* mess with it (see drivers/gpio/gpio_utils.h)
*/
sys_snode_t node;
/** Actual callback function being called when relevant. */
gpio_callback_handler_t handler;
/** A mask of pins the callback is interested in, if 0 the callback
* will never be called. Such pin_mask can be modified whenever
* necessary by the owner, and thus will affect the handler being
* called or not. The selected pins must be configured to trigger
* an interrupt.
*/
gpio_port_pins_t pin_mask;
};
三、驱动中应当实现的API
__subsystem struct gpio_driver_api {
int (*pin_configure)(const struct device *port, gpio_pin_t pin,
gpio_flags_t flags);
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);
};
四、相关的系统调用
/* GPIO口的常规配置,调用驱动,api->pin_configure*/
__syscall int gpio_config(const struct device *port, gpio_pin_t pin, gpio_flags_t flags);
/* GPIO口的中断配置,该接口也可用于复用管脚的中断配置,
调用驱动,api->pin_interrupt_configure */
__syscall int gpio_pin_interrupt_configure(const struct device *port,
gpio_pin_t pin,
gpio_flags_t flags);
/* 单个GPIO口的全部配置,会依据flags参数来调用常规配置、中断配置*/
static inline int gpio_pin_configure(const struct device *port,
gpio_pin_t pin,
gpio_flags_t flags)
/* 获取端口port的物理电平状态值,该值包含端口内所有pin的电平状态,
调用驱动,api->port_get_raw */
__syscall int gpio_port_get_raw(const struct device *port,
gpio_port_value_t *value);
/* 获取端口port的逻辑电平状态值,在pin脚初始化时有的会配置flag GPIO_ACTIVE_LOW,
这时物理状态与逻辑状态间,需要进行二次转换
该接口会再次调用 gpio_port_get_raw */
static inline int gpio_port_get(const struct device *port,
gpio_port_value_t *value)
/* 设置端口的多个pin脚的物理电平值(0/1),调用驱动,api->port_set_masked_raw*/
__syscall int gpio_port_set_masked_raw(const struct device *port,
gpio_port_pins_t mask,
gpio_port_value_t value);
/* 设置端口的多个pin脚的物理电平值,内部调用 gpio_port_set_masked_raw */
static inline int gpio_port_set_masked(const struct device *port,
gpio_port_pins_t mask,
gpio_port_value_t value)
/* 设置端口的多个pin脚的物理电平值为1,调用驱动 api->port_set_bits_raw*/
__syscall int gpio_port_set_bits_raw(const struct device *port,
gpio_port_pins_t pins);
/* 设置端口的多个pin脚的逻辑电平值为ACTIVE */
static inline int gpio_port_set_bits(const struct device *port,
gpio_port_pins_t pins)
/* 设置端口的多个pin脚的物理电平值为0,调用驱动 api->port_clear_bits_raw*/
__syscall int gpio_port_clear_bits_raw(const struct device *port,
gpio_port_pins_t pins);
/* 设置端口的多个pin脚的逻辑电平值为INACTIVE */
static inline int gpio_port_clear_bits(const struct device *port,
gpio_port_pins_t pins)
/* 端口翻转*/
__syscall int gpio_port_toggle_bits(const struct device *port,
gpio_port_pins_t pins);
/*********************************/
/* 还有一些其他的bit操作函数,*/
/*********************************/
重要的结构体
struct gpio_callback {
/** This is meant to be used in the driver and the user should not
* mess with it (see drivers/gpio/gpio_utils.h)
*/
/* 系统使用,用户勿操作*/
sys_snode_t node;
/** Actual callback function being called when relevant. */
/* 用户回调函数*/
gpio_callback_handler_t handler;
/** A mask of pins the callback is interested in, if 0 the callback
* will never be called. Such pin_mask can be modified whenever
* necessary by the owner, and thus will affect the handler being
* called or not. The selected pins must be configured to trigger
* an interrupt.
*/
/* 回调适用于哪些引脚*/
gpio_port_pins_t pin_mask;
};
/* 初始化callback结构,该结构体中保存有用户态回调函数的指针,以及回调函数适用于哪些引脚*/
tatic inline void gpio_init_callback(struct gpio_callback *callback,
gpio_callback_handler_t handler,
gpio_port_pins_t pin_mask)
/* 注册用户态回调函数,入参callback 来源于gpio_init_callback()的初始化*/
static inline int gpio_add_callback(const struct device *port,
struct gpio_callback *callback)
/* 移除回调*/
static inline int gpio_remove_callback(const struct device *port,
struct gpio_callback *callback)
/* 获取挂起中断,调用驱动,api->get_pending_int*/
__syscall int gpio_get_pending_int(const struct device *dev);
然后以stm32的GPIO驱动来简单分析下驱动的实现,仅分析驱动的实现过程,对接口的内部细节不做分析。文件:./zephyr/drivers/gpio/gpio_stm32.c;
一、驱动的定义及初始化
/* 数据类型定义 */
struct gpio_stm32_config {
/* gpio_driver_config needs to be first */
struct gpio_driver_config common;
/* port base address */
uint32_t *base;
/* IO port */
int port;
struct stm32_pclken pclken;
};
/**
* @brief driver data
*/
struct gpio_stm32_data {
/* gpio_driver_data needs to be first */
struct gpio_driver_data common;
/* device's owner of this data */
const struct device *dev;
/* user ISR cb */
sys_slist_t cb;
};
/* 驱动定义及初始化相关的宏定义 */
#define GPIO_DEVICE_INIT(__node, __suffix, __base_addr, __port, __cenr, __bus) \
static const struct gpio_stm32_config gpio_stm32_cfg_## __suffix = { \
.common = { \
.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_NGPIOS(16U), \
}, \
.base = (uint32_t *)__base_addr, \
.port = __port, \
.pclken = { .bus = __bus, .enr = __cenr } \
}; \
static struct gpio_stm32_data gpio_stm32_data_## __suffix; \
DEVICE_DT_DEFINE(__node, \
gpio_stm32_init, \
device_pm_control_nop, \
&gpio_stm32_data_## __suffix, \
&gpio_stm32_cfg_## __suffix, \
POST_KERNEL, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&gpio_stm32_driver)
#define GPIO_DEVICE_INIT_STM32(__suffix, __SUFFIX) \
GPIO_DEVICE_INIT(DT_NODELABEL(gpio##__suffix), \
__suffix, \
DT_REG_ADDR(DT_NODELABEL(gpio##__suffix)), \
STM32_PORT##__SUFFIX, \
DT_CLOCKS_CELL(DT_NODELABEL(gpio##__suffix), bits),\
DT_CLOCKS_CELL(DT_NODELABEL(gpio##__suffix), bus))
/* 驱动定义 */
#if DT_NODE_HAS_STATUS(DT_NODELABEL(gpioa), okay)
GPIO_DEVICE_INIT_STM32(a, A);
#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(gpioa), okay) */
/* 驱动定义*/
如果设备设备树中存在标签:gpioa,则利用驱动宏定义驱动
GPIO_DEVICE_INIT_STM32(a, A);
在上述宏中,会对驱动进行定义:
DEVICE_DT_DEFINE(__node, //节点
gpio_stm32_init, //初始化函数,会根据初始化等级,在驱动初始化阶段被调用
device_pm_control_nop, //电源管理相关
&gpio_stm32_data_## __suffix, //驱动实例的数据部分,可变
&gpio_stm32_cfg_## __suffix, //驱动实例的配置部分,不可变
POST_KERNEL, //初始化等级,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, //同等级内的优先级
&gpio_stm32_driver) //通用API
static int gpio_stm32_init(const struct device *device):在初始化接口中,主要完成时钟相关的配置,配置时钟后,GPIO模块变可以进行后续配置及使用。
GPIO引脚的输入输出、中断配置及中断回调的注册,则是在应用程序中通过系统调用完成。
static const struct gpio_driver_api gpio_stm32_driver = {
.pin_configure = gpio_stm32_config,
.port_get_raw = gpio_stm32_port_get_raw,
.port_set_masked_raw = gpio_stm32_port_set_masked_raw,
.port_set_bits_raw = gpio_stm32_port_set_bits_raw,
.port_clear_bits_raw = gpio_stm32_port_clear_bits_raw,
.port_toggle_bits = gpio_stm32_port_toggle_bits,
.pin_interrupt_configure = gpio_stm32_pin_interrupt_configure,
.manage_callback = gpio_stm32_manage_callback,
};
在gpio_stm32_driver 结构体的定义中则完成了系统调用与其实现的映射。在gpio.h文件中的系统调用都能够在这里找到具体实现。