led驱动测试成功

来源:互联网 发布:c多线程编程实战 pdf 编辑:程序博客网 时间:2024/04/30 03:10

今天终于把te6410下linux中的led驱动给测试成功了,看着不断闪烁的led,感觉到一种久违的成功!

首先贴出led驱动的代码:

//#include <linux/config.h>
#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 <asm/irq.h>
#include <mach/gpio.h>
#include <plat/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/hardware.h>
#include <linux/io.h>

#define DEVICE_NAME "leds"
#define LED_MAJOR 231


static unsigned long led_table [] = {
       S3C64XX_GPM(0),
       S3C64XX_GPM(1),
       S3C64XX_GPM(2),
       S3C64XX_GPM(3),
};

static unsigned int led_cfg_table [] = {
 S3C64XX_GPM_OUTPUT(0),
 S3C64XX_GPM_OUTPUT(1),
 S3C64XX_GPM_OUTPUT(2),
 S3C64XX_GPM_OUTPUT(3),
};

static int s3c6410_leds_ioctl(
 struct inode *inode,
 struct file *file,
 unsigned int cmd,
 unsigned long arg)
{
 unsigned long tmp;
 switch(cmd) {
 case 0:
 case 1:
  if (arg > 4) {
   return -EINVAL;
  }
  tmp = __raw_readl(S3C64XX_GPMDAT);
  if(cmd)
   tmp &= (~(1<<arg));
  else
   tmp |= (1<<arg);
  __raw_writel(tmp,S3C64XX_GPMDAT);
//  gpio_set_value(led_table[arg], !cmd);
  return 0;
 default:
  return -EINVAL;
 }
}


static struct file_operations s3c6410_leds_fops = {
 .owner = THIS_MODULE,
 .ioctl = s3c6410_leds_ioctl,
};

static struct cdev cdev_leds;
struct class * my_class;

static int __init s3c6410_leds_init(void)
{
 int ret;
 unsigned long tmp;
 int i;
 dev_t devno;
 printk(KERN_NOTICE "enter s3c6410_leds_init\n");


 devno = MKDEV(LED_MAJOR,0);

 ret = register_chrdev_region(devno,1,DEVICE_NAME);
 ret = 0;
 if(ret<0)
 {
  printk(KERN_NOTICE "can not register led device");
  return ret;
 }
 
 cdev_init(&cdev_leds,&s3c6410_leds_fops);
 cdev_leds.owner = THIS_MODULE;

 ret =cdev_add(&cdev_leds,devno,1);
 if(ret)
 {
  printk(KERN_NOTICE "can not add leds device");
  return ret;
 }

 my_class = class_create(THIS_MODULE,"my_class");
 if(IS_ERR(my_class))
 {
  printk("Err: Failed in creating class\n");
  return -1; 
 }

 device_create(my_class,NULL,MKDEV(LED_MAJOR,0),NULL,DEVICE_NAME);

 //gpm0-3 pull up
 tmp = __raw_readl(S3C64XX_GPMPUD);
 tmp &= (~0xFF);
 tmp |= 0xaa;
 __raw_writel(tmp,S3C64XX_GPMPUD);

 //gpm0-3 output mode
 tmp = __raw_readl(S3C64XX_GPMCON);
 tmp &= (~0xFFFF);
 tmp |= 0x1111;
 __raw_writel(tmp,S3C64XX_GPMCON);
 
 //gpm0-3 output 0
 tmp = __raw_readl(S3C64XX_GPMDAT);
 tmp |= 0x10;
 __raw_writel(tmp,S3C64XX_GPMDAT);

 //printk("S3C64XX_GPMCON is %x\n",__raw_readl(S3C64XX_GPMCON));
 //printk("S3C64XX_GPMDAT is %x\n",__raw_readl(S3C64XX_GPMDAT));
 //printk("S3C64XX_GPMPUD is %x\n",__raw_readl(S3C64XX_GPMPUD));

 printk(DEVICE_NAME " initialized\n");

 return 0;
}

static void __exit s3c6410_leds_exit(void)
{
 cdev_del(&cdev_leds);

 unregister_chrdev_region(MKDEV(LED_MAJOR,0),1);

 printk(KERN_NOTICE "s3c6440_leds_exit\n");
}


module_init(s3c6410_leds_init);
module_exit(s3c6410_leds_exit);

MODULE_LICENSE("GPL");

然后就是需要写一个Makefile文件,注意文件中的内核源码所在路径为解压飞凌提供的linux2.6.28内核的目录,同时解压后还必须进行编译。(不然后面生产驱动模块你会发现会少两个头文件的!)

 

ifneq ($(KERNELRELEASE),)

 obj-m := s3c6410_leds.o

# Otherwise we were called directly from the command

# line; invoke the kernel build system.

else
 KERNELDIR ?= /home/charcy/work/linux/kernel/linux2.6.28/
 PWD := $(shell pwd)

default:
 make -C $(KERNELDIR) M=$(PWD) modules

endif

clean:
 rm -f *.ko *.o *.mod *.mod.c  *.symvers *.order

写完后那就直接使用make命令,就可以生产s3c6410_leds.ko内核模块文件

之后就是要用到测试led的文件,也就是测试程序,如下(注意没有头文件哦):

 int main(void)
{
 int on=1;
 int led;
 int fd;
 
 fd = open("/dev/leds",0);
 if(fd<0)
 {
  perror("open device leds");
  exit(1);
 }

 printf("leds test show.press ctrl+c to exit\n");
 while(1)
 {
  for(led=0;led<4;led++)
  {
   ioctl(fd,on,led);
   usleep(60000);
  }
  on = !on;
 }
 close(fd);
 return 0;
}

使用命令:arm-linux-gcc led_test.c   -o  led_test

以上都是在主机上进行编译的,那么接下来就是将s3c6410_leds.ko、led_test这两个文件下载到开发板,依次执行以下命令:

#insmod s3c6410_leds.ko

#chmod 777 led_test

#./led_test

好了,这个时候就可以看到开发板上led灯在循环闪烁了:)


注:

相关的寄存器虚地址定义在arch/arm/plat-s3c64xx/include/plat/gpio-bank-m.h

#define S3C64XX_GPMCON (S3C64XX_GPM_BASE +0x00)

#define S3C64XX_GPMDAT (S3C64XX_GPM_BASE +0x04)

#define S3C64XX_GPMPUD (S3C64XX_GPM_BASE +0x08)

GPIO M端口的基地址定义在arch/arm/plat-s3c64xx/include/plat/regs-gpio.h

#define S3C64XX_GPM_BASE (S3C64XX_VA_GPIO +0x0820)

GPIO的虚地址定义在arch/arm/mach-s3c6400/include/mach/map.h

#define S3C64XX_VA_GPIO S3C_ADDR(0x00500000)


GPIO的虚地址是有全局虚地址S3C_ADDR_BASE计算出来的,void __iomem __force *作用是强制转化为地址。arch/arm/plat-s3c/include/plat/map.h

#define S3C_ADDR_BASE (0xF4000000)

 

#ifndef __ASSEMBLY__

#define S3C_ADDR(x) ((void __iomem __force *)S3C_ADDR_BASE+ (x))

#else

#define S3C_ADDR(x) (S3C_ADDR_BASE + (x))

#endif

由此可以得到GPM寄存器对应的虚地址分别为:

S3C64XX_GPMCON 0xF4500820

S3C64XX_GPMDAT 0xF4500824

S3C64XX_GPMPUD 0xF4500828

 

原创粉丝点击