1)实验平台:正点原子Linux开发板

2)摘自《正点原子I.MX6U嵌入式Linux驱动开发指南》

关注官方微信号公众号,获取更多资料:正点原子

大家将imx35_gpio_hwdata中的各个成员变量和图45.2.2.1中的GPIO寄存器表对比就会发现,imx35_gpio_hwdata结构体就是GPIO寄存器组结构。这样我们后面就可以通过mxc_gpio_hwdata这个全局变量来访问GPIO的相应寄存器了。

继续回到示例代码45.2.2.5的mxc_gpio_probe函数中,第417行,调用函数platform_get_resource获取设备树中内存资源信息,也就是reg属性值。前面说了reg属性指定了GPIO1控制器的寄存器基地址为0X0209C000,在配合前面已经得到的mxc_gpio_hwdata,这样Linux内核就可以访问gpio1的所有寄存器了。

第418行,调用devm_ioremap_resource函数进行内存映射,得到0x0209C000在Linux内核中的虚拟地址。

第422、423行,通过platform_get_irq函数获取中断号,第422行获取高16位GPIO的中断号,第423行获取底16位GPIO中断号。

第428、429行,操作GPIO1的IMR和ISR这两个寄存器,关闭GPIO1所有IO中断,并且清除状态寄存器。

第438~448行,设置对应GPIO的中断服务函数,不管是高16位还是低16位,中断服务函数都是mx3_gpio_irq_handler。

第450~453行,bgpio_init函数第一个参数为bgc,是bgpio_chip结构体指针。bgpio_chip结构体有个gc成员变量,gc是个gpio_chip结构体类型的变量。gpio_chip结构体是抽象出来的GPIO控制器,gpio_chip结构体如下所示(有缩减):

示例代码45.2.2.9 gpio_chip结构体

74struct gpio_chip {

75constchar *label;

76struct device *dev;

77struct module *owner;

78struct list_head list;

79

80int (*request)(struct gpio_chip *chip,

81 unsigned offset);

82void (*free)(struct gpio_chip *chip,

83 unsigned offset);

84int (*get_direction)(struct gpio_chip *chip,

85 unsigned offset);

86int (*direction_input)(struct gpio_chip *chip,

87 unsigned offset);

88int (*direction_output)(struct gpio_chip *chip,

89 unsigned offset,int value);

90int (*get)(struct gpio_chip *chip,

91 unsigned offset);

92void (*set)(struct gpio_chip *chip,

93 unsigned offset,int value);

......

145};

可以看出,gpio_chip大量的成员都是函数,这些函数就是GPIO操作函数。bgpio_init函数主要任务就是初始化bgc->gc。bgpio_init里面有三个setup函数:bgpio_setup_io、bgpio_setup_accessors和bgpio_setup_direction。这三个函数就是初始化bgc->gc中的各种有关GPIO的操作,比如输出,输入等等。第451~453行的GPIO_PSR、GPIO_DR和GPIO_GDIR都是I.MX6ULL的GPIO寄存器。这些寄存器地址会赋值给bgc参数的reg_dat、reg_set、reg_clr和reg_dir这些成员变量。至此,bgc既有了对GPIO的操作函数,又有了I.MX6ULL有关GPIO的寄存器,那么只要得到bgc就可以对I.MX6ULL的GPIO进行操作。

继续回到mxc_gpio_probe函数,第461行调用函数gpiochip_add向Linux内核注册gpio_chip,也就是port->bgc.gc。注册完成以后我们就可以在驱动中使用gpiolib提供的各个API函数。

45.2.3 gpio子系统API函数对于驱动开发人员,设置好设备树以后就可以使用gpio子系统提供的API函数来操作指定的GPIO,gpio子系统向驱动开发人员屏蔽了具体的读写寄存器过程。这就是驱动分层与分离的好处,大家各司其职,做好自己的本职工作即可。gpio子系统提供的常用的API函数有下面几个:

1、gpio_request函数

gpio_request函数用于申请一个GPIO管脚,在使用一个GPIO之前一定要使用gpio_request进行申请,函数原型如下:

int gpio_request(unsigned gpio, const char *label)

函数参数和返回值含义如下:

gpio:要申请的gpio标号,使用of_get_named_gpio函数从设备树获取指定GPIO属性信息,此函数会返回这个GPIO的标号。

