迅为IMX6ULL开发板Linux蜂鸣器实验

2020-08-14  本文已影响0人  TL_6cdd

在上一章讲解了添加 LED 灯驱动的整个流程和测试结果,这一章在来看一下蜂鸣器的驱动,蜂鸣器和

LED 灯的驱动其实是一样的,都是控制 GPIO 引脚输出高低电平,在本章继续学习一下蜂鸣器的驱动,也算

是在巩固一遍驱动的添加流程。

37.1 蜂鸣器设备注册流程 蜂鸣器设备注册流程

和 LED 灯驱动注册一样,蜂鸣器注册流程也分为下面几步:

5 硬件原理图分析,确定控制 LED 的 GPIO 信息。

6 根据 GPIO 信息在设备树文件中添加 pinctrl 信息

7 在设备树中创建 LED 的设备节点,并加入 GPIO 信息

8 编写 LED 设备驱动程序

接下来根据上面这四步来添加一下蜂鸣器的设备驱动。

2 37.2 蜂鸣器硬件原理图分析

蜂鸣器一端接 VSYS 电压(3.3V),另一端接控制引脚,只不过多了一个三极管。控制引脚为:SNVS_TAMPER1。

3 37.3 修改设备树文件 修改设备树文件

37.3.1  添加 pinctrl  信息

在 i.MX6UL 终结者开发板中使用 SNVS_TAMPER1 这个引脚来控制蜂鸣器设备。打开 topeet_emmc_4_3.dts

文件在 iomux 节点的 imx6ul-evk 子节点下创建一个名为“pinctrl_beep”的子节点,具体内容如下:

pinctrl_beep: gpio-beep {

fsl,pins = <

MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x10B0 /* beep */

>;

};

在 pinctrl_beep 节点中,将 SNVS_TAMPER1 引脚复用为 GPIO5_IO01,宏定义

MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 在 arch/arm/boot/dts/imx6ull-pinfunc-snvs.h 文件中。

37.3.2  添加 beep  设备节点

在 topeet_emmc_4_3.dts 文件下,在根节点“/”下创建 LED 节点,节点名为“beep”,具体内容如下:

1 beep {

2 #address-cells = <1>;

3 #size-cells = <1>;

4 compatible = "beep";

5 pinctrl-names = "default";

6 pinctrl-0 = <&pinctrl_beep>;

7 beep-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>;

8 status = "okay";

9 };

第 6 行,pinctrl-0 属性设置蜂鸣器所使用的 GPIO 对应的 pinctrl 节点。

第 7 行,beep-gpio 属性指定了蜂鸣器所使用的 GPIO。

接下来就是检查蜂鸣器使用的GPIO引脚SNVS_TAMPER1有没有被其他pinctrl节点使用,在检查GPIO5_IO01

这个 GPIO 有没有被其他外设使用,如果有的话都屏蔽掉。

设备树编写完成以后使用“make dtbs”命令重新编译设备树,然后使用新编译出来的 topeet_emmc_4_3.dtb

文件启动 Linux 系统。启动成功以后进入“/proc/device-tree”目录中查看“beep”节点是否存在,如果存

在的话就说明设备树基本修改成功(具体作用还要驱动验证),结果如下图所示:

37.4 编写蜂鸣器驱动程序 编写蜂鸣器驱动程序

本实验例程路径:i.MX6UL 终结者光盘资料/06_Linux 驱动例程/03_beep

蜂鸣器的驱动程序和 LED 的驱动程序基本一致,创建 beep.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 #define BEEP_CNT 1 /* 设备号个数 */

19 #define BEEP_NAME "beep" /* 名字 */

20 #define BEEPOFF 0 /* 关蜂鸣器 */

21 #define BEEPON 1 /* 开蜂鸣器 */

22

23

24 /* beep 设备结构体 */

25 struct beep_dev{

26 dev_t devid; /* 设备号 */

27 struct cdev cdev; /* cdev */

28 struct class *class; /* 类 */

29 struct device *device; /* 设备 */

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

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

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

33 int beep_gpio; /* beep 所使用的 GPIO 编号 */

34 };

