1. 程式人生 > >[嵌入式Linux驅動]S5PV210的煙霧感測器Linux驅動

[嵌入式Linux驅動]S5PV210的煙霧感測器Linux驅動

        自己寫的Linux下的MQ-2煙霧感測器驅動程式,硬體環境為三星的SMDKC110開發板,使用S5PV210(ARM Cortex-A8)作為處理器。

煙霧感測器原理圖


附帶有說明文件(想不到我之前還寫得那麼細緻!):

<智慧家居煙霧感測器驅動程式>

         煙霧感測器驅動分為兩個部分:煙霧報警部分 和 煙霧測量部分

************    
報警部分:
************
    
1. 驅動使用platform模型進行設計,分為SmokeDetect_device.c和SmokeDetect_driver.c兩個檔案
2. 註冊雜項裝置(misc),主裝置號固定是10(misc),從裝置號由系統自動分配,載入成功後使用lsmod可以看到:

    Smoke_Detect_device
    Smoke_Detect_driver

3. 本驅動註冊成功後生成 /dev/smarthome_smokedetect 節點
4. 對 smarthome_smokedetect 裝置節點的操作主要有:
    1)開啟操作open。使用open開啟裝置節點後會對GPIO進行初始化並申請中斷,此時煙霧報警功能已完全開啟。
        
        [   25.461039] SmokeDetect driver request_irq success!!!        //註冊中斷成功
        
        當感測器檢測到煙霧的時候,會觸發中斷。
                a.中斷服務程式會將蜂鳴器輸入設定為高電平,蜂鳴器開始報警。
                b.中斷服務程式會將中斷源引腳設定為輸入,關閉中斷。    

    2)讀操作read。每次進行讀取操作都將讀到一個結構體:
        struct SmokeDetect_Info{
            unsigned short flag;
        };

        #define NO_ALARM    0    //flag為0時 沒有煙霧告警
        #define IS_ALARM    1      //flag為1時 發生煙霧告警

    3)寫操作write。向裝置節點隨便寫入一個值,會執行相應操作:
            a.清除蜂鳴器的報警。
            b.將中斷源引腳設定為外部中斷源,重新開啟中斷。
                
    4)關閉操作close。 關閉操作會執行以下動作:
            a.清除蜂鳴器的報警。
            b.將中斷源引腳設定為輸入。
            c.登出中斷。
            
************    
煙霧測量部分:
************

1. 直接使用核心現有的ADC驅動,沒有另外新增驅動程式。使用的ADC驅動程式原始碼路徑:linux/arch/arm/mach-s5pv210/adc.c

   配置核心新增ADC驅動程式步驟:在核心原始碼樹裡面輸入make menuconfig,進入編譯配置介面
   
    System Type  --->
            [*] S5PXXXX ADC driver             //選中
   
    然後重新編譯燒寫核心,燒寫完成後啟動核心會在/dev 目錄底下生成名字為ADC的裝置節點

2. 把驅動程式裡面的這個巨集新增到應用程式中

    #define ADC_INPUT_PIN    _IOW('S',0x0c,unsigned long)

    然後使用ioctl選擇ADC輸入通道
    
    #define ADC_CHANNEL_0 0                //煙霧測量使用的是AIN0通道,

                                                                        //輸入值不能大於等於4,4以後是為觸控式螢幕預留的
    ioctl(adc_fd,ADC_INPUT_PIN,ADC_CHANNEL_0);
    
3. 使用read讀取ADC的數值

    read(adc_fd,buffer,4);            //讀出來的是一個整型值

4. 應用程式附帶了一個均值演算法,可參考使用。

S5PV210的Datasheet中的ADC原理圖


SmokeDetect_device.c

#include <asm/uaccess.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/platform_device.h>

<pre name="code" class="cpp">/* read the s5pv210 datasheet! */
#define S5PV210_GPH_BASE	0xe0200c00
#define GPH_SIZE 0x6c

#define S5PV210_GPD_BASE	0xe02000a0	
#define GPD_SIZE 0x34


void SmokeDetect_device_release(struct device * pdev);

static struct resource SmokeDetect_resource[]={