label:给gpio设置个名字。

返回值:0,申请成功;其他值,申请失败。

2、gpio_free函数

如果不使用某个GPIO了,那么就可以调用gpio_free函数进行释放。函数原型如下:

void gpio_free(unsigned gpio)

函数参数和返回值含义如下:

gpio:要释放的gpio标号。

返回值:无。

3、gpio_direction_input函数

此函数用于设置某个GPIO为输入,函数原型如下所示:

int gpio_direction_input(unsigned gpio)

函数参数和返回值含义如下:

gpio:要设置为输入的GPIO标号。

返回值:0,设置成功;负值,设置失败。

4、gpio_direction_output函数

此函数用于设置某个GPIO为输出,并且设置默认输出值,函数原型如下:

int gpio_direction_output(unsigned gpio, int value)

函数参数和返回值含义如下:

gpio:要设置为输出的GPIO标号。

value:GPIO默认输出值。

返回值:0,设置成功;负值,设置失败。

5、gpio_get_value函数

此函数用于获取某个GPIO的值(0或1),此函数是个宏,定义所示:

#define gpio_get_value __gpio_get_value

int __gpio_get_value(unsigned gpio)

函数参数和返回值含义如下:

gpio:要获取的GPIO标号。

返回值:非负值,得到的GPIO值;负值,获取失败。

6、gpio_set_value函数

此函数用于设置某个GPIO的值,此函数是个宏,定义如下

#define gpio_set_value __gpio_set_value

void __gpio_set_value(unsigned gpio, int value)

函数参数和返回值含义如下:

gpio:要设置的GPIO标号。

value:要设置的值。

返回值:无

关于gpio子系统常用的API函数就讲这些,这些是我们用的最多的。

45.2.4 设备树中添加gpio节点模板继续完成45.1.3中的test设备,在45.1.3中我们已经讲解了如何创建test设备的pinctrl节点。本节我们来学习一下如何创建test设备的GPIO节点。

1、创建test设备节点

在根节点“/”下创建test设备子节点,如下所示:

示例代码45.2.4.1 test设备节点

1 test {

2/* 节点内容 */

3};

2、添加pinctrl信息

在45.1.3中我们创建了pinctrl_test节点,此节点描述了test设备所使用的GPIO_IO00这个PIN的信息,我们要将这节点添加到test设备节点中,如下所示:

示例代码45.2.4.2 向test节点添加pinctrl信息

1 test {

2 pinctrl-names ="default";

3 pinctrl-0=;

4/* 其他节点内容 */

5};

第2行,添加pinctrl-names属性,此熟悉描述pinctrl名字为“default”。

第3行,添加pinctrl-0节点,此节点引用45.1.3中创建的pinctrl_test节点,表示tset设备的所使用的PIN信息保存在pinctrl_test节点中。

3、添加GPIO属性信息

我们最后需要在test节点中添加GPIO属性信息,表明test所使用的GPIO是哪个引脚,添加完成以后如下所示:

示例代码45.2.4.3 向test节点添加gpio属性

1 test {

2 pinctrl-names ="default";

3 pinctrl-0=;

4 gpio =

5};

第4行,test设备所使用的gpio。

关于pinctrl子系统和gpio子系统就讲解到这里,接下来就使用pinctrl和gpio子系统来驱动I.MX6ULL-ALPHA开发板上的LED灯。

45.2.5 与gpio相关的OF函数在示例代码45.2.4.3中,我们定义了一个名为“gpio”的属性,gpio属性描述了test这个设备所使用的GPIO。在驱动程序中需要读取gpio属性内容,Linux内核提供了几个与GPIO有关的OF函数,常用的几个OF函数如下所示:

1、of_gpio_named_count函数

of_gpio_named_count函数用于获取设备树某个属性里面定义了几个GPIO信息,要注意的是空的GPIO信息也会被统计到,比如:

gpios = <0

&gpio1 1 2

0

&gpio2 3 4>;

上述代码的“gpios”节点一共定义了4个GPIO,但是有2个是空的,没有实际的含义。通过of_gpio_named_count函数统计出来的GPIO数量就是4个,此函数原型如下:

int of_gpio_named_count(struct device_node *np,const char *propname)

