日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

編寫字符設備驅動控制樹莓派io口

作者:weixin_42963900 更新時間: 2022-10-29 編程語言

字符設備驅動

  • 1.注冊字符類設備號
    • 1.靜態分配一個設備號
    • 2.動態分配一個設備號
    • 3.注銷設備號
  • 2.注冊字符類設備
    • 1.定義一個cdev結構體
    • 2. 使用cdev_init函數初始化cdev結構體成員變量
    • 3.使用cdev_add函數注冊到內核
  • 3.創建設備節點
    • 1.方法一
    • 2.方法二
      • 1.使用class_create函數創建一個class類
      • 2.使用device_create函數在我們創建的類下面創建一個設備
  • 4.chrdev.c
  • 5 app.c
  • 6 編譯運行
    • 1.編譯chrdev_io.c
    • 2.編譯app.c
  • 7 在樹莓派運行

1.注冊字符類設備號

1.靜態分配一個設備號

int register_chrdev_region(dev_t, unsigned, const char *);

參數:
第一個:設備號的起始值。類型是dev_t類型
第二個:次設備號的個數
第三個:設備的名稱 (使用cat /proc/devices 輸出的就是設備號,設備的名稱)
返回值:成功返回0,失敗返回負數

dev_t類型:
dev_t是用來保存設備號的,是一個32位數。
高12位用來保存主設備號,低20位用來保存次設備號。

操作dev的函數
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
根據dev_t獲取主設備號
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
根據dev_t獲取次設備號
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
將我們的主設備號和次設備號組成一個dev_t類型,第一個參數是主設備號,第二個參數是次設備號。

2.動態分配一個設備號

int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);

參數:
第一個:保存生成的設備號
第二個:我們請求的第一個設備號,通常是0
第三個:連續申請的設備號的個數。
第四個:設備的名稱
返回值:成功返回0 ,失敗返回負數。

使用動態分配優先使用234 --255的設備號。

3.注銷設備號

void unregister_chrdev_region(dev_t, unsigned);

參數:
第一個:分配設備號的起始地址
第二個: 申請的連續設備號的個數。

2.注冊字符類設備

1.定義一個cdev結構體

2. 使用cdev_init函數初始化cdev結構體成員變量

void cdev_init(struct cdev *, const struct file_operations *);

參數:
第一個:要初始化的cdev
第二個:文件操作集
cdev->ops = fops;//實際就是把文件操作集寫給ops

3.使用cdev_add函數注冊到內核

int cdev_add(struct cdev *, dev_t, unsigned);
參數:
第一個:cdev的結構體指針
第二個:設備號
第三個:次設備號的數量

3.創建設備節點

1.方法一

使用mknod命令創建一個設備節點
格式:
mknod 名稱 類型 主設備號 次設備號

2.方法二

1.使用class_create函數創建一個class類

2.使用device_create函數在我們創建的類下面創建一個設備

4.chrdev.c

在/home/kun/build_new/linux_kernel/drivers/char/(這為我內核目錄下的字符設備路徑)目錄下 mkdir 07_chrdev_io ,編寫chrdev.c app.c Makefile。

#include <linux/init.h>    //初始化頭文件
#include <linux/module.h>  //最基本的文件,支持動態添加和卸載模塊
#include <linux/moduleparam.h> //驅動傳參頭文件
#include <linux/fs.h>             //文件操作相關的struct的定義
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/io.h>

#define DEVICE_NUMBER 1            //次設備號個數
#define DEVICE_SNAME   "schrdev"   //靜態注冊設備的名字
#define DEVICE_ANANME  "achrdev"   //動態注冊設備的名字
#define DEVICE_MINOR 0             //次設備號起始地址

#define DEVICE_CLASS_NAME "chrdev_class"
#define DEVICE_NODE_NAME  "chrdev_test"

#define GPIO4_DR 0xfe200000
#define GPIO4_H  0xfe20001c
#define GPIO4_L  0xfe200028


unsigned int *vir_gpio4_dr=NULL;
unsigned int *vir_gpio4_h=NULL;
unsigned int *vir_gpio4_l=NULL;

static int major_num, minor_num;

struct cdev cdev;

static dev_t  dev_num;

struct class *class;
struct device *device;

int chrdev_open( struct inode * inode, struct file *file)
{
	printk( "hello chrdev_open \n");
	
	return 0;
}


ssize_t chrdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
	char kbuf[64] = {0};
	if (copy_from_user(kbuf, ubuf, size) != 0)
	{
		printk("copy_from_user error\n ");
		return -1;
	}
	printk("kbuf is %s\n ", kbuf);
	*vir_gpio4_dr |=(001<<(3*4));
	if(kbuf[0]==1)
	{
		
		*vir_gpio4_h |=(1<<4);
	}
	else if(kbuf[0]==0)
	{
		*vir_gpio4_l |=(1<<4);
	}
	return 0;
}


