Zephyr驱动模型实现方式

Creative Commons
本作品采用知识共享署名

本文说明Zephyr在指定的驱动模型下实现驱动的一般步骤。

再看驱动模型

zephyr驱动模型一文可以看到Zephyr下一个驱动实现的几大要素:

  • driver name
  • driver init函数
  • driver各种操作函数driver_api
  • driver配置信息driver_cfg
  • driver的数据信息driver_data
    从驱动使用层面上来说,因为初始化是编译期已经固定好(参见zephyr驱动模型一文), 运行期会自动初始化,所以只用关注各种操作函数也就是driver_api

驱动实现步骤

  • Step1: 找到驱动头文件,理解驱动提供操作API的作用和参数含义
  • Step2: 在驱动头文件中,对应操作API和driver_api内操作函数指针
  • Step3: 在驱动代码中实现driver_api函数指针的函数,添加driver_data和driver_cfg
  • Step4: 实现driver初始化函数
  • Step5: 使用DEVICE_AND_API_INIT注册初始化函数,和实现好的driver_api

LED示例

Step1

找到驱动头文件include/led.h,理解驱动提供的API作用和参数含义,Zephyr为LED驱动定义了下面4种方法:

  1. 点亮LED
    __syscall int led_on(struct device *dev, u32_t led);
  2. 关闭LED
    __syscall int led_off(struct device *dev, u32_t led);
  3. 闪烁LED,delay_on亮灯时间 ms,delay_off 熄灯时间ms
    __syscall int led_blink(struct device *dev, u32_t led, u32_t delay_on, u32_t delay_off);
  4. 设置LED亮度,value亮度值, 百分比
    __syscall int led_set_brightness(struct device *dev, u32_t led, u8_t value);

Step2

找到对应的led_driver_api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef int (*led_api_blink)(struct device *dev, u32_t led,
u32_t delay_on, u32_t delay_off);


typedef int (*led_api_set_brightness)(struct device *dev, u32_t led,
u8_t value);

typedef int (*led_api_on)(struct device *dev, u32_t led);

typedef int (*led_api_off)(struct device *dev, u32_t led);


struct led_driver_api {
led_api_blink blink; -->led_blink
led_api_set_brightness set_brightness; -->led_set_brightness
led_api_on on; -->led_on
led_api_off off; -->led_off
};

Step3

具体的实现就按照你的硬件自由发挥了,一般情况下led_driver_api内规定的API不能少,例如下面就是nrf52_moderate的led api.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static int nrf52_led_blink(struct device *dev, u32_t led,
u32_t delay_on, u32_t delay_off)
{
}

static int nrf52_led_set_brightness(struct device *dev, u32_t led,
u8_t value)
{
}

static inline int nrf52_led_on(struct device *dev, u32_t led)
{
}

static inline int nrf52_led_off(struct device *dev, u32_t led)
{
}

static const struct led_driver_api nrf52_led_api = {
.blink = nrf52_led_blink,
.set_brightness = nrf52_led_set_brightness,
.on = nrf52_led_on,
.off = nrf52_led_off,
};

通常情况下,驱动内部需要自己管理一些数据和配置,这些可以放driver_data和driver_cfg。例如nrf52_moderate的led不需要配置就只需要定义driver_data

1
2
3
4
5
6
struct nrf52led_data {
struct device *gpio;
struct device *pwm;
struct nrf52_ledparam param[LED_NUMBER];
};
static struct nrf52led_data nrf52_led_data;

Step4

实现driver初始化函数,driver的初始化函数会在上电时被调用,这也是必须要实现的,里面的代码也是根据你对驱动设计而来,自由发挥。

1
2
3
static int nrf52_led_init(struct device *dev)
{
}

Step 5

将前面定义好的初始化函数,driver_api,driver_data,driver_cfg注册,注册的时候会绑定一个字符串形式的驱动名,之后用驱动的时候会根据这个字符串来查找驱动

1
2
3
4
DEVICE_AND_API_INIT(nrf52_led, "NRF_52",
&nrf52_led_init, &nrf52_led_data,
NULL, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&nrf52_led_api);

其它一些驱动的模型定义

GPIO

API

  1. 配置PIN, flags指定pin的IN/OUT,是否pull-down/up, 是否是中断,中断触发方式,flag定义参见include/dt-binding/gpio/gpio.h
    static inline int gpio_pin_configure(struct device *port, u32_t pin,int flags)
  2. 设置PIN输出电平value
    static inline int gpio_pin_write(struct device *port, u32_t pin, u32_t value)
  3. 读取PIN输入电平value
    static inline int gpio_pin_read(struct device port, u32_t pin, u32_t value)
  4. 设置PIN中断callback,此时将callback函数保存在callback
    static inline void gpio_init_callback(struct gpio_callback *callback, gpio_callback_handler_t handler, u32_t pin_mask)
  5. 为中断添加callback
    static inline int gpio_add_callback(struct device port, struct gpio_callback callback)
  6. 移除中断callback
    static inline int gpio_remove_callback(struct device port, struct gpio_callback callback)
  7. 使能指定pin上的callback
    static inline int gpio_pin_enable_callback(struct device *port, u32_t pin)
  8. 关闭指定pin上的callback
    static inline int gpio_pin_disable_callback(struct device *port, u32_t pin)
  9. 获取gpio上中断pending情况0:没有中断pengding, 非0:有中断pending
    __syscall int gpio_get_pending_int(struct device *dev);

driver_api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
typedef int (*gpio_config_t)(struct device *port, int access_op,
u32_t pin, int flags);
typedef int (*gpio_write_t)(struct device *port, int access_op,
u32_t pin, u32_t value);
typedef int (*gpio_read_t)(struct device *port, int access_op,
u32_t pin, u32_t *value);
typedef int (*gpio_manage_callback_t)(struct device *port,
struct gpio_callback *callback,
bool set);
typedef int (*gpio_enable_callback_t)(struct device *port,
int access_op,
u32_t pin);
typedef int (*gpio_disable_callback_t)(struct device *port,
int access_op,
u32_t pin);
typedef u32_t (*gpio_api_get_pending_int)(struct device *dev);

struct gpio_driver_api {
gpio_config_t config; -->gpio_pin_configure
gpio_write_t write; -->gpio_pin_write
gpio_read_t read; -->gpio_pin_read
gpio_manage_callback_t manage_callback; -->gpio_add_callback/gpio_remove_callback
gpio_enable_callback_t enable_callback; -->gpio_pin_enable_callback
gpio_disable_callback_t disable_callback; -->gpio_pin_disable_callback
gpio_api_get_pending_int get_pending_int; -->gpio_get_pending_int
};

PWM

API

  1. 设置PWM属性,period: pwm周期ms, pulse: pwm pulse宽度 ms
    static inline int pwm_pin_set_usec(struct device *dev, u32_t pwm, u32_t period, u32_t pulse)

driver_api

1
2
3
4
5
6
7
8
9
10
11
12
typedef int (*pwm_pin_set_t)(struct device *dev, u32_t pwm,
u32_t period_cycles, u32_t pulse_cycles);


typedef int (*pwm_get_cycles_per_sec_t)(struct device *dev, u32_t pwm,
u64_t *cycles);


struct pwm_driver_api {
pwm_pin_set_t pin_set; -->pwm_pin_set_t接收的cycle,因此传入的参数需要先转化成cycle
pwm_get_cycles_per_sec_t get_cycles_per_sec; -->获取cycle用,用于计算period和pulse各占多少cycle
};