函数参数和返回值含义如下:

nd:设备节点。

propname:要统计的GPIO属性。

返回值:正值,统计到的GPIO数量;负值,失败。

2、of_gpio_count函数

和of_gpio_named_count函数一样,但是不同的地方在于,此函数统计的是“gpios”这个属性的GPIO数量,而of_gpio_named_count函数可以统计任意属性的GPIO信息,函数原型如下所示:

int of_gpio_count(struct device_node *np)

函数参数和返回值含义如下:

nd:设备节点。

返回值:正值,统计到的GPIO数量;负值,失败。

3、of_get_named_gpio函数

此函数获取GPIO编号,因为Linux内核中关于GPIO的API函数都要使用GPIO编号,此函数会将设备树中类似的属性信息转换为对应的GPIO编号,此函数在驱动中使用很频繁!函数原型如下:

int of_get_named_gpio(struct device_node *np,

const char *propname,

int index)

函数参数和返回值含义如下:

nd:设备节点。

propname:包含要获取GPIO信息的属性名。

index:GPIO索引,因为一个属性里面可能包含多个GPIO,此参数指定要获取哪个GPIO的编号,如果只有一个GPIO信息的话此参数为0。

返回值:正值,获取到的GPIO编号;负值,失败。

45.3硬件原理图分析本章实验硬件原理图参考8.3小节即可。

45.4 实验程序编写本实验对应的例程路径为:开发板光盘->2、Linux驱动例程->5_gpioled。

本章实验我们继续研究LED灯,在第四十四章实验中我们通过设备树向dtsled.c文件传递相应的寄存器物理地址,然后在驱动文件中配置寄存器。本章实验我们使用pinctrl和gpio子系统来完成LED灯驱动。

45.4.1 修改设备树文件1、添加pinctrl节点

I.MX6U-ALPHA开发板上的LED灯使用了GPIO1_IO03这个PIN,打开imx6ull-alientek-emmc.dts,在iomuxc节点的imx6ul-evk子节点下创建一个名为“pinctrl_led”的子节点,节点内容如下所示:

示例代码45.4.1.1 GPIO1_IO03 pinctrl节点

1 pinctrl_led: ledgrp {

2 fsl,pins =<

3 MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0/* LED0 */

4>;

5};

第3行,将GPIO1_IO03这个PIN复用为GPIO1_IO03,也就是GPIO,GPI1O_IO03这个PIN的电气属性值为0X10B0,也就是设置IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03寄存器的值为0X10B0。

2、添加LED设备节点

在根节点“/”下创建LED灯节点,节点名为“gpioled”,节点内容如下:

示例代码45.4.1.2 创建LED灯节点

1 gpioled {

2 #address-cells =<1>;

3 #size-cells =<1>;

4 compatible ="atkalpha-gpioled";

5 pinctrl-names ="default";

6 pinctrl-0=;

7 led-gpio =;

8 status ="okay";

9}

第7行,pinctrl-0属性设置LED灯所使用的PIN对应的pinctrl节点。

第8行,led-gpio属性指定了LED灯所使用的GPIO,在这里就是GPIO1的IO03,低电平有效。稍后编写驱动程序的时候会获取led-gpio属性的内容来得到GPIO编号,因为gpio子系统的API操作函数需要GPIO编号。

3、检查PIN是否被其他外设使用

这一点非常重要!!!

很多初次接触设备树的驱动开发人员很容易因为这个小问题栽了大跟头!因为我们所使用的设备树基本都是在半导体厂商提供的设备树文件基础上修改而来的,而半导体厂商提供的设备树是根据自己官方开发板编写的,很多PIN的配置和我们所使用的开发板不一样。比如A这个引脚在官方开发板接的是I2C的SDA,而我们所使用的硬件可能将A这个引脚接到了其他的外设,比如LED灯上,接不同的外设,A这个引脚的配置就不同。一个引脚一次只能实现一个功能,如果A引脚在设备树中配置为了I2C的SDA信号,那么A引脚就不能再配置为GPIO,否则的话驱动程序在申请GPIO的时候就会失败。检查PIN有没有被其他外设使用包括两个方面:

①、检查pinctrl设置。

②、如果这个PIN配置为GPIO的话,检查这个GPIO有没有被别的外设使用。

