简单的驱动程序的编写
2026/9/13 23:47:34 网站建设 项目流程

LED驱动程序

#include <linux/module.h> #include <linux/poll.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[1] = { { 131, 0, "led_0", }}; int major; static struct class *gpio_class; // 提供结构体中的read write函数 static ssize_t gpio_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset) { // 访问的引脚放在buf[0] int cnt = sizeof(gpios) / sizeof(gpios[0]); if (size != 2) return -EINVAL; char temp_buf[2]; int err = copy_from_user(temp_buf, buf, 1); if (temp_buf[0] >= cnt) return -EINVAL; temp_buf[1] = gpio_get_value(gpios[temp_buf[0]].gpio); err = copy_to_user(buf, temp_buf, 2); return 2; } static ssize_t gpio_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) { unsigned char key_buf[2]; if (size != 2) return -EINVAL; int err = copy_from_user(key_buf, buf, size); if (key_buf[0] >= sizeof(gpios) / sizeof(gpios[0])) return -EINVAL; gpio_set_value(gpios[key_buf[0]].gpio, key_buf[1]); return 2; } // 定义自己的file_operations结构体 static struct file_operations gpio_key_drv = { .owner = THIS_MODULE, .read = gpio_drv_read, .write = gpio_drv_write} // 入口函数 static int __init gpio_drv_init(void) { printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); int count = sizeof(gpios) / sizeof(gpios[0]); for (int i = 0; i < count; i++) { int err = gpio_request(gpios[i].gpio, gpios[i].name); if (err) { printk("can not request gpio %s %d\n", gpios[i].name, gpios[i].gpio); return -ENODEV; } gpio_direction_output(gpios[i].gpio, 1); // set output } major = register_chrdev(0, "100ask_led", &gpio_key_drv); gpio_class = class_create(THIS_MODULE, "100ask_led_class"); if (IS_ERR(gpio_class)) { printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); unregister_chrdev(major, "100ask_led"); return PTR_ERR(gpio_class); } device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "100ask_led"); /* /dev/100ask_led */ } static void __exit gpio_drv_exit(void) { 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_gpio_key"); for (int i = 0; i < count; i++) { // free_irq(gpios[i].irq, &gpios[i]); // del_timer(&gpios[i].key_timer); gpio_free(gpios[i].gpio); } } /* 7. 其他完善:提供设备信息,自动创建设备节点 */ module_init(gpio_drv_init); module_exit(gpio_drv_exit); MODULE_LICENSE("GPL");

sr04驱动程序

如果想要进行测距的话,我们需要先通过trigger来发送一个10us的高电平,然后再echo端口获得对应的一个时间为T的高电平,我们可以根据高电平的持续时间T来计算对应的距离。

但trigger的两次高电平之间至少要间隔50us

copy_to_user函数,前两个参数都是地址,因此对于读取到的高电平维持时间T,传入时需要按照下面的格式:copy_to_user(buf, &T, 4) 这里的T为int所以为4

unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);
#include "asm-generic/gpio.h" #include "asm/delay.h" #include <linux/module.h> #include <linux/poll.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> #define CMD_TRIG 100 struct gpio_desc { int gpio; int irq; char *name; int key; struct timer_list key_timer; }; /* 主设备号 */ static int major = 0; static struct class *gpio_class; static struct gpio_desc gpios[2] = { { 115, 0, "trig", }, { 116, 0, "echo", }, }; /* 环形缓冲区 */ #define BUF_LEN 128 static int g_keys[BUF_LEN]; static int r, w; struct fasync_struct *button_fasync; #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(int key) { if (!is_key_buf_full()) { g_keys[w] = key; w = NEXT_POS(w); } } static int get_key(void) { int 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 ssize_t sr04_read(struct file *file, char __user *buf, size_t size, loff_t *offset) { if (is_key_buf_empty() && (file->flag & O_NONBLOCK)) return -EAGAIN; wait_event_interruptible(gpio_wait, !is_key_buf_empty()); val = get_key(); if (val == -1) return -ENODATA; int err = copy_to_user(buf, &val, 4); return 4; } static long sr04_ioctl(struct file *filp, unsigned int command, unsigned long arg) { switch (command) { case CMD_TRIG: { gpio_set_value(gpios[0].gpio, 1); udelay(20); gpio_set_value(gpios[0].gpio, 0); // start timer mod_timer(&gpios[1].key_timer, jiffies + msecs_to_jiffies(50)); } } return 0; } static unsigned int sr04_poll(struct file *fp, poll_table *wait) { // printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); poll_wait(fp, &gpio_wait, wait); return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM; } static int sr04_fasync(int fd, struct file *file, int on) { if (fasync_helper(fd, file, on, &button_fasync) >= 0) return 0; else return -EIO; } static struct file_operations sr04_drv = { .owner = THIS_MODULE, .read = sr04_read, .poll = sr04_poll, .fasync = sr04_fasync, .unlocked_ioctl = sr04_ioctl, } static irqreturn_t sr04_isr(int irq, void *dev_id) { struct gpio_desc *gpio_desc = dev_id; int val = gpio_get_value(gpio_desc->gpio); static u64 rising_time = 0; u64 time = 0; if (val) { rising_time = ktime_get_ns(); } else { if (rising_time == 0) { return IRQ_HANDLED; } // stop timer del_timer(&gpios[1].key_timer); time = ktime_get_ns() - rising_time; rising_time = 0; put_key(time); wake_up_interruptible(&gpio_wait); kill_fasync(&button_fasync, SIGIO, POLL_IN); } return IRQ_HANDLED; } static void sr04_timer_func(unsigned long data) { put_key(-1); wake_up_interruptible(&gpio_wait); kill_fasync(&button_fasync, SIGIO, POLL_IN); } static int __init sr04_init(void) { printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); int err; // trigger pin err = gpio_request(gpios[0].gpio, gpios[0].name); gpio_direction_output(gpios[0].gpio, 0); // echo pin gpios[1].irq = gpio_to_irq(gpios[1].gpio); err = request_irq(gpios[1].irq, sr04_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, gpios[1].name, &gpios[1]); /* 注册file_operations */ major = register_chrdev(0, "100ask_sr04", &sr04_drv); /* /dev/gpio_desc */ gpio_class = class_create(THIS_MODULE, "100ask_sr04_class"); if (IS_ERR(gpio_class)) { printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); unregister_chrdev(major, "100ask_sr04"); return PTR_ERR(gpio_class); } device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "sr04"); /* /dev/sr04 */ setup_timer(&gpios[1].key_timer, sr04_timer_func, (unsigned long)&gpios[1]); return err; } static void __exit sr04_exit(void) { printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); device_detroy(gpio_class, MKDEV(major, 0)); class_detroy(gpio_class); unregister_chrdev(major, "100ask_sr04"); gpio_free(gpios[0].gpio); free_irq(gpios[1].irq, &gpios[1]); } module_init(sr04_init); module_exit(sr04_exit); MODULE_LICENSE("GPL");

步进电机

对于步进电机,其内部的转子有6个齿,外圈的定子则有6个齿,当我们导通B时,3号会对准B。而此时2号和A会有一个夹角,因此当我们截断B并导通A时,2号会对准A,此时转子发生旋转,旋转角度为 15°(转子6个齿,所以相邻的两个之间夹角为60°,定子有8个齿,所以夹角为45°)。因此我们可以轮流 导通BADC,这样转子就完成了八分之一圈。

依此类推,8 个四节拍以后转子将转过完整的一圈,而其中单个节拍使转子转过的角度就很容易计算出来了,即 360/(8*4)=11.25度,这个值就叫做步进角度。

逻辑真值表如下:

#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, "motor_gpio0", }, { 116, 0, "motor_gpio1", }, { 117, 0, "motor_gpio2", }, { 118, 0, "motor_gpio3", }, }; /* 主设备号 */ static int major = 0; static struct class *gpio_class; /* 马达引脚设置数字 */ static int g_motor_pin_ctrl[8] = {0x2, 0x3, 0x1, 0x9, 0x8, 0xc, 0x4, 0x6}; static int g_motor_index = 0; void set_pins_for_motor(int index) { for (int i = 0; i < 4; i++) gpio_set_value(gpios[i].gpio, g_motor_pin_ctrl[index] & (1 << i) ? 1 : 0); } void disable_motor() { for (int i = 0; i < 4; i++) gpio_set_value(gpios[i].gpio, 0); } /* int buf[2]; * buf[0] = 步进的次数, > 0 : 逆时针步进; < 0 : 顺时针步进 * buf[1] = mdelay的时间 */ static ssize_t motor_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) { if (size != 8) return -EINVAL; int ker_buf[2]; int err = copy_from_user(ker_buf, buf, 8); if (ker_buf[0] > 0) { // 逆时针旋转 for (int step = 0; step < ker_buf[0]; step++) { set_pins_for_motor(g_motor_index); mdelay(ker_buf[1]); g_motor_index--; if (g_motor_index < 0) g_motor_index = 7; } } else { // 顺时针旋转 ker_buf[1] = 0 - ker_buf[1] for (int step = 0; step < ker_buf[0]; step++) { set_pins_for_motor(g_motor_index); mdelay(ker_buf[1]); g_motor_index++; if (g_motor_index > 7) g_motor_index = 0; } } /* 改进:旋转到位后让马达不再消耗电源 */ disable_motor(); return 8; } /* 定义自己的file_operations结构体 */ static struct file_operations gpio_motor = { .owner = THIS_MODULE, .write = motor_write, }; /* 在入口函数 */ static int __init motor_init(void) { int err; for (int i = 0; i < sizeof(gpios) / sizeof(gpios[0]); i++) { err = gpio_request(gpios[i].gpio, gpios[i].name); gpio_direction_output(gpios[i].gpio, 0); } major = register_chrdev(0, "100ask_motor", &gpio_key_drv); gpio_class = class_create(THIS_MODULE, "100ask_motor_class"); if (IS_ERR(gpio_class)) { printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); unregister_chrdev(major, "100ask_gpio_key"); return PTR_ERR(gpio_class); } device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "motor"); /* /dev/motor */ return err; } /* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数 */ static void __exit motor_exit(void) { printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); device_destroy(gpio_class, MKDEV(major, 0)); class_destroy(gpio_class); unregister_chrdev(major, "100ask_motor_class"); for (int i = 0; i < sizeof(gpios) / sizeof(gpio[0]); i++) { gpio_free(gpios[i].gpio); } } /* 7. 其他完善:提供设备信息,自动创建设备节点 */ module_init(motor_init); module_exit(motor_exit); MODULE_LICENSE("GPL");

DHT11

DHT11的通信协议非常简单,主机只需要拉低发送一个开始信号,然后再拉高即可,后续等待dht11发来数据。
dht11发来的数据:

  • 数据为0的一位信号=一个低脉冲+一个高脉冲。低脉冲持续50us,高脉冲持续26~28us。
  • 数据为1的一位信号=一个低脉冲+一个高脉冲。低脉冲持续50us,高脉冲持续70us。

而其发来的数据格式则是,8bit的湿度整数部分+8bit的湿度小数部分+8bit的温度整数部分+8bit的温度小数部分+8bit校验和。

注:arm-linux-gcc规定char为unsigned char,因此范围变为0~255。

#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, 唤醒APP parse_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); // 唤醒APP wake_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++; } } // 放入环形buffer crc = 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; // 唤醒APP wake_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");

DS18B20