	[0] = {
		.start = S5PV210_GPH_BASE,
		.end   = S5PV210_GPH_BASE + GPH_SIZE,
		.name  = "GPH_BASE",
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = S5PV210_GPD_BASE,
		.end   = S5PV210_GPD_BASE + GPD_SIZE,
		.name  = "GPD_BASE",
		.flags = IORESOURCE_MEM,
	},
	[2] = {
		.start = IRQ_EINT(16),
		.end   = IRQ_EINT16_31,
		.name  = "SmokeDetect_IRQ",
		.flags = IORESOURCE_IRQ,
	},			
};


struct platform_device SmokeDetect_device={
	.name = "SmokeDetect_drv",
	.id = -1,
	.dev={
		.release=SmokeDetect_device_release,
	},
	.num_resources = ARRAY_SIZE(SmokeDetect_resource),
	.resource = SmokeDetect_resource,
};


void SmokeDetect_device_release(struct device * pdev)
{
	printk("entering %s\n",__FUNCTION__);
}


static int __init SmokeDetect_device_init(void)
{
	printk("entering %s\n",__FUNCTION__);

	if( platform_device_register(&SmokeDetect_device) ){
		printk("%s: platform_device_register failed! \n",__FUNCTION__);
		return -EBUSY;
	}

	return 0;
}


static void __exit SmokeDetect_device_exit(void)
{
	printk("entering %s\n",__FUNCTION__);
	platform_device_unregister(&SmokeDetect_device);
}

module_init(SmokeDetect_device_init);
module_exit(SmokeDetect_device_exit);

MODULE_AUTHOR("kinyanderson");
MODULE_DESCRIPTION("SmokeDetect_device,use for smoke sensor");
MODULE_LICENSE("GPL");

SmokeDetect_driver.c

#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/irq.h>
#include <linux/irqnr.h>
#include <linux/hardirq.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/irqflags.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>

#define READING_WAIT_TIME 300

unsigned int eint_id=16;
struct resource * platform_resource[3];

static volatile unsigned long * GPH_BASE;
static volatile unsigned long * GPD_BASE;

/* Must caculate the offset carefully!!! */ 
			
#define GPH2_BASE  (GPH_BASE + 16)
#define rGPH2CON    GPH2_BASE
#define rGPH2DAT   (GPH2_BASE + 1)

#define GPD0_BASE   GPD_BASE
#define rGPD0CON    GPD0_BASE
#define rGPD0DAT   (GPD0_BASE + 1)	


#define GPH2_0_SET_INPUT(tmp) do{	\
	tmp =ioread32(rGPH2CON);	\
	tmp &= ~(0xf<<0);		\
	iowrite32(tmp,rGPH2CON);	\
}while(0)

#define GPH2_0_SET_EINT16(tmp) do{	\
	tmp =ioread32(rGPH2CON);	\
	tmp |= (0xf<<0);		\
	iowrite32(tmp,rGPH2CON);	\
}while(0)

#define GPD0_0_SET_OUTPUT(tmp) do{	\
	tmp =ioread32(rGPD0CON);	\
	tmp &= ~(0xf<<0);		\
	tmp |= (0x1<<0);		\
	iowrite32(tmp,rGPD0CON);	\
}while(0)

#define GPD0_0_SET_LOWLEVEL(tmp) do{	\
	tmp =ioread32(rGPD0DAT);	\
	tmp  &= ~(0x1<<0);		\
	iowrite32(tmp,rGPD0DAT);	\
}while(0)

#define GPD0_0_SET_HIGHLEVEL(tmp) do{	\
	tmp =ioread32(rGPD0DAT);	\
	tmp  |= (0x1<<0);		\
	iowrite32(tmp,rGPD0DAT);	\
}while(0)


struct SmokeDetect_Info{
	unsigned short flag;
};

#define NO_ALARM	0
#define IS_ALARM	1

static struct SmokeDetect_Info smokedetect_info={
	.flag=NO_ALARM,
};


/*** file_operation_function declare ****/

int SmokeDetect_driver_open (struct inode * inode_p, struct file *file_p);
int SmokeDetect_driver_close (struct inode *inode_p, struct file *file_p);

