Skip to content

Commit b82b5df

Browse files
committed
Add a DHT11 Sensor Driver Example Using GPIO
Use GPIO to communicate with the DHT11 sensor. Test detail: - Tested on Raspberry Pi 5B with Raspberry Pi OS (Debian 12, Linux version 6.6.74) - Verify that the DHT11 driver compiles and loads successfully - Verify that temperature and humidity data can be read correctly from the DHT11 sensor
1 parent f105003 commit b82b5df

File tree

4 files changed

+302
-0
lines changed

4 files changed

+302
-0
lines changed

.ci/non-working

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ intrpt
44
vkbd
55
syscall-steal
66
led
7+
dht11

examples/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ obj-m += vinput.o
3232
obj-m += vkbd.o
3333
obj-m += static_key.o
3434
obj-m += led.o
35+
obj-m += dht11.o
3536

3637
PWD := $(CURDIR)
3738

examples/dht11.c

+265
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
/*
2+
* dht11.c - Using GPIO to read temperature and humidity from DHT11 sensor.
3+
*/
4+
5+
#include <linux/cdev.h>
6+
#include <linux/delay.h>
7+
#include <linux/device.h>
8+
#include <linux/fs.h>
9+
#include <linux/gpio.h>
10+
#include <linux/init.h>
11+
#include <linux/module.h>
12+
#include <linux/printk.h>
13+
#include <linux/types.h>
14+
#include <linux/uaccess.h>
15+
#include <linux/version.h>
16+
17+
#include <asm/errno.h>
18+
19+
#define GPIO_PIN_4 575
20+
#define DEVICE_NAME "dht11"
21+
#define DEVICE_CNT 1
22+
23+
static char msg[64];
24+
25+
struct dht11_dev {
26+
dev_t dev_num;
27+
int major_num, minor_num;
28+
struct cdev cdev;
29+
struct class *cls;
30+
struct device *dev;
31+
};
32+
33+
static struct dht11_dev dht11_device;
34+
35+
/* Define GPIOs for LEDs.
36+
* TODO: According to the requirements, search /sys/kernel/debug/gpio to
37+
* find the corresponding GPIO location.
38+
*/
39+
static struct gpio dht11[] = { { GPIO_PIN_4, GPIOF_OUT_INIT_HIGH, "Signal" } };
40+
41+
static int dht11_read_data(void)
42+
{
43+
int timeout;
44+
uint8_t sensor_data[5] = { 0 };
45+
uint8_t i, j;
46+
47+
gpio_set_value(dht11[0].gpio, 0);
48+
mdelay(20);
49+
gpio_set_value(dht11[0].gpio, 1);
50+
udelay(30);
51+
gpio_direction_input(dht11[0].gpio);
52+
udelay(2);
53+
54+
timeout = 300;
55+
while (gpio_get_value(dht11[0].gpio) && timeout--)
56+
udelay(1);
57+
58+
if (timeout == -1)
59+
return -ETIMEDOUT;
60+
61+
timeout = 300;
62+
while (!gpio_get_value(dht11[0].gpio) && timeout--)
63+
udelay(1);
64+
65+
if (timeout == -1)
66+
return -ETIMEDOUT;
67+
68+
timeout = 300;
69+
while (gpio_get_value(dht11[0].gpio) && timeout--)
70+
udelay(1);
71+
72+
if (timeout == -1)
73+
return -ETIMEDOUT;
74+
75+
for (j = 0; j < 5; j++) {
76+
uint8_t byte = 0;
77+
for (i = 0; i < 8; i++) {
78+
timeout = 300;
79+
while (gpio_get_value(dht11[0].gpio) && timeout--)
80+
udelay(1);
81+
82+
if (timeout == -1)
83+
return -ETIMEDOUT;
84+
85+
timeout = 300;
86+
while (!gpio_get_value(dht11[0].gpio) && timeout--)
87+
udelay(1);
88+
89+
if (timeout == -1)
90+
return -ETIMEDOUT;
91+
92+
udelay(50);
93+
byte <<= 1;
94+
if (gpio_get_value(dht11[0].gpio))
95+
byte |= 0x01;
96+
}
97+
sensor_data[j] = byte;
98+
}
99+
100+
if (sensor_data[4] != (uint8_t)(sensor_data[0] + sensor_data[1] +
101+
sensor_data[2] + sensor_data[3]))
102+
return -EIO;
103+
104+
gpio_direction_output(dht11[0].gpio, 1);
105+
sprintf(msg, "Humidity: %d%%\nTemperature: %d deg C\n", sensor_data[0],
106+
sensor_data[2]);
107+
108+
return 0;
109+
}
110+
111+
static int device_open(struct inode *inode, struct file *file)
112+
{
113+
int ret, retry;
114+
115+
for (retry = 0; retry < 5; ++retry) {
116+
ret = dht11_read_data();
117+
if (ret == 0)
118+
return 0;
119+
msleep(10);
120+
}
121+
gpio_direction_output(dht11[0].gpio, 1);
122+
123+
return ret;
124+
}
125+
126+
static int device_release(struct inode *inode, struct file *file)
127+
{
128+
return 0;
129+
}
130+
131+
static ssize_t device_read(struct file *filp, char __user *buffer,
132+
size_t length, loff_t *offset)
133+
{
134+
int msg_len = strlen(msg);
135+
136+
if (*offset >= msg_len)
137+
return 0;
138+
139+
size_t remain = msg_len - *offset;
140+
size_t bytes_read = min(length, remain);
141+
142+
if (copy_to_user(buffer, msg + *offset, bytes_read))
143+
return -EFAULT;
144+
145+
*offset += bytes_read;
146+
147+
return bytes_read;
148+
}
149+
150+
static struct file_operations fops = {
151+
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
152+
.owner = THIS_MODULE,
153+
#endif
154+
.open = device_open,
155+
.release = device_release,
156+
.read = device_read
157+
};
158+
159+
/* Initialize the module - Register the character device */
160+
static int __init dht11_init(void)
161+
{
162+
int ret = 0;
163+
164+
/* Determine whether dynamic allocation of the device number is needed. */
165+
if (dht11_device.major_num) {
166+
dht11_device.dev_num =
167+
MKDEV(dht11_device.major_num, dht11_device.minor_num);
168+
ret = register_chrdev_region(dht11_device.dev_num, DEVICE_CNT,
169+
DEVICE_NAME);
170+
} else {
171+
ret = alloc_chrdev_region(&dht11_device.dev_num, 0, DEVICE_CNT,
172+
DEVICE_NAME);
173+
}
174+
175+
/* Negative values signify an error */
176+
if (ret < 0) {
177+
pr_alert("Failed to register character device, error: %d\n", ret);
178+
return ret;
179+
}
180+
181+
pr_info("Major = %d, Minor = %d\n", MAJOR(dht11_device.dev_num),
182+
MINOR(dht11_device.dev_num));
183+
184+
/* Prevents module unloading while operations are in use */
185+
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
186+
dht11_device.cdev.owner = THIS_MODULE;
187+
#endif
188+
189+
cdev_init(&dht11_device.cdev, &fops);
190+
ret = cdev_add(&dht11_device.cdev, dht11_device.dev_num, 1);
191+
if (ret) {
192+
pr_err("Failed to add the device to the system\n");
193+
goto fail1;
194+
}
195+
196+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
197+
dht11_device.cls = class_create(DEVICE_NAME);
198+
#else
199+
dht11_device.cls = class_create(THIS_MODULE, DEVICE_NAME);
200+
#endif
201+
if (IS_ERR(dht11_device.cls)) {
202+
pr_err("Failed to create class for device\n");
203+
ret = PTR_ERR(dht11_device.cls);
204+
goto fail2;
205+
}
206+
207+
dht11_device.dev = device_create(dht11_device.cls, NULL,
208+
dht11_device.dev_num, NULL, DEVICE_NAME);
209+
if (IS_ERR(dht11_device.dev)) {
210+
pr_err("Failed to create the device file\n");
211+
ret = PTR_ERR(dht11_device.dev);
212+
goto fail3;
213+
}
214+
215+
pr_info("Device created on /dev/%s\n", DEVICE_NAME);
216+
217+
ret = gpio_request(dht11[0].gpio, dht11[0].label);
218+
219+
if (ret) {
220+
pr_err("Unable to request GPIOs for dht11: %d\n", ret);
221+
goto fail4;
222+
}
223+
224+
ret = gpio_direction_output(dht11[0].gpio, 1);
225+
226+
if (ret) {
227+
pr_err("Failed to set GPIO %d direction\n", dht11[0].gpio);
228+
goto fail5;
229+
}
230+
231+
return 0;
232+
233+
fail5:
234+
gpio_free(dht11[0].gpio);
235+
236+
fail4:
237+
device_destroy(dht11_device.cls, dht11_device.dev_num);
238+
239+
fail3:
240+
class_destroy(dht11_device.cls);
241+
242+
fail2:
243+
cdev_del(&dht11_device.cdev);
244+
245+
fail1:
246+
unregister_chrdev_region(dht11_device.dev_num, DEVICE_CNT);
247+
248+
return ret;
249+
}
250+
251+
static void __exit dht11_exit(void)
252+
{
253+
gpio_set_value(dht11[0].gpio, 0);
254+
gpio_free(dht11[0].gpio);
255+
256+
device_destroy(dht11_device.cls, dht11_device.dev_num);
257+
class_destroy(dht11_device.cls);
258+
cdev_del(&dht11_device.cdev);
259+
unregister_chrdev_region(dht11_device.dev_num, DEVICE_CNT);
260+
}
261+
262+
module_init(dht11_init);
263+
module_exit(dht11_exit);
264+
265+
MODULE_LICENSE("GPL");

