三星6818芯片基于linux的LED驱动详解

  • led驱动.c文件的编写
  • 驱动的编译和生成
  • 编写程序代码
  • 下载驱动(.ko)和程序到板子

led驱动.c文件的编写

#PS:使用iowrite和ioread函数编写,没有用标准的gpio函数和ioctl函数会在下一篇超声波驱动介绍

  1. 一般驱动编写顺序是这样的
    1> 动态创建设备号
    2> 字符设备初始化
    3> 将设备添加到内核
    4> 创建设备
    5> 然后卸载驱动的时候记得释放资源就好
    附上代码:
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/io.h>static struct cdev gec6818_led_cdev;
//定义一个字符设备
static dev_t    led_num=0;
static struct class     *leds_class;
//class是给udev管理设备用的
static struct device    *leds_device;
//设备成功创建后的指针static void __iomem     *gpioe_base_va;     //gpioe的虚拟地址基址
static void __iomem     *gpioe_out_va;
static void __iomem     *gpioe_outenb_va;
static void __iomem     *gpioe_altfn0_va;
static void __iomem     *gpioe_altfn1_va;
static int  gec6818_led_open (struct inode * inode, struct file *file)
//打开驱动文件接口定义
{//配置GPIOE13为输出模式iowrite32(ioread32(gpioe_altfn0_va)&(~(3<<26)),gpioe_altfn0_va);//(这个操作可以参考之前那篇文章<三星6818基于uboot的流水灯程序>)//iowrite、ioread//1.从虚拟地址读取数据函数//#define ioread8(p)         ({ unsigned int __v = __raw_readb(p); __v; })//#define ioread16(p)     ({ unsigned int __v = le16_to_cpu((__force __le16)__raw_readw(p)); __v; })//#define ioread32(p)    ({ unsigned int __v = le32_to_cpu((__force __le32)__raw_readl(p)); __v; })//2.向虚拟地址写入数据函数//#define iowrite8(v,p)   __raw_writeb(v, p)//#define iowrite16(v,p)  __raw_writew((__force __u16)cpu_to_le16(v), p)//#define iowrite32(v,p)  __raw_writel((__force __u32)cpu_to_le32(v), p)//Linux 内核运行后,开启了 MMU(内存管理单元),所以不能直接访问 CPU 的物理地址,也就是说,不能直接使用物理地址访问系统的 IO 内存。必须将物理地址转换为虚拟地址,内核通过虚拟地址来访问系统的 IO 内存。iowrite32(ioread32(gpioe_outenb_va)|(1<<13),gpioe_outenb_va);  printk("gec6818_led_open \n");return 0;
}
static int  gec6818_led_release (struct inode * inode, struct file *file)
//释放接口定义
{printk("gec6818_led_release \n");return 0;
}
static ssize_t gec6818_led_write (struct file * file, const char __user * buf, size_t len, loff_t * off)
//读写接口定义
{int rt,v;unsigned char kbuf[64]={0};if(len > sizeof kbuf)return -EINVAL;       //返回参数无效错误码//常见错误码,这个错误码每个芯片都不一样要在内核里面找error文件#if 0#define   EPERM        1  /*Operation not permitted */#define ENOENT       2  /* No such file or directory */#define  ESRCH        3  /* No such process */#define    EINTR        4  /* Interrupted system call */#define    EIO      5  /* I/O error */#define  ENXIO        6  /* No such device or address */#define  E2BIG        7  /* Argument list too long */#define ENOEXEC      8  /* Exec format error */#define  EBADF        9  /* Bad file number */#define    ECHILD      10  /* No child processes */#define EAGAIN      11  /* Try again */#define  ENOMEM      12  /* Out of memory */#define  EACCES      13  /* Permission denied */#define  EFAULT      14  /* Bad address */#define    ENOTBLK     15  /* Block device required */#define  EBUSY       16  /* Device or resource busy */#define    EEXIST      17  /* File exists */#define    EXDEV       18  /* Cross-device link */#define  ENODEV      19  /* No such device */#define ENOTDIR     20  /* Not a directory */#define    EISDIR      21  /* Is a directory */#define EINVAL      22  /* Invalid argument */#define   ENFILE      23  /* File table overflow */#define    EMFILE      24  /* Too many open files */#define    ENOTTY      25  /* Not a typewriter */#define   ETXTBSY     26  /* Text file busy */#define EFBIG       27  /* File too large */#define ENOSPC      28  /* No space left on device */#define    ESPIPE      29  /* Illegal seek */#define   EROFS       30  /* Read-only file system */#define  EMLINK      31  /* Too many links */#define EPIPE       32  /* Broken pipe */#define    EDOM        33  /* Math argument out of domain of func */#define    ERANGE      34  /* Math result not representable */#endifrt = copy_from_user(kbuf,buf,len);//将用户空间的数据拷贝到内核空间//如果不改值,可不使用这个函数,直接获取buf里面内容//获取成功复制的字节数len = len - rt;switch(kbuf[0]){case 7:{if(kbuf[1]==1){v = ioread32(gpioe_out_va);v &=~(1<<13);iowrite32(v,gpioe_out_va);}else{v = ioread32(gpioe_out_va);v |=(1<<13);iowrite32(v,gpioe_out_va);           }}break;}printk("[gec6818_led_write]kbuf[0]=%d,kbuf[1]=%d\n",kbuf[0],kbuf[1]);  return len;
}
static ssize_t gec6818_led_read (struct file *file, char __user *buf, size_t len, loff_t * offs)
//读写接口定义
{int rt;char kbuf[6]={'1','2','3','4','\n','\0'};  //判断当前len是否合法if(len > sizeof kbuf)return -EINVAL;        //返回参数无效错误码//从内核空间拷贝到用户空间rt = copy_to_user(buf,kbuf,len);//获取成功复制的字节数len = len - rt;printk("gec6818_led_read,__user buf[%s],len[%d]\n",buf,len);return len;
}
static const struct file_operations gec6818_led_fops = {.owner      = THIS_MODULE,.write       = gec6818_led_write,.open      = gec6818_led_open,.release    = gec6818_led_release,.read        = gec6818_led_read,
};
//定义file_operations,初始化打开、关闭、读、写等函数接口,这是应用和驱动的交通枢纽
//这个是驱动的入口函数类似main
static int __init gec6818_led_init(void){int rt;    struct resource *gpioe_res=NULL;rt=alloc_chrdev_region(&led_num,0,1,"gec6818_leds");//动态分配,就是由内核自动分配空闲的设备号if(rt < 0){printk("alloc_chrdev_region fail\n");return rt;}printk("led_major = %d\n",MAJOR(led_num));printk("led_minor = %d\n",MINOR(led_num));//获取分配后的主设备号和次设备号//字符设备初始化cdev_init(&gec6818_led_cdev,&gec6818_led_fops);//字符设备添加到内核rt = cdev_add(&gec6818_led_cdev,led_num,1);if(rt < 0){printk("cdev_add fail\n");goto fail_cdev_add;}leds_class=class_create(THIS_MODULE, "gec6818_leds");//为设备创建一个class 类if (IS_ERR(leds_class))//如何判断当前的指针为错误码,使用IS_ERR函数进行判断{rt = PTR_ERR(leds_class);//PTR_ERR返回指针错误码数值printk("class_create gec6818_leds fail\n");goto fail_class_create;}//创建设备leds_device=device_create(leds_class, NULL, led_num, NULL, "gec6818_leds");if (IS_ERR(leds_device)){rt = PTR_ERR(leds_device);printk("device_create gec6818_leds fail\n");goto fail_device_create;}//起始地址GPIOE寄存器基址0xC001E000(这个地址可以参考之前那篇文章<三星6818基于uboot的流水灯程序>)//申请大小28字节//申请到的名字为GPIOE_MEMgpioe_res = request_mem_region(0xC001E000,0x28,"GPIOE_MEM");//申请物理内存区if(gpioe_res == NULL){printk("request_mem_region 0xC001E000,0x28 fail\n");goto fail_request_mem_region_gpioe;        }//IO内存动态映射,得到物理地址相应的虚拟地址gpioe_base_va = ioremap(0xC001E000,0x28);if(gpioe_base_va == NULL){printk("ioremap 0xC001E000,0x28 fail\n");goto fail_ioremap_gpioe;       }   //得到每个寄存器的虚拟地址gpioe_out_va      = gpioe_base_va+0x00;gpioe_outenb_va  = gpioe_base_va+0x04; gpioe_altfn0_va     = gpioe_base_va+0x20;     gpioe_altfn1_va     = gpioe_base_va+0x24; printk("gec6818 led init\n");return 0;fail_ioremap_gpioe:release_mem_region(0xC001E000,0x28); //释放物理内存区fail_request_mem_region_gpioe:iounmap(gpioe_base_va);//解除IO内存的动态映射fail_device_create:class_destroy(leds_class);//类的销毁fail_class_create:cdev_del(&gec6818_led_cdev);//将字符设备从内核删除fail_cdev_add:unregister_chrdev_region(led_num,1);//设备号的注销return rt;
}
//在驱动有入口函数对应就有出口函数,用于释放资源,销毁设备号等等
static void __exit gec6818_led_exit(void){iounmap(gpioe_base_va);release_mem_region(0xC001E000,0x28);device_destroy(leds_class,led_num);//设备的销毁 class_destroy(leds_class);cdev_del(&gec6818_led_cdev);unregister_chrdev_region(led_num,1);printk("gec6818 led exit\n");
}//驱动程序的入口:insmod led_drv.ko调用module_init,module_init又会去调用gec6818_led_init。
module_init(gec6818_led_init);//驱动程序的出口:rmsmod led_drv调用module_exit,module_exit又会去调用gec6818_led_exit。
module_exit(gec6818_led_exit);
//模块描述
MODULE_AUTHOR("stephenwen88@163.com");           //作者信息
MODULE_DESCRIPTION("gec6818 led driver");     //模块功能说明
MODULE_LICENSE("GPL");                    //许可证:驱动遵循GPL协议,这个必须加,否则会导致驱动异常

