日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动

發布時間:2023/12/9 linux 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?現在,我們來編寫自己第一個字符設備驅動 —— 點亮LED。(不完善,后面再完善)

硬件平臺:Exynos4412(FS4412)


編寫驅動分下面幾步:

a -- 查看原理圖、數據手冊,了解設備的操作方法;

b -- 在內核中找到相近的驅動程序,以它為模板進行開發,有時候需要從零開始;

c -- 實現驅動程序的初始化:比如向內核注冊這個驅動程序,這樣應用程序傳入文件名,內核才能找到相應的驅動程序;

d -- 設計所要實現的操作,比如 open、close、read、write 等函數;

e -- 實現中斷服務(中斷不是每個設備驅動所必須的);

f -- 編譯該驅動程序到內核中,或者用 insmod 命令加載;

g-- 測試驅動程序;


下面是一個點亮LED 的驅動:

第一步,當然是查看手冊,查看原理圖,找到相應寄存器;


查看手冊,四個LED 所用寄存器為:

led2

GPX2CON ? ?0x11000c40
GPX2DAT ? ? 0x11000c44

led3

GPX1CON ? ?0x11000c20
GPX1DAT ? ? 0x11000c24

led4 ?3-4 3-5

GPF3CON ? 0x114001e0
GPF3DAT ? ?0x114001e4


這里要注意:arm體系架構是io內存,必須要映射 ? ioremap( );? 其作用是物理內存向虛擬內存的映射。 用到?writel ? readl這兩個函數,詳細解釋會在后面不上,先看一下簡單用法:

以LED2為例,下面是地址映射及讀寫:

