驱动操作控制LED灯

控制LED灯:

驱动如何操作寄存器

rgb_led灯的寄存器是物理地址,在linux内核启动之后,

在使用地址的时候,操作的全是虚拟地址。需要将物理地址

转化为虚拟地址。在驱动代码中操作的虚拟地址就相当于

操作实际的物理地址。

物理地址<------>虚拟地址

void * ioremap(phys_addr_t offset, unsigned long size)

功能:将物理地址映射成虚拟地址

参数:

@offset :要映射的物理地址

@size   :大小(字节)

返回值:成功返回虚拟地址,失败返回NULL; 

void iounmap(void  *addr)

功能:取消映射

参数:

@addr :虚拟地址

返回值:无

RGB_led 

red  :gpioa28

GPIOXOUT   :控制高低电平的   0xC001A000

GPIOxOUTENB:输入输出模式    0xC001A004

GPIOxALTFN1:function寄存器  0xC001A024

green:gpioe13

0xC001e000

blue :gpiob12

0xC001b000

指针类型加1是加的他的类型大小

驱动控制灯

#include 
#include 
#include 
#include 
#include 
#include 
int major=0;
#define CNAME "hello"
char kbuf[128]={0};
int dev=0;
#define RED_BASE  0XC001A000
#define BLUE_BASE  0XC001B000
#define GREEN_BASE 0XC001E000
unsigned int *red_base=NULL;
unsigned int *blue_base=NULL;
unsigned int *green_base=NULL;



ssize_t mycdev_read (struct file *file, char __user *user, size_t size, loff_t * loff)
{
   //printk("this is read");
   if(size>128){
   	size=128;
	}
    dev=copy_to_user(user,kbuf,size);
	if(dev)
		{
		printk("copy to user errer");
		return dev;
	}
   return 0;
}
ssize_t mycdev_write (struct file *file, const char __user *user, size_t size, loff_t *loff)
{
   //printk("this is write");
      if(size>128){
   	size=128;
	}
	dev=copy_from_user(kbuf,user,size);
   return 0;
}
int mycdev_open (struct inode *inode, struct file *file)
{
   printk("this is open");
   return 0;
}
int mycdev_release (struct inode *inode, struct file *file)
{
   printk("this is close");
   return 0;
}
	
const struct file_operations fops={
   .open=mycdev_open,
   .read=mycdev_read,
   .write=mycdev_write,
   .release=mycdev_release,
};

static int __init hello_init(void)//入口
{
  major=register_chrdev(major,CNAME,&fops);
  if(major<0)
  {
   printk("register chrdev error");
  }
  red_base=ioremap(RED_BASE,36);
  if(red_base==NULL)
  	{
	  printk("red ioremap error\n");
	  return -ENOMEM;
  }
  blue_base=ioremap(BLUE_BASE,36);
  if(blue_base==NULL)
  	{
	  printk("blue ioremap error\n");
	  return -ENOMEM;
  }
    green_base=ioremap(GREEN_BASE,36);
  if(green_base==NULL)
  	{
	  printk("green ioremap error\n");
	  return -ENOMEM;
  }

	*red_base &=~(1<<28);
	*(red_base+1) |=1<<28;
	*(red_base+9) |=3<<24;


  return 0;
}
static void __exit hello_exit(void)//出口
{
  iounmap(green_base);
  iounmap(blue_base);
  iounmap(red_base);
  unregister_chrdev(major,CNAME);
}
module_init(hello_init);//告诉内核驱动的入口
module_exit(hello_exit);//告诉内核驱动的出口
MODULE_LICENSE("GPL");

应用层控制灯

驱动层(只有部分程序)

#define RED_ON *red_base |= 1<<28
#define RED_OF *red_base &= ~(1<<28)
ssize_t mycdev_write (struct file *file, const char __user *user, size_t size, loff_t *loff)
{
   //printk("this is write");
      if(size>128){
   	size=128;
	}
	dev=copy_from_user(kbuf,user,size);
	if(kbuf[0]==1)
		{
		RED_ON;
	}
	else{
		RED_OF;
	}
   return 0;
}

应用层(部分)

sleep(1);
buf[0]=buf[0]?0:1;
while(1)
	 {
    	write(fd,buf,sizeof(buf));
		sleep(1);
		buf[0]=buf[0]?0:1;
	 }

详细步骤

1.写宏定义灯的基地址

驱动操作控制LED灯_第1张图片

 2.虚拟地址声明

驱动操作控制LED灯_第2张图片

 3.去注册驱动下做地址映射

驱动操作控制LED灯_第3张图片

 物理地址 长度一个占四个 四九三十六

驱动操作控制LED灯_第4张图片

 驱动操作控制LED灯_第5张图片

 其他灯同理去映射虚拟地址驱动操作控制LED灯_第6张图片

 偏移地址驱动操作控制LED灯_第7张图片

 int四字节 +1等于+4

