【驱动篇】龙芯LS2K0300之单总线驱动

2024-06-24 00:04

本文主要是介绍【驱动篇】龙芯LS2K0300之单总线驱动,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

实验过程

实验目的: 在龙芯开发板上面使用单总线驱动DS18B20温度传感器

① 根据原理图连接DS18B20模块

② 将i2c0引脚的功能复用为GPIO

③ 注册字符设备,按照DS18B20的读写时序编写读写驱动接口

④ 编写测试用例解析传感器的数值

原理图

将板子上面的GPIO48连接传感器的DAT引脚,其余引脚连接如下

在这里插入图片描述

然后记得在设备树中把i2c0部分代码注释掉,将PIN16复用为GPIO48

驱动代码

定义相关传感器设备结构体

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/errno.h>#define DS18B20_DEV_NUM			1		  
#define DS18B20_DEV		        "ds18b20"	
#define DS18B20_GPIO            48
#define DS18B20_DQ_OUT(x)       gpio_direction_output(ds18b20.gpio, x)
#define DS18B20_DQ_IN           ds18b20_get_io()struct ds18b20_dev {dev_t dev_id;		struct cdev cdev;	struct class *class;	struct device *device;	int major;			int minor;	int gpio;				unsigned char data[2]; struct timer_list timer;	struct work_struct work;
};struct ds18b20_dev ds18b20;

传感器数据读写交互

static int ds18b20_get_io(void)
{gpio_direction_input(ds18b20.gpio);return gpio_get_value(ds18b20.gpio); 
}static void ds18b20_reset(void)
{DS18B20_DQ_OUT(0);  /* 拉低DQ,复位 */udelay(750);      /* 拉低750us */DS18B20_DQ_OUT(1);  /* DQ=1, 释放复位 */udelay(15);       /* 延迟15US */
}uint8_t ds18b20_check(void)
{uint8_t retry = 0;uint8_t rval = 0;while (DS18B20_DQ_IN && retry < 200)    /* 等待DQ变低, 等待200us */{retry++;udelay(1);}if (retry >= 200){rval = 1;}else{retry = 0;while (!DS18B20_DQ_IN && retry < 240)   /* 等待DQ变高, 等待240us */{retry++;udelay(1);}if (retry >= 240) rval = 1;}return rval;
}static uint8_t ds18b20_read_bit(void)
{uint8_t data = 0;DS18B20_DQ_OUT(0);udelay(2);DS18B20_DQ_OUT(1);udelay(12);if (DS18B20_DQ_IN){data = 1;}udelay(50);return data;
}static uint8_t ds18b20_read_byte(void)
{uint8_t i, b, data = 0;for (i = 0; i < 8; i++){b = ds18b20_read_bit(); /* DS18B20先输出低位数据 ,高位数据后输出 */data |= b << i;         /* 填充data的每一位 */}return data;
}static void ds18b20_write_byte(uint8_t data)
{uint8_t j;for (j = 1; j <= 8; j++){if (data & 0x01){DS18B20_DQ_OUT(0);  /*  Write 1 */udelay(2);DS18B20_DQ_OUT(1);udelay(60);}else{DS18B20_DQ_OUT(0);  /*  Write 0 */udelay(60);DS18B20_DQ_OUT(1);udelay(2);}data >>= 1;             /* 右移,获取高一位数据 */}
}static void ds18b20_start(void)
{ds18b20_reset();ds18b20_check();ds18b20_write_byte(0xcc);   /*  skip rom */ds18b20_write_byte(0x44);   /*  convert */
}static int ds18b20_init(void)
{gpio_direction_output(ds18b20.gpio, 0);ds18b20_reset();return ds18b20_check();	
}

注册字符设备,绑定相关回调函数