[cpp]?view plaincopy
  • int?*pgpx2con??;??
  • int?*pgpx2dat;??
  • ??
  • pgpx2con?=?ioremap(?GPX2CON,?4);??
  • pgpx2dat?=?ioremap(GPX2DAT,4);??
  • readl(pgpx2con);??
  • writel(0x01,?pgpx2dat?);??

  • 下面是驅動程序,后面會更完善 [cpp]?view plaincopy
  • #include?<linux/module.h>??
  • #include?<linux/fs.h>??
  • #include?<linux/cdev.h>??
  • #include?<linux/device.h>??
  • #include?<asm/io.h>??
  • #include?<asm/uaccess.h>??
  • ??
  • static?int?major?=?250;??
  • static?int?minor=0;??
  • static?dev_t?devno;??
  • static?struct?class?*cls;??
  • static?struct?device?*test_device;??
  • ??
  • #define??GPX2CON????0x11000c40??
  • #define??GPX2DAT????0x11000c44??
  • #define??GPX1CON????0x11000c20??
  • #define??GPX1DAT????0x11000c24??
  • #define??GPF3CON????0x114001e0??
  • #define??GPF3DAT????0x114001e4??
  • ??
  • static?int?*pgpx2con??;??
  • static?int?*pgpx2dat;??
  • ??
  • static?int?*pgpx1con??;??
  • static?int?*pgpx1dat;??
  • ??
  • static?int?*pgpf3con??;??
  • static?int?*pgpf3dat;??
  • ??
  • void?fs4412_led_off(int?num);??
  • ??
  • void?fs4412_led_on(int?num)??
  • {??
  • ????switch(num)??
  • ????{??
  • ????????case?1:??
  • ????????????writel(readl(pgpx2dat)?|(0x1<<7),?pgpx2dat);??
  • ????????????break;??
  • ????????case?2:??
  • ????????????writel(readl(pgpx1dat)?|(0x1<<0),?pgpx1dat);????????????
  • ????????????break;????????????
  • ????????case?3:??
  • ????????????writel(readl(pgpf3dat)?|(0x1<<4),?pgpf3dat);????
  • ????????????break;??
  • ????????case?4:??
  • ????????????writel(readl(pgpf3dat)?|(0x1<<5),?pgpf3dat);????????????
  • ????????????break;????
  • ????????default:??
  • ????????????fs4412_led_off(1);??
  • ????????????fs4412_led_off(2);??
  • ????????????fs4412_led_off(3);??
  • ????????????fs4412_led_off(4);??
  • ????????????break;??
  • ??????????????
  • ????}??
  • }??
  • ??
  • void?fs4412_led_off(int?num)??
  • {??
  • ????switch(num)??
  • ????{??
  • ????????case?1:??
  • ????????????writel(readl(pgpx2dat)?&(~(0x1<<7)),?pgpx2dat);??
  • ????????????break;??
  • ????????case?2:??
  • ????????????writel(readl(pgpx1dat)&(~(0x1<<0)),?pgpx1dat);??????????????
  • ????????????break;????????????
  • ????????case?3:??
  • ????????????writel(readl(pgpf3dat)?&(~(0x1<<4)),?pgpf3dat);?????
  • ????????????break;??
  • ????????case?4:??
  • ????????????writel(readl(pgpf3dat)?&(~(0x1<<5)),?pgpf3dat);?????????????
  • ????????????break;????????????
  • ????}??
  • }??
  • ??
  • static?int?led_open?(struct?inode?*inode,?struct?file?*filep)??
  • {//open??
  • ????fs4412_led_off(1);??
  • ????fs4412_led_off(2);??
  • ????fs4412_led_off(3);??
  • ????fs4412_led_off(4);????
  • ????return?0;??
  • }??
  • ??
  • static?int?led_release(struct?inode?*inode,?struct?file?*filep)??
  • {//close??
  • ????fs4412_led_off(1);??
  • ????fs4412_led_off(2);??
  • ????fs4412_led_off(3);??
  • ????fs4412_led_off(4);????
  • ????return?0;??
  • }??
  • ??
  • static?ssize_t?led_read(struct?file?*filep,?char?__user?*buf,?size_t?len,?loff_t?*pos)??
  • {??
  • ????return?0;??
  • }??
  • ??
  • static?ssize_t?led_write(struct?file?*filep,?const?char?__user?*buf,?size_t?len,?loff_t?*pos)??
  • {??
  • ????int?led_num;??
  • ??
  • ????if(len?!=4)??
  • ????{??
  • ????????return?-EINVAL;??
  • ????}??
  • ????if(copy_from_user(&led_num,buf,len))??
  • ????{??
  • ????????return?-EFAULT;???
  • ????}??
  • ??
  • ????fs4412_led_on(led_num);??
  • ????printk("led_num?=%d?\n",led_num);??
  • ??
  • ????return?0;??
  • }??
  • ??
  • static?struct?file_operations?hello_ops=??
  • {??
  • ????.open?????=?led_open,??
  • ????.release?=?led_release,??
  • ????.read?????=?led_read,??
  • ????.write????=?led_write,??
  • };??
  • ??
  • static?void?fs4412_led_init(void)??
  • {??
  • ????pgpx2con?=?ioremap(GPX2CON,4);??
  • ????pgpx2dat?=?ioremap(GPX2DAT,4);??
  • ??
  • ????pgpx1con?=?ioremap(GPX1CON,4);??
  • ????pgpx1dat?=ioremap(GPX1DAT,4);??
  • ??
  • ????pgpf3con??=?ioremap(GPF3CON,4);??
  • ????pgpf3dat?=ioremap(GPF3DAT,4);??
  • ??
  • ????writel((readl(pgpx2con)&?~(0xf<<28))?|(0x1<<28),pgpx2con)?;??
  • ????writel((readl(pgpx1con)&?~(0xf<<0))?|(0x1<<0),pgpx1con)?;?????
  • ????writel((readl(pgpf3con)&?~(0xff<<16))?|(0x11<<16),pgpf3con)?;?????
  • }??
  • ??
  • static?int?led_init(void)??
  • {??
  • ????int?ret;??????
  • ????devno?=?MKDEV(major,minor);??
  • ????ret?=?register_chrdev(major,"led",&hello_ops);??
  • ??
  • ????cls?=?class_create(THIS_MODULE,?"myclass");??
  • ????if(IS_ERR(cls))??
  • ????{??
  • ????????unregister_chrdev(major,"led");??
  • ????????return?-EBUSY;??
  • ????}??
  • ????test_device?=?device_create(cls,NULL,devno,NULL,"led");//mknod?/dev/hello??
  • ????if(IS_ERR(test_device))??
  • ????{??
  • ????????class_destroy(cls);??
  • ????????unregister_chrdev(major,"led");??
  • ????????return?-EBUSY;??
  • ????}?????
  • ????fs4412_led_init();??
  • ????return?0;??
  • }??
  • ??
  • void?fs4412_led_unmap(void)??
  • {??
  • ????iounmap(pgpx2con);??
  • ????iounmap(pgpx2dat?);??
  • ??
  • ????iounmap(pgpx1con);??
  • ????iounmap(pgpx1dat?);??
  • ??
  • ????iounmap(pgpf3con?);??
  • ????iounmap(pgpf3dat?);??
  • }??
  • ??
  • static?void?led_exit(void)??
  • {??
  • ????fs4412_led_unmap();??
  • ????device_destroy(cls,devno);??
  • ????class_destroy(cls);???
  • ????unregister_chrdev(major,"led");??
  • ????printk("led_exit?\n");??
  • }??
  • ??
  • MODULE_LICENSE("GPL");??
  • module_init(led_init);??
  • module_exit(led_exit);??
  • 測試程序:

    [cpp]?view plaincopy
  • #include?<sys/types.h>??
  • #include?<sys/stat.h>??
  • #include?<fcntl.h>??
  • #include?<stdio.h>??
  • ??
  • main()??
  • {??
  • ????int?fd,i,lednum;??
  • ??
  • ????fd?=?open("/dev/led",O_RDWR);??
  • ????if(fd<0)??
  • ????{??
  • ????????perror("open?fail?\n");??
  • ????????return?;??
  • ????}??
  • ????for(i=0;i<100;i++)??
  • ????{??
  • ????????lednum=0;??
  • ????????write(fd,&lednum,sizeof(int));??
  • ????????lednum?=?i%4+1;??
  • ????????write(fd,&lednum,sizeof(int));????
  • ????????sleep(1);??
  • ????}??
  • ????close(fd);??
  • }??
  • makefile:

    [cpp]?view plaincopy
  • ifneq??($(KERNELRELEASE),)??
  • obj-m:=hello.o??
  • $(info?"2nd")??
  • else??
  • #KDIR?:=?/lib/modules/$(shell?uname?-r)/build??
  • KDIR?:=?/home/xiaoming/linux-3.14-fs4412??
  • PWD:=$(shell?pwd)??
  • all:??
  • ????$(info?"1st")??
  • ????make?-C?$(KDIR)?M=$(PWD)?modules??
  • ????arm-none-linux-gnueabi-gcc?test.c??
  • ????sudo?cp?hello.ko?a.out?/rootfs/test/??
  • clean:??
  • ????rm?-f?*.ko?*.o?*.symvers?*.mod.c?*.mod.o?*.order??
  • endif??

  • 編譯結束后,將a.out 和 hello.ko 拷貝到開發板中:

    # insmod hello.ko

    #mknod /dev/hello c 250 0

    #./a.out

    會看到跑馬燈效果。

    后面會對該驅動完善。

    總結

    以上是生活随笔為你收集整理的Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。