在開發板上,有三個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