UP-MOBNET-Ⅱ型实验箱LED驱动控制实验
熟悉 Linux 系统下硬件驱动编程、编程实现对嵌入式设备上 LED 灯的控制
硬件:UP-MobNet-II 型网关部分嵌入式实验平台、U盘 软件:Vmware Workstation 、linux虚拟机、Xshell + ARM-LINUX 交叉编译开发环境
交叉编译和实验平台系统的烧写这里不再赘述:
UP-MobNet-II 型网关部分平台上共有 5 个 LED 显示灯,位于 LCD 显示屏下方,分别为 D601、D602、D603、D604、D605,对应的 Exynos4412 处理器的引脚依次为 GPX3_2、GPX3_3、GPX1_2、GPD0_4、GPL0_3上。5 个 LED 显示灯分别共阳极 3.3V 电压,因此相应 GPIO 低电平点亮,高电平熄灭。
Linux 系统下,应用程序不可直接操作底层硬件寄存器,必须经过驱动层来完成对硬件的操作。
驱动程序分析:/UP-CUP4412/SRC/exp/driver/02_leds/driver/s3c-leds.c
1、进入宿主机中 UP-CUP4412 型光盘内核目录:
解释:内核目录这里的内核目录是/UP-CUP4412/SRC/kernel/ linux-3.0.15/,将内核解压后的目录
2、运行 make menuconfig 命令配置内核对 LED 模块的相关支持 命令:
选择 Device Drivers --->选项,如图
进入 Character devices ---> 菜单
选择 < M > S3C LEDs Driver 模块方式编译 LED 驱动,如图:
退出保存配置:
3、重新编译内核,运行 make 命令 最终在内核源码目录的 drivers/char/目录下生成 LED 驱动程序 s3c-leds.ko
备注:以上在内核中添加对 LED 设备的支持的步骤,在网关系统设备出厂自带内核中已经默认添加进 入了,用户可以省略以上步骤。以上步骤在于重现系统的构造。LED 相应驱动已经在本实验目录的 driver 目录下给出。
1、进入实验目录:
2、清除中间代码,重新编译
当前目录下生成可执行程序 test_led。
4、查看下自己建立的设备 leds 属性:
5、执行应用程序测试该驱动及设备
一般的驱动程序是不允许应用程序调用的,只有当驱动程序留出这种供外界访问的接口才行,这种接口一般包括read,write,open,ioctl等接口,如果驱动中预留出了这些接口,就可以在应用程序中调用,比如fd=open(设备,参数);或者fd=ioctl(设备,参数);,这样就会调用到这个设备驱动中的open或者ioctl函数。所以一般如果想再应用程序中调试某个驱动程序,常见的方法就是自己建立一个驱动模块,这个模块中预留出对外接口,比如ioctl。然后在你新建的这个驱动模块中完成ioctl函数。在该实验中应用程序就是通过IOCTL调用设备驱动程序的预留接口: 就是下面的这一个小方法:
LED IOCTL 处理函数,主要完成从用户空间传递数据进行 GPIO 引脚设置功能 通过ioctl传过来的参数来处理GPIO引脚设置,从而使得灯亮和灭 这是内核层处理用户传来的数据,即用户到内核通过IOCTL传递
文章目录
实验箱
名称 | 移动互联网教学科研平台Ⅱ型 |
---|---|
型号 | UP-MOBNET-Ⅱ |
编号 | 03019024 |
批号 | 32017040520 |
实验内容
熟悉 Linux 系统下硬件驱动编程、编程实现对嵌入式设备上 LED 灯的控制
实验环境
硬件:UP-MobNet-II 型网关部分嵌入式实验平台、U盘 软件:Vmware Workstation 、linux虚拟机、Xshell + ARM-LINUX 交叉编译开发环境
交叉编译和实验平台系统的烧写这里不再赘述:
实验原理
硬件接口原理
UP-MobNet-II 型网关部分平台上共有 5 个 LED 显示灯,位于 LCD 显示屏下方,分别为 D601、D602、D603、D604、D605,对应的 Exynos4412 处理器的引脚依次为 GPX3_2、GPX3_3、GPX1_2、GPD0_4、GPL0_3上。5 个 LED 显示灯分别共阳极 3.3V 电压,因此相应 GPIO 低电平点亮,高电平熄灭。
驱动层程序分析
Linux 系统下,应用程序不可直接操作底层硬件寄存器,必须经过驱动层来完成对硬件的操作。
驱动程序分析:/UP-CUP4412/SRC/exp/driver/02_leds/driver/s3c-leds.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <plat/gpio-cfg.h>
#include <asm/irq.h>
#include <mach/gpio.h>
MODULE_LICENSE("GPL");
#define DEVICE_NAME "leds" //驱动名称
#define DEVICE_MAJOR 231 //驱动主设备号
#define DEVICE_MINOR 0
struct cdev *mycdev;
struct class *myclass;
dev_t devno;
// LED GPIO 列表
static unsigned long led_table [] = {
EXYNOS4_GPX3(2),
EXYNOS4_GPX3(3),
EXYNOS4_GPX1(2),
EXYNOS4_GPL0(4),
EXYNOS4_GPL0(3),
};
// LED GPIO 输出类型配置列表
static unsigned int led_cfg_table [] = {
S3C_GPIO_OUTPUT,
S3C_GPIO_OUTPUT,
S3C_GPIO_OUTPUT,
S3C_GPIO_OUTPUT,
S3C_GPIO_OUTPUT,
};
// LED IOCTL 处理函数,主要完成从用户空间传递数据进行 GPIO 引脚设置功能
static long uptech_leds_ioctl(
struct file *file,
unsigned int cmd,
unsigned long arg)
{
printk("number = %ld\n",arg);
switch(cmd) {
case 0:
case 1:
if (arg < 0 || arg > 4) {
return -EINVAL;
}
if(cmd == 0 || cmd == 1)
{
// LED GPIO 设置函数接口
gpio_set_value(led_table[arg],!cmd);
}
return 0;
default:
return -EINVAL;
} }
// 驱动层 file_operations 接口函数初始化
static struct file_operations uptech_leds_fops = {
.owner = THIS_MODULE,
.ioctl = uptech_leds_ioctl,
};
//驱动程序入口初始化函数,设置 LED GPIO、向内核注册设备。
static int __init uptech_leds_init(void)
{
int i;
int err;
devno = MKDEV(DEVICE_MAJOR, DEVICE_MINOR);
mycdev = cdev_alloc();
cdev_init(mycdev, &uptech_leds_fops);
err = cdev_add(mycdev, devno, 1); // 初始化 cdev 结构
if (err != 0)
printk("Exynos4412 leds device register failed!\n");
myclass = class_create(THIS_MODULE, "leds");
if(IS_ERR(myclass)) {
printk("Err: failed in creating class.\n");
return -1;
}
device_create(myclass,NULL, MKDEV(DEVICE_MAJOR,DEVICE_MINOR), NULL,
DEVICE_NAME);
// LED GPIO 配置初始化
for (i = 0; i < 5; i++) {
s3c_gpio_cfgpin(led_table[i], led_cfg_table[i]);
s3c_gpio_setpull(led_table[i],S3C_GPIO_PULL_DOWN);
gpio_set_value(led_table[i],1);
}
printk(DEVICE_NAME "leds initialized\n");
return 0;
}
// 驱动卸载函数
static void __exit uptech_leds_exit(void)
{
cdev_del(mycdev); // 释放注册的字符设备
device_destroy(myclass,devno);
class_destroy(myclass);
}
// 声明驱动程序入口函数
module_init(uptech_leds_init);
// 声明驱动程序出口函数
module_exit(uptech_leds_exit);
应用层程序分析:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
int main(int argc, char **argv)
{
int i;
int on;
int led_number;
int fd;
/*
根据命令行参数内容,进行控制。将命令行参数 1 设置成 LED number,参数 2 设置成 LED 点亮熄
灭状态 on
*/
if (argc != 3 || sscanf(argv[1], "%d", &led_number) != 1 ||
sscanf(argv[2],"%d", &on) != 1 ||
on < 0 || on > 1 || led_number < 0 || led_number >= 5) {
fprintf(stderr, "Usage:\n");
fprintf(stderr, "\t ./led led_number on|off\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, "\t led_number from 0 to 4\n");
fprintf(stderr, "\t on: 1 off: 0\n");
exit(1);
}
//打开 LED 设备节点
fd = open("/dev/leds", 0);
if (fd < 0) {
perror("open device /dev/leds");
exit(1);
}
//调用驱动层 ioctl 接口,实现对 LED 控制
ioctl(fd, on, led_number);
for(i=0;i<100;i++)
usleep(1000);
//关闭 LED 设备接口
close(fd);
return 0;
}
Makefile:
CROSS = arm-linux-
CC = $(CROSS)gcc
AR = $(CROSS)ar
STRIP = $(CROSS)strip
LDFLAGS +=
EXEC = test_led
OBJS = led.o
all: $(EXEC)
$(EXEC): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBM) $(LDLIBS) $(LIBGCC) -lm
clean:
-rm -f $(EXEC) *.elf *.gdb *.o
实验步骤
实验目录:
/UP-CUP4412/SRC/kernel/ linux-3.0.15/
/UP-CUP4412/SRC/exp/diver/02_leds/
在内核中添加 LED 设备模块驱动
1、进入宿主机中 UP-CUP4412 型光盘内核目录:
解释:内核目录这里的内核目录是/UP-CUP4412/SRC/kernel/ linux-3.0.15/,将内核解压后的目录
[root@localhost ~]# cd /UP-CUP4412/SRC/kernel/linux-3.0.15/
2、运行 make menuconfig 命令配置内核对 LED 模块的相关支持 命令:
选择 Device Drivers --->选项,如图
进入 Character devices ---> 菜单
选择 < M > S3C LEDs Driver 模块方式编译 LED 驱动,如图:
退出保存配置:
3、重新编译内核,运行 make 命令 最终在内核源码目录的 drivers/char/目录下生成 LED 驱动程序 s3c-leds.ko
备注:以上在内核中添加对 LED 设备的支持的步骤,在网关系统设备出厂自带内核中已经默认添加进 入了,用户可以省略以上步骤。以上步骤在于重现系统的构造。LED 相应驱动已经在本实验目录的 driver 目录下给出。
编译 LED 应用测试程序
1、进入实验目录:
2、清除中间代码,重新编译
当前目录下生成可执行程序 test_led。
实验测试
把模块和应用程序拷到U盘,在实验箱挂载:[root@UP-TECH 02_leds]# insmod driver/s3c-leds.ko
[ 5323.825976] ledsleds initialized
[root@UP-TECH 02_leds]#
特别强调
以上在内核中添加对 LED 设备的支持的步骤,在网关系统设备出厂自带内核中已经默认添加进
入了,leds相关的驱动层已经默认添加了,所以再次添加就会失败。
这里就可以不用再insmod挂载模块了
4、查看下自己建立的设备 leds 属性:
[root@UP-TECH 02_leds]# ll /dev/leds
crw-r--r-- 1 root root 231, 0 Jul 5 10:30 /dev/leds
[root@UP-TECH 02_leds]#
5、执行应用程序测试该驱动及设备
开始时默认全部引脚都是亮的:
点亮 LED3/4
熄灭 LED0/1/2
只点亮LED3
卸载U盘:
总结
应用程序如何调用设备驱动程序接口
一般的驱动程序是不允许应用程序调用的,只有当驱动程序留出这种供外界访问的接口才行,这种接口一般包括read,write,open,ioctl等接口,如果驱动中预留出了这些接口,就可以在应用程序中调用,比如fd=open(设备,参数);或者fd=ioctl(设备,参数);,这样就会调用到这个设备驱动中的open或者ioctl函数。所以一般如果想再应用程序中调试某个驱动程序,常见的方法就是自己建立一个驱动模块,这个模块中预留出对外接口,比如ioctl。然后在你新建的这个驱动模块中完成ioctl函数。在该实验中应用程序就是通过IOCTL调用设备驱动程序的预留接口: 就是下面的这一个小方法:
ioctl(fd, on, led_number);
设备驱动程序的接口函数如何实现
LED IOCTL 处理函数,主要完成从用户空间传递数据进行 GPIO 引脚设置功能 通过ioctl传过来的参数来处理GPIO引脚设置,从而使得灯亮和灭 这是内核层处理用户传来的数据,即用户到内核通过IOCTL传递
static long uptech_leds_ioctl(
struct file *file,
unsigned int cmd,
unsigned long arg)
{
printk("number = %ld\n",arg);
switch(cmd) {
case 0:
case 1:
if (arg < 0 || arg > 4) {
return -EINVAL;
}
if(cmd == 0 || cmd == 1)
{
// LED GPIO 设置函数接口
gpio_set_value(led_table[arg],!cmd);
}
return 0;
default:
return -EINVAL;
} }
// 驱动层 file_operations 接口函数初始化
static struct file_operations uptech_leds_fops = {
.owner = THIS_MODULE,
.ioctl = uptech_leds_ioctl,
};