当前位置:网站首页>三星6818LED驱动的编写
三星6818LED驱动的编写
2022-07-21 17:26:00 【I&You】
三星6818芯片基于linux的LED驱动详解
led驱动.c文件的编写
#PS:使用iowrite和ioread函数编写,没有用标准的gpio函数和ioctl函数会在下一篇超声波驱动介绍
- 一般驱动编写顺序是这样的
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 */
#endif
rt = 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_MEM
gpioe_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("[email protected]"); //作者信息
MODULE_DESCRIPTION("gec6818 led driver"); //模块功能说明
MODULE_LICENSE("GPL"); //许可证:驱动遵循GPL协议,这个必须加,否则会导致驱动异常
驱动的编译和生成
- 查看kernel/Documentation/kbuild的kbuild.txt编写Makefile
- 附上代码:
obj-m +=led_drv.o
/* Loadable module goals - obj-m $(obj-m) specify object files which are built as loadable kernel modules. A module may be built from one source file or several source files. In the case of one source file, the kbuild makefile simply adds the file to $(obj-m). Example: #drivers/isdn/i4l/Makefile obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o Note: In this example $(CONFIG_ISDN_PPP_BSDCOMP) evaluates to 'm' If a kernel module is built from several source files, you specify that you want to build a module in the same way as above; however, kbuild needs to know which object files you want to build your module 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) modules
clean:
rm *.o *.order .*.cmd *.mod.c *.symvers .tmp_versions -rf
编写程序代码
- 附上代码
#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
如果对你有帮忙麻烦点赞收藏一下~~~~
边栏推荐
- Pytorch learning (I): linear regression
- Self study golang [Chapter 3: the first go language program] use GoLand to create the first go program, the main function and init function, and use go to run Windows commands and realize the data out
- Explain the usage of stack, heap and method area in memory
- FastJson、JackJson处理时间、日期问题注意事项
- Verilog - Basketball 24s timer
- 力扣之螺旋矩阵,一起旋转起来(都能看懂)
- 量化交易中如何判断趋势
- scrollIntoView
- Différence entre les versions Runtime + compilateur et Runtime only
- Document operation II (5000 word summary)
猜你喜欢
Self study golang [3.6 slice practice code] slice length, upper limit, copy, delete and increase
Postman configures the global variable postman sets the global token
Quantitative transaction Diary - summary in February 2021
Zhang Chi Consulting: the fifth Six Sigma project of a packaging and printing group passed the expert review
鼠标移入select options会触发mouseleave 事件处理方案
文件操作《二》(5000字总结篇)
软考中级【数据库系统工程师】第0章:如何自学备考,考试介绍考什么,备考教材,上午和下午的体型分数分布,备考课程链接,个人备考感谢
Self study the definition of golang [3.5go language array, range keyword] array, and use the for loop to traverse one-dimensional array
Verilog application - 24 second basketball counter
mysql
随机推荐
Verilog -- 74hc194 multifunctional bidirectional shift register
不断提升认知,从而达到交易的最高级别——稳定盈利(终)
张驰咨询:六西格玛如何帮助公司减少客户投诉
scrollIntoView
Vscode add custom comment
vscode添加自定义注释
How to judge the trend in quantitative trading
Science and technology create value | cloud expansion technology is listed in the real list · top 100 of China's scientific and technological innovation brands list
指针总结篇
File operation and related functions
Partage de la stratégie de percée ultime des commerçants professionnels pendant de nombreuses années (avec un ensemble complet de modèles de transaction)
Ffmpeg audio decoding (seconds understand)
C语言刷题笔记之基础不牢地动山摇 <一>
C textbox password box setting
Self study golang [3.7map exercise code]
Conversion of JS time and timestamp
JS时间和时间戳的转换
Qml-跨窗口拖动图片、物体
交易进阶必由之路:从小白到稳定盈利(三)
Self study golang [3.3go language loop statement] for loop syntax structure, omit initial conditions, omit incremental conditions, omit the application of end conditions