struct file_operations chrdev_ops=
{
	.owner = THIS_MODULE,
	.open = chrdev_open,
	.write = chrdev_write,
};

module_param( major_num, int, S_IRUGO);
module_param( minor_num, int, S_IRUGO);

MODULE_PARM_DESC( major_num, "e.g:a=1"); 
MODULE_PARM_DESC( minor_num, "e.g:a=1"); 


static int 
chrdev_init( void)
{

	int ret;

	if( major_num) //靜態注冊
	{
		printk( "mjor_num=%d \n", major_num);
		printk( "minor_num=%d \n", minor_num);
		dev_num = MKDEV( major_num, minor_num);
		ret = register_chrdev_region( dev_num, DEVICE_NUMBER, DEVICE_SNAME);

		if( ret<0)
		{
			printk( "register_chrdev_region error\n");
		}
		
		printk( "register_chrdev_region success\n");
	}
	else   //動態注冊
	{
		ret = alloc_chrdev_region( &dev_num, DEVICE_MINOR, DEVICE_NUMBER, DEVICE_ANANME);
		if( ret<0)
		{
			printk( "alloc_chrdev_region error\n");
		}
		printk( "alloc_chrdev_region success\n");

		major_num = MAJOR( dev_num);
		minor_num = MINOR( dev_num);
		
		printk( "mjor_num=%d \n", major_num);
		printk( "minor_num=%d \n", minor_num);
	}
	
	//初始化cdev
	cdev.owner = THIS_MODULE;
	
	cdev_init( &cdev, &chrdev_ops);
	//向系統注冊設備
	cdev_add( &cdev, dev_num, DEVICE_NUMBER);
	class = class_create( THIS_MODULE, DEVICE_CLASS_NAME);
	device = device_create( class,NULL,dev_num,NULL,DEVICE_NODE_NAME);

	//對物理地址進行虛擬映射
	vir_gpio4_dr = ioremap( 0xfe200000,4);
	if(vir_gpio4_dr == NULL )
	{
		printk( "gpio4dr ioremap error\n");
		return EBUSY;
	}
	
	vir_gpio4_h = ioremap( GPIO4_H,4);
	if( vir_gpio4_h == NULL)
	{
		printk( "gpio4h ioremap error\n");
		return EBUSY;
	}

	vir_gpio4_l = ioremap( GPIO4_L,4);
	if(vir_gpio4_l == NULL)
	{
		printk( "gpio4l ioremap error\n");
		return EBUSY;
	}
	printk( "gpio ioremap success\n");
	return 0;
}

static void 
chrdev_exit( void)
{

	unregister_chrdev_region( MKDEV( major_num, minor_num), DEVICE_NUMBER);

	cdev_del( &cdev);

	device_destroy( class,dev_num);

	class_destroy( class);
	
	printk( "bye bye \n");
}

module_init( chrdev_init);
module_exit( chrdev_exit);

MODULE_LICENSE( "GPL");

5 app.c

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc,char *argv[])
{
	int fd;
	
	char buf[64] = "0";
	
	fd = open( "/dev/chrdev_test",O_RDWR);//打開設備節點
	
	if(fd < 0)
	{
		perror( "open error \n");
		return fd;
	}

	buf[0]= atoi( argv[1]);
	
	write( fd,buf,sizeof(buf)); //向內核層寫數據
	
	close( fd);
	
	return 0;
}

6 編譯運行

1.編譯chrdev_io.c

Makefile文件內容

obj-m +=chrdev_io.o
KDIR:=/home/kun/build_new/linux_kernel
PWD?=$(shell pwd)
all:
	make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -C $(KDIR) M=$(PWD) modules
clean:
	rm *.mod.c *.order *.ko *.o *.mod *.symvers

KDIR:為自己在linux上存放的內核目錄路徑
執行make,生成.ko文件

2.編譯app.c

arm-linux-gnueabihf-gcc app.c -o app -static

7 在樹莓派運行

將步驟6生成的chrdev.ko app移動至樹莓派
執行 sudo insmod chrdev.ko即可加載驅動
執行 ./app 1 即可使GPIO4引腳輸出高電平
執行 ./app 0即可使GPIO4引腳輸出低電平
在這里插入圖片描述
在這里插入圖片描述

原文鏈接:https://blog.csdn.net/weixin_42963900/article/details/127536968

欄目分類
最近更新