mirror of
https://github.com/sysprog21/lkmpg.git
synced 2025-06-04 18:14:35 +08:00
Merge pull request #316 from jeremy90307/master
Add a DHT11 Sensor Driver Example Using GPIO
This commit is contained in:
@ -4,3 +4,4 @@ intrpt
|
||||
vkbd
|
||||
syscall-steal
|
||||
led
|
||||
dht11
|
||||
|
@ -32,6 +32,7 @@ obj-m += vinput.o
|
||||
obj-m += vkbd.o
|
||||
obj-m += static_key.o
|
||||
obj-m += led.o
|
||||
obj-m += dht11.o
|
||||
|
||||
PWD := $(CURDIR)
|
||||
|
||||
|
265
examples/dht11.c
Normal file
265
examples/dht11.c
Normal file
@ -0,0 +1,265 @@
|
||||
/*
|
||||
* dht11.c - Using GPIO to read temperature and humidity from DHT11 sensor.
|
||||
*/
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include <asm/errno.h>
|
||||
|
||||
#define GPIO_PIN_4 575
|
||||
#define DEVICE_NAME "dht11"
|
||||
#define DEVICE_CNT 1
|
||||
|
||||
static char msg[64];
|
||||
|
||||
struct dht11_dev {
|
||||
dev_t dev_num;
|
||||
int major_num, minor_num;
|
||||
struct cdev cdev;
|
||||
struct class *cls;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static struct dht11_dev dht11_device;
|
||||
|
||||
/* Define GPIOs for LEDs.
|
||||
* TODO: According to the requirements, search /sys/kernel/debug/gpio to
|
||||
* find the corresponding GPIO location.
|
||||
*/
|
||||
static struct gpio dht11[] = { { GPIO_PIN_4, GPIOF_OUT_INIT_HIGH, "Signal" } };
|
||||
|
||||
static int dht11_read_data(void)
|
||||
{
|
||||
int timeout;
|
||||
uint8_t sensor_data[5] = { 0 };
|
||||
uint8_t i, j;
|
||||
|
||||
gpio_set_value(dht11[0].gpio, 0);
|
||||
mdelay(20);
|
||||
gpio_set_value(dht11[0].gpio, 1);
|
||||
udelay(30);
|
||||
gpio_direction_input(dht11[0].gpio);
|
||||
udelay(2);
|
||||
|
||||
timeout = 300;
|
||||
while (gpio_get_value(dht11[0].gpio) && timeout--)
|
||||
udelay(1);
|
||||
|
||||
if (timeout == -1)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
timeout = 300;
|
||||
while (!gpio_get_value(dht11[0].gpio) && timeout--)
|
||||
udelay(1);
|
||||
|
||||
if (timeout == -1)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
timeout = 300;
|
||||
while (gpio_get_value(dht11[0].gpio) && timeout--)
|
||||
udelay(1);
|
||||
|
||||
if (timeout == -1)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
for (j = 0; j < 5; j++) {
|
||||
uint8_t byte = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
timeout = 300;
|
||||
while (gpio_get_value(dht11[0].gpio) && timeout--)
|
||||
udelay(1);
|
||||
|
||||
if (timeout == -1)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
timeout = 300;
|
||||
while (!gpio_get_value(dht11[0].gpio) && timeout--)
|
||||
udelay(1);
|
||||
|
||||
if (timeout == -1)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
udelay(50);
|
||||
byte <<= 1;
|
||||
if (gpio_get_value(dht11[0].gpio))
|
||||
byte |= 0x01;
|
||||
}
|
||||
sensor_data[j] = byte;
|
||||
}
|
||||
|
||||
if (sensor_data[4] != (uint8_t)(sensor_data[0] + sensor_data[1] +
|
||||
sensor_data[2] + sensor_data[3]))
|
||||
return -EIO;
|
||||
|
||||
gpio_direction_output(dht11[0].gpio, 1);
|
||||
sprintf(msg, "Humidity: %d%%\nTemperature: %d deg C\n", sensor_data[0],
|
||||
sensor_data[2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret, retry;
|
||||
|
||||
for (retry = 0; retry < 5; ++retry) {
|
||||
ret = dht11_read_data();
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
msleep(10);
|
||||
}
|
||||
gpio_direction_output(dht11[0].gpio, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int device_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t device_read(struct file *filp, char __user *buffer,
|
||||
size_t length, loff_t *offset)
|
||||
{
|
||||
int msg_len = strlen(msg);
|
||||
|
||||
if (*offset >= msg_len)
|
||||
return 0;
|
||||
|
||||
size_t remain = msg_len - *offset;
|
||||
size_t bytes_read = min(length, remain);
|
||||
|
||||
if (copy_to_user(buffer, msg + *offset, bytes_read))
|
||||
return -EFAULT;
|
||||
|
||||
*offset += bytes_read;
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
static struct file_operations fops = {
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
||||
.owner = THIS_MODULE,
|
||||
#endif
|
||||
.open = device_open,
|
||||
.release = device_release,
|
||||
.read = device_read
|
||||
};
|
||||
|
||||
/* Initialize the module - Register the character device */
|
||||
static int __init dht11_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Determine whether dynamic allocation of the device number is needed. */
|
||||
if (dht11_device.major_num) {
|
||||
dht11_device.dev_num =
|
||||
MKDEV(dht11_device.major_num, dht11_device.minor_num);
|
||||
ret = register_chrdev_region(dht11_device.dev_num, DEVICE_CNT,
|
||||
DEVICE_NAME);
|
||||
} else {
|
||||
ret = alloc_chrdev_region(&dht11_device.dev_num, 0, DEVICE_CNT,
|
||||
DEVICE_NAME);
|
||||
}
|
||||
|
||||
/* Negative values signify an error */
|
||||
if (ret < 0) {
|
||||
pr_alert("Failed to register character device, error: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pr_info("Major = %d, Minor = %d\n", MAJOR(dht11_device.dev_num),
|
||||
MINOR(dht11_device.dev_num));
|
||||
|
||||
/* Prevents module unloading while operations are in use */
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
||||
dht11_device.cdev.owner = THIS_MODULE;
|
||||
#endif
|
||||
|
||||
cdev_init(&dht11_device.cdev, &fops);
|
||||
ret = cdev_add(&dht11_device.cdev, dht11_device.dev_num, 1);
|
||||
if (ret) {
|
||||
pr_err("Failed to add the device to the system\n");
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
|
||||
dht11_device.cls = class_create(DEVICE_NAME);
|
||||
#else
|
||||
dht11_device.cls = class_create(THIS_MODULE, DEVICE_NAME);
|
||||
#endif
|
||||
if (IS_ERR(dht11_device.cls)) {
|
||||
pr_err("Failed to create class for device\n");
|
||||
ret = PTR_ERR(dht11_device.cls);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
dht11_device.dev = device_create(dht11_device.cls, NULL,
|
||||
dht11_device.dev_num, NULL, DEVICE_NAME);
|
||||
if (IS_ERR(dht11_device.dev)) {
|
||||
pr_err("Failed to create the device file\n");
|
||||
ret = PTR_ERR(dht11_device.dev);
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
pr_info("Device created on /dev/%s\n", DEVICE_NAME);
|
||||
|
||||
ret = gpio_request(dht11[0].gpio, dht11[0].label);
|
||||
|
||||
if (ret) {
|
||||
pr_err("Unable to request GPIOs for dht11: %d\n", ret);
|
||||
goto fail4;
|
||||
}
|
||||
|
||||
ret = gpio_direction_output(dht11[0].gpio, 1);
|
||||
|
||||
if (ret) {
|
||||
pr_err("Failed to set GPIO %d direction\n", dht11[0].gpio);
|
||||
goto fail5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail5:
|
||||
gpio_free(dht11[0].gpio);
|
||||
|
||||
fail4:
|
||||
device_destroy(dht11_device.cls, dht11_device.dev_num);
|
||||
|
||||
fail3:
|
||||
class_destroy(dht11_device.cls);
|
||||
|
||||
fail2:
|
||||
cdev_del(&dht11_device.cdev);
|
||||
|
||||
fail1:
|
||||
unregister_chrdev_region(dht11_device.dev_num, DEVICE_CNT);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit dht11_exit(void)
|
||||
{
|
||||
gpio_set_value(dht11[0].gpio, 0);
|
||||
gpio_free(dht11[0].gpio);
|
||||
|
||||
device_destroy(dht11_device.cls, dht11_device.dev_num);
|
||||
class_destroy(dht11_device.cls);
|
||||
cdev_del(&dht11_device.cdev);
|
||||
unregister_chrdev_region(dht11_device.dev_num, DEVICE_CNT);
|
||||
}
|
||||
|
||||
module_init(dht11_init);
|
||||
module_exit(dht11_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
35
lkmpg.tex
35
lkmpg.tex
@ -1933,6 +1933,41 @@ Finally, remove the module:
|
||||
sudo rmmod led
|
||||
\end{codebash}
|
||||
|
||||
\subsection{DHT11 sensor}
|
||||
\label{sec:gpio_dht11}
|
||||
The DHT11 sensor is a well-known entry-level sensor commonly used to measure humidity and temperature.
|
||||
In this subsection, we will use GPIO to communicate through a single data line.
|
||||
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}.
|
||||
|
||||
In the implementation, the data pin of the DHT11 sensor is connected to GPIO4 on the Raspberry Pi.
|
||||
The sensor's VCC and GND pins are connected to 3.3V and GND, respectively.
|
||||
For more details about the Raspberry Pi pin assignments, refer to \href{https://pinout.xyz/}{Raspberry Pi Pinout}.
|
||||
The materials used include a Raspberry Pi 5, a DHT11 sensor, and jumper wires.
|
||||
|
||||
\samplec{examples/dht11.c}
|
||||
Make and install the module:
|
||||
\begin{codebash}
|
||||
make
|
||||
sudo insmod dht11.ko
|
||||
\end{codebash}
|
||||
|
||||
Check the Output of the DHT11 Sensor:
|
||||
\begin{codebash}
|
||||
sudo cat /dev/dht11
|
||||
\end{codebash}
|
||||
|
||||
Expected Output:
|
||||
\begin{verbatim}
|
||||
$ sudo cat /dev/dht11
|
||||
Humidity: 61%
|
||||
Temperature: 30°C
|
||||
\end{verbatim}
|
||||
|
||||
Finally, remove the module:
|
||||
\begin{codebash}
|
||||
sudo rmmod dht11
|
||||
\end{codebash}
|
||||
|
||||
\section{Scheduling Tasks}
|
||||
\label{sec:scheduling_tasks}
|
||||
There are two main ways of running tasks: tasklets and work queues.
|
||||
|
Reference in New Issue
Block a user