lkmpg.tex

+35
Original file line numberDiff line numberDiff line change
@@ -1933,6 +1933,41 @@ \subsection{Control the LED's on/off state}
19331933
sudo rmmod led
19341934
\end{codebash}
19351935

1936+
\subsection{DHT11 sensor}
1937+
\label{sec:gpio_dht11}
1938+
The DHT11 sensor is a well-known entry-level sensor commonly used to measure humidity and temperature.
1939+
In this subsection, we will use GPIO to communicate through a single data line.
1940+
The DHT11 communication protocol can be referred to in the \href{https://www.mouser.com/datasheet/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf?srsltid=AfmBOoppls-QTd864640bVtbK90sWBsFzJ_7SgjOD2EpwuLLGUSTyYnv}{datasheet}.
1941+
1942+
In the implementation, the data pin of the DHT11 sensor is connected to GPIO4 on the Raspberry Pi.
1943+
The sensor's VCC and GND pins are connected to 3.3V and GND, respectively.
1944+
For more details about the Raspberry Pi pin assignments, refer to \href{https://pinout.xyz/}{Raspberry Pi Pinout}.
1945+
The materials used include a Raspberry Pi 5, a DHT11 sensor, and jumper wires.
1946+
1947+
\samplec{examples/dht11.c}
1948+
Make and install the module:
1949+
\begin{codebash}
1950+
make
1951+
sudo insmod dht11.ko
1952+
\end{codebash}
1953+
1954+
Check the Output of the DHT11 Sensor:
1955+
\begin{codebash}
1956+
sudo cat /dev/dht11
1957+
\end{codebash}
1958+
1959+
Expected Output:
1960+
\begin{verbatim}
1961+
$ sudo cat /dev/dht11
1962+
Humidity: 61%
1963+
Temperature: 30°C
1964+
\end{verbatim}
1965+
1966+
Finally, remove the module:
1967+
\begin{codebash}
1968+
sudo rmmod dht11
1969+
\end{codebash}
1970+
19361971
\section{Scheduling Tasks}
19371972
\label{sec:scheduling_tasks}
19381973
There are two main ways of running tasks: tasklets and work queues.

0 commit comments

Comments
 (0)