在本章实验中LED灯使用的PIN为GPIO1_IO03,因此先检查GPIO_IO03这个PIN有没有被其他的pinctrl节点使用,在imx6ull-alientek-emmc.dts中找到如下内容:

示例代码45.4.1.3 pinctrl_tsc节点

480 pinctrl_tsc: tscgrp {

481 fsl,pins =<

482 MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0xb0

483 MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0xb0

484 MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0xb0

485 MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0xb0

486>;

487};

pinctrl_tsc节点是TSC(电阻触摸屏接口)的pinctrl节点,从第484行可以看出,默认情况下GPIO1_IO03作为了TSC外设的PIN。所以我们需要将第484行屏蔽掉!和C语言一样,在要屏蔽的内容前后加上“/*”和“*/”符号即可。其实在I.MX6U-ALPHA开发板上并没有用到TSC接口,所以第482~485行的内容可以全部屏蔽掉。

因为本章实验我们将GPIO1_IO03这个PIN配置为了GPIO,所以还需要查找一下有没有其他的外设使用了GPIO1_IO03,在imx6ull-alientek-emmc.dts中搜索“gpio1 3”,找到如下内容:

示例代码45.4.1.4 tsc节点

723&tsc {

724 pinctrl-names ="default";

725 pinctrl-0=;

726 xnur-gpio =;

727 measure-delay-time =<0xffff>;

728 pre-charge-time =<0xfff>;

729 status ="okay";

730};

tsc是TSC的外设节点,从726行可以看出,tsc外设也使用了GPIO1_IO03,同样我们需要将这一行屏蔽掉。然后在继续搜索“gpio1 3”,看看除了本章的LED灯以外还有没有其他的地方也使用了GPIO1_IO03,找到一个屏蔽一个。

设备树编写完成以后使用“makedtbs”命令重新编译设备树,然后使用新编译出来的imx6ull-alientek-emmc.dtb文件启动Linux系统。启动成功以后进入“/proc/device-tree”目录中查看“gpioled”节点是否存在,如果存在的话就说明设备树基本修改成功(具体还要驱动验证),结果如图45.4.1.1所示:

图45.4.1.1 gpio子节点

45.4.2 LED灯驱动程序编写设备树准备好以后就可以编写驱动程序了,本章实验在第四十四章实验驱动文件dtsled.c的基础上修改而来。新建名为“5_gpioled”文件夹,然后在5_gpioled文件夹里面创建vscode工程,工作区命名为“gpioled”。工程创建好以后新建gpioled.c文件,在gpioled.c里面输入如下内容:

示例代码45.4.2.1 gpioled.c驱动文件代码

1 #include

2 #include

3 #include

4 #include

5 #include

6 #include

7 #include

8 #include

9 #include

10 #include

11 #include

12 #include

13 #include

14 #include

15 #include

16 #include

17/***************************************************************

18 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.

19文件名 : gpioled.c

20作者 : 左忠凯

21版本 : V1.0

22描述 : 采用pinctrl和gpio子系统驱动LED灯。

23其他 : 无

24论坛 : www.openedv.com

25日志 : 初版V1.0 2019/7/13 左忠凯创建

26 ***************************************************************/

27 #define GPIOLED_CNT 1 /* 设备号个数 */

28 #define GPIOLED_NAME "gpioled" /* 名字 */

29 #define LEDOFF 0 /* 关灯 */

30 #define LEDON 1 /* 开灯 */

31

32/* gpioled设备结构体 */

33struct gpioled_dev{

34 dev_t devid; /* 设备号 */

35struct cdev cdev; /* cdev */

36struct class *class; /* 类 */

37struct device *device; /* 设备 */

38int major; /* 主设备号 */

39int minor; /* 次设备号 */

40struct device_node *nd; /* 设备节点 */

41int led_gpio; /* led所使用的GPIO编号 */

42};

43

44struct gpioled_dev gpioled;/* led设备 */

45

46/*

47 * @description : 打开设备

48 * @param – inode : 传递给驱动的inode

49 * @param – filp : 设备文件,file结构体有个叫做private_data的成员变量

50 * 一般在open的时候将private_data指向设备结构体。

51 * @Return : 0 成功;其他失败

52 */

