一、修改设备树
1、在pinctrl子系统对应节点添加子节点,即在 iomuxc 、iomuxc_snvs 节点添加新的子节点,即添加设备对应的 IO 。
示例:
pinctrl_i2c1: i2c1grp {
fsl,pins = <
MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
>;
};
节点标签:节点名{
引脚属性字符串 = <
使用的引脚_引脚复用功能 电气特性(IO 的上/下拉、驱动能力和速度等)
>;
}
注意:节点标签,节点名可自定义,但节点标签前缀一定要为 pinctrl_
2、在 I2C 、SPI 等节点下添加子节点,杂项驱动(MISC)在根节点下添加节点,配置设备相关属性信息:
示例:
&i2c1 {
clock-frequency = ;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
ap3216c@1e {
compatible = "iot,ap3216c";
reg = ;
};
};
&节点标签 {
属性 = 值;
pinctrl-names = "default"; //设备的状态(state),default(默认状态)和sleep(休眠状态)。
pinctrl-0 = <&节点标签>; //"default"状态,对应的引脚在pinctrl-0定义
pinctrl-1 = <&节点标签>; //"sleep"状态,对应的引脚在pinctrl-1定义
status = "okay"; //状态值
/*追加子节点-i2c1上的设备*/
子节点名@地址 {
兼容性属性 = 匹配字符串;
reg = ; //地址
};
};
3、编译设备树
make dtbs
二、编写设备驱动、应用程序
1、编写设备驱动程序
/* 驱动的probe函数 */
static int xxx_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
/* 具体实现 */
}
/* 驱动的remove函数 */
static int xxx_remove(struct i2c_client *client)
{
/* 具体实现 */
}
/* 传统匹配方式ID列表 */
static const struct i2c_device_id xxx_id[] = {
{"xxx,xxx", 0}, //匹配在设备树对应xxx节点下定义的compatible
{}
};
/* 设备树匹配列表 */
static const struct of_device_id ap3216c_of_match[] = {
{ .compatible = "iot,xxx" }, //匹配在设备树对应xxx节点下定义的compatible
{ /* Sentinel */ }
};
//=======================================================================================
/* i2c驱动结构体 */
static struct i2c_driver xxx_driver = {
.probe = xxx_probe, //探测函数(查询、注册、创建设备等)
.remove = xxx_remove, //卸载函数(删除设备)
.driver = {
.owner = THIS_MODULE, //当前模块
.name = "xxx", //设备名
.of_match_table = xxx_of_match, //设备树匹配表(匹配设备树里面的定义)
},
.id_table = xxx_id, //id匹配表
};
//=======================================================================================
/* 驱动入口函数 */
static int __init xxx_init(void)
{
int ret = 0;
ret = i2c_add_driver(&xxx_driver);
return ret;
}
/* 驱动出口函数 */
static void __exit xxx_exit(void)
{
i2c_del_driver(&xxx_driver);
}
module_init(xxx_init); //指定驱动入口函数
module_exit(xxx_exit); //指定驱动出口函数
MODULE_LICENSE("GPL"); //LICENSE
MODULE_AUTHOR("pjw"); //作者信息
(1)根据结构体需要编写对应的 probe函数、remove函数、设备树匹配表、ID匹配表等。
- platform 有对应的 platform_driver 结构体。
- I2C 有对应的 i2c_driver 结构体。
- SPI 有对应的 spi_driver 结构体。
(2)指定驱动入口函数和出口函数。
- platform 使用 platform_driver_register(注册)函数、platform_driver_unregister(注销)函数。
- I2C 使用 i2c_add_driver(注册)函数、i2c_del_driver (注销)函数。
- SPI 使用 spi_register_driver(注册)函数、spi_unregister_driver(注销)函数。
2、编写应用程序
3、编写Makefile
三、编译
make -j12 //编译驱动
arm-linux-gnueabihf-gcc xxxApp.c -o xxxApp //编译应用程序
四、测试设备驱动
1、Linux 系统选择通过 TFTP 从网络启动,并且使用 NFS 挂载网络根文件系统
(1)将编译出的 imx6ull-iot-emmc.dtb (arch/arm/boot/dts/目录)与 zImage(arch/arm/boot/目录)复制到 tftp 文件夹
(2)设置 bootargs 环境变量,根文件系统从 nfs/roofts 启动
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs rw nfsroot=192.168.137.18:/home/pjw/linux/nfs/rootfs ip=192.168.137.20:192.168.137.18:192.168.1.1:255.255.255.0::eth0:off'
(3)设置 bootcmd 环境变量,通过 TFTP 启动内核和设备树
setenv bootcmd 'tftp 80800000 zImage;tftp 83000000 imx6ull-iot-emmc.dtb;bootz 80800000 - 83000000'
(4)使用新的 imx6ull-alientek-emmc.dtb 启动 Linux 内核。启动成功以后进入 /proc/device-tree/ 目录中查看节点。
2、加载驱动模块
depmod //第一次加载驱动的时候需要运行此命令
modprobe xxx.ko //加载驱动 xxx
lsmod //查看当前系统中存在的模块
cat /proc/devices //查看当前系统中的设备
3、测试应用程序
./xxxApp /dev/xxx