cat /proc/devices 查看主设备号

sudo mknod hello(路径:任意的) c/b(C代表字符设备 b代表块设备)主设备号 次设备号
生成hello:应用层可以打开的文件

设置驱动层程序:hello.c
- #include
- #include
- #include
- #include
//添加头文件 - #define CNAME "hello"
- int major=0;
- ssize_t mycdev_read (struct file *file, char __user *user, size_t size, loff_t * loff)
- {
- printk("this is read\n");
-
- return 0;
- }
- ssize_t mycdev_write (struct file *file, const char __user *user, size_t size, loff_t *loff)
- {
- printk("this is write\n");
-
- return 0;
- }
- int mycdev_open (struct inode *inode, struct file *file)
- {
- printk("this is open\n");
- return 0;
- }
- int mycdev_release (struct inode *inode, struct file *file)
- {
- printk("this is close\n");
- 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");
-
- }
- return 0;
- }
- static void __exit hello_exit(void)//出口
- {
- unregister_chrdev(major,CNAME);
- }
- module_init(hello_init);//告诉内核驱动的入口
-
- module_exit(hello_exit);//告诉内核驱动的出口
-
- MODULE_LICENSE("GPL");
设置应用层程序:test.c![]()
(读写的方向是站在用户的角度来说的)
#includeint copy_from_user(void *to, const void __user *from, int n)
功能:从用户空间拷贝数据到内核空间
参数:
@to :内核中内存的首地址
@from:用户空间的首地址
@n :拷贝数据的长度(字节)
返回值:成功返回0,失败返回未拷贝的字节的个数
int copy_to_user(void __user *to, const void *from, int n)
功能:从内核空间拷贝数据到用户空间
参数:
@to :用户空间内存的首地址
@from:内核空间的首地址 __user需要加作用是告诉编译器这是用户空间地址
@n :拷贝数据的长度(字节)
返回值:成功返回0,失败返回未拷贝的字节的个数
驱动层:hello.c
- #include
- #include
- #include
- #include
//添加头文件 - #include
- #define CNAME "hello"
- int major=0;
- char kbuf[128]={0}; //存储数据
- int dev=0;
- ssize_t mycdev_read (struct file *file, char __user *user, size_t size, loff_t * loff)
- {
- //printk("this is read\n");
- if(size>sizeof(kbuf))
- {
- size=sizeof(kbuf);
- }
- dev=copy_to_user(user,kbuf,size); //从内核空间到用户空间
- if(dev)
- {
- printk("copy to user err");
- 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\n");
- if(size>sizeof(kbuf))
- {
- size=sizeof(kbuf);
- }
- dev=copy_from_user(kbuf,user,size); //从用户空间到内核空间
- return 0;
- }
- int mycdev_open (struct inode *inode, struct file *file)
- {
- printk("this is open\n");
- return 0;
- }
- int mycdev_release (struct inode *inode, struct file *file)
- {
- printk("this is close\n");
- 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");
- }
- return 0;
- }
- static void __exit hello_exit(void)//出口
- {
- unregister_chrdev(major,CNAME);
- }
- module_init(hello_init);//告诉内核驱动的入口
- module_exit(hello_exit);//告诉内核驱动的出口
- MODULE_LICENSE("GPL");
应用程序:test.c
- #include
- #include
- #include
- #include
- #include
- #include
- char buf[128]="hello world!";
- int main(int argc, const char *argv[])
- {
- int fd;
- fd=open("./hello",O_RDWR);
- if(fd==-1)
- {
- perror("open error");
- return -1;
- }
- write(fd,buf,sizeof(buf));
- memset(buf,0,sizeof(buf));
- read(fd,buf,sizeof(buf));
- printf("buf is :%s\n",buf);
- close(fd);
- return 0;
- }
$make
$sudo insmod hello.ko
$gcc test.c
$./a.out

$dmesg

驱动如何操作寄存器
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

R:GPIOA
G: GPIOE

B: GPIOB

宏定义基地址 ,设置虚拟地址

将物理地址映射成虚拟地址
指针类型加1是加的类型大小

添加头文件:#include
取消映射:

将hello.ko拷贝到开发板内核文件夹中
指针类型加1是加的类型大小
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/printk.h>
- #include <linux/uaccess.h>
- #include <linux/fs.h>
- #include <linux/io.h> //添加头文件
- 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; //指针类型加1是加的类型大小 int占4字节
- *(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");

开发板上电测试:

1、 驱动层
判断语句 、宏定义开关
- #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;
- }
2、应用层
需要将test.c 在开发板中进行编译,还需要拷贝编译生成的a.out,所以直接在Makefile中添加两行代码:
-
- while(1)
- {
- write(fd,buf,sizeof(buf));
- sleep(1);
- buf[0]=buf[0]?0:1;
- }