53staticint led_open(struct inode *inode,struct file *filp)

54{

55 filp->private_data =&gpioled;/* 设置私有数据 */

56return0;

57}

58

59/*

60 * @description : 从设备读取数据

61 * @param – filp : 要打开的设备文件(文件描述符)

62 * @param - buf : 返回给用户空间的数据缓冲区

63 * @param - cnt : 要读取的数据长度

64 * @param – offt : 相对于文件首地址的偏移

65 * @return : 读取的字节数,如果为负值,表示读取失败

66 */

67static ssize_t led_read(struct file *filp,char __user *buf,

size_t cnt, loff_t *offt)

68{

69return0;

70}

71

72/*

73 * @description : 向设备写数据

74 * @param - filp : 设备文件,表示打开的文件描述符

75 * @param - buf : 要写给设备写入的数据

76 * @param - cnt : 要写入的数据长度

77 * @param – offt : 相对于文件首地址的偏移

78 * @return : 写入的字节数,如果为负值,表示写入失败

79 */

80static ssize_t led_write(struct file *filp,constchar __user *buf,

size_t cnt, loff_t *offt)

81{

82int retvalue;

83unsignedchar databuf[1];

84unsignedchar ledstat;

85struct gpioled_dev *dev = filp->private_data;

86

87 retvalue = copy_from_user(databuf, buf, cnt);

88if(retvalue <0){

89 printk("kernel write failed!\r\n");

90return-EFAULT;

91}

92

93 ledstat = databuf[0]; /* 获取状态值 */

94

95if(ledstat == LEDON){

96 gpio_set_value(dev->led_gpio,0);/* 打开LED灯 */

97}elseif(ledstat == LEDOFF){

98 gpio_set_value(dev->led_gpio,1);/* 关闭LED灯 */

99}

100return0;

101}

102

103/*

104 * @description : 关闭/释放设备

105 * @param – filp : 要关闭的设备文件(文件描述符)

106 * @return : 0 成功;其他失败

107 */

108staticint led_release(struct inode *inode,struct file *filp)

109{

110return0;

111}

112

113/* 设备操作函数 */

114staticstruct file_operations gpioled_fops ={

115.owner = THIS_MODULE,

116.open = led_open,

117.read = led_read,

118.write = led_write,

119.release = led_release,

120};

121

122/*

123 * @description : 驱动入口函数

124 * @param : 无

125 * @return : 无

126 */

127staticint __init led_init(void)

128{

129int ret =0;

130

131/* 设置LED所使用的GPIO */

132/* 1、获取设备节点:gpioled */

133 gpioled.nd = of_find_node_by_path("/gpioled");

134if(gpioled.nd ==NULL){

135 printk("gpioled node not find!\r\n");

136return-EINVAL;

137}else{

138 printk("gpioled node find!\r\n");

139}

140

141/* 2、获取设备树中的gpio属性,得到LED所使用的LED编号 */

142 gpioled.led_gpio = of_get_named_gpio(gpioled.nd,"led-gpio",0);

143if(gpioled.led_gpio <0){

144 printk("can't get led-gpio");

145return-EINVAL;

146}

147 printk("led-gpio num = %d\r\n", gpioled.led_gpio);

148

149/* 3、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯 */

150 ret = gpio_direction_output(gpioled.led_gpio,1);

151if(ret <0){

152 printk("can't set gpio!\r\n");

153}

154

155/* 注册字符设备驱动 */

156/* 1、创建设备号 */

157if(gpioled.major){ /* 定义了设备号 */

158 gpioled.devid = MKDEV(gpioled.major,0);

159 register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);

160}else{ /* 没有定义设备号 */

161 alloc_chrdev_region(&gpioled.devid,0, GPIOLED_CNT,GPIOLED_NAME); /* 申请设备号 */

162 gpioled.major = MAJOR(gpioled.devid);/* 获取分配号的主设备号 */

163 gpioled.minor = MINOR(gpioled.devid);/* 获取分配号的次设备号 */

164}

165 printk("gpioled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor);

166

167/* 2、初始化cdev */

168 gpioled.cdev.owner = THIS_MODULE;

169 cdev_init(&gpioled.cdev,&gpioled_fops);

170

171/* 3、添加一个cdev */

172 cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);

173

174/* 4、创建类 */

