ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

基于[三星6818]芯片超声波测距驱动编写

2020-12-08 20:05:11  阅读:223  来源: 互联网

标签:info 6818 struct int csb var gpio 超声波 测距


基于[三星6818]芯片超声波测距驱动编写

编写驱动代码

#PS:这次使用了标准gpio函数和ioctl函数进行编写,进一步缩小代码行数

头文件内容:

#define GEC6818_C7_STA		_IOR('K',  1, unsigned long)//这个是io的命令定义 有三种:_IOR,_IOW,_IORW在使用ioctl函数的时候要用到
#define GEC6818_C8_STA		_IOW('K',  2, unsigned long)

struct csb_gpio_info{
    unsigned int gpio_ID;
    unsigned char* gpio_flag;
};//gpio函数要用的参数结构体

static struct csb_gpio_info gpio_info[2]={//给结构体赋值
    {
        PAD_GPIO_C+7,//这个参数要参考kernel里面的代码,每个芯片都不一样
        "gpioc_7"//这个就什么只是一个标记而已
    },
    {
        PAD_GPIO_C+8,
        "gpioc_8"
    }
};

1> 定义和初始化混杂设备

(混杂设备定义简单,利于实验练习,可对比上一篇LED驱动使用的标准字符设备定义)

static int __init csb_drv_init(void){//这个是驱动程序入口函数类似于Main不过驱动入口要声明,参考函数module_init()
    int rt,var;
    rt = misc_register(&s3c_adc_miscdev);//这里初始化混杂设备,s3c_adc_miscdev这个参数下面有介绍
    if (rt!=0) {
            printk(KERN_ERR "cannot register miscdev on minor=%d (%d)\n",
                    MISC_DYNAMIC_MINOR, rt);
            goto err_clk;
    }
    for (var = 0; var < 2; ++var) {
    	//为了防止gpio驱动有冲突先释放
        gpio_free(gpio_info[var].gpio_ID);
    }
    for (var = 0; var < 2; ++var) {
        rt=gpio_request(gpio_info[var].gpio_ID,gpio_info[var].gpio_flag);//申请GPIO驱动
        if(rt < 0)
        {
                printk("gpio_request:%s fail\n",gpio_info[var].gpio_flag);

                goto gpio_request_fail;

        }
    }

    printk("gec6818 csb init\n");
    return 0;

    gpio_request_fail:
    for (var = 0; var < 2; ++var) {
        gpio_free(gpio_info[var].gpio_ID);
    }
    err_clk:
    misc_deregister(&s3c_adc_miscdev);//注销混杂设备
    return rt;
}
//***************************************************************
static void __exit csb_drv_register(void){
    misc_register(&s3c_adc_miscdev);//注销混杂设备
}

//驱动程序的入口:insmod led_drv.ko调用module_init,module_init又会去调用gec6818_key_init。
module_init(csb_drv_init);

//驱动程序的出口:rmsmod led_drv调用module_exit,module_exit又会去调用gec6818_key_exit。
module_exit(csb_drv_register);

2> 定义驱动文件集

#ps: 文件集是啥?就是定义你应用程序打开驱动文件、write驱动文件和ioctl驱动文件时会被调用的函数

