#include <linux/err.h>#include <linux/gpio.h> #include <linux/of_gpio.h> #include <linux/gpio/consumer.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/property.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/types.h> #include <linux/delay.h> #include <linux/device.h> #include <asm/uaccess.h> static int dht11_major = 0; static int dht11_gpio=80; static struct class *dht11_class;//类 static struct device *dht11_dev;//设备 static const char* dht11_name = "dht11"; typedef struct DHT11_SENSOR_DATA { u16 temp;//温度 u16 hum;//湿度 }dht11_data; #define DHT11_DQ_High gpio_direction_output(dht11_gpio, 1) #define DHT11_DQ_Low gpio_direction_output(dht11_gpio, 0) #define DHT11_IO_IN gpio_direction_input(dht11_gpio) #define delay_us(x) udelay(x) #define delay_ms(x) msleep(x) static u8 DHT11_Read_DQ(void) { DHT11_IO_IN; return gpio_get_value(dht11_gpio); } //复位DHT11 static void DHT11_Rst(void) { DHT11_DQ_Low; msleep (20); //拉低至少18ms DHT11_DQ_High; //DQ=1 delay_us (30); //主机拉高20~40us } //等待DHT11的回应 //返回1:未检测到DHT11的存在 //返回0:存在 static u8 DHT11_Check(void) { u8 retry=0;//定义临时变量 DHT11_IO_IN;//SET INPUT while ((DHT11_Read_DQ()==1)&&retry<100)//DHT11会拉低40~80us { retry++; delay_us(1); }; if(retry>=100)return 1; else retry=0; while ((DHT11_Read_DQ()==0)&&retry<100)//DHT11拉低后会再次拉高40~80us { retry++; delay_us(1); }; if(retry>=100)return 1; return 0; } //从DHT11读取一个位 //返回值:1/0 static u8 DHT11_Read_Bit(void) { u8 retry=0; while((DHT11_Read_DQ()==1)&&retry<100)//等待变为低电平 { retry++; delay_us(1); } retry=0; while((DHT11_Read_DQ()==0)&&retry<100)//等待变高电平 { retry++; delay_us(1); } delay_us(40);//等待40us if(DHT11_Read_DQ()==1) return 1; else return 0; } //从DHT11读取一个字节 //返回值:读到的数据 static u8 DHT11_Read_Byte(void) { u8 i,dat; dat=0; for (i=0;i<8;i++) { dat<<=1; dat|=DHT11_Read_Bit(); } return dat; } //从DHT11读取一次数据 //temp:温度值(范围:0~50°) //humi:湿度值(范围:20%~90%) //返回值:0,正常;1,读取失败 static u8 DHT11_Read_Data(u16 *temp,u16 *humi) { u8 buf[5]; u8 i; DHT11_Rst(); if(DHT11_Check()==0) { for(i=0;i<5;i++)//读取40位数据 { buf[i]=DHT11_Read_Byte(); } if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4]) { *humi=buf[0]<<8|buf[1]; *temp=buf[2]<<8|buf[3]; printk("buf=%d,%d,%d,%d,%d\n",buf[0],buf[1],buf[2],buf[3],buf[4]); } }else return 1; return 0; } //初始化DHT11的IO口 DQ 同时检测DHT11的存在 //返回1:不存在 //返回0:存在 static void DHT11_Init(void) { DHT11_Rst(); //复位DHT11 DHT11_Check();//等待DHT11的回应 } int DHT11_open(struct inode *inode, struct file *flips) { printk("--------------%s--------------\n",__FUNCTION__); return 0; } static ssize_t DHT11_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { printk("--------------%s--------------\n",__FUNCTION__); dht11_data Last_dht11_data; if(DHT11_Read_Data(&Last_dht11_data.temp,&Last_dht11_data.hum) == 0)//读取温湿度值 { if (raw_copy_to_user(buf,&Last_dht11_data,sizeof(Last_dht11_data)) ) { return EFAULT ; } } } static int DHT11_close(struct inode *inode, struct file *flip) { printk("--------------%s--------------\n",__FUNCTION__); return 0; } static struct file_operations dht11_fops = { .owner = THIS_MODULE, .read = DHT11_read, .open = DHT11_open, .release = DHT11_close, }; static const struct of_device_id of_dht11_match[] = { { .compatible = "dht11", }, {}, }; MODULE_DEVICE_TABLE(of, of_dht11_match); static int dht11_probe(struct platform_device *pdev) { int ret; enum of_gpio_flags flag;//(flag == OF_GPIO_ACTIVE_LOW) ? printk("-------%s-------------\n", __FUNCTION__); struct device_node *dht11_gpio_node = pdev->dev.of_node; //dht11_gpio = of_get_named_gpio_flags(dht11_gpio_node->child, "gpios", 0, &flag); if (!gpio_is_valid(dht11_gpio)) { printk("dht11-gpio: %d is invalid\n", dht11_gpio); return -ENODEV; } else printk("dht11-gpio: %d is valid!\n", dht11_gpio); if (gpio_request(dht11_gpio, "dht11-gpio")) { printk("gpio %d request failed!\n", dht11_gpio); gpio_free(dht11_gpio); return -ENODEV; } else printk("gpio %d request success!\n", dht11_gpio); //能够读到配置信息之后就可以开始创建设备节点 dht11_major = register_chrdev(0, "dht11",&dht11_fops); if(dht11_major <0) { printk(KERN_ERR "reg error!\n"); goto err_0; } else printk("dht11_major =%d\n",dht11_major); dht11_class = class_create(THIS_MODULE,"dht11_class");//creat class if( IS_ERR(dht11_class)) { printk(KERN_ERR "fail create class\n"); ret = PTR_ERR(dht11_class); goto err_1; } //creat dht11_dev--->>/dev/dht11_dev dht11_dev = device_create(dht11_class, NULL,MKDEV(dht11_major,0), NULL, dht11_name); if(IS_ERR(dht11_dev)) { printk(KERN_ERR "fail create device!\n"); ret = PTR_ERR(dht11_dev); goto err_2; } //init dht11 DHT11_Init(); printk("dht11 Initing...\n"); return 0; err_2: device_destroy(dht11_class,MKDEV(dht11_major,0)); err_1: class_destroy(dht11_class); err_0: unregister_chrdev(dht11_major,dht11_name); return -1; } static int dht11_remove(struct platform_device *pdev) { printk("-------%s-------------\n", __FUNCTION__); device_destroy(dht11_class,MKDEV(dht11_major,0)); class_destroy(dht11_class); unregister_chrdev(dht11_major,dht11_name); return 0; } static void dht11_shutdown(struct platform_device *pdev) { printk("-------%s-------------\n", __FUNCTION__); } static struct platform_driver dht11_driver = { .probe = dht11_probe, .remove = dht11_remove, .shutdown = dht11_shutdown, .driver = { .name = "dht11_driver", .of_match_table = of_dht11_match, }, }; module_platform_driver(dht11_driver);
复制代码首先是GPIO引脚确定的方式,我将朋友的通过设备树锁定的方式改为在驱动代码里面动态分配,通过宏定义进行指定,不在设备树里面锁定任何GPIO;然后是里面的printk打印会出现在串口终端中,不管串口终端有没有在执行别的进程;对于内核源码中的udelay() msleep() gpio_direction_output() gpio_get_value()等函数则与ARM的单片机用法基本一样,可以完美兼容,唯一的不同点就是应用层代码对/dev设备进行wirte/read的时机和方式有要求。
然后是makefile代码,这个直接套用模板即可,内核源码目录用回之前的/mnt/imx8maaxksrc:
编译完成之后,需要使用
insmod dht11_drv.ko
复制代码
可以看到,DHT11驱动源码中的dht11_probe函数加入的printk打印在插入的时候就已经生效了。
然后是单独对DHT11器件进行简单读写的main函数:
这里我注意到一点,对于/dev/dht11设备,读取间隔是1秒1次,不管是在主线程(main())中还是在新开辟的线程中,都必须要有这个间隔,不然读取会失败,一般对于DHT11这种轮询器件而言,都是有必要开启一个单独线程进行读取的,比较Linux中线程资源非常充足,而DHT11对于实时性要求又比较高。
然后我这边套用一套以前自己写的HTTP网页服务器代码,非常简单,是通过TCP方式进行GET/POST操作,简单得不能再简单那种:
#include <stdio.h>#include <stdlib.h> #include <stdbool.h> #include <stdint.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <time.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <pthread.h> #include <linux/input.h> #include <sys/mman.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/stat.h> #include <arpa/inet.h> #include <netinet/in.h> #define RUN 1 #define STOP 0 pthread_t id1,id2; int fd_socket_server,fd_socket_conn; pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; char sendbuf[420],recvbuf[1000]; int thread_flag=0,status=STOP; #define TEMP_PATH "/sys/class/thermal/thermal_zone0/temp" #define DHT11_PATH "/dev/dht11" typedef struct { short temp; short humi; }dht11_data; dht11_data dht11_data1; int fd_dht11; float dht11_temp,dht11_humi; void *Thread_CPU_Temp(void *arg) { void *ret; int fd,retval; float temp=0; char buf[30]; while(1) { read( fd_dht11 , &dht11_data1 , sizeof(dht11_data1)); if(dht11_data1.temp != 0xffff) { dht11_temp = (dht11_data1.temp>>8)*1.0 + (dht11_data1.temp&0xff)*0.1; dht11_humi = (dht11_data1.humi>>8)*1.0 + (dht11_data1.humi&0xff)*0.1; } sleep(1); } while(1) { // pthread_mutex_lock(&mut); // while(!status) // { // pthread_cond_wait(&cond, &mut); // } // pthread_mutex_unlock(&mut); fd = open(TEMP_PATH, O_RDONLY); if (fd<0) { fprintf(stderr,"无法打开thermal_zone0/temp文件\n"); return ret; } if (read(fd,buf,30)<0) { fprintf(stderr,"读取温度数据失败\n"); return ret; } temp = atoi(buf)/1000.0; printf("CPU温度��?.1f\n",temp); buf[0]=(int)temp/10%10+'0'; buf[1]=(int)temp%10+'0'; buf[2]='.'; buf[3]=(int)(temp*10)%10+'0'; buf[4]=0; send(fd_socket_conn,buf,5,0); sleep(1); } } float Get_CPU_Temp() { float temp; char buf_cpu_temp_read[30]; int fd = open(TEMP_PATH, O_RDONLY); if (fd < 0) { printf("无法打开thermal_zone0/temp文件\n"); return 0; } if (read(fd,buf_cpu_temp_read,30) < 0) { printf("读取温度数据失败\n"); return 0; } return atoi(buf_cpu_temp_read)/1000.0; } void thread_resume() { if (status == STOP) { pthread_mutex_lock(&mut); status = RUN; pthread_cond_signal(&cond); printf("CPU温度检测线程恢复运行\n"); pthread_mutex_unlock(&mut); } else { printf("CPU温度检测线程一直在运行\n"); } } void thread_pause() { if (status == RUN) { pthread_mutex_lock(&mut); status = STOP; printf("CPU温度检测线程暂停(挂起)\n"); pthread_mutex_unlock(&mut); } else { printf("CPU温度检测线程一直在暂停\n"); } } char *buf_html; char a[90]={"HTTP/1.1 200 OK\nContent-Length:00075\nServer: Hatschi Server 1.0\nContent-Type: text/html\n\n"}; int flag_get=0,flag_post=0,flag_keep_alive=0,flag_post_once=1,len_buf_html=0; int File_Read_Length(char* filename) { FILE *f=fopen(filename,"rb"); if (!f) { printf("Can't open bin file!\n"); return 0; } fseek(f,0,SEEK_END); int len = ftell(f); fseek(f,0,SEEK_SET); fclose(f); return len; } void File_Read_All_Text(char* filename) { FILE *f = fopen(filename,"r"); fseek(f,0,SEEK_END); long len = ftell(f); buf_html=(char*)malloc(len+1); rewind(f); fread(buf_html,sizeof(char),len,f); buf_html[len] ='\n'; buf_html[len+1] ='\0'; } void *Thread_Send(void *arg) { int len,i; float cpu_temp; while(1) { if(flag_keep_alive&&flag_post_once) { flag_post_once=0; if(flag_get) printf("收到客户端GET请求,服务器可进行一次POST操作\n"); else if(flag_post) printf("收到客户端POST回应,服务器可进行一次POST操作\n"); File_Read_All_Text("/home/proj/1.html"); len_buf_html=File_Read_Length("/home/proj/1.html"); printf("\n\nHTML文件长度:len_buf_html=%d\n\n",len_buf_html); cpu_temp=Get_CPU_Temp(); printf("\n\nCPU温度:%f %.1f %.1f\n\n",cpu_temp,dht11_temp,dht11_humi); for(i=0;i<len_buf_html;i++) if(buf_html[i]=='H'&&buf_html[i+1]=='U'&&buf_html[i+2]=='M'&&buf_html[i+3]=='1') printf("\n\n----- i=%d -----\n\n",i); buf_html[156]=(int)(cpu_temp)/10%10+'0'; buf_html[157]=(int)(cpu_temp)%10+'0'; buf_html[159]=(int)(cpu_temp*10)%10+'0'; buf_html[180]=(int)(dht11_temp)/10%10+'0'; buf_html[181]=(int)(dht11_temp)%10+'0'; buf_html[183]=(int)(dht11_temp*10)%10+'0'; buf_html[204]=(int)(dht11_humi)/10%10+'0'; buf_html[205]=(int)(dht11_humi)%10+'0'; buf_html[207]=(int)(dht11_humi*10)%10+'0'; a[31]=len_buf_html/10000%10+'0'; a[32]=len_buf_html/1000%10+'0'; a[33]=len_buf_html/100%10+'0'; a[34]=len_buf_html/10%10+'0'; a[35]=len_buf_html%10+'0'; for(len=0;a[len]!='\0';len++) putchar(a[len]); putchar('\n'); send(fd_socket_conn,a,len,0); for(len=0;buf_html[len]!='\0';len++); send(fd_socket_conn,buf_html,len,0); close(fd_socket_conn); } } } int Char2Int(unsigned char s[]) { int result; if('0'<=s[0]&&s[0]<='9'&&'0'<=s[1]&&s[1]<='9'&&'0'<=s[2]&&s[2]<='9'&& '0'<=s[3]&&s[3]<='9'&&'0'<=s[4]&&s[4]<='9') result=(s[0]-'0')*10000+(s[1]-'0')*1000+(s[2]-'0')*100+(s[3]-'0')*10+(s[4]-'0'); else if('0'<=s[0]&&s[0]<='9'&&'0'<=s[1]&&s[1]<='9'&&'0'<=s[2]&&s[2]<='9'&& '0'<=s[3]&&s[3]<='9'&&s[4]=='\0') result=(s[0]-'0')*1000+(s[1]-'0')*100+(s[2]-'0')*10+(s[3]-'0'); return result; } void LED_Control(int n) { FILE *f=fopen("/sys/class/leds/status_usr0/brightness","w"); if (f==NULL) printf("LED操作文件打开失败"); else if(n==1) fprintf(f,"%d",1); else if(n==0) fprintf(f,"%d",0); fclose(f); } int main(int argc,char *argv[]) { int ret,locate=0; fd_dht11=open(DHT11_PATH,O_RDONLY); pthread_create(&id2,NULL,Thread_CPU_Temp,NULL); socklen_t addrsize=sizeof(struct sockaddr); struct sockaddr_in sockaddr_in_server,sockaddr_in_conn; //初始化该结构体变��? bzero(&sockaddr_in_server,sizeof(sockaddr_in_server)); sockaddr_in_server.sin_family=AF_INET; sockaddr_in_server.sin_addr.s_addr=inet_addr(argv[1]); sockaddr_in_server.sin_port=htons(Char2Int((unsigned char*)argv[2])); fd_socket_server=socket(AF_INET,SOCK_STREAM,0); if(fd_socket_server==-1) { printf("套接字创��?初始化失��?\n"); return -1; } ret=bind(fd_socket_server,(struct sockaddr *)&sockaddr_in_server,addrsize); if(ret==-1) { printf("套接字绑定失��?\n"); return -1; } ret=listen(fd_socket_server,5); if(ret==-1) { printf("服务器监听失��?\n"); return -1; } int i,j; while(1) { fd_socket_conn=accept(fd_socket_server,(struct sockaddr *)&sockaddr_in_conn,&addrsize); if(fd_socket_conn==-1) { printf("服务器接听失��?\n"); return -1; } else if(fd_socket_conn>=0) { printf("已有客户端成功连接服务器!\n"); ret=pthread_create(&id1,NULL,Thread_Send,NULL); if(ret==0) printf("TCP发送阻塞线程创建成��?\n"); } bzero(recvbuf,100); recv(fd_socket_conn,recvbuf,1000,0); printf("------\n%s\n------\n",recvbuf); if(recvbuf[0]=='G'&&recvbuf[1]=='E'&&recvbuf[2]=='T') { printf("客户端浏览器发出GET请求\n"); flag_get=1; flag_post=0; flag_post_once=1; } if(recvbuf[0]=='P'&&recvbuf[1]=='O'&&recvbuf[2]=='S'&&recvbuf[3]=='T') { printf("客户端浏览器发出POST回应\n"); flag_get=0; flag_post=1; flag_post_once=1; } for(i=0;i<=1000;i++) { if(recvbuf[i]=='H'&&recvbuf[i+1]=='T'&&recvbuf[i+2]=='T'&&recvbuf[i+3]=='P'&& recvbuf[i+4]=='/') { printf("HTTP协议版本/类型��?"); for(j=i;j<=i+7;j++) putchar(recvbuf[j]); putchar('\n'); } if(recvbuf[i]=='H'&&recvbuf[i+1]=='o'&&recvbuf[i+2]=='s'&&recvbuf[i+3]=='t') { printf("主机地址及端口号��?"); for(j=i+6;j<=i+22;j++) putchar(recvbuf[j]); putchar('\n'); } if(recvbuf[i]=='C'&&recvbuf[i+1]=='o'&&recvbuf[i+2]=='n'&&recvbuf[i+3]=='n'&& recvbuf[i+4]=='e'&&recvbuf[i+5]=='c'&&recvbuf[i+6]=='t'&&recvbuf[i+7]=='i'&& recvbuf[i+8]=='o'&&recvbuf[i+9]=='n') { printf("连接方式��?"); for(j=i+12;j<=i+21;j++) putchar(recvbuf[j]); putchar('\n'); flag_keep_alive=1; } if(recvbuf[i]=='l'&&recvbuf[i+1]=='e'&&recvbuf[i+2]=='d'&&recvbuf[i+3]=='_'&& recvbuf[i+4]=='s'&&recvbuf[i+5]=='w'&&recvbuf[i+6]=='i'&&recvbuf[i+7]=='t'&& recvbuf[i+8]=='c'&&recvbuf[i+9]=='h'&&recvbuf[i+10]=='=') { printf("检测到LED控制指令:\n"); if(recvbuf[i+11]=='o'&&recvbuf[i+12]=='n') { printf("LED开\n"); LED_Control(1); } else if(recvbuf[i+11]=='o'&&recvbuf[i+12]=='f'&&recvbuf[i+13]=='f') { printf("LED关\n"); LED_Control(0); } } } } /* while(1) { fd_socket=socket(AF_INET,SOCK_STREAM,0); if(fd_socket==-1) { printf("套接字创��?初始化失��?\n"); return -1; } ret=connect(fd_socket,(struct sockaddr *)&sockaddr_in_settings,addrsize); if(ret==0) { printf("与服务器建立连接\n"); ret=pthread_create(&id1,NULL,Thread_Send,NULL); if(ret==0) printf("TCP发送阻塞线程被创建!\n"); break; } } while(1) { bzero(recvbuf,100); ret=recv(fd_socket,recvbuf,100,0); if(ret==0) { printf("与服务器失去连接\n"); ret=pthread_cancel(id1); if(ret==0) printf("TCP发送阻塞线程被取消\n"); break; } printf("服务器端发来信息��?s\n",recvbuf); } */ }
复制代码网页HTML源码也非常简单:
<html><head> <title>英蓓特MAAX IMX8网页实验</title> </head> <body> <p>MAAX IMX8网页服务器</p> <p>dontello1996</p> <p>方式:GET+POST</p> <p>CPU温度: . ℃</p> <p>环境温度: . ℃</p> <p>环境湿度: . %</p> <p>LED灯控制实验</p> <form method="POST"> <p><select name="led_switch"> <option value ="on">开</option> <option value ="off">关</option> </select></p> <input type="submit" value="提交"> </form> </body> </html>
复制代码设备树源码dts添加一个名为dht11的设备,重新编译设备树,并插入ko文件之后,会在/dev下生成dht11文件:
/{dht11 { compatible = "dht11"; pinctrl-names = "default"; };
复制代码
编译完成后,使用
./main 192.168.1.4 6666
复制代码每刷新一次网页,就会读取一组新的CPU温度和温湿度数据:


DHT11的驱动ko:

热门资料
全部回复 0