static int ds18b20_open(struct inode *inode, struct file *filp)
{return 0;
}static ssize_t ds18b20_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) 
{int ret;ret = copy_to_user(buf, &ds18b20.data[0], 2);if(ret)return -ENOMEM;return ret;
}static struct file_operations ds18b20_fops = {.owner	= THIS_MODULE,.open = ds18b20_open,.read	= ds18b20_read,
};static void ds18b20_work_callback(struct work_struct *work)
{ds18b20_start();                            /*  ds1820 start convert */ds18b20_reset();ds18b20_check();ds18b20_write_byte(0xcc);                   /*  skip rom */ds18b20_write_byte(0xbe);                   /*  convert */ds18b20.data[0] = ds18b20_read_byte();      /*  LSB */ds18b20.data[1] = ds18b20_read_byte();      /*  MSB */
}static void ds18b20_timer_callback(struct timer_list *arg)
{schedule_work(&ds18b20.work);	mod_timer(&ds18b20.timer, jiffies + (1000 * HZ/1000));	
}static int ds18b20_module_init(void)
{int ret = 0;ds18b20.gpio = DS18B20_GPIO;if (!gpio_is_valid(ds18b20.gpio)) {return -EINVAL;}ret = gpio_request(ds18b20.gpio, "DS18B20-GPIO");if (ret) {printk(KERN_ERR "ds18b20 : Failed to request gpio\n");return ret;}ds18b20_init();if (ds18b20.major) {		ds18b20.dev_id = MKDEV(ds18b20.major, 0);ret = register_chrdev_region(ds18b20.dev_id, DS18B20_DEV_NUM, DS18B20_DEV);if(ret < 0) {pr_err("cannot register %s char driver [ret=%d]\n", DS18B20_DEV, DS18B20_DEV_NUM);goto free_gpio;}} else {					ret = alloc_chrdev_region(&ds18b20.dev_id, 0, DS18B20_DEV_NUM, DS18B20_DEV);if(ret < 0) {pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", DS18B20_DEV, ret);goto free_gpio;}ds18b20.major = MAJOR(ds18b20.dev_id);	ds18b20.minor = MINOR(ds18b20.dev_id);	}ds18b20.cdev.owner = THIS_MODULE;cdev_init(&ds18b20.cdev, &ds18b20_fops);cdev_add(&ds18b20.cdev, ds18b20.dev_id, DS18B20_DEV_NUM);if(ret < 0)goto del_unregister;ds18b20.class = class_create(THIS_MODULE, DS18B20_DEV);if (IS_ERR(ds18b20.class)) {goto del_cdev;}ds18b20.device = device_create(ds18b20.class, NULL, ds18b20.dev_id, NULL, DS18B20_DEV);if (IS_ERR(ds18b20.device)) {goto destroy_class;}timer_setup(&ds18b20.timer, ds18b20_timer_callback, 0);ds18b20.timer.expires=jiffies + msecs_to_jiffies(1000);add_timer(&ds18b20.timer);INIT_WORK(&ds18b20.work, ds18b20_work_callback);return 0;destroy_class:class_destroy(ds18b20.class);
del_cdev:cdev_del(&ds18b20.cdev);
del_unregister:unregister_chrdev_region(ds18b20.dev_id, DS18B20_DEV_NUM);
free_gpio:gpio_free(ds18b20.gpio);return -EIO;
}

整合代码

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/errno.h>#define DS18B20_DEV_NUM			1		  
#define DS18B20_DEV		        "ds18b20"	
#define DS18B20_GPIO            48	
#define DS18B20_DQ_OUT(x)       gpio_direction_output(ds18b20.gpio, x)
#define DS18B20_DQ_IN           ds18b20_get_io()struct ds18b20_dev {dev_t dev_id;		struct cdev cdev;	struct class *class;	struct device *device;	int major;			int minor;	int gpio;				unsigned char data[2]; struct timer_list timer;	struct work_struct work;
};struct ds18b20_dev ds18b20;static int ds18b20_get_io(void)
{gpio_direction_input(ds18b20.gpio);return gpio_get_value(ds18b20.gpio); 
}static void ds18b20_reset(void)
{DS18B20_DQ_OUT(0);  /* 拉低DQ,复位 */udelay(750);      /* 拉低750us */DS18B20_DQ_OUT(1);  /* DQ=1, 释放复位 */udelay(15);       /* 延迟15US */
}uint8_t ds18b20_check(void)
{uint8_t retry = 0;uint8_t rval = 0;while (DS18B20_DQ_IN && retry < 200)    /* 等待DQ变低, 等待200us */{retry++;udelay(1);}if (retry >= 200){rval = 1;}else{retry = 0;while (!DS18B20_DQ_IN && retry < 240)   /* 等待DQ变高, 等待240us */{retry++;udelay(1);}if (retry >= 240) rval = 1;}return rval;
}static uint8_t ds18b20_read_bit(void)
{uint8_t data = 0;DS18B20_DQ_OUT(0);udelay(2);DS18B20_DQ_OUT(1);udelay(12);if (DS18B20_DQ_IN){data = 1;}udelay(50);return data;
}static uint8_t ds18b20_read_byte(void)
{uint8_t i, b, data = 0;for (i = 0; i < 8; i++){b = ds18b20_read_bit(); /* DS18B20先输出低位数据 ,高位数据后输出 */data |= b << i;         /* 填充data的每一位 */}return data;
}static void ds18b20_write_byte(uint8_t data)
{uint8_t j;for (j = 1; j <= 8; j++){if (data & 0x01){DS18B20_DQ_OUT(0);  /*  Write 1 */udelay(2);DS18B20_DQ_OUT(1);udelay(60);}else{DS18B20_DQ_OUT(0);  /*  Write 0 */udelay(60);DS18B20_DQ_OUT(1);udelay(2);}data >>= 1;             /* 右移,获取高一位数据 */}
}static void ds18b20_start(void)
{ds18b20_reset();ds18b20_check();ds18b20_write_byte(0xcc);   /*  skip rom */ds18b20_write_byte(0x44);   /*  convert */
}static int ds18b20_init(void)
{gpio_direction_output(ds18b20.gpio, 0);ds18b20_reset();return ds18b20_check();	
}static int ds18b20_open(struct inode *inode, struct file *filp)
{return 0;
}static ssize_t ds18b20_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) 
{int ret;ret = copy_to_user(buf, &ds18b20.data[0], 2);if(ret)return -ENOMEM;return ret;
}static struct file_operations ds18b20_fops = {.owner	= THIS_MODULE,.open = ds18b20_open,.read	= ds18b20_read,
};static void ds18b20_work_callback(struct work_struct *work)
{ds18b20_start();                            /*  ds1820 start convert */ds18b20_reset();ds18b20_check();ds18b20_write_byte(0xcc);                   /*  skip rom */ds18b20_write_byte(0xbe);                   /*  convert */ds18b20.data[0] = ds18b20_read_byte();      /*  LSB */ds18b20.data[1] = ds18b20_read_byte();      /*  MSB */
}static void ds18b20_timer_callback(struct timer_list *arg)
{schedule_work(&ds18b20.work);	mod_timer(&ds18b20.timer, jiffies + (1000 * HZ/1000));	
}static int ds18b20_module_init(void)
{int ret = 0;ds18b20.gpio = DS18B20_GPIO;if (!gpio_is_valid(ds18b20.gpio)) {return -EINVAL;}ret = gpio_request(ds18b20.gpio, "DS18B20-GPIO");if (ret) {printk(KERN_ERR "ds18b20 : Failed to request gpio\n");return ret;}ds18b20_init();if (ds18b20.major) {		ds18b20.dev_id = MKDEV(ds18b20.major, 0);ret = register_chrdev_region(ds18b20.dev_id, DS18B20_DEV_NUM, DS18B20_DEV);if(ret < 0) {pr_err("cannot register %s char driver [ret=%d]\n", DS18B20_DEV, DS18B20_DEV_NUM);goto free_gpio;}} else {					ret = alloc_chrdev_region(&ds18b20.dev_id, 0, DS18B20_DEV_NUM, DS18B20_DEV);if(ret < 0) {pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", DS18B20_DEV, ret);goto free_gpio;}ds18b20.major = MAJOR(ds18b20.dev_id);	ds18b20.minor = MINOR(ds18b20.dev_id);	}ds18b20.cdev.owner = THIS_MODULE;cdev_init(&ds18b20.cdev, &ds18b20_fops);cdev_add(&ds18b20.cdev, ds18b20.dev_id, DS18B20_DEV_NUM);if(ret < 0)goto del_unregister;ds18b20.class = class_create(THIS_MODULE, DS18B20_DEV);if (IS_ERR(ds18b20.class)) {goto del_cdev;}ds18b20.device = device_create(ds18b20.class, NULL, ds18b20.dev_id, NULL, DS18B20_DEV);if (IS_ERR(ds18b20.device)) {goto destroy_class;}timer_setup(&ds18b20.timer, ds18b20_timer_callback, 0);ds18b20.timer.expires=jiffies + msecs_to_jiffies(1000);add_timer(&ds18b20.timer);INIT_WORK(&ds18b20.work, ds18b20_work_callback);return 0;destroy_class:class_destroy(ds18b20.class);
del_cdev:cdev_del(&ds18b20.cdev);
del_unregister:unregister_chrdev_region(ds18b20.dev_id, DS18B20_DEV_NUM);
free_gpio:gpio_free(ds18b20.gpio);return -EIO;
}static void ds18b20_module_exit(void)
{cdev_del(&ds18b20.cdev);unregister_chrdev_region(ds18b20.dev_id, DS18B20_DEV_NUM); device_destroy(ds18b20.class, ds18b20.dev_id);class_destroy(ds18b20.class);del_timer(&ds18b20.timer);cancel_work_sync(&ds18b20.work);gpio_free(ds18b20.gpio); 
}module_init(ds18b20_module_init);
module_exit(ds18b20_module_exit);
MODULE_LICENSE("GPL");

Makefile文件

obj-m += ds18b20.o 
KDIR:=/home/asensing/loongson/linux-4.19
ARCH=loongarch 
CROSS_COMPILE=loongarch64-linux-gnu-
PWD?=$(shell pwd) 
all:make -C $(KDIR) M=$(PWD) modules 

构建脚本

export PATH=$PATH:/home/asensing/loongson/loongson-gnu-toolchain-8.3-x86_64-loongarch64-linux-gnu-rc1.3-1/bin
make -j8
loongarch64-linux-gnu-gcc test.c -o test
FILE=$PWD/$(basename $PWD).ko
scp $FILE test root@192.168.137.216:/home/root

测试用例

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"#define DEV_NAME "/dev/ds18b20"int main()
{int fd, ret;unsigned char result[2];unsigned char TH, TL;short tmp = 0;float temperature;int flag = 0;fd = open(DEV_NAME, 0);if(fd < 0){printf("open %s device failed\n", DEV_NAME);exit(1);}elseprintf("Open %s success!\n", DEV_NAME);while(1){ret = read(fd, &result, sizeof(result)); if(ret == 0) {	/* 读取到数据 */TL = result[0];TH = result[1];if(TH > 7) {	/* 负数处理 */TH = ~TH;TL = ~TL;flag = 1;	/* 标记为负数 */}tmp = TH;tmp <<= 8;tmp += TL;if(flag == 1) {temperature = (float)(tmp+1) * 0.0625; /* 计算负数的温度 */temperature = -temperature;}else {temperature = (float)tmp *0.0625;	/* 计算正数的温度 */}            if(temperature < 125 && temperature > -55) {	/* 温度范围 */printf("Environment Temperature Now : %0.2f℃\n", temperature);}}flag = 0;sleep(1);}close(fd);	/* 关闭文件 */
}

实验效果

插入驱动后,使用测试用例实时读取环境温度值

在这里插入图片描述

广东太热,我要回非洲!

在这里插入图片描述

参考

以上驱动参考自RT-Thread的工程:rtt-psoc62/one-wire/ds18b20.c at main · hywing/rtt-psoc62 (github.com)

这篇关于【驱动篇】龙芯LS2K0300之单总线驱动的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1088661

相关文章

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

驱动(RK3588S)第七课时:单节点设备树

目录 需求一、设备树的概念1、设备树的后缀名:2、设备树的语法格式3、设备树的属性(重要)4、设备树格式举例 二、设备树所用函数1、如何在内核层种获取设备树节点:2、从设备树上获取 gpio 口的属性3、获取节点上的属性只针对于字符串属性的4、函数读取 np 结点中的 propname 属性的值,并将读取到的 u32 类型的值保存在 out_value 指向的内存中,函数的返回值表示读取到的

驱动安装注册表指令

HKCR: HKEY_CLASSES_ROOT HKCU: HKEY_CURRENT_USER HKLM: HKEY_LOCAL_MACHINE HKU: HEKY_USER HER: 相对根键

UMDF驱动安装

VS2013 + WDF8.1,UMDF驱动选择User Mode Driver,不要选User Mode Driver 2.0,否则Win7安装有问题,如图 另外,在驱动安装时不要忘记WUDFUpdate_<主版本号><次版本号>.dll文件,具体文件名在INF中查找。此文件可在WDF的安装目录中找到。注意:在WDF的安装目录中会有3个WUDFUpdate_xxx.dll文件,x86,x6

电脑驱动分类

电脑驱动程序(驱动程序)是操作系统与硬件设备之间的桥梁,用于使操作系统能够识别并与硬件设备进行通信。以下是常见的驱动分类: 1. 设备驱动程序 显示驱动程序:控制显卡和显示器的显示功能,负责图形渲染和屏幕显示。 示例:NVIDIA、AMD 显示驱动程序。打印机驱动程序:允许操作系统与打印机通信,控制打印任务。 示例:HP、Canon 打印机驱动程序。声卡驱动程序:管理音频输入和输出,与声卡硬件

龙芯小本debian无线(wifi)连接设置

本人有一台龙芯8089b小本,已被我重装了debian系统。由于学习nodeJs,所以想用它当node服务器; 正准备下载node等时发现命令模式下不能自动连接无线,于是网上找来一篇debian连接wifi的文章, 来源参考http://blog.163.com/wangpeng922@126/blog/static/64133083201211131100539/ 查看网卡信息:#

龙芯小本8089b安装debian7+java+tomcat+mysql

之前团购了一个龙芯小本8089b,cpu很差劲,内存也只有1G,根本不能用来娱乐,于是想把它换个纯字符系统,然后搭建java服务器平台,用作局域网固定的mini服务器开发用。 以下是我搭建过程,当然实际比这做的多,这是多次尝试之后的成功过程,分享给大家,自己也做个笔记 debian7纯字符系统安装龙芯专有java安装tomcat安装mysql安装 一、debian7安装参考 圣域☆

麒麟系统安装GPU驱动

1.nvidia 1.1显卡驱动 本机显卡型号:nvidia rtx 3090 1.1.1下载驱动 打开 https://www.nvidia.cn/geforce/drivers/ 也可以直接使用下面这个地址下载 https://www.nvidia.com/download/driverResults.aspx/205464/en-us/ 1.1.3安装驱动 右击,

龙芯+FreeRTOS+LVGL实战笔记(新)——05部署主按钮

本专栏是笔者另一个专栏《龙芯+RT-Thread+LVGL实战笔记》的姊妹篇,主要的区别在于实时操作系统的不同,章节的安排和任务的推进保持一致,并对源码做了改进和优化,各位可以先到本人主页下去浏览另一专栏的博客列表(目前已撰写36篇,图1所示),再决定是否订阅。此外,也可以前往本人在B站的视频合集(图2所示)观看所有演示视频,合集首个视频链接为: 借助RT-Thread和LVGL

windows10 卸载网络驱动以及重新安装

右键桌面此电脑的图标,点击管理,设备管理器—网络适配器,找到下图中的驱动(不同的系统或者显卡会导致网卡驱动名称与下图不一样,多为Realtek开头),右键选择卸载设备,然后重启电脑,系统会自动重新安装驱动 新电脑首次安装驱动: 根据主板厂家,比如华硕,进入华硕官网,点击服务支持,点击下载中心,选择型号,点击右侧驱动程序和工具软件,选择windows版本,下载相应的驱动,下载完之后在对应文件中找