再看驱动模型
从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种方法:
- 点亮LED
__syscall int led_on(struct device *dev, u32_t led); - 关闭LED
__syscall int led_off(struct device *dev, u32_t led); - 闪烁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); - 设置LED亮度,value亮度值, 百分比
__syscall int led_set_brightness(struct device *dev, u32_t led, u8_t value);
Step2
找到对应的led_driver_api1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18typedef 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
24static 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_data1
2
3
4
5
6struct 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
3static int nrf52_led_init(struct device *dev)
{
}
Step 5
将前面定义好的初始化函数,driver_api,driver_data,driver_cfg注册,注册的时候会绑定一个字符串形式的驱动名,之后用驱动的时候会根据这个字符串来查找驱动1
2
3
4DEVICE_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
- 配置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) - 设置PIN输出电平value
static inline int gpio_pin_write(struct device *port, u32_t pin, u32_t value) - 读取PIN输入电平value
static inline int gpio_pin_read(struct device port, u32_t pin, u32_t value) - 设置PIN中断callback,此时将callback函数保存在callback
static inline void gpio_init_callback(struct gpio_callback *callback, gpio_callback_handler_t handler, u32_t pin_mask) - 为中断添加callback
static inline int gpio_add_callback(struct device port, struct gpio_callback callback) - 移除中断callback
static inline int gpio_remove_callback(struct device port, struct gpio_callback callback) - 使能指定pin上的callback
static inline int gpio_pin_enable_callback(struct device *port, u32_t pin) - 关闭指定pin上的callback
static inline int gpio_pin_disable_callback(struct device *port, u32_t pin) - 获取gpio上中断pending情况0:没有中断pengding, 非0:有中断pending
__syscall int gpio_get_pending_int(struct device *dev);
driver_api
1 | typedef int (*gpio_config_t)(struct device *port, int access_op, |
PWM
API
- 设置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 | typedef int (*pwm_pin_set_t)(struct device *dev, u32_t pwm, |