目录
0、设置ip
1、修改显示界面
2、 修改客户端
3、 修改服务器程序通信部分
4、 修改驱动处理程序
5、 重写驱动程序
6、 展示
0、设置ip
因为ifconfig命令要被淘汰了,所以我们改成使用ip命令设置ubuntu的ip
ip addr add 192.168.5.10/24 dev ens36
ip addr show
1、修改显示界面
之前共用一个get value现在给每个人一个单独的显示框
2、 修改客户端
#dht11elif cmd[2] == '0' and cmd[3] == '5':if cmd[4] == 'g':global_var.TEM=cmd[4]+cmd[5]global_var.HUM=cmd[6]+cmd[7]message = f"{global_var.TEM}°C {global_var.HUM}%"window['DHT11_O'].update(message)else:print("DHT11: message ERROR")
elif event == 'dht11':set_tx_buf('dht11', 'g')send_cmd(client_socket)
DHT11 测量温度的精度为± 2℃,检测范围为-20℃ -60℃。湿度的精度为± 5%RH,检测范围为 5%RH-95%RH,常用于对精度和实时性要求不高的温湿度测量场合。
所以温度要用三位数表示把低温也加上,湿度的话两位就够了
#dht11elif cmd[2] == '0' and cmd[3] == '5':if cmd[4] == 'g':global_var.TEM=cmd[5]+cmd[6]+cmd[7]global_var.HUM=cmd[8]+cmd[9]message = f"{global_var.TEM}°C {global_var.HUM}%"window['DHT11_O'].update(message)else:print("DHT11: message ERROR")
3、 修改服务器程序通信部分
printf("dht11!!!\n");if ('g' == cmd[4]){opt = dht11_handle(buf);tx_buffer = buf;}printf(">>>>>>%s\n",tx_buffer); if (send(acceptfd, tx_buffer, strlen(tx_buffer), 0) < 0){perror("send failed"); }break;
局部变量太多了东西展示都给driver_handele做
4、 修改驱动处理程序
/*
*author : xintianyu
*function : Handle dht11 Settings
*date : 2024-4-20
-----------------------
author date modify*/
int dht11_handle(char *data)
{/*传入参数后面要做通用处理使用空指针*/char *device = "/dev/cebss_dht11";unsigned char buf[2];int ret = NOERROR;static int fd;/* 打开文件 */fd = open(device, O_RDWR);if (fd == -1){printf("can not open file %s\n", device);return ERROR;}
read:if (read(fd, buf, 2) == 2){printf("get Humidity: %d, Temperature : %d\n", buf[0], buf[1]);sprintf(data,"@005g%d%d", buf[0], buf[1]);}else{sleep(5);printf("get dht11: -1\n");goto read;}close(fd);return ret;
}
5、 重写驱动程序
#include "asm-generic/errno-base.h"
#include "asm-generic/gpio.h"
#include "linux/jiffies.h"
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>struct gpio_desc{int gpio;int irq;char *name;int key;struct timer_list key_timer;
} ;static struct gpio_desc gpios[] = {{115, 0, "dht11", },
};/* 主设备号 */
static int major = 0;
static struct class *gpio_class;static u64 g_dht11_irq_time[84];
static int g_dht11_irq_cnt = 0;/* 环形缓冲区 */
#define BUF_LEN 128
static char g_keys[BUF_LEN];
static int r, w;struct fasync_struct *button_fasync;static irqreturn_t dht11_isr(int irq, void *dev_id);
static void parse_dht11_datas(void);#define NEXT_POS(x) ((x+1) % BUF_LEN)static int is_key_buf_empty(void)
{return (r == w);
}static int is_key_buf_full(void)
{return (r == NEXT_POS(w));
}static void put_key(char key)
{if (!is_key_buf_full()){g_keys[w] = key;w = NEXT_POS(w);}
}static char get_key(void)
{char key = 0;if (!is_key_buf_empty()){key = g_keys[r];r = NEXT_POS(r);}return key;
}static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);// static void key_timer_expire(struct timer_list *t)
static void key_timer_expire(unsigned long data)
{// 解析数据, 放入环形buffer, 唤醒APPparse_dht11_datas();
}/* 实现对应的open/read/write等函数,填入file_operations结构体 */
static ssize_t dht11_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{int err;char kern_buf[2];if (size != 2)return -EINVAL;g_dht11_irq_cnt = 0;/* 1. 发送18ms的低脉冲 */err = gpio_request(gpios[0].gpio, gpios[0].name);gpio_direction_output(gpios[0].gpio, 0);gpio_free(gpios[0].gpio);mdelay(18);gpio_direction_input(gpios[0].gpio); /* 引脚变为输入方向, 由上拉电阻拉为1 *//* 2. 注册中断 */err = request_irq(gpios[0].irq, dht11_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, gpios[0].name, &gpios[0]);mod_timer(&gpios[0].key_timer, jiffies + 10); /* 3. 休眠等待数据 */wait_event_interruptible(gpio_wait, !is_key_buf_empty());free_irq(gpios[0].irq, &gpios[0]);//printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);/* 设置DHT11 GPIO引脚的初始状态: output 1 */err = gpio_request(gpios[0].gpio, gpios[0].name);if (err){printk("%s %s %d, gpio_request err\n", __FILE__, __FUNCTION__, __LINE__);}gpio_direction_output(gpios[0].gpio, 1);gpio_free(gpios[0].gpio);/* 4. copy_to_user */kern_buf[0] = get_key();kern_buf[1] = get_key();printk("get val : 0x%x, 0x%x\n", kern_buf[0], kern_buf[1]);if ((kern_buf[0] == (char)-1) && (kern_buf[1] == (char)-1)){printk("get err val\n");return -EIO;}err = copy_to_user(buf, kern_buf, 2);return 2;
}static int dht11_release (struct inode *inode, struct file *filp)
{return 0;
}/* 定义自己的file_operations结构体 */
static struct file_operations dht11_drv = {.owner = THIS_MODULE,.read = dht11_read,.release = dht11_release,
};static void parse_dht11_datas(void)
{int i;u64 high_time;unsigned char data = 0;int bits = 0;unsigned char datas[5];int byte = 0;unsigned char crc;/* 数据个数: 可能是81、82、83、84 */if (g_dht11_irq_cnt < 81){/* 出错 */put_key(-1);put_key(-1);// 唤醒APPwake_up_interruptible(&gpio_wait);g_dht11_irq_cnt = 0;return;}// 解析数据for (i = g_dht11_irq_cnt - 80; i < g_dht11_irq_cnt; i+=2){high_time = g_dht11_irq_time[i] - g_dht11_irq_time[i-1];data <<= 1;if (high_time > 50000) /* data 1 */{data |= 1;}bits++;if (bits == 8){datas[byte] = data;data = 0;bits = 0;byte++;}}// 放入环形buffercrc = datas[0] + datas[1] + datas[2] + datas[3];if (crc == datas[4]){put_key(datas[0]);put_key(datas[2]);}else{put_key(-1);put_key(-1);}g_dht11_irq_cnt = 0;// 唤醒APPwake_up_interruptible(&gpio_wait);
}static irqreturn_t dht11_isr(int irq, void *dev_id)
{struct gpio_desc *gpio_desc = dev_id;u64 time;/* 1. 记录中断发生的时间 */time = ktime_get_ns();g_dht11_irq_time[g_dht11_irq_cnt] = time;/* 2. 累计次数 */g_dht11_irq_cnt++;/* 3. 次数足够: 解析数据, 放入环形buffer, 唤醒APP */if (g_dht11_irq_cnt == 84){del_timer(&gpio_desc->key_timer);parse_dht11_datas();}return IRQ_HANDLED;
}/* 在入口函数 */
static int __init dht11_init(void)
{int err;int i;int count = sizeof(gpios)/sizeof(gpios[0]);printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);for (i = 0; i < count; i++){ gpios[i].irq = gpio_to_irq(gpios[i].gpio);/* 设置DHT11 GPIO引脚的初始状态: output 1 */err = gpio_request(gpios[i].gpio, gpios[i].name);gpio_direction_output(gpios[i].gpio, 1);gpio_free(gpios[i].gpio);setup_timer(&gpios[i].key_timer, key_timer_expire, (unsigned long)&gpios[i]);//timer_setup(&gpios[i].key_timer, key_timer_expire, 0);//gpios[i].key_timer.expires = ~0;//add_timer(&gpios[i].key_timer);//err = request_irq(gpios[i].irq, dht11_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "100ask_gpio_key", &gpios[i]);}/* 注册file_operations */major = register_chrdev(0, "100ask_dht11", &dht11_drv); /* /dev/gpio_desc */gpio_class = class_create(THIS_MODULE, "100ask_dht11_class");if (IS_ERR(gpio_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "100ask_dht11");return PTR_ERR(gpio_class);}device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "mydht11"); /* /dev/mydht11 */return err;
}/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数*/
static void __exit dht11_exit(void)
{int i;int count = sizeof(gpios)/sizeof(gpios[0]);printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(gpio_class, MKDEV(major, 0));class_destroy(gpio_class);unregister_chrdev(major, "100ask_dht11");for (i = 0; i < count; i++){//free_irq(gpios[i].irq, &gpios[i]);//del_timer(&gpios[i].key_timer);}
}/* 7. 其他完善:提供设备信息,自动创建设备节点 */module_init(dht11_init);
module_exit(dht11_exit);MODULE_LICENSE("GPL");
韦东山老师基于中断的驱动程序准确度太低了,所以我们写个不基于中断的试试
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/uaccess.h> #define DHT11_GPIO 115 // 假设DHT11连接到GPIO 115
#define DHT11_DATA_LEN 8 // DHT11数据长度(包括温湿度值、校验和) static int dht11_gpio;
static u8 dht11_data[DHT11_DATA_LEN];static void dht11_set_gpio_output(void)
{ gpio_direction_output(dht11_gpio, 0);
} static void dht11_set_gpio_input(void)
{ gpio_direction_input(dht11_gpio);
} static int dht11_read_data(void)
{ int i, j; u8 last_state = 0; // 发送开始信号 dht11_set_gpio_output(); gpio_set_value(dht11_gpio, 0); udelay(18000); gpio_set_value(dht11_gpio, 1); udelay(20); dht11_set_gpio_input(); // 等待DHT11响应 while (gpio_get_value(dht11_gpio)) { udelay(1); if (j++ > 100) { return -1; // 超时 } } // 读取数据 for (i = 0; i < DHT11_DATA_LEN; i++) { dht11_data[i] = 0; for (j = 0; j < 8; j++) { while (!gpio_get_value(dht11_gpio)) { udelay(1); if (j++ > 100) { return -1; // 超时 } } udelay(40); if (!gpio_get_value(dht11_gpio)) { dht11_data[i] |= (1 << (7 - j)); } } } // 检查校验和 u8 sum = 0; for (i = 0; i < DHT11_DATA_LEN - 1; i++) { sum += dht11_data[i]; } if (sum != dht11_data[DHT11_DATA_LEN - 1]) { return -1; // 校验和错误 } return 0;
}
static int dht11_open(struct inode *inode, struct file *file)
{ return 0;
} static ssize_t dht11_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{ int ret; char data_str[32]; int temp_integral, temp_decimal; int humi_integral, humi_decimal; ret = dht11_read_data(); if (ret < 0) { return ret; } // 解析温度和湿度值 temp_integral = dht11_data[2]; temp_decimal = dht11_data[3]; humi_integral = dht11_data[0]; humi_decimal = dht11_data[1]; // 将数据转换为字符串格式 snprintf(data_str, sizeof(data_str), "Temp: %d.%d C, Humidity: %d.%d %%\n", temp_integral, temp_decimal, humi_integral, humi_decimal); // 将数据复制到用户空间 if (copy_to_user(buf, data_str, strlen(data_str))) { return -EFAULT; } return strlen(data_str);
}static int dht11_release(struct inode *inode, struct file *file)
{ return 0;
} static loff_t dht11_lseek(struct file *file, loff_t offset, int whence)
{ return -EINVAL; // 不支持lseek操作
}static const struct file_operations dht11_fops = { .owner = THIS_MODULE, .read = dht11_read, .open = dht11_open, .release = dht11_release, .llseek = dht11_lseek,
}; static int __init dht11_init(void)
{ int ret; // 请求GPIO资源 dht11_gpio = gpio_request(DHT11_GPIO, "DHT11"); if (dht11_gpio < 0) { printk(KERN_ERR "Failed to request GPIO %d\n", DHT11_GPIO); return -ENODEV; } // 导出GPIO到用户空间(可选) // gpio_export(DHT11_GPIO, false); // 注册字符设备 ret = register_chrdev(0, "dht11", &dht11_fops); if (ret < 0) { printk(KERN_ERR "Failed to register character device\n"); gpio_free(dht11_gpio); return ret; } // 创建设备节点 ret = device_create(class_create(THIS_MODULE, "dht11"), NULL, MKDEV(ret, 0), NULL, "cebss_dht11"); if (ret < 0) { printk(KERN_ERR "Failed to create device\n"); unregister_chrdev(ret, "dht11"); gpio_free(dht11_gpio); return ret; } printk(KERN_INFO "DHT11 driver loaded\n"); return 0;
} static void __exit dht11_exit(void)
{ device_destroy(class_destroy(dht11_class), MKDEV(major, 0)); unregister_chrdev(major, "dht11"); gpio_free(dht11_gpio); printk(KERN_INFO "DHT11 driver unloaded\n");
} module_init(dht11_init);
module_exit(dht11_exit);
MODULE_LICENSE("GPL");
不认识,因为注册的时候没用变量直接给返回值拿来用了
主设备号也没对上
static int __init dht11_init(void)
{ int ret; // 请求GPIO资源 dht11_gpio = gpio_request(DHT11_GPIO, "DHT11"); if (dht11_gpio < 0) { printk(KERN_ERR "Failed to request GPIO %d\n", DHT11_GPIO); return -ENODEV; } // 导出GPIO到用户空间(可选) // gpio_export(DHT11_GPIO, false); // 注册字符设备 major = register_chrdev(0, "dht11", &dht11_fops); if (major < 0) { printk(KERN_ERR "Failed to register character device\n"); gpio_free(dht11_gpio); return ret; } gpio_class = class_create(THIS_MODULE, "dht11");// 创建设备节点 ret = device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "cebss_dht11"); if (ret < 0) { printk(KERN_ERR "Failed to create device\n"); unregister_chrdev(major, "dht11"); gpio_free(dht11_gpio); return ret; } printk(KERN_INFO "DHT11 driver loaded\n"); return 0;
} static void __exit dht11_exit(void)
{ device_destroy(gpio_class, MKDEV(major, 0)); unregister_chrdev(major, "dht11"); gpio_free(dht11_gpio); printk(KERN_INFO "DHT11 driver unloaded\n");
}
/home/book/program/cebss/driver/01_driver/06_dht11/dht11_drv.c: In function ‘dht11_read_data’:
/home/book/program/cebss/driver/01_driver/06_dht11/dht11_drv.c:65:5: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
u8 sum = 0;
这个是经典问题,变量要在最开始定义
/home/book/program/cebss/driver/01_driver/06_dht11/dht11_drv.c: In function ‘dht11_init’:
/home/book/program/cebss/driver/01_driver/06_dht11/dht11_drv.c:151:9: warning: assignment makes integer from pointer without a cast [-Wint-conversion]
ret = device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "cebss_dht11");
这个函数的返回值是device结构体
WARNING: "__bad_udelay" [/home/book/program/cebss/driver/01_driver/06_dht11/dht11_drv.ko] undefined!
这个警告是可能是因为gcc或内核不支持这个级别演延时导致的,不过问题不大
查了内核源码发现好多人都用了,证明是支持的,所以可能是头文件包含的问题。
算了自己写一个假的好了正常要考虑架构频率进程切换等很多问题所以就简单延时一下好了
void my_udelay(unsigned long usecs)
{ unsigned long loops = usecs * LOOP_PER_USEC; while(loops--);
}
[root@100ask:/mnt]# insmod dht11_drv.ko
[ 2587.699869] ------------[ cut here ]------------
[ 2587.711912] WARNING: CPU: 0 PID: 362 at fs/sysfs/dir.c:31 sysfs_warn_dup+0x64/0x74
[ 2587.722923] sysfs: cannot create duplicate filename '/class/dht11'
[ 2587.731625] ---[ end trace b08c58d5eeb9152c ]---
[ 2587.739230] ------------[ cut here ]------------
[ 2587.744038] WARNING: CPU: 0 PID: 362 at lib/kobject.c:240 kobject_add_internal+0x2a8/0x344
[ 2587.753533] ---[ end trace b08c58d5eeb9152d ]---
[ 2587.761908] Failed to create device
insmod: ERROR: could not insert module dht11_drv.ko: File exists
和单板自带的驱动撞class名了
[root@100ask:/mnt]# insmod dht11_drv.ko
[ 2757.481544] Failed to request GPIO 115
insmod: ERROR: could not insert module dht11_drv.ko: No such device
啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊,改了一晚上啦,都不好不知道咋回事算了还是用老师的代码吧
下面是我改过的最新版
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <asm/io.h> // 假设DHT11连接到GPIO 115
#define DHT11_GPIO 115
// DHT11数据长度(包括温湿度值、校验和)
#define DHT11_DATA_LEN 8
// 假设每个循环迭代需要1微秒
#define LOOP_PER_USEC 1000static int dht11_gpio;
static u8 dht11_data[DHT11_DATA_LEN];
static struct class *gpio_class;
static int major = 0;void my_udelay(unsigned long usecs)
{ unsigned long loops = usecs * LOOP_PER_USEC; while(loops--);
}static void dht11_set_gpio_output(void)
{ gpio_direction_output(dht11_gpio, 0);
} static void dht11_set_gpio_input(void)
{ gpio_direction_input(dht11_gpio);
} static int dht11_read_data(void)
{ int i = 0, j = 0;u8 sum = 0; // 发送开始信号 dht11_set_gpio_output(); gpio_set_value(dht11_gpio, 0); mdelay(18); gpio_set_value(dht11_gpio, 1); my_udelay(20); dht11_set_gpio_input(); // 等待DHT11响应 while (gpio_get_value(dht11_gpio)) { my_udelay(1); if (j++ > 100) {printk("等待响应失败\n"); return -1; // 超时 } } // 读取数据 for (i = 0; i < DHT11_DATA_LEN; i++) { dht11_data[i] = 0; for (j = 0; j < 8; j++){my_udelay(40); if (!gpio_get_value(dht11_gpio)) { dht11_data[i] |= (1 << (7 - j)); } } } // // 检查校验和// for (i = 0; i < DHT11_DATA_LEN - 1; i++) { // sum += dht11_data[i]; // } // if (sum != dht11_data[DHT11_DATA_LEN - 1]) {// printk("和校验错误区\n");// return -1; // 校验和错误 // } return 0;
}
static int dht11_open(struct inode *inode, struct file *file)
{ return 0;
} static ssize_t dht11_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{ int ret; char data_str[32]; int temp_integral, temp_decimal; int humi_integral, humi_decimal; ret = dht11_read_data(); if (ret < 0) { return ret; } // 解析温度和湿度值 temp_integral = dht11_data[2]; temp_decimal = dht11_data[3]; humi_integral = dht11_data[0]; humi_decimal = dht11_data[1]; // 将数据转换为字符串格式 snprintf(data_str, sizeof(data_str), "%3d %2d\n", temp_integral, humi_integral); printk("%s\n", data_str);// 将数据复制到用户空间 if (copy_to_user(buf, data_str, 6)) { return -EFAULT; } return strlen(data_str);
}static int dht11_release(struct inode *inode, struct file *file)
{ return 0;
} static loff_t dht11_lseek(struct file *file, loff_t offset, int whence)
{ return -EINVAL; // 不支持lseek操作
}static const struct file_operations dht11_fops = { .owner = THIS_MODULE, .read = dht11_read, .open = dht11_open, .release = dht11_release, .llseek = dht11_lseek,
}; static int __init dht11_init(void)
{// 请求GPIO资源 // dht11_gpio = gpio_request(DHT11_GPIO, "DHT11"); // if (dht11_gpio < 0) { // printk(KERN_ERR "Failed to request GPIO %d\n", DHT11_GPIO); // return -ENODEV; // } // 导出GPIO到用户空间(可选) // gpio_export(DHT11_GPIO, false); // 注册字符设备 major = register_chrdev(0, "dht11", &dht11_fops); if (major < 0) { printk(KERN_ERR "Failed to register character device\n"); gpio_free(dht11_gpio);return major; } gpio_class = class_create(THIS_MODULE, "dht11");// 创建设备节点 device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "cebss_dht11"); if (IS_ERR(gpio_class)) { printk(KERN_ERR "Failed to create device\n"); unregister_chrdev(major, "dht11"); gpio_free(dht11_gpio); return PTR_ERR(gpio_class); } printk(KERN_INFO "DHT11 driver loaded\n"); return 0;
} static void __exit dht11_exit(void)
{ device_destroy(gpio_class, MKDEV(major, 0));class_destroy(gpio_class); unregister_chrdev(major, "dht11"); gpio_free(dht11_gpio);printk(KERN_INFO "DHT11 driver unloaded\n");
} module_init(dht11_init);
module_exit(dht11_exit);
MODULE_LICENSE("GPL");
最后挣扎一下
试试内核中的dht11程序
也是不行啊,没办法单核cpu这中持续采集就是没法很准确就这样吧。哎