嵌入式驅動編寫-點亮LED驅動程序


在開發板上,有三個LED燈.如何通過應用程序點亮這三個燈如何編寫驅動程序


操作硬件的時候,我們需要準備開發板的原理圖和開發手冊,,根據這兩個文檔來進行配置









  在source insight 編寫代碼

1 第一個led驅動程序

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>

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\n");
	/* 配置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\n");

	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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/* 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!\n");
	}
	if (argc != 2)
	{
		printf("Usage :\n");
		printf("%s <on|off>\n", 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 <on|off>
# ./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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>

#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\n");
      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\n");
    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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/*
  *  ledtest <dev> <on|off>
  */

void print_usage(char *file)
{
    printf("Usage:\n");
    printf("%s <dev> <on|off>\n",file);
    printf("eg. \n");
    printf("%s /dev/leds on\n", file);
    printf("%s /dev/leds off\n", file);
    printf("%s /dev/led1 on\n", file);
    printf("%s /dev/led1 off\n", 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\n", 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 <dev> <on|off>
eg. 
./ledtest /dev/leds on
./ledtest /dev/leds off
./ledtest /dev/led1 on
./ledtest /dev/led1 off
# ./ledtest 1
Usage:
./ledtest <dev> <on|off>
eg. 
./ledtest /dev/leds on
./ledtest /dev/leds off
./ledtest /dev/led1 on
./ledtest /dev/led1 off
# ./ledtest of
Usage:
./ledtest <dev> <on|off>
eg. 
./ledtest /dev/leds on
./ledtest /dev/leds off
./ledtest /dev/led1 on
./ledtest /dev/led1 off
# ./ledtest ooff
Usage:
./ledtest <dev> <on|off>
eg. 
./ledtest /dev/leds on
./ledtest /dev/leds off
./ledtest /dev/led1 on
./ledtest /dev/led1 off
# ./ledtest off 
Usage:
./ledtest <dev> <on|off>
eg. 
./ledtest /dev/leds on
./ledtest /dev/leds off
./ledtest /dev/led1 on
./ledtest /dev/led1 off
# ./ledtest on 
Usage:
./ledtest <dev> <on|off>
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










發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章