驱动的编译和生成

  1. 查看kernel/Documentation/kbuild的kbuild.txt编写Makefile
  2. 附上代码:
obj-m +=led_drv.o
/*
Loadable module goals - obj-m$(obj-m) specify object files which are built as loadablekernel modules.A module may be built from one source file or several sourcefiles. In the case of one source file, the kbuild makefilesimply adds the file to $(obj-m).Example:#drivers/isdn/i4l/Makefileobj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.oNote: In this example $(CONFIG_ISDN_PPP_BSDCOMP) evaluates to 'm'If a kernel module is built from several source files, you specifythat you want to build a module in the same way as above; however,kbuild needs to know which object files you want to build yourmodule from, so you have to tell it by setting a $(<module_name>-y)variable.
*/
KERNEL_DIR :=/home/jiba/6818SoucereCode/6818GEC/kernel
CROSS_COMPILE :=/home/jiba/6818SoucereCode/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-
PWD:=$(shell pwd)default:$(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_DIR) M=$(PWD) modulesclean:rm  *.o *.order .*.cmd  *.mod.c *.symvers .tmp_versions -rf

编写程序代码

  1. 附上代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>static char d7_on [2]={7,1};
//驱动write函数switch判断如果d7_on[1]为1就灭0就亮
static char d7_off[2]={7,0};int main(int argc, char **argv)
{int fd=-1;int len;char buf[64]="hello teacher.wen";//打开gec6818_leds设备fd = open("/dev/gec6818_leds",O_RDWR);if(fd < 0){perror("open /dev/gec6818_leds:");return fd;}while(1){write(fd,d7_on,2);sleep(2);write(fd,d7_off,2);sleep(2);}close(fd);return 0;
}