175 gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);

176if(IS_ERR(gpioled.class)){

177return PTR_ERR(gpioled.class);

178}

179

180/* 5、创建设备 */

181 gpioled.device = device_create(gpioled.class,NULL, gpioled.devid,NULL,GPIOLED_NAME);

182if(IS_ERR(gpioled.device)){

183return PTR_ERR(gpioled.device);

184}

185return0;

186}

187

188/*

189 * @description : 驱动出口函数

190 * @param : 无

191 * @return : 无

192 */

193staticvoid __exit led_exit(void)

194{

195/* 注销字符设备驱动 */

196 cdev_del(&gpioled.cdev);/* 删除cdev */

197 unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);/* 注销 */

198

199 device_destroy(gpioled.class, gpioled.devid);

200 class_destroy(gpioled.class);

201}

202

203 module_init(led_init);

204 module_exit(led_exit);

205 MODULE_LICENSE("GPL");

206 MODULE_AUTHOR("zuozhongkai");

第41行,在设备结构体gpioled_dev中加入led_gpio这个成员变量,此成员变量保存LED等所使用的GPIO编号。

第55行,将设备结构体变量gpioled设置为filp的私有数据private_data。

第85行,通过读取filp的private_data成员变量来得到设备结构体变量,也就是gpioled。这种将设备结构体设置为filp私有数据的方法在Linux内核驱动里面非常常见。

第96、97行,直接调用gpio_set_value函数来向GPIO写入数据,实现开/关LED的效果。不需要我们直接操作相应的寄存器。

第133行,获取节点“/gpioled”。

第142行,通过函数of_get_named_gpio函数获取LED所使用的LED编号。相当于将gpioled节点中的“led-gpio”属性值转换为对应的LED编号。

第150行,调用函数gpio_direction_output设置GPIO1_IO03这个GPIO为输出,并且默认高电平,这样默认就会关闭LED灯。

可以看出gpioled.c文件中的内容和第四十四章的dtsled.c差不多,只是取消掉了配置寄存器的过程,改为使用Linux内核提供的API函数。在GPIO操作上更加的规范化,符合Linux代码框架,而且也简化了GPIO驱动开发的难度,以后我们所有例程用到GPIO的地方都采用此方法。

44.4.3 编写测试APP本章直接使用第四十二章的测试APP,将上一章的ledApp.c文件复制到本章实验工程下即可。

45.5 运行测试45.5.1 编译驱动程序和测试APP1、编译驱动程序

编写Makefile文件,本章实验的Makefile文件和第四十章实验基本一样,只是将obj-m变量的值改为gpioled.o,Makefile内容如下所示:

示例代码45.5.1.1 Makefile文件

1 KERNELDIR:= /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek

......

4 obj-m := gpioled.o.o

......

11 clean:

12$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第4行,设置obj-m变量的值为gpioled.o。

输入如下命令编译出驱动模块文件:

make-j32

编译成功以后就会生成一个名为“gpioled.ko”的驱动模块文件。

2、编译测试APP

输入如下命令编译测试ledApp.c这个测试程序:

arm-linux-gnueabihf-gcc ledApp.c -o ledApp

编译成功以后就会生成ledApp这个应用程序。

45.5.2 运行测试将上一小节编译出来的gpioled.ko和ledApp这两个文件拷贝到rootfs/lib/modules/4.1.15目录中,重启开发板,进入到目录lib/modules/4.1.15中,输入如下命令加载gpioled.ko驱动模块:

depmod //第一次加载驱动的时候需要运行此命令

modprobe gpioled.ko //加载驱动

驱动加载成功以后会在终端中输出一些信息,如图45.5.2.1所示:

图45.5.2.1 驱动加载成功以后输出的信息

从图45.5.2.1可以看出,gpioled这个节点找到了,并且GPIO1_IO03这个GPIO的编号为3。驱动加载成功以后就可以使用ledApp软件来测试驱动是否工作正常,输入如下命令打开LED灯:

./ledApp /dev/gpioled 1 //打开LED灯

输入上述命令以后观察I.MX6U-ALPHA开发板上的红色LED灯是否点亮,如果点亮的话说明驱动工作正常。在输入如下命令关闭LED灯:

./ledApp /dev/gpioled 0 //关闭LED灯

