子系统(驱动和硬件之间又加了一个环节)
- 所谓子系统就是将一类驱动进行规范化,提供统一的接口,这样驱动开发者不需要去实现底层的重复驱动代码,而是将重点放到设备驱动程序本身
- 所谓子系统就是将共有的东西抽象为一个统一框架,提供上层统一的接口,而底层由每个厂家自己实现,这样驱动层去调用这些底层接口的时候就不需要考虑底层的实现,而只需要使用统一的API即可。
- 上述linux驱动程序需要依靠linux子系统的接口API,而linux子系统又要依靠bsp驱动程序的API
公司代码会分配至少两个部分,驱动程序和BSP驱动程序(与硬件(设备寄存器与总线信息)紧密相关,其中dtb的编写也是bsp开发人员的任务)
GPIO子系统
步骤:
(1)申请GPIO资源
static inline int devm_gpio_request(struct device *dev,
unsigned gpio,
const char *label)
(2)申请完资源后我们需要设置GPIO方向
static inline int gpio_direction_input(unsigned gpio)
static inline int gpio_direction_output(unsigned gpio, int value)
(3)获取和设置GPIO的值
#define gpio_get_value __gpio_get_value
static inline int __gpio_get_value(unsigned gpio)
#define gpio_set_value __gpio_set_value
static inline void __gpio_set_value(unsigned gpio, int value)
(4)释放GPIO资源
static inline void devm_gpio_free(struct device *dev, unsigned int gpio)
当引入设备树之后
所有的设备信息一般都需要先从设备树中获取,下面函数用于从设备树中获取 GPIO 编号:
static inline int of_get_named_gpio(struct device_node *np,//设备节点
const char *propname,//gpio属性名称
int index)//gpio属性名称的索引
GPIO与中断信号线
在linux中,GPIO引脚也可以作为中断信号线,即对于gpio控制器而言其也可以是一个中断控制器。通常我们利用一个GPIO当作一个中断信号线,例如外部触发信号。
Gpio转为中断信号线函数:
#define gpio_to_irq __gpio_to_irq
static inline int __gpio_to_irq(unsigned gpio)
//这样去实现一个按键,
int key_irq = gpio_to_irq(KEY_IRQ_PIN);
由于目前驱动开发都结合设备树来开发,所以上面的子系统接口API都已经丢弃使用,
在新的 GPIO 子系统中,所有的操作函数都是 gpiod 为前缀
步骤:
(1)从设备树中获取GPIO的描述信息
struct gpio_desc *__must_check gpiod_get( struct device *dev,//设备结构体
const char *con_id,//gpio属性id名字
enum gpiod_flags flags)//gpio输入输出状态标记
struct gpio_desc *__must_check devm_gpiod_get( struct device *dev,
const char *con_id,
enum gpiod_flags flags)
(2)申请完资源后我们需要设置GPIO方向
int gpiod_direction_input(struct gpio_desc *desc)
int gpiod_direction_output(struct gpio_desc *desc, int value)
(3)获取和设置GPIO输出电平
int gpiod_get_value(const struct gpio_desc *desc)
void gpiod_set_value(struct gpio_desc *desc, int value)
(4)释放GPIO资源
void gpiod_put(struct gpio_desc *desc)
void devm_gpiod_put(struct device *dev, struct gpio_desc *desc)
GPIO与中断信号线
int gpiod_to_irq(const struct gpio_desc *desc)
Pinctrl子系统(解决IO复用管理的问题)一般都是搭配GPIO子系统
节点分析
- Compatible用于匹配pinctrl子系统的底层驱动程序
- Reg指定该GPIO控制器的寄存器映射地址
- Interrupts指定该GPIO控制器的中断信号线
- Clocks属性指定pintrl时钟的主时钟源,clocks-names属性对clocks进行了说明
- Gpio-controller属性表明该节点是一个控制器
- #gpio-cells属性了在引用GPIO节点时需要有三个单元
- #interrupt-cells属性指定了引用该节点作为中断时需要有的三个单元
上述为pinctrl设备树节点,该设备树节点用于给 pinctrl子系统记录 GPIO 的寄存器和 GPIO 引脚的复用功能。
映射复用功能:
由于pinctrl子系统是管理GPIO引脚复用的,因此在将GPIO映射为其它功能时,我们需要在 pinctrl设备树节点中添加我们的功能比如:们需要使用串口 uart0 功能,那么 uart一定是复 用了两个 GPIO 引脚,
设备树节点引用复用功能:
上述设备节点出现了
pinctrl-0 = <&uart0_pins>属性,指定pin的状态功能,若作普通引脚pinctrl-1 = <&uart0_sleep>;
pinctrl-names = "default"属性,指定其ID名称
步骤:
(1)设备树添加我们的LED设备节点
(2)为了能够方便调试,使用sysfs设备文件系统驱动框架,这样就不要写测试APP了
(3)使用paltform驱动框架
(4)编译模块.ko调试(sysfs虚拟文件系统下)