下载驱动(.ko)和程序到板子


insmod 加载驱动
运行程序OK~~~
要源码可以找我拿,wx:a812417530
如果对你有帮忙麻烦点赞收藏一下~~~~

三星6818LED驱动的编写相关推荐

  1. PX4原生固件SPI驱动动编写与IMU传感器替换

    适用于PX4原生固件 核心目标:完成XSENS的MTI3,IMU替换.MTI3是一款航姿参考系统,可以独立的输出四元数,加速度,磁力计等,角速度等航姿信息.里面有完整的卡尔曼滤波,可以替换飞控本身里面 ...

  2. 三星 4521 linux 驱动下载,三星scx-4521f驱动下载_三星scx-4521f驱动官方下载-太平洋下载中心...

    三星scx-4521f驱动下载本驱动是SAMSUNG三星scx-4521f多功能一体打印机驱动,适用于WindowsXP以上的操作系统,方便用户更好的使用三星scx-4521f打印机的打印.复印.扫描 ...

  3. Linux 字符设备驱动的编写

    Linux 字符设备驱动的编写 作者:解琛 时间:2020 年 8 月 17 日 Linux 字符设备驱动的编写 一.Linux 设备分类 二.open() 三.数据结构 3.1 struct fil ...

  4. 三星framebuffer驱动代码分析

    一.驱动总体概述 本次的驱动代码是Samsung公司为s5pv210这款SoC编写的framebuffer驱动,对应于s5pv210中的内部外设Display Controller (FIMD)模块. ...

  5. Linux驱动开发-编写(EEPROM)AT24C02驱动

    1. 前言 AT24C02是IIC接口的EEPROM存储芯片,这颗芯片非常经典,百度搜索可以找到非常多的资料,大多都是51.STM32单片机的示例代码,大多采用模拟时序.裸机系统运行.当前文章介绍在L ...

  6. linux 驱动 内核模式,Linux内核模块和驱动的编写

    Linux内核是一个整体是结构,因此向内核添加任何东西,或者删除某些功能,都十分困难.为了解决这个问题引入了内核机制.从而可以动态的想内核中添加或者删除模块. 模块不被编译在内核中,因而控制了内核的大 ...

  7. linux更新驱动脚本,编写Linux驱动常见错误(不断更新)!

    工作中遇到的编写Linux驱动的常见错误和注意事项整理,将不断更新. 问题1.驱动的init函数声明错误 出错: [root@localhost]# insmod phyinfo.ko insmod: ...

  8. Linux内核驱动如何编写?我们先从字符驱动入门开始

    几年前正式转到linux开发岗位的时候,由于项目急需编写linux驱动来控制项目采集设备(板卡),我便被安排做这一部分工作.那时候挺慌的-,在之前的一年多时间里基本都是window应用开发,对于lin ...

  9. Nand flash驱动的编写与移植

    1 Nand flash工作原理     S3C2410板的Nand Flash支持由两部分组成:Nand Flash控制器(集成在S3C2410 CPU)和Nand Flash存储 芯片(K9F12 ...

  10. 三星linux打印机驱动官网下载,三星SL-C515驱动

    软件标签: 三星SL-C515打印机驱动是三星官方针对该型号打印机推出的驱动程序,是打印机可以正常使用的必备程序,能够帮助用户解决打印机无法被电脑识别或者不能打印等问题,从而保证打印机的正常使用,小编 ...

最新文章

  1. 认知行为技术是计算机技术吗,基于认知行为模型的多Agent建模技术研究与应用_问答库...
  2. alpine linux 源码安装,关于docker:如何安装Go in alpine linux
  3. 将undefault和null的数据转换成bool类型的数据 使用!!
  4. oracle定义转储目录,Oracle 12.1新特性:在线rename或relocate数据文件
  5. winform checkbox要点击两次_这个Winform的UI库也太全了!四十多个控件都在这一个项目里了...
  6. 基于JAVA+SpringMVC+Mybatis+MYSQL的个人记账管理系统
  7. 手机html文件转TXT,Html 转换 Txt
  8. Android Dialog宽度设置固定大小
  9. html中滚动字幕是什么属性,html中Marquee属性详解(滚动显示文本/图片)
  10. 每日一诗词 —— 行路难
  11. ASP.NET EXCEL导入,身份证、手机号长度校验数据校验
  12. 织梦制作二级全国分站教程,多城市分站插件代码调用
  13. LRU算法,走迷宫,数根,星际战争
  14. 全国计算机注册时密码为什么老是错误,电脑密码正确却显示密码错误怎么办
  15. 商务谈判中如何表示同意Agreement
  16. SOLIDWORKS如何在工程图中直接更换图纸格式
  17. 非LL(1)文法到LL(1)文法的等价变换
  18. multisim变压器反馈式_基于Multisim的负反馈放大电路仿真分析
  19. 单片机 LCD1602显示实验
  20. [Java] Lock(锁)的tryLock失败是否需要unlock?

热门文章

  1. HTML之meta属性大全
  2. Chrome网页观看百度云视频加速
  3. 彻底了解DVD:从入门到精通
  4. java使用密文链接数据库_Java基础——数据库连接信息使用密文
  5. BZOJ 3503([Cqoi2014]和谐矩阵-gauss消元)
  6. ORA-01830: date format picture ends before converting entire input string的几种原因
  7. C#实现右下角弹窗效果
  8. websphere java和进程管理_Websphere 学习(一)
  9. 大数据“超能力”:数据安全和隐私该如何保障?
  10. Dev C++源代码未编译