Skip to content

Commit 3ddd7e1

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 3ddd7e1

File tree

4 files changed

+311
-0
lines changed

4 files changed

+311
-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

+274
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
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 == 0) {
59+
return -ETIMEDOUT;
60+
}
61+
62+
timeout = 300;
63+
while (!gpio_get_value(dht11[0].gpio) && timeout--)
64+
udelay(1);
65+
66+
if (timeout == 0) {
67+
return -ETIMEDOUT;
68+
}
69+
70+
timeout = 300;
71+
while (gpio_get_value(dht11[0].gpio) && timeout--)
72+
udelay(1);
73+
74+
if (timeout == 0) {
75+
return -ETIMEDOUT;
76+
}
77+
78+
for (j = 0; j < 5; j++) {
79+
uint8_t byte = 0;
80+
for (i = 0; i < 8; i++) {
81+
timeout = 300;
82+
while (gpio_get_value(dht11[0].gpio) && timeout--)
83+
udelay(1);
84+
85+
if (timeout == 0) {
86+
return -ETIMEDOUT;
87+
}
88+
89+
timeout = 300;
90+
while (!gpio_get_value(dht11[0].gpio) && timeout--)
91+
udelay(1);
92+
93+
if (timeout == 0) {
94+
return -ETIMEDOUT;
95+
}
96+
97+
udelay(50);
98+
byte <<= 1;
99+
if (gpio_get_value(dht11[0].gpio))
100+
byte |= 0x01;
101+
}
102+
sensor_data[j] = byte;
103+
}
104+
105+
if (sensor_data[4] != (uint8_t)(sensor_data[0] + sensor_data[1] +
106+
sensor_data[2] + sensor_data[3]))
107+
return -EIO;
108+
109+
gpio_direction_output(dht11[0].gpio, 1);
110+
sprintf(msg, "Humidity: %d%%\nTemperature: %d deg C\n", sensor_data[0],
111+
sensor_data[2]);
112+
113+
return 0;
114+
}
115+
116+
static int device_open(struct inode *inode, struct file *file)
117+
{
118+
int ret, retry;
119+
120+
for (retry = 0; retry < 5; ++retry) {
121+
ret = dht11_read_data();
122+
if (ret == 0)
123+
return 0;
124+
msleep(10);
125+
}
126+
127+
if (ret) {
128+
gpio_direction_output(dht11[0].gpio, 1);
129+
return ret;
130+
}
131+
132+
return 0;
133+
}
134+
135+
static int device_release(struct inode *inode, struct file *file)
136+
{
137+
return 0;
138+
}
139+
140+
static ssize_t device_read(struct file *filp, char __user *buffer,
141+
size_t length, loff_t *offset)
142+
{
143+
int msg_len = strlen(msg);
144+
145+
if (*offset >= msg_len)
146+
return 0;
147+
148+
size_t remain = msg_len - *offset;
149+
size_t bytes_read = min(length, remain);
150+
151+
if (copy_to_user(buffer, msg + *offset, bytes_read))
152+
return -EFAULT;
153+
154+
*offset += bytes_read;
155+
156+
return bytes_read;
157+
}
158+
159+
static struct file_operations fops = {
160+
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
161+
.owner = THIS_MODULE,
162+
#endif
163+
.open = device_open,
164+
.release = device_release,
165+
.read = device_read
166+
};
167+
168+
/* Initialize the module - Register the character device */
169+
static int __init dht11_init(void)
170+
{
171+
int ret = 0;
172+
173+
/* Determine whether dynamic allocation of the device number is needed. */
174+
if (dht11_device.major_num) {
175+
dht11_device.dev_num =
176+
MKDEV(dht11_device.major_num, dht11_device.minor_num);
177+
ret = register_chrdev_region(dht11_device.dev_num, DEVICE_CNT,
178+
DEVICE_NAME);
179+
} else {
180+
ret = alloc_chrdev_region(&dht11_device.dev_num, 0, DEVICE_CNT,
181+
DEVICE_NAME);
182+
}
183+
184+
/* Negative values signify an error */
185+
if (ret < 0) {
186+
pr_alert("Failed to register character device, error: %d\n", ret);
187+
return ret;
188+
}
189+
190+
pr_info("Major = %d, Minor = %d\n", MAJOR(dht11_device.dev_num),
191+
MINOR(dht11_device.dev_num));
192+
193+
/* Prevents module unloading while operations are in use */
194+
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
195+
dht11_device.cdev.owner = THIS_MODULE;
196+
#endif
197+
198+
cdev_init(&dht11_device.cdev, &fops);
199+
ret = cdev_add(&dht11_device.cdev, dht11_device.dev_num, 1);
200+
if (ret) {
201+
pr_err("Failed to add the device to the system\n");
202+
goto fail1;
203+
}
204+
205+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
206+
dht11_device.cls = class_create(DEVICE_NAME);
207+
#else
208+
dht11_device.cls = class_create(THIS_MODULE, DEVICE_NAME);
209+
#endif
210+
if (IS_ERR(dht11_device.cls)) {
211+
pr_err("Failed to create class for device\n");
212+
ret = PTR_ERR(dht11_device.cls);
213+
goto fail2;
214+
}
215+
216+
dht11_device.dev = device_create(dht11_device.cls, NULL,
217+
dht11_device.dev_num, NULL, DEVICE_NAME);
218+
if (IS_ERR(dht11_device.dev)) {
219+
pr_err("Failed to create the device file\n");
220+
ret = PTR_ERR(dht11_device.dev);
221+
goto fail3;
222+
}
223+
224+
pr_info("Device created on /dev/%s\n", DEVICE_NAME);
225+
226+
ret = gpio_request(dht11[0].gpio, dht11[0].label);
227+
228+
if (ret) {
229+
pr_err("Unable to request GPIOs for dht11: %d\n", ret);
230+
goto fail4;
231+
}
232+
233+
ret = gpio_direction_output(dht11[0].gpio, 1);
234+
235+
if (ret) {
236+
pr_err("Failed to set GPIO %d direction\n", dht11[0].gpio);
237+
goto fail5;
238+
}
239+
240+
return 0;
241+
242+
fail5:
243+
gpio_free(dht11[0].gpio);
244+
245+
fail4:
246+
device_destroy(dht11_device.cls, dht11_device.dev_num);
247+
248+
fail3:
249+
class_destroy(dht11_device.cls);
250+
251+
fail2:
252+
cdev_del(&dht11_device.cdev);
253+
254+
fail1:
255+
unregister_chrdev_region(dht11_device.dev_num, DEVICE_CNT);
256+
257+
return ret;
258+
}
259+
260+
static void __exit dht11_exit(void)
261+
{
262+
gpio_set_value(dht11[0].gpio, 0);
263+
gpio_free(dht11[0].gpio);
264+
265+
device_destroy(dht11_device.cls, dht11_device.dev_num);
266+
class_destroy(dht11_device.cls);
267+
cdev_del(&dht11_device.cdev);
268+
unregister_chrdev_region(dht11_device.dev_num, DEVICE_CNT);
269+
}
270+
271+
module_init(dht11_init);
272+
module_exit(dht11_exit);
273+
274+
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)