- #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
暂无评论,快来抢沙发吧