35

36 struct beep_dev beep; /* beep 设备 */

37

38 /*

39 * @description : 打开设备

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

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

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

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

44 */

45 static int beep_open(struct inode *inode, struct file *filp)

46 {

47 filp->private_data = &beep; /* 设置私有数据 */

48 return 0;

49 }

50

51 /*

52 * @description : 向设备写数据

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

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

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

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

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

58 */

59 static ssize_t beep_write(struct file *filp, const char __user *buf,

60 size_t cnt, loff_t *offt)

61 {

62 int retvalue;

63 unsigned char databuf[1];

64 unsigned char beepstat;

65 struct beep_dev *dev = filp->private_data;

66

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

68 if(retvalue < 0) {

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

70 return -EFAULT;

71 }

72

73 beepstat = databuf[0]; /* 获取状态值 */

74

75 if(beepstat == BEEPON) {

76 gpio_set_value(dev->beep_gpio, 1); /* 打开蜂鸣器 */

77 } else if(beepstat == BEEPOFF) {

78 gpio_set_value(dev->beep_gpio, 0); /* 关闭蜂鸣器 */

79 }

80 return 0;

81 }

82

83 /*

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

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

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

87 */

88 static int beep_release(struct inode *inode, struct file *filp)

89 {

90 return 0;

91 }

92

93 /* 设备操作函数 */

94 static struct file_operations beep_fops = {

95 .owner = THIS_MODULE,

96 .open = beep_open,

97 .write = beep_write,

98 .release = beep_release,

99 };

100

101 /*

102 * @description : 驱动入口函数

103 * @param : 无

104 * @return : 无

105 */

106 static int __init beep_init(void)

107 {

108 int ret = 0;

109

110 /* 设置 BEEP 所使用的 GPIO */

111 /* 1、获取设备节点:beep */

112 beep.nd = of_find_node_by_path("/beep");

113 if(beep.nd == NULL) {

114 printk("beep node not find!\r\n");

115 return -EINVAL;

116 } else {

117 printk("beep node find!\r\n");

118 }

119

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

121 beep.beep_gpio = of_get_named_gpio(beep.nd, "beep-gpio", 0);

122 if(beep.beep_gpio < 0) {

123 printk("can't get beep-gpio");

124 return -EINVAL;

125 }

126 printk("led-gpio num = %d\r\n", beep.beep_gpio);

127

128 /* 3、设置 GPIO5_IO01 为输出,并且输出低电平,默认关闭 BEEP */

129 ret = gpio_direction_output(beep.beep_gpio, 0);

130 if(ret < 0) {

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

132 }

133

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

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

136 if (beep.major) { /* 定义了设备号 */

137 beep.devid = MKDEV(beep.major, 0);

138 register_chrdev_region(beep.devid, BEEP_CNT, BEEP_NAME);

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

140 alloc_chrdev_region(&beep.devid, 0, BEEP_CNT, BEEP_NAME);

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

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

143 }

144 printk("beep major=%d,minor=%d\r\n",beep.major, beep.minor);

145

146 /* 2、初始化 cdev */

147 beep.cdev.owner = THIS_MODULE;

148 cdev_init(&beep.cdev, &beep_fops);

149

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

151 cdev_add(&beep.cdev, beep.devid, BEEP_CNT);

152

153 /* 4、创建类 */

154 beep.class = class_create(THIS_MODULE, BEEP_NAME);

155 if (IS_ERR(beep.class)) {

156 return PTR_ERR(beep.class);

157 }

158

159 /* 5、创建设备 */

160 beep.device = device_create(beep.class, NULL, beep.devid, NULL,

161 BEEP_NAME);

162 if (IS_ERR(beep.device)) {

163 return PTR_ERR(beep.device);

164 }

165

166 return 0;

167 }

168

169 /*

170 * @description : 驱动出口函数

171 * @param : 无

172 * @return : 无

173 */

174 static void __exit beep_exit(void)