输入上述命令以后观察I.MX6U-ALPHA开发板上的红色LED灯是否熄灭。如果要卸载驱动的话输入如下命令即可:

rmmodgpioled.ko

嵌入式linux系统蜂鸣器实验,「正点原子Linux连载」第四十六章Linux蜂鸣器实验相关推荐

  1. 【正点原子STM32连载】第四十六章 FATFS实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

    1)实验平台:正点原子MiniPro H750开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560 3)全套实验源码+手册+视频 ...

  2. 【正点原子STM32连载】第四十五章 SD卡实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

    1)实验平台:正点原子MiniPro H750开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560 3)全套实验源码+手册+视频 ...

  3. 【正点原子FPGA连载】第四十六章SD卡读写测试实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1

    1)实验平台:正点原子新起点V2开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=609758951113 2)全套实验源码+手册+视频下载地址:ht ...

  4. 【正点原子MP157连载】第三十九章 LCD驱动实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  5. 【正点原子FPGA连载】第三十六章 基于OV5640的PL以太网视频传输实验-摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0

    1)实验平台:正点原子领航者ZYNQ开发板 2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761 3)全套实验源码+手册+视频下 ...

  6. 【正点原子STM32连载】 第四十五章 FLASH模拟EEPROM实验 摘自【正点原子】STM32F103 战舰开发指南V1.2

    第四十五章 FLASH模拟EEPROM实验 STM32本身没有自带EEPROM,但是STM32具有IAP(在应用编程)功能,所以我们可以把它的FLASH当成EEPROM来使用.本章,我们将利用STM3 ...

  7. 【正点原子FPGA连载】第四十九章OV5640摄像头HDMI灰度显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1

    1)实验平台:正点原子新起点V2开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=609758951113 2)全套实验源码+手册+视频下载地址:ht ...

  8. 【正点原子FPGA连载】第五十六章 双目OV5640摄像头HDMI显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1

    1)实验平台:正点原子新起点V2开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=609758951113 3)全套实验源码+手册+视频下载地址:ht ...

  9. 【正点原子STM32连载】第四十二章 FLASH模拟EEPROM实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

    1)实验平台:正点原子MiniPro H750开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560 3)全套实验源码+手册+视频 ...

最新文章

  1. AngularJS2.0 教程系列(一)
  2. 《OpenGL游戏程序设计》学习笔记---第七章图像、位图与OpenGL
  3. hdu 2459 (后缀数组+RMQ)
  4. Mysql基础运维及复制架构——PRIT非完整恢复
  5. 运行 vue 报node错
  6. AtCoder AGC035F Two Histograms (组合计数、容斥原理)
  7. php发送post请求的三种方法
  8. RTP/RTCP/RTSP协议分析(2)
  9. 传递对象_看懂Xlua实现原理——从宏观到微观(1)传递c#对象到Lua
  10. 2021年兰州师大附中高考成绩查询,2021年兰州重点高中名单及排名,兰州高中高考成绩排名榜...
  11. 如何过滤 adb logcat 命令的输出
  12. LeetCode刷题(24)
  13. Mybatis之二级缓存简析
  14. mongoDB简单介绍及安装
  15. sed 以及 awk用法
  16. php array函数 array_sum 求数组所有值和
  17. 简易局域网聊天java_简单的局域网聊天程序(java版本的)
  18. 数字版权管理 (DRM)
  19. 使用融资的心得和教训
  20. Sort sort =new Sort(Sort.Direction.ASC,“id“)

热门文章

  1. 考研DS备考|算法复习|编程or上机准备
  2. 智能冰箱宝APP控制除臭味有效杀菌
  3. Linux系统网络正常可以ping通但是ssh连接不上
  4. FS4521恒压线性充电IC
  5. 银河麒麟系统Arm64 编译Gdal
  6. 伟大创意检验10要 - 如何检验你的创意是否适合支撑你创业
  7. imos 学习笔记三 下载指定时间段视频信息 c#
  8. 微信赞赏不适合国内免费模式主导的互联网市场
  9. B8HTML5期末大作业:漫画网站设计——二次元漫画(8页) 漫画网页设计制作 简单静态HTML网页作品 我的漫画网页作业成品 学生漫画网站模板
  10. JavaScript概述