static int  gec6818_csb_open (struct inode * inode, struct file *file)
{

        //配置GPIOC8为输出模式
        gpio_direction_output(gpio_info[1].gpio_ID,0);//使用标准gpio接口配置GPIO为输出模式
        //配置GPIOC7为输入模式
        gpio_direction_input(gpio_info[0].gpio_ID);//使用标准gpio接口配置GPIO为输入模式
        printk("gec6818_csb_open \n");
        return 0;
}
static int  gec6818_csb_release (struct inode * inode, struct file *file)
{
        printk("gec6818_csb_release \n");
        return 0;
}
static long gec6818_csb_ioctl (struct file *filp, unsigned int cmd, unsigned long args)
//第一个参数:驱动文件的文件描述符,第二个是iotcl的命令(去看头文件内容有),第三个参数你可以传指针,传值等会详细介绍一下
//这个函数是今天驱动的重点,ioctl函数,当应用层调用ioctl()时它会被调用
{
    switch (cmd) {
        case  GEC6818_C7_STA:{
                long csb_val;
		int rt; 
                csb_val = sr04_get_distance();//这个函数就是返回测量的距离大家不用详细去看
		rt = copy_to_user((void*)args,&csb_val,4);
		/** static inline long copy_to_user(void __user *to,const void *from, unsigned long n)
			to:用户空间数据的地址
			from:内核空间数据的地址 (所以我们可以通过函数参数args传进来)
			n:要拷贝的字节数    */
		if(rt != 0){
			return -EFAULT;
		}
        }break;
        case  GEC6818_C8_STA:{
                gpio_set_value(gpio_info[1].gpio_ID,args);//gpio标准函数设置输出电平
        }break;
    }
        return 0;
}
static struct file_operations csb_fops = {//这是驱动文件的文件集机构体
    .owner 		= THIS_MODULE,
    .open 		= gec6818_csb_open,
    .release            = gec6818_csb_release,
    .unlocked_ioctl     = gec6818_csb_ioctl,
};
/** struct file_operations {
	struct module *owner;
...........
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
	...........
};    
文件操作集的作用:
每个字符设备都必须有一个文件操作集,文件操作集是驱动程序给应用程序访问硬件的一个接口,应用层与驱动程序函数接口的对应关系如下:
*/
static struct miscdevice s3c_adc_miscdev = {//混杂设备属性的结构体
        .minor		= MISC_DYNAMIC_MINOR,	//指定了ADC的次设备号为131,也可以MISC_DYNAMIC_MINOR,动态分配次设备号
        .name		= "csb",		//设备名称,/dev/adc
        .fops		= &csb_fops,//文件操作集
};

3>粗略介绍下超声波检测距离函数里面几个细节

int sr04_get_distance(void)
{
	int t=0;

	gpio_set_value(gpio_info[1].gpio_ID,1);
	udelay(20);
	gpio_set_value(gpio_info[1].gpio_ID,0);
	
	while(gpio_get_value(gpio_info[0].gpio_ID)==0)//PEin(6)==0)
	{
		t++;
		
		udelay(1);
		
		if(t >= 1000000)
			return 0xFFFFFFFF;
	}
	
	t=0;
	
	while(gpio_get_value(gpio_info[0].gpio_ID))
	{
		t++;
	
		udelay(8);
	
	}

	t=t/2;
	
	return 3*t;
}
这里主要讲一下udelay()函数,内核延时有长延时和短延时它们各自的原理都不一样,对CPU占用也各自不同
我有这个相关文档可以找我拿,WX:a812417530

4> 编译内核参考

LED驱动文章

编写应用程序

#define GEC6818_C7_STA		_IOR('K',  1, unsigned long)//这个是ioctl命令,这里定义必须与驱动程序定义一样
#define GEC6818_C8_STA		_IOW('K',  2, unsigned long)

int main(void){
    int csb_fd,distance;
    //打开gec6818_leds设备
    csb_fd = open("/dev/csb",O_RDWR);

    if(csb_fd < 0)
    {
            perror("open /dev/csb:fail");

            return -1;
    }
    while (1) {
        ioctl(csb_fd,GEC6818_C7_STA,&distance);//这个函数会直接调用我们驱动代码里面的gec6818_csb_ioctl()函数
        if(distance==0xFFFFFFFF)
        {
            printf("sr04 is error\r\n");
        }else {
            printf("the distance = %d\n",distance);
        }
        sleep(1);
    }

}

然后用开发板的交叉编译工具编译,把程序和驱动下载到开发板

在这里插入图片描述
分别加载驱动和运行程序,完成~~~~~

标签:info,6818,struct,int,csb,var,gpio,超声波,测距
来源: https://blog.csdn.net/a812417530/article/details/110878583

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有