175 {

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

177 cdev_del(&beep.cdev); /* 删除 cdev */

178 unregister_chrdev_region(beep.devid, BEEP_CNT); /* 注销设备号 */

179

180 device_destroy(beep.class, beep.devid);

181 class_destroy(beep.class);

182 }

183

184 module_init(beep_init);

185 module_exit(beep_exit);

186 MODULE_LICENSE("GPL");

187 MODULE_AUTHOR("topeet");

5 37.5 编写应用测试程序 编写应用测试程序

创建应用测试程序 beep_test.c,内容如下:

1 #include "stdio.h"

2 #include "unistd.h"

3 #include "sys/types.h"

4 #include "sys/stat.h"

5 #include "fcntl.h"

6 #include "stdlib.h"

7 #include "string.h"

8

9 #define BEEPOFF 0

10 #define BEEPON 1

11

12 /*

13 * @description : main 主程序

14 * @param - argc : argv 数组元素个数

15 * @param - argv : 具体参数

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

17 */

18 int main(int argc, char *argv[])

19 {

20 int fd, retvalue;

21 char *filename;

22 unsigned char databuf[1];

23

24 if(argc != 3){

25 printf("Error Usage!\r\n");

26 return -1;

27 }

28

29 filename = argv[1];

30

31 /* 打开 beep 驱动 */

32 fd = open(filename, O_RDWR);

33 if(fd < 0){

34 printf("file %s open failed!\r\n", argv[1]);

35 return -1;

36 }

37

38 databuf[0] = atoi(argv[2]); /* 要执行的操作:打开或关闭 */

39

40 /* 向/dev/beep 文件写入数据 */

41 retvalue = write(fd, databuf, sizeof(databuf));

42 if(retvalue < 0){

43 printf("BEEP Control Failed!\r\n");

44 close(fd);

45 return -1;

46 }

47

48 retvalue = close(fd); /* 关闭文件 */

49 if(retvalue < 0){

50 printf("file %s close failed!\r\n", argv[1]);

51 return -1;

52 }

53 return 0;

54 }

beep_test.c 的文件内容和 gpioled_test.c 的内容差不多,都是对文件的打开、关闭、写操作。

6 37.6 编译运行测试 编译运行测试

37.6.1  编译蜂鸣器驱动文件

同 LED 驱动文件一样,创建 Makefile 文件,将 obj-m 的值改为 beep.o,Makefile 文件内容如下:

KERNELDIR := /home/topeet/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga

CURRENT_PATH := $(shell pwd)

obj-m := beep.o

build: kernel_modules

kernel_modules:

$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:

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

首先我们在终端输入两个命令(设置两个环境变量):

export ARCH=arm

export CROSS_COMPILE=arm-linux-gnueabihf-

然后执行“make”命令编译模块,编译完成生成 beep.ko,如下图所示:

37.6.2  编译应用测试程序

输入如下命令编译应用测试程序:

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

编译完成后,会生成 beep_test 可执行文件。如下图所示:

37.6.3  运行测试

启动开发板,将编译好的 beep.ko 驱动模块和 beep_test 应用测试文件拷贝到/lib/modules/4.1.15 目录下(检

查开发板根文件系统中有没有“/lib/modules/4.1.15”这个目录,如果没有的话需要自行创建一下。开发板

中使用的是光盘资料里面提供的 busybox 文件系统,光盘资料的“i.MX6UL 终结者光盘资料\08_开发板系统

镜像\03_文件系统镜像\01_Busybox 文件系统”目录下)。输入下面命令加载模块:

depmod

modprobe beep

驱动加载成功后,显示下面的信息:

然后使用应用测试程序来验证一下驱动是否正确,输入下面的命令打开蜂鸣器:

./beep_test /dev/beep 1 //打开蜂鸣器

查看开发板上的蜂鸣器是否有响声,如果有鸣叫声说明驱动正常工作。

然后输入下面命令关闭蜂鸣器:

./beep_test /dev/beep 0 //关闭蜂鸣器

正常情况下,蜂鸣器停止鸣叫。

使用下面的命令卸载模块:

rmmod beep //卸载模块

上一篇下一篇

猜你喜欢

热点阅读