自己写的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
#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