驱动操作控制LED灯_第8张图片

 #include

取消地址

驱动操作控制LED灯_第9张图片

 驱动操作控制LED灯_第10张图片

vi MAKEFILE

 打开开发板,关闭内核的驱动操作控制LED灯_第11张图片

再make

放到nfs里

驱动操作控制LED灯_第12张图片

 驱动操作控制LED灯_第13张图片

 进行应用:

内核层:驱动操作控制LED灯_第14张图片

 驱动操作控制LED灯_第15张图片

 驱动操作控制LED灯_第16张图片

 驱动操作控制LED灯_第17张图片

 应用层

驱动操作控制LED灯_第18张图片

 make一下

vi test.c

驱动操作控制LED灯_第19张图片

 驱动操作控制LED灯_第20张图片

 驱动操作控制LED灯_第21张图片

 

 驱动操作控制LED灯_第22张图片

 

 vi MAKEFILE

驱动操作控制LED灯_第23张图片

 驱动操作控制LED灯_第24张图片

 驱动操作控制LED灯_第25张图片

 驱动操作控制LED灯_第26张图片

 驱动操作控制LED灯_第27张图片

 打开nfs

驱动操作控制LED灯_第28张图片

 驱动操作控制LED灯_第29张图片

 驱动操作控制LED灯_第30张图片

 驱动操作控制LED灯_第31张图片

 驱动操作控制LED灯_第32张图片

自动创建设备节点

#include 

自动创建设备节点:

struct class *cls;

cls = class_create(owner, name) /void class_destroy(struct class *cls)

功能:向用户空间提交目录信息

参数:

@owner :THIS_MODULE

@name  :目录名字

返回值:成功返回struct class *指针

失败返回错误码指针 int (-5)

if(IS_ERR(cls)){

}

struct device *device_create(struct class *class, struct device *parent,

                           dev_t devt, void *drvdata, const char *fmt, ...)

/void device_destroy(struct class *class, dev_t devt)

功能:向用户空间提交文件信息

参数:

           @class :目录名字(向用户空间提交目录信息时产生的返回值)

           @parent:NULL

           @devt  :设备号    MKDEV(major,0)

           @drvdata :NULL

           @fmt   :文件的名字

返回值:成功返回struct device *指针

              失败返回错误码指针 int (-5)

struct class*cls;
struct device *dev;	
//自动创建设备节点
	cls = class_create(THIS_MODULE,CNAME);
	if(IS_ERR(cls)){
		printk("class create error\n");
		return PTR_ERR(cls);
	}
	dev = device_create(cls,NULL,MKDEV(major,0),NULL
		,CNAME);
	if(IS_ERR(dev)){
		printk("device create error\n");
		return PTR_ERR(dev);
	}
//出口-》这里写了卸载时注销的函数
static void __exit hello_exit(void)//出口
{
  device_destroy(devc,MKDEV(major,1));//注销内容
  class_destroy(cls);//注销目录
  iounmap(green_base);
  iounmap(blue_base);
  iounmap(red_base);
  unregister_chrdev(major,CNAME);
}

注意:MKDEV(major,0)为一个宏用于配置主设备号和次设备号

详细步骤

写在入口

声明:

驱动操作控制LED灯_第33张图片

 在入口处

驱动操作控制LED灯_第34张图片

 改为

驱动操作控制LED灯_第35张图片

 驱动操作控制LED灯_第36张图片

 驱动操作控制LED灯_第37张图片

 

驱动操作控制LED灯_第38张图片

 驱动操作控制LED灯_第39张图片

 驱动操作控制LED灯_第40张图片

 make

驱动操作控制LED灯_第41张图片

 自动创建设备节点建立在dev里

ioctl函数

用户:

   #include 

   int ioctl(int fd, int request, ...);

(让点灯的代码变得简洁)

   参数:

@fd     : 打开文件产生的文件描述符

@request: 请求码(读写|第三个参数传递的字节的个数),

:在sys/ioctl.h中有这个请求码的定义方式。

@...    :可写、可不写,如果要写,写一个内存的地址

内核:

 long (*unlocked_ioctl) (struct file *, unsigned int,unsigned long)

作用:此函数指针原型位于struct file_operations结构体当中,配合应用层ioctl函数实现指令传递的功能

驱动操作控制LED灯_第42张图片

 驱动操作控制LED灯_第43张图片

 vi test.c

驱动操作控制LED灯_第44张图片

 驱动操作控制LED灯_第45张图片

 驱动操作控制LED灯_第46张图片

驱动操作控制LED灯_第47张图片

 驱动操作控制LED灯_第48张图片

 驱动操作控制LED灯_第49张图片

驱动操作控制LED灯_第50张图片

 驱动操作控制LED灯_第51张图片

 驱动操作控制LED灯_第52张图片

驱动操作控制LED灯_第53张图片 

你可能感兴趣的:(linux,单片机,嵌入式硬件)