pinctrl 子系统-----led灯闪烁实验
Linux 设备驱动理论看了有一段时间了,根据理论自己琢磨了一个从设备树到顶部进程的小实验 ----led闪烁实验
pinctrl 子系统的添加
如果从头开始时,首先第一步就是了解硬件信息
根据三张图我们得知:
LED_R 的阴极连接到 i.MX6ULL 芯片上 GPIO1_IO04 引脚, LED_G 连接到 CSI_HSYNC,LED_B
连接到 CSI_VSYNC。而 CSI_HSYNC 和 CSI_VSYNC 作为摄像头的某一功能被复用为 GPIO
LED 灯 | 原理图的标号 | 具体引脚名 | GPIO 端口及引脚编号 |
---|---|---|---|
R 灯 | GPIO_4 | GPIO1_IO04 | GPIO1_IO04 |
G 灯 | CSI_HSYNC | CSI_HSYNC | GPIO4_IO20 |
B 灯 | CSI_VSYNC | CSI_VSYNC | GPIO4_IO19 |
pinctrl 子系统主要用于管理芯片的引脚,也就是将上述的硬件信息抽象出来放到Linux里,也就是告诉系统,我这个系统的配置寄存器在哪里,要配置成什么样,(输出?输入…)?
我们打开厂商写好的引脚功能文件imx6ul-pinfunc.h,查找如下
led引脚 | 配置地址别名 |
---|---|
GPIO1_IO04 | MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 |
CSI_HSYNC | MX6UL_PAD_CSI_HSYNC__GPIO4_IO20 |
CSI_VSYNC | MX6UL_PAD_CSI_VSYNC__GPIO4_IO19 |
属性配置:
由上图知道如下信息
寄存器位数 | 功能 | 我们选择的功能 |
---|---|---|
31-17 | 保留 | 0x0000 |
16 | 0:关闭迟滞;1:使能迟滞 | 0 |
15-14 | 不同电阻上下拉 | 00 |
13 | 使能输出状态保存 0:保持;1:上拉 | 0 |
12 | 0:关闭拉保持;1:使能拉/保持 | 1 |
11 | 0:禁止漏极开路1:使能漏极开路 | |
10-8 | 保留 | 000 |
7-6 | 速度 | 10 |
5:3 | 输出使能 | 110 |
2-1 | 保留 | 00 |
0 | 0:低速;1:高速 | 1 |
综上:0x000010B1
故根据子系统格式为
pinctrl_rgb_led:rgb_led{
fsl,pins = <
MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x000010B1
MX6UL_PAD_CSI_HSYNC__GPIO4_IO20 0x000010B1
MX6UL_PAD_CSI_VSYNC__GPIO4_IO19 0x000010B1
>;
};
添加到 imx6ull-mmc-npi.dts 如下,表明我们引脚基础配置已经配置好。
设备树节点的添加
然后我们再添加一个设备节点 也就是告诉Linux ,我们有一个RGB灯。使用pinctrl_rgb_led子系统配置
/* 添加 rgb_led 节点 */
rgb_led{
pinctrl-names = "default";
compatible = "fire,rgb-led";//与平台驱动做匹配
pinctrl-0 = <&pinctrl_rgb_led>;
rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;//低电平有效
rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>;
rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;
status = "okay";
};
#ebf_linux_kernel 目录下进行编译
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
#或
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- npi_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
./arch/arm/boot/dts”目录下生成“imx6ull-mmc-npi.dtb”文件,将其替换掉板子/usr/lib/linux-image-4.19.35-imx6/ 目录下的 imx6ull-mmc-npi.dtb 文件并输入 sudo reboot 重启开发板
我们开始写驱动与测试进程
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/platform_device.h>
/*------------------字符设备内容----------------------*/
#define DEV_NAME "rgb-leds"
#define DEV_CNT (1)
//定义字符设备的设备号
static dev_t led_devno;
//定义字符设备结构体chr_dev
static struct cdev led_chr_dev;
struct class *class_led; //保存创建的类
struct device *device; // 保存创建的设备
struct device_node *rgb_led_device_node; //RGB led的设备树节点
int leds_rgb[3];//保存获取得到的红绿蓝灯引脚编号
/*字符设备操作函数集,open函数*/
static int led_chr_dev_open(struct inode *inode, struct file *filp)
{
printk("\n open form driver \n");
return 0;
}
/*字符设备操作函数集,write函数*/
static ssize_t led_chr_dev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
unsigned char write_data[2] = {0}; //用于保存接收到的数据
int error = -1;
if(cnt<2)
return -1;
error = copy_from_user(&write_data, buf, cnt);
printk("\n write_data %d %d \n",write_data[0],write_data[1]);
if(error < 0) {
return -1;
}
if(write_data[0]>0&&write_data[0]<4)
{
if(write_data[1] == 0 ||write_data[1] == 1)
gpio_direction_output(leds_rgb[write_data[0]-1],write_data[1]);
}
#if 0
/*设置 GPIO1_04 输出电平*/
if(write_data & 0x04)
{
gpio_direction_output(rgb_led_red, 0); // GPIO1_04引脚输出低电平,红灯亮
}
else
{
gpio_direction_output(rgb_led_red, 1); // GPIO1_04引脚输出高电平,红灯灭
}
/*设置 GPIO4_20 输出电平*/
if(write_data & 0x02)
{
gpio_direction_output(rgb_led_green, 0); // GPIO4_20引脚输出低电平,绿灯亮
}
else
{
gpio_direction_output(rgb_led_green, 1); // GPIO4_20引脚输出高电平,绿灯灭
}
/*设置 GPIO4_19 输出电平*/
if(write_data & 0x01)
{
gpio_direction_output(rgb_led_blue, 0); // GPIO4_19引脚输出低电平,蓝灯亮
}
else
{
gpio_direction_output(rgb_led_blue, 1); // GPIO4_19引脚输出高电平,蓝灯灭
}
#endif
return 0;
}
/*字符设备操作函数集*/
static struct file_operations led_chr_dev_fops =
{
.owner = THIS_MODULE,
.open = led_chr_dev_open,
.write = led_chr_dev_write,
};
/*----------------平台驱动函数集-----------------*/
static int led_probe(struct platform_device *pdv)
{
int ret = 0; //用于保存申请设备号的结果
printk(KERN_EMERG "\t match successed \n");
/*获取RGB的设备树节点*/
rgb_led_device_node = of_find_node_by_path("/rgb_led");
if(rgb_led_device_node == NULL)
{
printk(KERN_EMERG "\t get rgb_led failed! \n");
}
leds_rgb[0] = of_get_named_gpio(rgb_led_device_node, "rgb_led_red", 0);
leds_rgb[1] = of_get_named_gpio(rgb_led_device_node, "rgb_led_green", 0);
leds_rgb[2] = of_get_named_gpio(rgb_led_device_node, "rgb_led_blue", 0);
printk("rgb_led_red = %d,\n rgb_led_green = %d,\n rgb_led_blue = %d,\n", leds_rgb[0],\
leds_rgb[1],leds_rgb[2]);
gpio_direction_output(leds_rgb[0], 1);
gpio_direction_output(leds_rgb[1], 1);
gpio_direction_output(leds_rgb[2], 1);
/*---------------------注册 字符设备部分-----------------*/
//第一步
//采用动态分配的方式,获取设备编号,次设备号为0,
//设备名称为rgb-leds,可通过命令cat /proc/devices查看
//DEV_CNT为1,当前只申请一个设备编号
ret = alloc_chrdev_region(&led_devno, 0, DEV_CNT, DEV_NAME);
if(ret < 0){
printk("fail to alloc led_devno\n");
goto alloc_err;
}
//第二步
//关联字符设备结构体cdev与文件操作结构体file_operations
led_chr_dev.owner = THIS_MODULE;
cdev_init(&led_chr_dev, &led_chr_dev_fops);
//第三步
//添加设备至cdev_map散列表中
ret = cdev_add(&led_chr_dev, led_devno, DEV_CNT);
if(ret < 0)
{
printk("fail to add cdev\n");
goto add_err;
}
//第四步
/*创建类 */
class_led = class_create(THIS_MODULE, DEV_NAME);
/*创建设备*/
device = device_create(class_led, NULL, led_devno, NULL, DEV_NAME);
return 0;
add_err:
//添加设备失败时,需要注销设备号
unregister_chrdev_region(led_devno, DEV_CNT);
printk("\n error! \n");
alloc_err:
return -1;
}
static const struct of_device_id rgb_led[] = {
{ .compatible = "fire,rgb-led"},
{ /* sentinel */ }
};
/*定义平台设备结构体*/
struct platform_driver led_platform_driver = {
.probe = led_probe,
.driver = {
.name = "rgb-leds-platform",
.owner = THIS_MODULE,
.of_match_table = rgb_led,
}
};
/*
*驱动初始化函数
*/
static int __init led_platform_driver_init(void)
{
int DriverState;
DriverState = platform_driver_register(&led_platform_driver);
printk(KERN_EMERG "\tDriverState is %d\n",DriverState);
return 0;
}
/*
*驱动注销函数
*/
static void __exit led_platform_driver_exit(void)
{
printk(KERN_EMERG "HELLO WORLD exit!\n");
device_destroy(class_led,led_devno);
class_destroy(class_led);//删除类
platform_driver_unregister(&led_platform_driver);
}
module_init(led_platform_driver_init);
module_exit(led_platform_driver_exit);
MODULE_LICENSE("GPL");
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char *argv[])
{
int error = -1;
printf("led_tiny test\n");
/*打开文件*/
int fd = open("/dev/rgb-leds", O_RDWR);
if(fd < 0)
{
printf("open file : %s failed !\n", argv[0]);
return -1;
}
unsigned char commend[2]= {1,0};
/*判断命令的有效性*/
while(1)
{
/*写入命令*/
error = write(fd,&commend,sizeof(commend));
if(error < 0)
{
printf("write file error! \n");
close(fd);
fd = -1;
break;
/*判断是否关闭成功*/
}
sleep(5);
commend[1]= !commend[1];
if(write(fd,&commend,sizeof(commend)) < 0)
{
printf("write file error! \n");
close(fd);
fd = -1;
break;
/*判断是否关闭成功*/
}
commend[1]= !commend[1];
commend[0] == 3?commend[0]= 1:commend[0]++;
sleep(5);
}
/*关闭文件*/
if(fd>0)
error = close(fd);
if(error < 0)
{
printf("close file error! \n");
}
return 0;
}
sudo insmod rgb-leds.ko
sudo ./rgb_leds_app
效果
图片已加速 实际为5s闪烁
版权声明:本文为CSDN博主「小白语记」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhangli_CSDN/article/details/122453402
pinctrl 子系统-----led灯闪烁实验
Linux 设备驱动理论看了有一段时间了,根据理论自己琢磨了一个从设备树到顶部进程的小实验 ----led闪烁实验
pinctrl 子系统的添加
如果从头开始时,首先第一步就是了解硬件信息
根据三张图我们得知:
LED_R 的阴极连接到 i.MX6ULL 芯片上 GPIO1_IO04 引脚, LED_G 连接到 CSI_HSYNC,LED_B
连接到 CSI_VSYNC。而 CSI_HSYNC 和 CSI_VSYNC 作为摄像头的某一功能被复用为 GPIO
LED 灯 | 原理图的标号 | 具体引脚名 | GPIO 端口及引脚编号 |
---|---|---|---|
R 灯 | GPIO_4 | GPIO1_IO04 | GPIO1_IO04 |
G 灯 | CSI_HSYNC | CSI_HSYNC | GPIO4_IO20 |
B 灯 | CSI_VSYNC | CSI_VSYNC | GPIO4_IO19 |
pinctrl 子系统主要用于管理芯片的引脚,也就是将上述的硬件信息抽象出来放到Linux里,也就是告诉系统,我这个系统的配置寄存器在哪里,要配置成什么样,(输出?输入…)?
我们打开厂商写好的引脚功能文件imx6ul-pinfunc.h,查找如下
led引脚 | 配置地址别名 |
---|---|
GPIO1_IO04 | MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 |
CSI_HSYNC | MX6UL_PAD_CSI_HSYNC__GPIO4_IO20 |
CSI_VSYNC | MX6UL_PAD_CSI_VSYNC__GPIO4_IO19 |
属性配置:
由上图知道如下信息
寄存器位数 | 功能 | 我们选择的功能 |
---|---|---|
31-17 | 保留 | 0x0000 |
16 | 0:关闭迟滞;1:使能迟滞 | 0 |
15-14 | 不同电阻上下拉 | 00 |
13 | 使能输出状态保存 0:保持;1:上拉 | 0 |
12 | 0:关闭拉保持;1:使能拉/保持 | 1 |
11 | 0:禁止漏极开路1:使能漏极开路 | |
10-8 | 保留 | 000 |
7-6 | 速度 | 10 |
5:3 | 输出使能 | 110 |
2-1 | 保留 | 00 |
0 | 0:低速;1:高速 | 1 |
综上:0x000010B1
故根据子系统格式为
pinctrl_rgb_led:rgb_led{
fsl,pins = <
MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x000010B1
MX6UL_PAD_CSI_HSYNC__GPIO4_IO20 0x000010B1
MX6UL_PAD_CSI_VSYNC__GPIO4_IO19 0x000010B1
>;
};
添加到 imx6ull-mmc-npi.dts 如下,表明我们引脚基础配置已经配置好。
设备树节点的添加
然后我们再添加一个设备节点 也就是告诉Linux ,我们有一个RGB灯。使用pinctrl_rgb_led子系统配置
/* 添加 rgb_led 节点 */
rgb_led{
pinctrl-names = "default";
compatible = "fire,rgb-led";//与平台驱动做匹配
pinctrl-0 = <&pinctrl_rgb_led>;
rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;//低电平有效
rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>;
rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;
status = "okay";
};
#ebf_linux_kernel 目录下进行编译
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
#或
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- npi_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
./arch/arm/boot/dts”目录下生成“imx6ull-mmc-npi.dtb”文件,将其替换掉板子/usr/lib/linux-image-4.19.35-imx6/ 目录下的 imx6ull-mmc-npi.dtb 文件并输入 sudo reboot 重启开发板
我们开始写驱动与测试进程
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/platform_device.h>
/*------------------字符设备内容----------------------*/
#define DEV_NAME "rgb-leds"
#define DEV_CNT (1)
//定义字符设备的设备号
static dev_t led_devno;
//定义字符设备结构体chr_dev
static struct cdev led_chr_dev;
struct class *class_led; //保存创建的类
struct device *device; // 保存创建的设备
struct device_node *rgb_led_device_node; //RGB led的设备树节点
int leds_rgb[3];//保存获取得到的红绿蓝灯引脚编号
/*字符设备操作函数集,open函数*/
static int led_chr_dev_open(struct inode *inode, struct file *filp)
{
printk("\n open form driver \n");
return 0;
}
/*字符设备操作函数集,write函数*/
static ssize_t led_chr_dev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
unsigned char write_data[2] = {0}; //用于保存接收到的数据
int error = -1;
if(cnt<2)
return -1;
error = copy_from_user(&write_data, buf, cnt);
printk("\n write_data %d %d \n",write_data[0],write_data[1]);
if(error < 0) {
return -1;
}
if(write_data[0]>0&&write_data[0]<4)
{
if(write_data[1] == 0 ||write_data[1] == 1)
gpio_direction_output(leds_rgb[write_data[0]-1],write_data[1]);
}
#if 0
/*设置 GPIO1_04 输出电平*/
if(write_data & 0x04)
{
gpio_direction_output(rgb_led_red, 0); // GPIO1_04引脚输出低电平,红灯亮
}
else
{
gpio_direction_output(rgb_led_red, 1); // GPIO1_04引脚输出高电平,红灯灭
}
/*设置 GPIO4_20 输出电平*/
if(write_data & 0x02)
{
gpio_direction_output(rgb_led_green, 0); // GPIO4_20引脚输出低电平,绿灯亮
}
else
{
gpio_direction_output(rgb_led_green, 1); // GPIO4_20引脚输出高电平,绿灯灭
}
/*设置 GPIO4_19 输出电平*/
if(write_data & 0x01)
{
gpio_direction_output(rgb_led_blue, 0); // GPIO4_19引脚输出低电平,蓝灯亮
}
else
{
gpio_direction_output(rgb_led_blue, 1); // GPIO4_19引脚输出高电平,蓝灯灭
}
#endif
return 0;
}
/*字符设备操作函数集*/
static struct file_operations led_chr_dev_fops =
{
.owner = THIS_MODULE,
.open = led_chr_dev_open,
.write = led_chr_dev_write,
};
/*----------------平台驱动函数集-----------------*/
static int led_probe(struct platform_device *pdv)
{
int ret = 0; //用于保存申请设备号的结果
printk(KERN_EMERG "\t match successed \n");
/*获取RGB的设备树节点*/
rgb_led_device_node = of_find_node_by_path("/rgb_led");
if(rgb_led_device_node == NULL)
{
printk(KERN_EMERG "\t get rgb_led failed! \n");
}
leds_rgb[0] = of_get_named_gpio(rgb_led_device_node, "rgb_led_red", 0);
leds_rgb[1] = of_get_named_gpio(rgb_led_device_node, "rgb_led_green", 0);
leds_rgb[2] = of_get_named_gpio(rgb_led_device_node, "rgb_led_blue", 0);
printk("rgb_led_red = %d,\n rgb_led_green = %d,\n rgb_led_blue = %d,\n", leds_rgb[0],\
leds_rgb[1],leds_rgb[2]);
gpio_direction_output(leds_rgb[0], 1);
gpio_direction_output(leds_rgb[1], 1);
gpio_direction_output(leds_rgb[2], 1);
/*---------------------注册 字符设备部分-----------------*/
//第一步
//采用动态分配的方式,获取设备编号,次设备号为0,
//设备名称为rgb-leds,可通过命令cat /proc/devices查看
//DEV_CNT为1,当前只申请一个设备编号
ret = alloc_chrdev_region(&led_devno, 0, DEV_CNT, DEV_NAME);
if(ret < 0){
printk("fail to alloc led_devno\n");
goto alloc_err;
}
//第二步
//关联字符设备结构体cdev与文件操作结构体file_operations
led_chr_dev.owner = THIS_MODULE;
cdev_init(&led_chr_dev, &led_chr_dev_fops);
//第三步
//添加设备至cdev_map散列表中
ret = cdev_add(&led_chr_dev, led_devno, DEV_CNT);
if(ret < 0)
{
printk("fail to add cdev\n");
goto add_err;
}
//第四步
/*创建类 */
class_led = class_create(THIS_MODULE, DEV_NAME);
/*创建设备*/
device = device_create(class_led, NULL, led_devno, NULL, DEV_NAME);
return 0;
add_err:
//添加设备失败时,需要注销设备号
unregister_chrdev_region(led_devno, DEV_CNT);
printk("\n error! \n");
alloc_err:
return -1;
}
static const struct of_device_id rgb_led[] = {
{ .compatible = "fire,rgb-led"},
{ /* sentinel */ }
};
/*定义平台设备结构体*/
struct platform_driver led_platform_driver = {
.probe = led_probe,
.driver = {
.name = "rgb-leds-platform",
.owner = THIS_MODULE,
.of_match_table = rgb_led,
}
};
/*
*驱动初始化函数
*/
static int __init led_platform_driver_init(void)
{
int DriverState;
DriverState = platform_driver_register(&led_platform_driver);
printk(KERN_EMERG "\tDriverState is %d\n",DriverState);
return 0;
}
/*
*驱动注销函数
*/
static void __exit led_platform_driver_exit(void)
{
printk(KERN_EMERG "HELLO WORLD exit!\n");
device_destroy(class_led,led_devno);
class_destroy(class_led);//删除类
platform_driver_unregister(&led_platform_driver);
}
module_init(led_platform_driver_init);
module_exit(led_platform_driver_exit);
MODULE_LICENSE("GPL");
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char *argv[])
{
int error = -1;
printf("led_tiny test\n");
/*打开文件*/
int fd = open("/dev/rgb-leds", O_RDWR);
if(fd < 0)
{
printf("open file : %s failed !\n", argv[0]);
return -1;
}
unsigned char commend[2]= {1,0};
/*判断命令的有效性*/
while(1)
{
/*写入命令*/
error = write(fd,&commend,sizeof(commend));
if(error < 0)
{
printf("write file error! \n");
close(fd);
fd = -1;
break;
/*判断是否关闭成功*/
}
sleep(5);
commend[1]= !commend[1];
if(write(fd,&commend,sizeof(commend)) < 0)
{
printf("write file error! \n");
close(fd);
fd = -1;
break;
/*判断是否关闭成功*/
}
commend[1]= !commend[1];
commend[0] == 3?commend[0]= 1:commend[0]++;
sleep(5);
}
/*关闭文件*/
if(fd>0)
error = close(fd);
if(error < 0)
{
printf("close file error! \n");
}
return 0;
}
sudo insmod rgb-leds.ko
sudo ./rgb_leds_app
效果
图片已加速 实际为5s闪烁
版权声明:本文为CSDN博主「小白语记」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhangli_CSDN/article/details/122453402
暂无评论