1. 程式人生 > 其它 >基於[三星6818]晶片超聲波測距驅動編寫

基於[三星6818]晶片超聲波測距驅動編寫

技術標籤:linux核心驅動程式

基於[三星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);
    }

}

然後用開發板的交叉編譯工具編譯,把程式和驅動下載到開發板

在這裡插入圖片描述
分別載入驅動和執行程式,完成~~~~~