ssize_t SmokeDetect_driver_write 
(struct file *file_p, const char __user *buff, size_t size, loff_t *offset);

ssize_t SmokeDetect_driver_read 
(struct file *file_p, char __user *buff,size_t size, loff_t *offset);

static int __devexit SmokeDetect_driver_remove(struct platform_device * pdev);
static int __devinit SmokeDetect_dirver_probe(struct platform_device * pdev);


irqreturn_t SmokeDetect_driver_irq_handler(int irq, void *arg);


/*** Struct declare ****/

static struct platform_driver SmokeDetect_driver={

	.probe=SmokeDetect_dirver_probe,
	.remove = SmokeDetect_driver_remove,

	.driver  = {
		.name = "SmokeDetect_drv",	  
		.owner = THIS_MODULE,
	},
};

static struct file_operations SmokeDetect_fop={
	.open = SmokeDetect_driver_open,
	.read = SmokeDetect_driver_read,
	.write = SmokeDetect_driver_write,
	.release = SmokeDetect_driver_close,
};

static struct miscdevice SmokeDetect_miscdev = {	
	.minor			= MISC_DYNAMIC_MINOR,	     //dynamic
	.name			= "smarthome_smokedetect",
	.fops			= &SmokeDetect_fop,
};


irqreturn_t SmokeDetect_driver_irq_handler(int irq, void *arg)
{
	printk("entering %s\n",__FUNCTION__);	

	if(smokedetect_info.flag==IS_ALARM){
		return IRQ_HANDLED;
	}

	unsigned int tmp;

	GPH2_0_SET_INPUT(tmp);		       //close the eint source pin
	smokedetect_info.flag=IS_ALARM;
	GPD0_0_SET_HIGHLEVEL(tmp);	       //open the alarm

	printk("%s: >>> smoke alarm <<< \n",__FUNCTION__);
	return IRQ_HANDLED;
}


/*** file_operation_function implement ****/

int SmokeDetect_driver_open
(struct inode * inode_p, struct file *file_p)
{
	printk("entering %s\n",__FUNCTION__);
	unsigned int tmp;
	int ret;

        smokedetect_info.flag=NO_ALARM;

/* initing the gpio */		

	GPD0_0_SET_OUTPUT(tmp);
	GPD0_0_SET_LOWLEVEL(tmp);
	
	printk("%s: gpio init finished!!!\n",__FUNCTION__);

	ret=request_irq(platform_resource[2]->start,
			SmokeDetect_driver_irq_handler,
		        IRQF_TRIGGER_LOW|IRQF_SHARED,
			platform_resource[2]->name,
			(void *)&eint_id);
	if(ret){
		printk("SmokeDetect driver request_irq failed!!! %d\n",ret);
		return -EBUSY;
	}
        else{
		printk("SmokeDetect driver request_irq success!!!\n");
	}
	return 0;
}

int SmokeDetect_driver_close 
(struct inode *inode_p, struct file *file_p)
{
	printk("entering %s\n",__FUNCTION__);
	unsigned int tmp;

	free_irq(platform_resource[2]->start,(void *)&eint_id);

	GPH2_0_SET_INPUT(tmp);			//close the eint source pin
	GPD0_0_SET_LOWLEVEL(tmp);			//clear the alarm

	smokedetect_info.flag=NO_ALARM;
	return 0;
}

ssize_t SmokeDetect_driver_read 
(struct file *file_p, char __user *buff, size_t size, loff_t *offset)
{
	printk("entering %s\n",__FUNCTION__);

	unsigned char i;
	i=0;

	while(copy_to_user(buff,(void *)&smokedetect_info,sizeof(smokedetect_info))){
		
		msleep(READING_WAIT_TIME);
		if(i++ >= 2){
			printk("%s: copy_to_user failed!!!\n",__FUNCTION__);
			return -EBUSY;
		}
	}

	return 0;
}

ssize_t SmokeDetect_driver_write 
(struct file *file_p, const char __user *buff, size_t size, loff_t *offset)
{
	printk("entering %s\n",__FUNCTION__);

	if(smokedetect_info.flag==NO_ALARM){
		return 0;
	}

	unsigned int tmp;

	smokedetect_info.flag=NO_ALARM;

	GPD0_0_SET_LOWLEVEL(tmp);		//clear the alarm
	GPH2_0_SET_EINT16(tmp);			//open the eint source pin

	printk("%s: clear the smoke alarm warning...\n",__FUNCTION__);
	return 0;
}


