[嵌入式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;
}