[嵌入式Linux驱动]S5PV210的烟雾传感器Linux驱动

        自己写的Linux下的MQ-2烟雾传感器驱动程序,硬件环境为三星的SMDKC110开发板,使用S5PV210(ARM Cortex-A8)作为处理器。


烟雾传感器原理图

[嵌入式Linux驱动]S5PV210的烟雾传感器Linux驱动_第1张图片


附带有说明文档(想不到我之前还写得那么细致!):

<智能家居烟雾传感器驱动程序>

         烟雾传感器驱动分为两个部分:烟雾报警部分 和 烟雾测量部分

************    
报警部分:
************
    
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原理图

[嵌入式Linux驱动]S5PV210的烟雾传感器Linux驱动_第2张图片


SmokeDetect_device.c

#include 
#include 
#include 
#include 
#include 

/* 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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#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

你可能感兴趣的:(Linux)