/*** driver_operation ****/

static int __devinit SmokeDetect_dirver_probe(struct platform_device * pdev)
{
	printk("entering %s\n",__FUNCTION__);

	struct resource * pcheck;

	platform_resource[0]=platform_get_resource(pdev,IORESOURCE_MEM,0);

	if(NULL==platform_resource[0]){
		printk("%s: platform_resource[0] failed!\n",__FUNCTION__);	
		goto err1;
	}

	platform_resource[1]=platform_get_resource(pdev,IORESOURCE_MEM,1);

	if(NULL==platform_resource[1]){
		printk("%s: platform_resource[1] failed!\n",__FUNCTION__); 
		goto err1;
	}

	platform_resource[2]=platform_get_resource(pdev,IORESOURCE_IRQ,0);

	if(NULL==platform_resource[2]){
		printk("%s: platform_resource[2] failed!\n",__FUNCTION__); 
		goto err1;
	}

	pcheck=request_mem_region(platform_resource[0]->start,
					platform_resource[0]->end - platform_resource[0]->start + 1,
					platform_resource[0]->name);

	if(NULL==pcheck){
		printk("%s: request_mem_region failed!\n",__FUNCTION__);	
		goto err1;						//return device busy!
	}

	pcheck=request_mem_region(platform_resource[1]->start,
					platform_resource[1]->end - platform_resource[1]->start + 1,
					platform_resource[1]->name);

	if(NULL==pcheck){
		printk("%s: request_mem_region failed!\n",__FUNCTION__);	
		goto err2;						//return device busy!
	}

	GPH_BASE=(unsigned long *)ioremap(platform_resource[0]->start,
						platform_resource[0]->end - platform_resource[0]->start + 1);

	GPD_BASE=(unsigned long *)ioremap(platform_resource[1]->start,
						platform_resource[1]->end - platform_resource[1]->start + 1);

	if(misc_register(&SmokeDetect_miscdev)){
		printk("%s: misc_register failed!\n",__FUNCTION__);	
		goto err3;	
	}
	return 0;

err3:
	iounmap(GPD_BASE);
	iounmap(GPH_BASE);

	release_mem_region(platform_resource[1]->start,
				platform_resource[1]->end - platform_resource[1]->start + 1);
err2:
        release_mem_region(platform_resource[0]->start,
				platform_resource[0]->end - platform_resource[0]->start + 1);
err1:
	return -EBUSY;	
}


static int __devexit SmokeDetect_driver_remove(struct platform_device * pdev)
{
	printk("entering %s\n",__FUNCTION__);

	iounmap(GPD_BASE);
	iounmap(GPH_BASE);

	release_mem_region(platform_resource[0]->start,
			   platform_resource[0]->end - platform_resource[0]->start + 1);

	release_mem_region(platform_resource[1]->start,
			   platform_resource[1]->end - platform_resource[1]->start + 1);

	if(misc_deregister(&SmokeDetect_miscdev)){
		printk("%s: misc_deregister failed!\n",__FUNCTION__);	
		return -EPERM;	
	}

	return 0;
}


static int __init SmokeDetect_driver_init(void)
{
	printk("entering %s\n",__FUNCTION__);

	if(platform_driver_register(&SmokeDetect_driver)){
		printk("%s: driver_register failed!\n",__FUNCTION__);
		return -EBUSY;
	}
	return 0;
}


static void __exit SmokeDetect_driver_exit(void)
{
	printk("entering %s\n",__FUNCTION__);
	platform_driver_unregister(&SmokeDetect_driver);
}

module_init(SmokeDetect_driver_init);
module_exit(SmokeDetect_driver_exit);

MODULE_AUTHOR("kinyanderson");
MODULE_DESCRIPTION("SmokeDetect_driver,use for smoke sensor");
MODULE_LICENSE("GPL");

應用程式app.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <error.h>
#include <pthread.h>