DS18B20温度传感器具有线路简单、体积小的特点,用来测量温度非常简单,在一根通信线上可以挂载多个DS18B20温度传感器。用户可以通过编程实现9~12位的温度读数,每个DS18B20有唯一的64位序列号,保存在rom中,因此一条总线上可以挂载多个DS18B20。

DS18B20内部有个64位只读存储器(ROM)和64位配置存储器(SCRATCHP),64位配置存储器(SCRATCHP)由9个Byte组成,包含温度数据、配置信息等,具体格式如下图:

其通信时序如下图所示:

#include "asm-generic/errno-base.h" #include "asm-generic/gpio.h" #include "asm/gpio.h" #include "asm/uaccess.h" #include <linux/module.h> #include <linux/poll.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, "ds18b20", }, }; /* 主设备号 */ static int major = 0; static struct class *gpio_class; static spinlock_t ds18b20_spinlock; static void ds18b20_udelay(int us) { u64 time = ktime_get_ns(); while (ktime_get_ns() - time < us * 1000) ; } static int ds18b20_reset_and_wait_ack(void) { int time_out = 100; gpio_set_value(gpios[0].gpio, 0); ds18b20_udelay(480); gpio_direction_input(gpios[0].gpio); while (gpio_get_value(gpios[0].gpio) && time--) { ds18b20_udelay(1); } if (time == 0) return -EIO; // 等待ACK结束 time_out = 300; while (!gpio_get_value(gpios[0].gpio) && time--) ds18b20_udelay(1); if (time == 0) return -EIO; return 0; } static void ds18b20_send_cmd(unsigned char cmd) { gpio_direction_output(gpios[0].gpio, 1); for (int i = 0; i < 8; i++) { if (cmd & (1 << i)) { // 发送1 gpio_direction_output(gpios[0].gpio, 0); ds18b20_udelay(2); gpio_direction_output(gpios[0].gpio, 1); ds18b20_udelay(60); } else { // 发送0 gpio_direction_output(gpios[0].gpio, 0); ds18b20_udelay(60); gpio_direction_output(gpios[0].gpio, 1); } } } static void ds18b20_read_data(unsigned char *buf) { gpio_direction_output(gpios[0].gpio, 1); unsigned char data; for (int i = 0; i < 8; i++) { gpio_direction_output(gpios[0].gpio, 0); ds18b20_udelay(2); gpio_direction_input(gpios[0].gpio); // 立即释放总线,转为输入 ds18b20_udelay(13); // 等待 13µs,使总采样点约在 15µs if (gpio_get_value(gpios[0].gpio)) { data |= (1 << i); } ds18b20_udelay(50); } buf[0] = data; } /********************************************************/ /*DS18B20的CRC8校验程序*/ /********************************************************/ /* 参考: https://www.cnblogs.com/yuanguanghui/p/12737740.html */ static unsigned char calcrc_1byte(unsigned char abyte) { unsigned char i, crc_1byte; crc_1byte = 0; // 设定crc_1byte初值为0 for (i = 0; i < 8; i++) { if (((crc_1byte ^ abyte) & 0x01)) { crc_1byte ^= 0x18; crc_1byte >>= 1; crc_1byte |= 0x80; } else crc_1byte >>= 1; abyte >>= 1; } return crc_1byte; } /* 参考: https://www.cnblogs.com/yuanguanghui/p/12737740.html */ static unsigned char calcrc_bytes(unsigned char *p, unsigned char len) { unsigned char crc = 0; while (len--) // len为总共要校验的字节数 { crc = calcrc_1byte(crc ^ *p++); } return crc; // 若最终返回的crc为0,则数据传输正确 } static int ds18b20_verify_crc(unsigned char *buf) { unsigned char crc; crc = calcrc_bytes(buf, 8); if (crc == buf[8]) return 0; else return -1; } static void ds18b20_calc_val(unsigned char ds18b20_buf[], int result[]) { unsigned char tempL = 0, tempH = 0; unsigned int integer; unsigned char decimal1, decimal2, decimal; tempL = ds18b20_buf[0]; // 读温度低8位 tempH = ds18b20_buf[1]; // 读温度高8位 if (tempH > 0x7f) // 最高位为1时温度是负 { tempL = ~tempL; // 补码转换,取反加一 tempH = ~tempH + 1; integer = tempL / 16 + tempH * 16; // 整数部分 decimal1 = (tempL & 0x0f) * 10 / 16; // 小数第一位 decimal2 = (tempL & 0x0f) * 100 / 16 % 10; // 小数第二位 decimal = decimal1 * 10 + decimal2; // 小数两位 } else { integer = tempL / 16 + tempH * 16; // 整数部分 decimal1 = (tempL & 0x0f) * 10 / 16; // 小数第一位 decimal2 = (tempL & 0x0f) * 100 / 16 % 10; // 小数第二位 decimal = decimal1 * 10 + decimal2; // 小数两位 } result[0] = integer; result[1] = decimal; } static ssize_t ds18b20_read(struct file *file, char __user *buf, size_t size, loff_t *offset) { unsigned long flags; int err; unsigned char kern_buf[9]; int result_buf[2]; if (size != 8) return -EINVAL; // 关中断 spin_lock_irqsave(&ds18b20_spinlock, flags); // 发送reset信号,并等待回应 err = ds18b20_reset_and_wait_ack(); if (err) { spin_unlock_irqrestore(&ds18b20_spinlock, flags); printk("ds18b20_reset_and_wait_ack err\n"); return err; } // 发命令 0xcc ds18b20_send_cmd(0xcc); // 发命令启动温度转换 0x44 ds18b20_send_cmd(0x44); // 恢复中断 spin_unlock_irqrestore(&ds18b20_spinlock, flags); // 等待 set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(msecs_to_jiffies(1000)); // 关中断 spin_lock_irqsave(&ds18b20_spinlock, flags); // 发送rest err = ds18b20_reset_and_wait_ack(); if (err) { spin_unlock_irqrestore(&ds18b20_spinlock, flags); printk("ds18b20_reset_and_wait_ack err\n"); return err; } // 发命令 0xcc ds18b20_send_cmd(0xcc); // 发命令 0xbe ds18b20_send_cmd(0xbe); // 读9字节数据 for (int i = 0; i < 9; i++) { ds18b20_read_data(&kern_buf[i]); } // 恢复中断 spin_unlock_irqrestore(&ds18b20_spinlock, flags); // 计算CRC if (ds18b20_verify_crc(kern_buf)) { printk("ds18b20_verify_crc err\n"); return err; } // copy_to_user ds18b20_calc_val(kern_buf, result_buf); err = copy_to_user(buf, result_buf, 8); return 8; } static struct file_opetations ds18b20_drv{ .owner = THIS_MODULE, .read = ds18b20_read}; static int __init ds18b20_init(void) { int cnt = sizeof(gpios) / sizeof(gpios[0]); int err; printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); spin_lock_init(&ds18b20_spinlock); for (int i = 0; i < cnt; i++) { err = gpio_request(gpios[i].gpio, gpios[i].name); gpio_direction_output(gpios[i].gpio, 1); } major = register_chrdev(0, "100ask_ds18b20", &gpio_key_drv); gpio_class = class_create(THIS_MODULE, "100ask_ds18b20_class"); if (IS_ERR(gpio_class)) { printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); unregister_chrdev(major, "100ask_ds18b20"); return PTR_ERR(gpio_class); } device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "myds18b20"); return err; } /* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数 */ static void __exit ds18b20_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_ds18b20"); for (i = 0; i < count; i++) { gpio_free(gpios[i].gpio); } } /* 7. 其他完善:提供设备信息,自动创建设备节点 */ module_init(ds18b20_init); module_exit(ds18b20_exit); MODULE_LICENSE("GPL");

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询