在开发板上,有三个LED灯.如何通过应用程序点亮这三个灯如何编写驱动程序
操作硬件的时候,我们需要准备开发板的原理图和开发手册,,根据这两个文档来进行配置
在source insight 编写代码
1 第一个led驱动程序
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static struct class *firstled_drv_class;
static struct class_device *firstled_drv_class_dev;
volatile unsigned long *gpfcon = NULL;//指向控制寄存器的地址的指针
volatile unsigned long *gpfdat = NULL;//指向数据寄存器的地址的指针
static int firstled_drv_open(struct inode *inode, struct file *file)
{
//printk("firstled_drv_open
");
/* 配置GPF4,5,6为输出 */
*gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));//将 4 5 6 位清零
*gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));// 将 4 5 6 设为1 输出引脚
return 0;
}
static ssize_t firstled_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val;
//printk("firstled_drv_write
");
copy_from_user(&val, buf, count); // copy_to_user();
if (val == 1)
{
// 点灯
*gpfdat &= ~((1<<4) | (1<<5) | (1<<6));
}
else
{
// 灭灯
*gpfdat |= (1<<4) | (1<<5) | (1<<6);
}
return 0;
}
/*定义一个file_operations结构体
怎么用 告诉内核?
*/
static struct file_operations firstled_drvfops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = firstled_drv_open,
.write = firstled_drv_write,
};
int major;
//驱动的入口 写出来是一般的函数 需要修饰
static int firstled_drv_init(void)
{
//注册告诉内核 主设备号 名字(随便写) 结构体
//app找到这个驱动 不是根据名字 是根据 设备类型 主设备号
//主设备号 随便写个123 写 0会自动分配
major = register_chrdev(0, "firstled_drv", &firstled_drv_fops); // 注册, 告诉内核
firstled_drv_class = class_create(THIS_MODULE, "firstled_drv");
firstled_drv_class_dev = class_device_create(firstled_drv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
gpfdat = gpfcon + 1;
return 0;
}
static void firstled_drv_exit(void)
{
//123
unregister_chrdev(major, "firstled_drv"); // 卸载
class_device_unregister(firstled_drv_class_dev);
class_destroy(firstled_drv_class);
iounmap(gpfcon);
}
//通过修饰 成为入口函数 内核会找到这个函数
//对与驱动 是c
module_init(firstled_drv_init);
//通过修饰 成为出口函数 内核来调用
//用宏来定义一个指针
module_exit(firstled_drv_exit);
MODULE_LICENSE("GPL");
2 编写makefile文件
KERN_DIR = /work/system/linux-2.6.22.6
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += firstled_drv.o
将代码上传到linux服务器,编译 放到网络文件系统上,进入开发板,挂接驱动,
insmod firstled_drv.ko
# cat /proc/devices
Character devices:
1 mem
2 pty
3 ttyp
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
6 lp
7 vcs
10 misc
13 input
14 sound
29 fb
90 mtd
99 ppdev
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
204 s3c2410_serial
252 firstled_drv
253 usb_endpoint
254 rtc
Block devices:
1 ramdisk
7 loop
8 sd
31 mtdblock
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
创建设备目录节点
# mknod /dev/xyz c 252 0
3 编写测试程序
#include
#include
#include
#include
/* firstleddrvtest on
* firstleddrvtest off
*/
int main(int argc, char **argv)
{
int fd;
int val = 1;
fd = open("/dev/xyz", O_RDWR);
if (fd < 0)
{
printf("can't open!
");
}
if (argc != 2)
{
printf("Usage :
");
printf("%s
", argv[0]);
return 0;
}
if (strcmp(argv[1], "on") == 0)
{
val = 1;
}
else
{
val = 0;
}
write(fd, &val, 4);
return 0;
}
上传到服务器,编译,放到网络文件系统
arm-linux-gcc ledtest firstleddevtest.c
cp firstleddevtest /work/nfs_root/first_fs
在开发板上运行程序
# cd /mnt/
# ./firstled_drv_test
first_drv_open
Usage :
./firstled_drv_test
# ./firstled_drv_test off
first_drv_open
first_drv_write
# ./firstled_drv_test on
first_drv_open
first_drv_write
# ./firstled_drv_test off
first_drv_open
first_drv_write
#
看到开发板灯亮灯灭
4 继续编写LED驱动代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "leds" /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
#define LED_MAJOR 231 /* 主设备号 */
static struct class *leds_class;
static struct class_device *leds_class_devs[4];
/* bit0<=>D10, 0:亮, 1:灭
* bit1<=>D11, 0:亮, 1:灭
* bit2<=>D12, 0:亮, 1:灭
*/
static char leds_status = 0x0;
static DECLARE_MUTEX(leds_lock); // 定义赋值
//static int minor;
static unsigned long gpio_va;
#define GPIO_OFT(x) ((x) - 0x56000000)
#define GPFCON (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56000050)))
#define GPFDAT (*(volatile unsigned long *)(gpio_va + GPIO_OFT(0x56000054)))
/* 应用程序对设备文件/dev/leds执行open(...)时,
* 就会调用s3c24xx_leds_open函数
*/
static int s3c24xx_leds_open(struct inode *inode, struct file *file)
{
int minor = MINOR(inode->i_rdev); //MINOR(inode->i_cdev);
switch(minor)
{
case 0: /* /dev/leds */
{
// 配置3引脚为输出
//s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);
GPFCON &= ~(0x3<<(4*2));
GPFCON |= (1<<(4*2));
//s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
GPFCON &= ~(0x3<<(5*2));
GPFCON |= (1<<(5*2));
//s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP);
GPFCON &= ~(0x3<<(6*2));
GPFCON |= (1<<(6*2));
// 都输出0
//s3c2410_gpio_setpin(S3C2410_GPF4, 0);
GPFDAT &= ~(1<<4);
//s3c2410_gpio_setpin(S3C2410_GPF5, 0);
GPFDAT &= ~(1<<5);
//s3c2410_gpio_setpin(S3C2410_GPF6, 0);
GPFDAT &= ~(1<<6);
down(&leds_lock);
leds_status = 0x0;
up(&leds_lock);
break;
}
case 1: /* /dev/led1 */
{
s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);
s3c2410_gpio_setpin(S3C2410_GPF4, 0);
down(&leds_lock);
leds_status &= ~(1<<0);
up(&leds_lock);
break;
}
case 2: /* /dev/led2 */
{
s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
s3c2410_gpio_setpin(S3C2410_GPF5, 0);
leds_status &= ~(1<<1);
break;
}
case 3: /* /dev/led3 */
{
s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP);
s3c2410_gpio_setpin(S3C2410_GPF6, 0);
down(&leds_lock);
leds_status &= ~(1<<2);
up(&leds_lock);
break;
}
}
return 0;
}
static int s3c24xx_leds_read(struct file *filp, char __user *buff,
size_t count, loff_t *offp)
{
int minor = MINOR(filp->f_dentry->d_inode->i_rdev);
char val;
switch (minor)
{
case 0: /* /dev/leds */
{
copy_to_user(buff, (const void *)&leds_status, 1);
break;
}
case 1: /* /dev/led1 */
{
down(&leds_lock);
val = leds_status & 0x1;
up(&leds_lock);
copy_to_user(buff, (const void *)&val, 1);
break;
}
case 2: /* /dev/led2 */
{
down(&leds_lock);
val = (leds_status>>1) & 0x1;
up(&leds_lock);
copy_to_user(buff, (const void *)&val, 1);
break;
}
case 3: /* /dev/led3 */
{
down(&leds_lock);
val = (leds_status>>2) & 0x1;
up(&leds_lock);
copy_to_user(buff, (const void *)&val, 1);
break;
}
}
return 1;
}
static ssize_t s3c24xx_leds_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
//int minor = MINOR(inode->i_rdev); //MINOR(inode->i_cdev);
int minor = MINOR(file->f_dentry->d_inode->i_rdev);
char val;
copy_from_user(&val, buf, 1);
switch (minor)
{
case 0: /* /dev/leds */
{
s3c2410_gpio_setpin(S3C2410_GPF4, (val & 0x1));
s3c2410_gpio_setpin(S3C2410_GPF5, (val & 0x1));
s3c2410_gpio_setpin(S3C2410_GPF6, (val & 0x1));
down(&leds_lock);
leds_status = val;
up(&leds_lock);
break;
}
case 1: /* /dev/led1 */
{
s3c2410_gpio_setpin(S3C2410_GPF4, val);
if (val == 0)
{
down(&leds_lock);
leds_status &= ~(1<<0);
up(&leds_lock);
}
else
{
down(&leds_lock);
leds_status |= (1<<0);
up(&leds_lock);
}
break;
}
case 2: /* /dev/led2 */
{
s3c2410_gpio_setpin(S3C2410_GPF5, val);
if (val == 0)
{
down(&leds_lock);
leds_status &= ~(1<<1);
up(&leds_lock);
}
else
{
down(&leds_lock);
leds_status |= (1<<1);
up(&leds_lock);
}
break;
}
case 3: /* /dev/led3 */
{
s3c2410_gpio_setpin(S3C2410_GPF6, val);
if (val == 0)
{
down(&leds_lock);
leds_status &= ~(1<<2);
up(&leds_lock);
}
else
{
down(&leds_lock);
leds_status |= (1<<2);
up(&leds_lock);
}
break;
}
}
return 1;
}
/* 这个结构是字符设备驱动程序的核心
* 当应用程序操作设备文件时所调用的open、read、write等函数,
* 最终会调用这个结构中指定的对应函数
*/
static struct file_operations s3c24xx_leds_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = s3c24xx_leds_open,
.read = s3c24xx_leds_read,
.write = s3c24xx_leds_write,
};
/*
* 执行insmod命令时就会调用这个函数
*/
static int __init s3c24xx_leds_init(void)
//static int __init init_module(void)
{
int ret;
int minor = 0;
gpio_va = ioremap(0x56000000, 0x100000);
if (!gpio_va) {
return -EIO;
}
/* 注册字符设备
* 参数为主设备号、设备名字、file_operations结构;
* 这样,主设备号就和具体的file_operations结构联系起来了,
* 操作主设备为LED_MAJOR的设备文件时,就会调用s3c24xx_leds_fops中的相关成员函数
* LED_MAJOR可以设为0,表示由内核自动分配主设备号
*/
ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c24xx_leds_fops);
if (ret < 0) {
printk(DEVICE_NAME " can't register major number
");
return ret;
}
leds_class = class_create(THIS_MODULE, "leds");
if (IS_ERR(leds_class))
return PTR_ERR(leds_class);
leds_class_devs[0] = class_device_create(leds_class, NULL, MKDEV(LED_MAJOR, 0), NULL, "leds"); /* /dev/leds */
for (minor = 1; minor < 4; minor++) /* /dev/led1,2,3 */
{
leds_class_devs[minor] = class_device_create(leds_class, NULL, MKDEV(LED_MAJOR, minor), NULL, "led%d", minor);
if (unlikely(IS_ERR(leds_class_devs[minor])))
return PTR_ERR(leds_class_devs[minor]);
}
printk(DEVICE_NAME " initialized
");
return 0;
}
/*
* 执行rmmod命令时就会调用这个函数
*/
static void __exit s3c24xx_leds_exit(void)
{
int minor;
/* 卸载驱动程序 */
unregister_chrdev(LED_MAJOR, DEVICE_NAME);
for (minor = 0; minor < 4; minor++)
{
class_device_unregister(leds_class_devs[minor]);
}
class_destroy(leds_class);
iounmap(gpio_va);
}
/* 这两行指定驱动程序的初始化函数和卸载函数 */
module_init(s3c24xx_leds_init);
module_exit(s3c24xx_leds_exit);
/* 描述驱动程序的一些信息,不是必须的 */
MODULE_LICENSE("GPL");
编写makefile代码
KERN_DIR = /work/system/linux-2.6.22.6
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += myleds.o
测试程序
#include
#include
#include
#include
/*
* ledtest
*/
void print_usage(char *file)
{
printf("Usage:
");
printf("%s
",file);
printf("eg.
");
printf("%s /dev/leds on
", file);
printf("%s /dev/leds off
", file);
printf("%s /dev/led1 on
", file);
printf("%s /dev/led1 off
", file);
}
int main(int argc, char **argv)
{
int fd;
char* filename;
char val;
if (argc != 3)
{
print_usage(argv[0]);
return 0;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if (fd < 0)
{
printf("error, can't open %s
", filename);
return 0;
}
if (!strcmp("on", argv[2]))
{
// 亮灯
val = 0;
write(fd, &val, 1);
}
else if (!strcmp("off", argv[2]))
{
// 灭灯
val = 1;
write(fd, &val, 1);
}
else
{
print_usage(argv[0]);
return 0;
}
return 0;
}
运行程序
# mknod /dev/leds c 231 0
# mknod /dev/led1 c 231 1
# mknod /dev/led2 c 231 2
# mknod /dev/led3 c 231 3
# ./ledtest 0
Usage:
./ledtest
eg.
./ledtest /dev/leds on
./ledtest /dev/leds off
./ledtest /dev/led1 on
./ledtest /dev/led1 off
# ./ledtest 1
Usage:
./ledtest
eg.
./ledtest /dev/leds on
./ledtest /dev/leds off
./ledtest /dev/led1 on
./ledtest /dev/led1 off
# ./ledtest of
Usage:
./ledtest
eg.
./ledtest /dev/leds on
./ledtest /dev/leds off
./ledtest /dev/led1 on
./ledtest /dev/led1 off
# ./ledtest ooff
Usage:
./ledtest
eg.
./ledtest /dev/leds on
./ledtest /dev/leds off
./ledtest /dev/led1 on
./ledtest /dev/led1 off
# ./ledtest off
Usage:
./ledtest
eg.
./ledtest /dev/leds on
./ledtest /dev/leds off
./ledtest /dev/led1 on
./ledtest /dev/led1 off
# ./ledtest on
Usage:
./ledtest
eg.
./ledtest /dev/leds on
./ledtest /dev/leds off
./ledtest /dev/led1 on
./ledtest /dev/led1 off
# ./ledtest /dev/led1 off
# ./ledtest /dev/led1 on
# ./ledtest /dev/led2 off
# ./ledtest /dev/led2 on
# ./ledtest /dev/leds on
# ./ledtest /dev/leds off
# ./ledtest /dev/led3 off
# ./ledtest /dev/led3 on
# ./ledtest /dev/leds on