#include <sys/wait.h>
#include <sys/ioctl.h>

#define ADC_DRV_PATH "/dev/adc"
#define SMOKEDETECT_DRV_PATH "/dev/smarthome_smokedetect"

/* Defined by s5p-adc driver */

#define ADC_INPUT_PIN  _IOW('S',0x0c,unsigned long)
#define ADC_CHANNEL_0 0
#define ADC_CHANNEL_1 1
#define CAL_NUM 20

struct SmokeDetect_Info{
	unsigned short flag;
};

#define NO_ALARM	0
#define IS_ALARM	1

static struct SmokeDetect_Info smokedetect_info;

void* smokedetect_value_monitor(void *arg);
void* smokedetect_alarm_monitor(void *arg);

static unsigned int smokedetect_value_algorithm(unsigned int * array);

unsigned int global_value;
unsigned int smokedetect_value_array[CAL_NUM];


int main(void)
{
	printf(">>> Start the app !<<<\n");

	pthread_t smokedetect_value_monitor_tid;
	pthread_t smokedetect_alarm_monitor_tid;	
	
	int ret;

	ret = pthread_create(&smokedetect_value_monitor_tid,NULL,smokedetect_value_monitor,NULL);
	if(ret){
		perror("failed to create the smokedetect_value_monitor thread!\n");
		goto err;
	}

	ret = pthread_create(&smokedetect_alarm_monitor_tid,NULL,smokedetect_alarm_monitor,NULL);
	if(ret){
		perror("failed to create the smokedetect_alarm_monitor thread!\n");
		goto err;
	}
	
	pthread_join(smokedetect_value_monitor_tid,NULL);
	pthread_join(smokedetect_alarm_monitor_tid,NULL);

	printf(">>> App finish !<<<\n");
	
err:
	return 0;
}


void* smokedetect_alarm_monitor(void *arg)
{
	int smokedetect_drv_fd;
	int ret;

	smokedetect_drv_fd = open(SMOKEDETECT_DRV_PATH,O_RDWR);
	if(smokedetect_drv_fd < 0){
		printf("can not open smarthome_smokedetect!!!\n");
		goto err1;
	}

	while(1)
	{
		ret=read(smokedetect_drv_fd,&smokedetect_info,sizeof(smokedetect_info));	
		if(ret<0){
			printf("can not read smarthome_smokedetect!!!\n");
			goto err2;
		}

		if(smokedetect_info.flag){

			printf("App: Warning!!! There is smoke alarm!!!\n");			
			sleep(5);
			printf("App: Handling the smoke alarm...\n");			
			write(smokedetect_drv_fd,&ret,4);
		}
		sleep(3);				
	}
	
err2:	
	close(smokedetect_drv_fd);	
err1:
	return NULL;
}


void* smokedetect_value_monitor(void *arg)
{
	int s5p_adc_fd;
	unsigned short i;
	int ret;
	
	printf("entering smokedetect_value_monitor!\n");

/* open the file */
	s5p_adc_fd=open(ADC_DRV_PATH,O_RDWR);
	if(s5p_adc_fd < 0){
		perror("failed to open the s5p-adc device!\n");
		goto err1;
	}

/*ioctl set the channel */	
	ioctl(s5p_adc_fd,ADC_INPUT_PIN,ADC_CHANNEL_0);			//smoke_sensor use channel 0

	for(i=0;i<CAL_NUM;i++)	{
		ret=read(s5p_adc_fd,smokedetect_value_array+i,4);
		
		if(ret<0){
			printf("can not read s5p-adc !!!\n");
			goto err2;		
		}
		
		if((CAL_NUM-1) == i){
			global_value = smokedetect_value_algorithm(smokedetect_value_array);
			printf("the smokedetect_value is: %u \n",global_value);
			sleep(1);			//delay 1000ms
			i=0;
		}
	}
	
err2:	
	close(s5p_adc_fd);
err1:
	return NULL;
}

static unsigned int smokedetect_value_algorithm(unsigned int * array)
{
	unsigned short i;
	unsigned int result = 0;

	for(i=0;i<CAL_NUM;i++){
		result += *(array+i);
	}
	result /= CAL_NUM;	
	return result;
}