一、設計要求
- 將環境中溫濕度數值、環境的光照強度和煙霧的信息獲取到開發板,顯示在圖形界面上。
- 當溫度值高于閾值時,溫度指示燈變紅、蜂鳴器告警并且啟動直流電機正轉降溫;當濕度值高于閾值時,濕度指示燈變紅、蜂鳴器告警并且繼電器吸合接通除濕電路;當光照度大于閾值時,光照度指示燈變紅、蜂鳴器告警并且步進電機正轉關閉窗簾;當檢測到煙霧時,煙霧指示燈變紅、蜂鳴器告警;報警聲音的頻率可通過圖形界面進行設置。
- 溫濕度告警閾值、光照度告警閾值可以通過界面進行設置并且保存到配置文件,系統重新上電后,讀取配置文件的配置參數。
- 實驗箱可通過UDP協議將溫濕度、照度和煙霧數據傳輸到x86上的ubuntu上的圖形界面顯示,并且實驗箱和x86上的ubuntu的可以通過圖形界面進行文本信息的交流。
- 修改啟動腳本,使系統上電后自動運行本次實驗設計的圖形界面。
二、實驗原理
????????該嵌入式 Linux 環境監測系統采用模塊化設計,主要包含硬件采集、核心控制、設備執行、軟件交互四大模塊。
????????硬件采集模塊集成溫濕度傳感器、光敏電阻與煙霧傳感器,負責實時感知環境參數變化,為系統提供原始數據。
????????核心控制模塊以粵嵌GEC6818開發板為載體,搭載Linux操作系統,通過編寫設備驅動程序,實現對傳感器數據的讀取解析,同時運行QT應用程序進行數據處理與邏輯判斷。
????????設備執行模塊由繼電器、步進電機、蜂鳴器和直流電機組成,根據核心控制模塊的指令,繼電器在光照強度高于設定的閾值時吸合,觸點閉合,電路通斷狀態改變,當光敏元件上的光照強度降低到一定閾值以下時,光敏元件的導通電流或電阻值恢復原狀,繼電器也會恢復到原來的狀態;步進電機可在檢測光照強度高于閾值時啟動,可以與窗簾聯動,通過驅動拉開窗簾,使其參數值降低;蜂鳴器在環境參數超設定閾值時,觸發聲光報警;直流電機可在檢測到煙霧和環境溫度高于閾值時啟動風扇,使其參數值降低;
????????軟件交互模塊基于QT框架開發可視化界面,不僅能實時展示環境數據,還支持閾值參數設置,便于用戶遠程監控與管理系統,各模塊協同工作實現環境監測與智能調控功能。
三、系統模塊分析
3.1.1 環境監測模塊
3.1.1.1 ?溫濕度監測模塊
????????溫濕度監測中,采用了DHT11溫濕度傳感器。VDD_IO為傳感器供電,電容C67濾波保證電壓穩定。R124作為上拉電阻,使數據傳輸引腳在空閑時保持高電平狀態,R125則將傳感器數據引腳連接至XEINT24_B引腳,以便與外部控制設備通信。工作時,微控制器先發送起始信號,傳感器響應后,通過單總線傳輸40位包含溫濕度整數、小數部分及校驗和的數據,微控制器接收并校驗數據,若準確無誤,便對溫濕度數據進行后續處理與應用,如顯示或依據數據觸發相關控制動作。
溫度檢測的驅動代碼如下:
//頭文件來源于linux內核源碼
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/gpio.h> //函數聲明
#include "cfg_type.h" //端口宏定義
#include <linux/ioport.h> //IO相關
#include <linux/delay.h> //延時函數
#include <linux/time.h>
#include "led_cmd.h"
//1.定義一個字符設備
static struct cdev dht11_cdev;
static unsigned int dht11_major = 0; //主設備號;0--動態;>0--靜態
static unsigned int dht11_minor = 0; //次設備號
static dev_t dht11_dev_num; //設備號
static struct class *dht11_class;
static struct device *dht11_device;
#define DHT_DATA (??????)
static int gec6818_dht11_open(struct inode *inode, struct file *filp)
{gpio_set_value(DHT_DATA, 1);printk("dht11 driver is openning\n"); //應用程序打開驅動文件時打印return 0;
}static int gec6818_dht11_release(struct inode *inode, struct file *filp)
{gpio_set_value(DHT_DATA, 1);printk("dht11 driver is closing\n");//應用程序關閉驅動文件時打印return 0;
}
static unsigned char get_byte(void)
{int i=0, k=0;int ret_data = 0;for(k=0; k<8; k++){i=0;//復位計數值while(!gpio_get_value(DHT_DATA))//等待總線拉高跳出{i++;udelay(1);if(i > 120){printk("DTH11 high timeout error\n");break;}}udelay(22);ret_data <<= 1;//逐漸將數據移到最高位if(gpio_get_value(DHT_DATA))ret_data |= 0x01;//接收最高位放在第一位 1 只取1,0初始化已經填充i=0;//復位計數值while(gpio_get_value(DHT_DATA))//等待總線拉低跳出{i++;udelay(1);if(i > 200){printk("DTH11 low level timeout error\n");break;}} }return ret_data;
}static long gec6818_dht11_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{int ret, i=0 ; unsigned long flags;//中斷保存unsigned char dth11_data[5]={0};//數據組unsigned char checksum = 0;//校驗和if(cmd == GEC6818_GET_DHTDATA){local_irq_save(flags);//關閉中斷ret =gpio_direction_output(DHT_DATA, 0);//設置輸出if(ret != 0)printk("gpio_direction_output DHT_DATA error\n");gpio_set_value(DHT_DATA, 0);//主機拉低18msmsleep(20);ret =gpio_direction_input(DHT_DATA);//設置輸入 if(ret != 0){printk("gpio_direction_input DHT_DATA error\n");local_irq_restore(flags); //恢復中斷return ret;}udelay(10);if(gpio_get_value(DHT_DATA))//如果響應會拉低總線電平{printk("DTH11 timeout error\n");local_irq_restore(flags); //恢復中斷return -EAGAIN;//超時重試}i = 0;while(!gpio_get_value(DHT_DATA))//等待總線拉高跳出{i++;udelay(1);if(i > 160){printk("DTH11 high timeout error\n");local_irq_restore(flags);//恢復中斷 return -EAGAIN;//超時重試}}i = 0;while(gpio_get_value(DHT_DATA))//等待總線拉低跳出{i++;udelay(1);if(i > 160){printk("DTH11 low level timeout error\n");local_irq_restore(flags); //恢復中斷return -EAGAIN;//超時重試}}for(i = 0; i<5; i++)dth11_data[i] = get_byte();
checksum = dth11_data[0] + dth11_data[1] + dth11_data[2] + dth11_data[3];//統計校驗和if(checksum != dth11_data[4])//判斷校驗和{printk("DTH11 checksum error\n");return -EAGAIN; //超時重試}local_irq_restore(flags);//恢復中斷}elsereturn -ENOIOCTLCMD;//無效命令 printk("th_data = %hhd temp_data = %hhd\n", dth11_data[0], dth11_data[2]);ret = copy_to_user(((unsigned char *)arg)+2,&dth11_data[0],2); if( ret != 0 )return -EFAULT;ret = copy_to_user((unsigned char *)arg,&dth11_data[2],2)if( ret != 0 )return -EFAULT;return 0;
}static const struct file_operations gec6818_dht11_fops = {.owner = THIS_MODULE,.unlocked_ioctl = gec6818_dht11_ioctl,.open = gec6818_dht11_open,.release = gec6818_dht11_release,
};//驅動的安裝函數
static int __init gec6818_dht11_init(void)
{int ret;//2.申請設備號(靜態注冊 or 動態分配)if(dht11_major == 0){ret=alloc_chrdev_region(&dht11_dev_num,dht11_minor,1,"dht11_device"); }else{dht11_dev_num = MKDEV(dht11_major,dht11_minor);ret = register_chrdev_region(dht11_dev_num, 1, "dht11_device");}if(ret < 0){printk("can not get dht11 device number\n");return ret; //返回錯誤的原因}//4.初始化cdevcdev_init(&dht11_cdev, &gec6818_dht11_fops);//5.將初始化好的cdev加入內核ret = cdev_add(&dht11_cdev, dht11_dev_num, 1);if(ret < 0){printk("cdev add error\n");goto cdev_add_err;}//6.創建classdht11_class = class_create(THIS_MODULE, "dht11_class");if(IS_ERR(dht11_class)){ret =PTR_ERR(dht11_class);printk("dht11 driver class create error\n");goto class_create_err;} //7.創建devicedht11_device = device_create(dht11_class, NULL,//設備名稱dht11_dev_num, NULL, "dht11_dev");//dev/dht11_drvif(IS_ERR(dht11_device)){ret =PTR_ERR(dht11_device);printk("dht11 driver device create error\n");goto device_create_err;}if(gpio_request(DHT_DATA,"DHT_DATA") !=0)//申請物理內存區printk("gpio_request DHT_DATA error\n"); ret =gpio_direction_output(DHT_DATA, 1);//設置輸出if(ret != 0){printk("gpio_direction_output DHT_DATA error\n");gpio_free(DHT_DATA);goto device_create_err;}printk(KERN_INFO "gec6818_dht11_init\n");return 0;//出錯處理
device_create_err:class_destroy(dht11_class);
class_create_err:cdev_del(&dht11_cdev);
cdev_add_err: unregister_chrdev_region(dht11_dev_num, 1);return ret;
}//驅動的卸載函數
static void __exit gec6818_dht11_exit(void)
{gpio_free(DHT_DATA);device_destroy(dht11_class, dht11_dev_num);class_destroy(dht11_class);cdev_del(&dht11_cdev);unregister_chrdev_region(dht11_dev_num, 1);printk(KERN_INFO "gec6818_dht11_exit\n");
}module_init(gec6818_dht11_init);//入口函數
module_exit(gec6818_dht11_exit);//出口函數MODULE_AUTHOR("sugar@NNLG.com");
MODULE_DESCRIPTION("dht11 driver for GEC6818");
MODULE_LICENSE("GPL"); //符合GPL協議
MODULE_VERSION("V1.0");
?
3.1.1.2 ?光照強度監測模塊
????????光照強度監測中,采用了光照傳感器,光照傳感器CS1利用光電效應,其等效電阻隨光照強度變化而改變 。CS1與R117、R116組成分壓電路,光照強度改變會使R116兩端電壓變化,該模擬電壓信號經XadcAIN3引腳傳輸至ADC模塊 ,轉換為數字信號后被微控制器等處理單元接收,依據預設轉換關系換算出實際光照強度值,以此實現對光照強度的檢測。
光照強度監測的驅動代碼如下:
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>#include <linux/io.h>
#include <linux/gpio.h>
#include <cfg_type.h>
#include <linux/miscdevice.h>
#include <linux/ioctl.h>#define GEC6818_ADC_IN0 _IOR('A', 0, unsigned long)
#define GEC6818_ADC_IN1 _IOR('A', 1, unsigned long)
#define GEC6818_ADC_IN2 _IOR('A', 2, unsigned long) //光敏電阻
#define GEC6818_ADC_IN3 _IOR('A', 3, unsigned long) static void __iomem *adc_base_va; //adc的虛擬地址基址
static void __iomem *adccon_va;
static void __iomem *adcdat_va;
static void __iomem *prescalercon_va;
static int gec6818_adc_open (struct inode * inode, struct file *file)
{printk("gec6818_adc_open \n");return 0;
}
static int gec6818_adc_release (struct inode * inode, struct file *file)
{printk("gec6818_adc_release \n");return 0;
}static long gec6818_adc_ioctl (struct file *filp, unsigned int cmd, unsigned long args)
{unsigned long adc_val=0,adc_vol=0; int rt=0;//adc通道選擇#define switch(cmd){case GEC6818_ADC_IN0:iowrite32(ioread32(adccon_va)&(~(7<<3)),adccon_va);break;case GEC6818_ADC_IN1:{iowrite32(ioread32(adccon_va)&(~(7<<3)),adccon_va);iowrite32(ioread32(adccon_va)|(1<<3),adccon_va);}break; case GEC6818_ADC_IN2:{iowrite32(ioread32(adccon_va)&(~(7<<3)),adccon_va);iowrite32(ioread32(adccon_va)|(2<<3),adccon_va);}break;case GEC6818_ADC_IN3:{iowrite32(ioread32(adccon_va)&(~(7<<3)),adccon_va);iowrite32(ioread32(adccon_va)|(3<<3),adccon_va);}break; default:printk("IOCTLCMD failed\n");return -ENOIOCTLCMD;}//將電源開啟iowrite32(ioread32(adccon_va)&(~(1<<2)),adccon_va);//預分頻值=199+1,ADC的工作頻率=200MHz/(199+1)=1MHziowrite32(ioread32(prescalercon_va)&(~(0x3FF<<0)),prescalercon_va);iowrite32(ioread32(prescalercon_va)|(199<<0),prescalercon_va);//預分頻值使能iowrite32(ioread32(prescalercon_va)|(1<<15),prescalercon_va);//ADC使能//iowrite32(ioread32(adccon_va)&(~(1<<0)),adccon_va);iowrite32(ioread32(adccon_va)|(1<<0),adccon_va);//等待AD轉換結束while(ioread32(adccon_va)&(1<<0));//讀取12bit數據adc_val = ioread32(adcdat_va)&0xFFF;//關閉CLKIN時鐘輸入iowrite32(ioread32(prescalercon_va)&(~(1<<15)),prescalercon_va);//關閉ADC電源iowrite32(ioread32(adccon_va)|(1<<2),adccon_va);//將AD轉換的結果值 換算為 電壓值adc_vol = adc_val*1800/4095;rt = copy_to_user((void *)args,&adc_vol,4);if(rt != 0)return -EFAULT;return 0;
}
static ssize_t gec6818_adc_read (struct file *file, char __user *buf, size_t len, loff_t * offs)
{return 0;
}static const struct file_operations gec6818_adc_fops = {.owner = THIS_MODULE,.unlocked_ioctl = gec6818_adc_ioctl,.open = gec6818_adc_open,.release = gec6818_adc_release,.read = gec6818_adc_read,
};static struct miscdevice gec6818_adc_miscdev = {.minor = MISC_DYNAMIC_MINOR, //MISC_DYNAMIC_MINOR,動態分配次設備號.name = "adc_drv", //設備名稱,/dev/adc_drv .fops = &gec6818_adc_fops, //文件操作集
};
//入口函數
static int __init gec6818_adc_init(void)
{int rt=0;// struct resource *adc_res=NULL;//混雜設備的注冊rt = misc_register(&gec6818_adc_miscdev);if (rt) {printk("misc_register fail\n");return rt;}//IO內存動態映射,得到物理地址相應的虛擬地址adc_base_va = ioremap(0xC0053000,0x14);if(adc_base_va == NULL){printk("ioremap 0xC0053000,0x14 fail\n");goto fail_ioremap_adc; } //得到每個寄存器的虛擬地址adccon_va = adc_base_va+0x00;adcdat_va = adc_base_va+0x04; prescalercon_va = adc_base_va+0x10; printk("gec6818 adc init\n");return 0;fail_ioremap_adc:misc_deregister(&gec6818_adc_miscdev);return rt;
}
//出口函數
static void __exit gec6818_adc_exit(void)
{ iounmap(adc_base_va);misc_deregister(&gec6818_adc_miscdev);printk("gec6818 adc exit\n");
}
//驅動程序的入口:insmod led_drv.ko調用module_init,module_init又會去調用gec6818_adc_init。
module_init(gec6818_adc_init);//驅動程序的出口:rmsmod led_drv調用module_exit,module_exit又會去調用gec6818_adc_exit。
module_exit(gec6818_adc_exit)//模塊描述
MODULE_AUTHOR("sugar@NNLG"); //作者信息
MODULE_DESCRIPTION("gec6818 adc driver"); //模塊功能說明
MODULE_LICENSE("GPL"); //許可證:驅動遵循GPL協議
3.1.1.3 ?煙霧濃度監測模塊
????????該煙霧檢測模塊原理圖中,采用了MQ-2煙霧傳感器,MQ-2煙霧傳感器利用氣敏特性,其電阻值會隨周圍煙霧濃度變化而改變。VDD_5V為電路供電,R100與MQ-2組成分壓電路,煙霧濃度變化時,分壓值也隨之改變。該模擬信號經R101輸入到LM393電壓比較器,與R99(可調電阻)設定的基準電壓對比,當煙霧濃度達到一定程度,比較器輸出電平翻轉,通過XEINT22_B引腳將信號傳輸給主控單元,同時D16發光二極管亮起,實現煙霧檢測與指示;C57、C60、C61等電容起到濾波作用,確保電路穩定工作。
煙霧濃度監測的驅動代碼如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <cfg_type.h>#define DEVICE_NAME "gec_gas_drv" //設備名稱
//煙霧結構體
struct gas_desc {int gpio;int number;char *name; struct timer_list timer;
};//煙霧設備的管腳,編號,名字
static struct gas_desc gas[] = {{ ?????, 0, "gas0" },
};//初始值
static volatile char gas_values[] = {'0'
};
//等待隊列
static DECLARE_WAIT_QUEUE_HEAD(gas_waitq);
static volatile int ev_press = 0; //進入休眠的條件//煙霧的次數
static void gec6818_gas_timer(unsigned long _data)
{struct gas_desc *bdata = (struct gas_desc *)_data;int down;int number;unsigned tmp;tmp = gpio_get_value(bdata->gpio);/* active low */ //檢測到煙霧,比較器輸出低電平down = !tmp;//printk(KERN_DEBUG "KEY %d: %08x\n", bdata->number, down);number = bdata->number;//前后兩次狀態不一樣 才認為是檢測到一次煙霧if (down !=(gas_values[number] & 1)) {gas_values[number] = '0' + down; // +'0' 將數字轉換為charev_press = 1;//將等待隊列中的進程變為可運行態,具體進程什么時候被執行,取決于調度程序;wake_up_interruptible(&gas_waitq);}
}
//煙霧中斷
static irqreturn_t gas_interrupt(int irq, void *dev_id)
{struct gas_desc *bdata = (struct gas_desc *)dev_id;mod_timer(&bdata->timer, jiffies + msecs_to_jiffies(40));return IRQ_HANDLED;
}//打開設備
static int gec6818_gas_open(struct inode *inode, struct file *file)
{int irq;int i;int err = 0;//中斷申請for (i = 0; i < ARRAY_SIZE(gas); i++) {if (!gas[i].gpio)continue;gpio_free(gas[i].gpio);setup_timer(&gas[i].timer, gec6818_gas_timer,(unsigned long)&gas[i]);irq = gpio_to_irq(gas[i].gpio); //IRQ_TYPE_EDGE_BOTH雙邊沿觸發err = request_irq(irq, gas_interrupt, IRQ_TYPE_EDGE_BOTH, gas[i].name, (void *)&gas[i]);if (err)break;}//出現錯誤,中斷釋放if (err) {i--;for (; i >= 0; i--) {if (!gas[i].gpio)continue;irq = gpio_to_irq(gas[i].gpio);disable_irq(irq);free_irq(irq, (void *)&gas[i]);del_timer_sync(&gas[i].timer);}return -EBUSY;}ev_press = 1;return 0;
}
//關閉中斷
static int gec6818_gas_close(struct inode *inode, struct file *file)
{int irq, i;for (i = 0; i < ARRAY_SIZE(gas); i++) {if (!gas[i].gpio)continue;irq = gpio_to_irq(gas[i].gpio);free_irq(irq, (void *)&gas[i]);del_timer_sync(&gas[i].timer);}return 0;
}
//煙霧數據讀取
static int gec6818_gas_read(struct file *filp, char __user *buff,size_t count, loff_t *offp)
{//printk("**********cin***************\n");unsigned long err;if (!ev_press) {if (filp->f_flags & O_NONBLOCK)return -EAGAIN;else //當ev_press為非0時,立馬返回,不會休眠,當為0時,線程加入等待隊列鏈表中,然后線程進入休眠狀態。wait_event_interruptible(gad_waitq, ev_press); //進入}ev_press = 0;err = copy_to_user((void *)buff,(const void *)(&gas_values),min(siaeof(gas_values),count));//printk("**********cout***************\n");return err ? -EFAULT : min(sizeof(gas_values), count);
}
//煙霧頭部,Poll方法只是做一個登記 真正的阻塞發生在select.c 中的 do_select函數
static unsigned int gec6818_gas_poll( struct file *file,struct poll_table_struct *wait)
{unsigned int mask = 0;//把等待隊列添加到poll_tablepoll_wait(file, &gas_waitq, wait);if (ev_press) //if (有數據可讀)mask |= POLLIN | POLLRDNORM; return mask; //返回掩碼
}
//文件操作集
static struct file_operations dev_fops = {.owner = THIS_MODULE,.open = gec6818_gas_open,.release = gec6818_gas_close, .read = gec6818_gas_read,.poll = gec6818_gas_poll,
};
//雜項設備
static struct miscdevice misc = {.minor = MISC_DYNAMIC_MINOR,.name = DEVICE_NAME,.fops = &dev_fops,
};
//驅動的初始化函數--->從內核中申請資源(內核、中斷、設備號、鎖....)
static int __init button_dev_init(void)
{int ret;ret = misc_deregister(&misc);printk(DEVICE_NAME"\tinitialized\n");return ret;
}
//驅動退出函數 --->將申請的資源還給內核
static void __exit button_dev_exit(void)
{misc_deregister(&misc);
}
module_init(button_dev_init); //驅動的入口函數會調用一個用戶的初始化函數
module_exit(button_dev_exit); //驅動的出口函數會調用一個用戶的退出函數//驅動的描述信息: #modinfo *.ko , 驅動的描述信息并不是必需的。
MODULE_AUTHOR("sugar@nnlg"); //驅動的作者
MODULE_DESCRIPTION("Gas of driver"); //驅動的描述
MODULE_LICENSE("GPL"); //遵循的協議
?
3.1.2 智能調控模塊
3.1.2.1 溫度調控? ? ? ?
????????溫度調控中,由直流電機驅動風扇降低環境。LG9110是直流電機驅動芯片,由VDD_5V供電。微控制器通過XEINT20_B和XEINT21_B引腳向LG9110的IA和IB引腳輸入控制信號,不同的電平組合能夠控制芯片內部的電路通斷,進而控制輸出引腳OA和OB的電平狀態,以此實現對直流電機正轉、反轉、停止等狀態的控制。當檢測到環境溫度過高,微控制器輸出相應信號使電機正轉,帶動風扇運轉,加速空氣流動來降低溫度;C62和C63電容組成濾波電路,能穩定電源電壓,減少電源波動對電機驅動芯片和電機的影響,確保電機穩定運行。
直流電機驅動代碼如下所示:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <mach/platform.h>
#include <asm/gpio.h>#define DEVICE_NAME "dc_motor" //設備名字
//電機管腳
static int motor_gpios[] = {(????),//電機管腳,需要自己修改(????),//電機管腳,需要自己修改
};#define MOTOR_NUM ARRAY_SIZE(motor_gpios) //電機的數量
//電機初始化
static void motor_init(void)
{gpio_set_value(motor_gpios[0], 0);gpio_set_value(motor_gpios[1], 0);
}
//電機正傳
static void motor_foreward(void)
{gpio_set_value(motor_gpios[0],1);gpio_set_value(motor_gpios[1],0);
}//電機反轉
static void motor_rollback(void)
{gpio_set_value(motor_gpios[1],1);gpio_set_value(motor_gpios[0],0);
}
//電機設備打開
static int gec6818_motor_open(struct inode *inode, struct file *filp)
{printk(DEVICE_NAME ":open\n");motor_init();return 0;
}
//電機控制程序
static long gec6818_motor_ioctl(struct file *filp, unsigned int cmd,unsigned long arg)
{switch(cmd) {case 0:if (arg > MOTOR_NUM) {return -EINVAL;}motor_init();printk("Motor Stop.\n");break;case 1:if (arg > MOTOR_NUM) {return -EINVAL;}motor_rollback();printk("Motor Rollback.\n");break;case 4:if (arg > MOTOR_NUM) {return -EINVAL;} motor_foreward();printk("Motor Foreward.\n");break;default:return -EINVAL;}return 0;
}
//文件操作集
static struct file_operations gec6818_motor_dev_fops = {.owner = THIS_MODULE,.unlocked_ioctl = gec6818_motor_ioctl,.open = gec6818_motor_open
};
//雜項設備
static struct miscdevice gec6818_motor_dev = {.minor = MISC_DYNAMIC_MINOR,.name = DEVICE_NAME,.fops = &gec6818_motor_dev_fops,
};//驅動的初始化函數--->從內核中申請資源(內核、中斷、設備號、鎖....)
static int __init gec6818_motor_dev_init(void)
{int ret;int i;for (i = 0; i < MOTOR_NUM; i++) {ret = gpio_request(motor_gpios[i], "MOTOR");if (ret) {printk("%s: request GPIO %d for MOTOR failed, ret = %d\n", DEVICE_NAME,motor_gpios[i], ret);return ret;}gpio_direction_output(motor_gpios[i],0);}gpio_set_value(motor_gpios[0], 0);gpio_set_value(motor_gpios[1], 0);ret = misc_register(&gec6818_motor_dev);printk(DEVICE_NAME"\tinitialized\n");return ret;
}//驅動退出函數 --->將申請的資源還給內核
static void __exit gec6818_motor_dev_exit(void)
{int i;for (i = 0; i < MOTOR_NUM; i++) {gpio_free(motor_gpios[i]);}misc_deregister(&gec6818_motor_dev);
}
module_init(gec6818_motor_dev_init); //驅動的入口函數會調用一個用戶的初始化函數
module_exit(gec6818_motor_dev_exit); //驅動的出口函數會調用一個用戶的退出函數
//驅動的描述信息: #modinfo *.ko , 驅動的描述信息并不是必需的。
MODULE_AUTHOR("sugar@NNLG"); //驅動的作者
MODULE_DESCRIPTION("Dc_Motor of driver"); //驅動的描述
MODULE_LICENSE("GPL"); //遵循的協議
?
3.1.2.2 光照強度調控
????????光照強度的調控中,采用步進電機對超過閾值的光照強度進行控制。VDD_5V為電路供電,C58和C59組成濾波電路,穩定電源電壓。ULN2003A是達林頓管陣列,起電流放大作用。微控制器通過XEINT16_B、XEINT17_B、XEINT18_B、XEINT19_B引腳輸出控制信號,輸入到 ULN2003A 的對應引腳,經放大后從其1C-7C引腳輸出,通過BA-BD線路驅動步進電機轉動。步進電機可連接如百葉窗等裝置,通過轉動改變百葉窗葉片角度或位置,進而控制進入室內或檢測區域的光線量,達到調節光照強度的目的。
直流電機驅動代碼如下所示:
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_ALERT */
#include <linux/init.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <linux/wait.h>
#include <mach/irqs.h>
#include <linux/gpio.h>
#include <asm/gpio.h>
#include <asm/delay.h>
#include <linux/clk.h>
#include <mach/gpio.h>
#include <mach/soc.h>
#include <mach/platform.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio.h>
#define DEVICE_NAME "stepmotor" //設備名字
#define BUF_SIZE 1024 //Buf的大小static char tmpbuf[BUF_SIZE];
static unsigned int StepMajor=0;
static unsigned int StepMinor=0;
static struct cdev *step_cdev;
static dev_t dev;
struct class *my_class;
//結構體步進電機
struct stepmotor_des {int io_port;char *name;
} ;
//步進電機的管腳和名字
struct stepmotor_des step_motor[] = {??????//定義管腳和名字,需要查看原理圖修改
};
#define SIZE ARRAY_SIZE(step_motor) //大小
//字符設備打開
static int step_chardev_open(struct inode *inode,struct file *file)
{// 4 9 3 5int i;//管腳申請for(i=0;i<SIZE;i++){gpio_request( step_motor[i].io_port,step_motor[i].name );gpio_direction_output( step_motor[i].io_port,0 );}printk("open major=%d, minor=%d\n", imajor(inode), iminor(inode));return 0;
}
//字符設備釋放
static int step_chardev_release(struct inode *inode,struct file *file)
{int i;for(i=0;i<SIZE;i++){gpio_free(step_motor[i].io_port);}//printk("close major=%d, minor=%d\n", imajor(inode), iminor(inode));return 0;
}
//字符設備讀值
static ssize_t step_chardev_read(struct file *file,char __user *buf,size_t const count,loff_t *offset)
{if(count < BUF_SIZE){if(copy_to_user(buf,tmpbuf,count)){printk("copy to user fail \n");return -EFAULT;}}else{printk("read size must be less than %d\n", BUF_SIZE);return -EINVAL;}*offset += count;return count;
}
//字符設備寫值
static ssize_t step_chardev_write(struct file *file, const char __user *buf,size_t const count,loff_t *offset)
{if(count < BUF_SIZE){if(copy_from_user(tmpbuf,buf,count)){printk("copy from user fail \n");return -EFAULT;}}else{printk("size must be less than %d\n", BUF_SIZE);return -EINVAL;}*offset += count;return count;
}
//字符設備ied的正傳
void step_motor_forward(int speed)
{gpio_direction_output( step_moter[0].io_port,1);gpio_direction_output( step_moter[1].io_port,0);gpio_direction_output( step_moter[2].io_port,0);gpio_direction_output( step_moter[3].io_port,0);mdelay(speed);gpio_direction_output( step_moter[0].io_port,0);gpio_direction_output( step_moter[1].io_port,1);mdelay(speed);gpio_direction_output(step_moter[1].io_port,0);gpio_direction_output( step_moter[2].io_port,1);mdelay(speed);gpio_direction_output(step_moter[2].io_port,0);gpio_direction_output( step_moter[3].io_port,1);mdelay(speed);
}
字符設備
void step_motor_backward(int speed)
{gpio_direction_output( step_motor[0].io_port,1);gpio_direction_output( step_motor[1].io_port,0);gpio_direction_output( step_motor[2].io_port,0);gpio_direction_output( step_motor[3].io_port,0);mdelay(speed);gpio_direction_output( step_motor[0].io_port,1);gpio_direction_output( step_motor[3].io_port,1);mdelay(speed);gpio_direction_output( step_motor[3].io_port,0);gpio_direction_output( step_motor[2].io_port,1);mdelay(speed);gpio_direction_output( step_motor[2].io_port,0);gpio_direction_output( step_motor[1].io_port,1);mdelay(speed);
}//字符設備控制函數
static long step_chardev_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{printk("test-ioctl: param %u %lu\n", cmd, arg);switch(cmd){case 1:{step_motor_forward(arg);break;}case 2:{step_motor_backward(arg);break;}case 3:{step_motor_backward(arg);break;}default:printk(" Unkown key");break;}return 0;
}
文件操作集
static struct file_operations chardev_fops={.owner = THIS_MODULE,.open = step_chardev_open,.release = step_chardev_release,.read = step_chardev_read,.write = step_chardev_write,.unlocked_ioctl = step_chardev_ioctl,//.ioctl = step_chardev_ioctl,
};//驅動的初始化函數--->從內核中申請資源(內核、中斷、設備號、鎖....)
static int __init step_motor_init(void)
{ int result;if(StepMajor){dev=MKDEV(StepMajor,StepMinor);result=register_chrdev_region(dev,1,DEVICE_NAME);} else {result=register_chrdev_region(dev,1,DEVICE_NAME);StepMajor=MAJOR(dev);dev=MKDEV(StepMajor,StepMinor);}if(result<0){printk(KERN_WARNING"LED: cannot get major %d \n",StepMajor);return result;}step_cdev=cdev_alloc();cdev_init(step_cdev,&chardev_fops);step_cdev->owner=THIS_MODULE;result=cdev_add(step_cdev,dev,1);if(result)printk("<1>Error %d while register led device!\n",result);my_class = class_create(THIS_MODULE,DEVICE_NAME);if(IS_ERR(my_class)){printk("Failed to create class \n");return -1;}device_create(my_class,NULL,dev,NULL,DEVICE_NAME); return 0;
}
//驅動退出函數 --->將申請的資源還給內核
static void __exit step_motor_exit(void)
{unregister_chrdev_region(MKDEV(StepMajor,StepMinor),1);cdev_del(step_cdev); device_destroy(my_class,MKDEV(StepMajor,StepMinor));class_destroy(my_class);
}module_init(step_motor_init); //驅動的入口函數會調用一個用戶的初始化函數
module_exit(step_motor_exit); //驅動的出口函數會調用一個用戶的退出函數
//驅動的描述信息: #modinfo *.ko , 驅動的描述信息并不是必需的。
MODULE_AUTHOR("SUGAR@NNLG"); //驅動的作者
MODULE_DESCRIPTION("Step_Motor of driver"); //驅動的描述
MODULE_LICENSE("GPL"); //遵循的協議
?
3.1.2.3 負載調控
????????負載調控中,在環境變量超過閾值時,采用繼電器吸合,改變電路連接狀態,切斷負載的供電。在該電路中,VDD_5V為電源,C64和C65起到濾波作用,穩定電源電壓。當環境變量(如溫度、煙霧濃度等 )超過閾值,微控制器通過 XEINT23_B引腳輸出信號,經R107限流后輸入到三極管Q1(8050)的基極,使Q1導通。此時繼電器U22(HK4100)的線圈得電,繼電器吸合,改變電路連接狀態,切斷負載(通過USBJACK連接的設備)的供電。D17(1N4004)為續流二極管,防止繼電器線圈斷電時產生的反向電動勢損壞元件。D18(LED-B)可用于指示繼電器工作狀態。
3.1.3 狀態指示與報警監測模塊
3.1.3.1 發光小燈狀態指示
????????發光小燈用于指示溫度、濕度、光照強度和煙霧濃度是否超過閾值,對應燈亮起,表示該參數值超過閾值。
????????溫度指示燈在ADC模塊中,通過ADC通道(如XadcAIN1、XadcAIN2、XadcAIN3)檢測溫度傳感器信號,并通過GPIO控制LED;濕度指示燈在ADC模塊中,與DHT11傳感器相關,通過XEINT24_B或XEINT26等GPIO控制LED;光照強度指示燈在 LIGHT SENSOR部分,通過XadcAIN1或 XadcAIN2檢測光照,LED連接到VDD_IO 或 VDD18V;煙霧濃度指示燈在MQ-2模塊或通過ADC檢測煙霧傳感器信號,LED控制信號可連接到XEINT27或XEINT29。
發光指示燈驅動代碼如下所示:
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include "led_cmd.h"
#include <linux/io.h>#define DEVICE_NAME "led4" //設備名字
struct resource *myLedRes;
unsigned int led_gpio[4];
//led_open 函數
static int led_open(struct inode *inode, struct file *filp)
{printk(DEVICE_NAME ":open!!!\n");return 0;
}
//LED 控制函數
static long gec6818_leds_ioctl(struct file *filp, unsigned int cmd,unsigned long arg)
{#if 1printk("led_num = %d \n", LED_NUM);if(_IOC_TYPE(cmd) != LED_MAGIC) return - EINVAL;if(_IOC_NR(cmd) > LED_MAX_NR) return - EINVAL; gpio_set_value(led_gpio[_IOC_NR(cmd)], arg);printk(DEVICE_NAME": %d %lu\n", _IOC_NR(cmd), arg);#endif//printk("xxxx %lu, %d xxxxx \n", cmd, arg);return 0;
}
//文件操作集
static struct file_operations led_fops = {.owner = THIS_MODULE,.open = led_open,.unlocked_ioctl = gec6818_leds_ioctl,
};
//雜項設備
static struct miscdevice misc = {.minor = MISC_DYNAMIC_MINOR, //雜項設備主設備號10 ,自動分配次設備號.name = DEVICE_NAME, //生成設備文件的名字.fops = &led_fops,
};
//device 和 driver 匹配成功后,會自動調用該函數
//*dev -->與該platform driver匹配的platform device
static int __devinit gec6818_led_probe(struct platform_device *pdev)
{int i, ret;for(i=0; i< 4; i++){myLedRes = platform_get_resource(pdev, IORESOURCE_MEM,i);if(myLedRes == NULL){printk("<0>""Error while get the myLedRes %i\n",i);return -EBUSY;}else{led_gpio[i] = myLedRes->start;ret = gpio_request(led_gpio[i],"led_gpio");if(ret <0 ){printk("fail to request led gpio \n");return ret;}gpio_direction_output(led_gpio[i], 0);}}ret = misc_register(&misc); //注冊雜項設備pr_err(DEVICE_NAME"\tinitialized\n");return ret;
}
static int __devexit gec6818_led_remove(struct platform_device *dev)
{int i;//釋放管腳for (i = 0; i < LED_NUM; i++){gpio_free(led_gpio[i]);}misc_deregister(&misc);return 0;
}
static struct platform_driver gec6818_led_driver = {.probe = gec6818_led_probe,.remove = __devexit_p(gec6818_led_remove),.driver = {.owner = THIS_MODULE,.name = "led4_driver", //buttons_driver必須和設備的名字一致。},
};
//驅動的初始化函數--->從內核中申請資源
static int __init led_dev_init(void)
{int ret;ret = platform_driver_register(&gec6818_led_driver);return ret;
}
//驅動退出函數 --->將申請的資源還給內核
static void __exit led_dev_exit(void)
{platform_driver_unregister(&gec6818_led_driver);
}
module_init(led_dev_init); //驅動的入口函數會調用一個用戶的初始化函數
module_exit(led_dev_exit); //驅動的出口函數會調用一個用戶的退出函數
//驅動的描述信息: #modinfo *.ko , 驅動的描述信息并不是必需的。
MODULE_AUTHOR("sugar@NNLG"); //驅動的作者
MODULE_DESCRIPTION("led of driver"); //驅動的描述
MODULE_LICENSE("GPL"); //遵循的協議
MODULE_VERSION("v1.0");
編譯過程省略。
3.1.3.1 報警監測
????????報警檢測中,采用蜂鳴器,通過PWN脈沖寬度調制技術實現報警功能。VDD_5V為蜂鳴器Buzzer1提供工作電源。微控制器輸出的PWM信號通過PWM2引腳,經電阻R117(1KΩ)限流后輸入到三極管Q3(型號9014)的基極。當PWM信號為高電平時,三極管Q3導通,使得電源電壓能夠施加到蜂鳴器兩端,蜂鳴器得電發聲;當PWM信號為低電平時,三極管Q3截止,蜂鳴器失電停止發聲 。通過改變PWM信號的頻率和占空比,可控制蜂鳴器發出不同頻率和響度的聲音,從而實現不同模式的報警功能。
蜂鳴器驅動代碼如下所示:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/miscdevice.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <cfg_type.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <mach/platform.h>
#include <mach/devices.h>
#include <mach/soc.h>
#include "led_cmd.h"
#define DEVICE_NAME "pwm"
#define NS_IN_1HZ (1000000000UL)
#define BUZZER_PWM_ID 2
#define BUZZER_PMW_GPIO (????)static struct pwm_device *pwm2buzzer;
static struct semaphore lock;
static void pwm_set_freq(unsigned long freq) {int period_ns = NS_IN_1HZ / freq;pwm_config(pwm2buzzer, period_ns / 2, period_ns);pwm_enable(pwm2buzzer);
}
static void pwm_stop(void) {pwm_config(pwm2buzzer, 0, NS_IN_1HZ / 100);pwm_disable(pwm2buzzer);
}
static int gec6818_pwm_open(struct inode *inode, struct file *file) {if (!down_trylock(&lock))return 0;elsereturn -EBUSY;
}
static int gec6818_pwm_close(struct inode *inode, struct file *file) {up(&lock);return 0;
}
static long gec6818_pwm_ioctl(struct file *filep, unsigned int cmd,unsigned long arg)
{//if(_IOC_TYPE(cmd) != PWM_MAGIC) return - EINVAL;//if(_IOC_NR(cmd) > PWM_MAX_NR) return - EINVAL; switch ( ????) { //根據命令序列號控制蜂鳴器的狀態case BUZZER_IOCTL_SET_FREQ:if (arg == 0)return -EINVAL;pwm_set_freq(arg);break;case BUZZER_IOCTL_STOP:default:pwm_stop();break;}return 0;
}
static struct file_operations gec6818_pwm_ops = {.owner = THIS_MODULE,.open = gec6818_pwm_open,.release = gec6818_pwm_close, .unlocked_ioctl = gec6818_pwm_ioctl,
};
static struct miscdevice gec6818_misc_dev = {.minor = MISC_DYNAMIC_MINOR,.name = DEVICE_NAME,.fops = &gec6818_pwm_ops,
};
static int __init gec6818_pwm_dev_init(void) {int ret;ret = gpio_request(BUZZER_PMW_GPIO, DEVICE_NAME);if (ret) {printk("request GPIO %d for pwm failed\n", BUZZER_PMW_GPIO);return ret;}gpio_direction_output(BUZZER_PMW_GPIO, 0);pwm2buzzer = pwm_request(BUZZER_PMW_ID, DEVICE_NAME);if (IS_ERR(pwm2buzzer)) {printk("request pwm %d for %s failed\n", BUZZER_PWM_ID, DEVICE_NAME);return -ENODEV;}pwm_stop();gpio_free(BUZZER_PMW_GPIO);//init_MUTEX(&lock);sema_init(&lock,1); //ret = misc_register(&gec6818_misc_dev);printk(DEVICE_NAME "\tinitialized\n");return ret;
}
static void __exit gec6818_pwm_dev_exit(void) {pwm_stop();misc_deregister(&gec6818_misc_dev);
}
module_init(gec6818_pwm_dev_init);
module_exit(gec6818_pwm_dev_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("GEC Inc.");
MODULE_DESCRIPTION("S5PV6818 PWM Driver");
?四、QT頁面設計
4.1?新建項目工程
1、點擊菜單:File→New File or Project→Qt→Qt Desinger Form Class→Choose。
2、關聯一個UI設計文件:Dialog with Buttons Bottom→Next。
3、在Qt項目中選擇類名:在Class name中輸入類名→Next。
4、雙擊.ui結尾的文件,進入界面設計編輯器,通過布局管理、屬性編輯、信號與槽連接的步驟實現進行多個UI界面的設計。
4.2 界面跳轉
????????Qt界面跳轉可通過多種方式實現:
????????多窗口切換:創建獨立窗口實例,通過show()和hide()方法控制顯示/隱藏(如主窗口與對話框切換),支持窗口間傳參和狀態保持。
???????QStackedWidget:在同一窗口內管理多個頁面,通過setCurrentIndex()按索引切換,適合向導或多步驟界面,輕量級且無需頻繁創建/銷毀窗口。
????????QTabWidget:以選項卡形式組織頁面,用戶點擊選項卡標簽切換,界面直觀,適合分類展示內容。
這些方式靈活適配不同場景,通過合理選擇可實現流暢的用戶界面導航。
????????在本次環境系統設計中,通過loginDlg類的on_loginBtn_clicked槽函數中實現界面跳轉。當用戶點擊登錄按鈕,若用戶名輸入框內容(去除首尾空格后)為“star”且密碼輸入框內容為“123456”,則創建Led窗口實例,設置其固定大小為 800×480,顯示該窗口并關閉當前登錄窗口;若用戶名或密碼錯誤,彈出警告消息框提示錯誤,清空用戶名和密碼輸入框內容,并將光標移至用戶名輸入框,具體代碼參數設置。
4.3 信號與槽的關聯
????????在Qt中,信號與槽是對象間通信的核心機制,信號由對象狀態改變時自動發出(如按鈕點擊),槽是接收信號的函數(如自定義處理函數或 Lambda 表達式)。通過QObject::connect()函數可手動關聯信號與槽,格式為connect(信號發送者,&發送者類::信號,信號接收者,&接收者類::槽),也可通過Qt Designer實現自動關聯(槽名需遵循on_對象名_信號名()規則。信號與槽的參數類型需匹配,支持參數過濾(槽可只接收部分信號參數),這種機制實現了組件間的松耦合,使代碼結構清晰且易于維護。如下圖3-37,在登陸界面的ui文件下對“告警值:”使用go to slot,能夠跳轉到對應的槽函數,快速關聯信號與槽,實現組件的交互邏輯。
4.4 驅動程序的調用
????????在Qt中通過“pen()”函數調用驅動程序:首先需要包含對應的“<fcntl.h>”和“<unistd.h>”頭文件,然后使用“open(‘/dev/設備名’, O_RDWR)”以讀寫模式打開設備文件并獲取文件描述符(若返回值小于0則打開失敗),接著通過“read()”、“write()”或“ioctl()”函數進行數據讀寫或設備控制,操作完成后使用“close()”關閉文件描述符,同時需注意權限問題、進行錯誤處理,復雜場景下可將操作放入獨立線程避免阻塞UI。
????????例如對直流電機驅動程序的調用,首先通過open函數以讀寫模式(O_RDWR)打開直流電機設備文件/dev/dc_motor ,嘗試獲取對直流電機驅動程序的訪問權限。文件描述符fd用于后續對設備的操作。當open函數返回的fd小于 0 ,即設備文件打開失敗時,使用perror打印錯誤信息,然后通過exit(1)終止程序。接著,使用ioctl函數對已打開的直流電機設備進行控制。通過傳入不同的flag值(4 代表正轉、1 代表反轉、0 代表停止 ),向驅動程序傳遞控制指令。最后,使用close函數關閉之前打開的設備文件描述符fd,釋放系統資源。