Linux 驱动开发 - 编写 DS18B20 驱动
- 2022 年 4 月 06 日
本文字数:3281 字
阅读完需:约 11 分钟
1. 前言
当前文章介绍如何在 Linux 系统下编写一个 DS18B20 温度传感器驱动,测量环境温度,并将 DS18B20 注册成字符设备,通过文件接口将温度数据传递给应用层。
当前使用的开发板是友善之臂的 Tiny4412 开发板,CPU 是三星的 Exynos-4412,主频是 4 核 1.5GHZ,当前运行的 Linux 内核版本是 3.5。使用的温度传感器是 DS18B20,是一个数字温度传感器,非常经典的一款温度传感器,常年应用在各大高校毕设、实验室、毕设、课设场景。DS1820 接线比较简单,只需要一根线就行,加上两根电源线,一共 3 根线,并且 DS18B20 支持硬件序列号寻址,支持一个 IO 口上挂载多个 DS18B20。
2. DS18B20 介绍
DS18B20 特性:
(1)全数字温度转换及输出。(2)先进的单总线数据通信。(3)最高 12 位分辨率,精度可达土 0.5 摄氏度。(4)12 位分辨率时的最大工作周期为 750 毫秒。(5)可选择寄生工作方式。(6)检测温度范围为–55° C ~+125° C (–67° F ~+257° F)(7)内置 EEPROM,限温报警功能。(8)64 位光刻 ROM,内置产品序列号,方便多机挂接。(9)多样封装形式,适应不同硬件系统。
DS18B20 引脚功能
GND 电压地
DQ 单数据总线
VDD 电源电压
NC 空引脚
DS18B20 读取温度的步骤:
发送复位信号-->
检测回应信号--->
发送0xCC-->发送0x44->
发送复位信号—>
检测回应信号—>
写0xcc--->
写0xbe--->
循环8次读取温度低字节--->
循环8次读取温度高字节---->
打印温度信息
DS18B20 温度转换示例:
u16 temp;
u8 TL,TH;
u16 intT,decT; //温度值的整数和小数部分
TL=DS18B20_Read_Byte(); //读取温度低8位LSB
TH=DS18B20_Read_Byte(); //读取温度高8位MSB
temp=((u16)TH<<8)|TL; //将读出的温度高低位组合成16位的值
intT = temp >> 4; //分离出温度值整数部分
decT = temp & 0xF; //分离出温度值小数部分
printf("A: %d.%d\r\n",(int)intT,(int)decT); //打印实际温度值
3. 硬件接线图
Tiny4412 开发板扩展 GPIO 口:
4. 示例代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h> /*杂项字符设备头文件*/
#include <linux/fs.h> /*文件操作集合*/
#include <linux/delay.h> /*延时函数*/
#include <linux/uaccess.h>
#include <asm/io.h>
/*DS18B20 GPIO接口: GPB_4*/
/*定义指针,用于接收虚拟地址*/
volatile unsigned int *DS18B20_GPBCON;
volatile unsigned int *DS18B20_GPBDAT;
#define DS18B20_INPUT() {*DS18B20_GPBCON &= ~(0xf << 4 * 4);}
#define DS18B20_OUTPUT() {*DS18B20_GPBCON &= ~(0xf << 4 * 4);*DS18B20_GPBCON |= (0x1 << 4 * 4);}
/*
函数功能:等待DS18B20的回应
返回1:未检测到DS18B20的存在
返回0:存在
*/
unsigned char DS18B20_Check(void)
{
unsigned char retry=0;
DS18B20_INPUT() ///SET PG11 INPUT
while((*DS18B20_GPBDAT & (1 << 4))&&retry<200)
{
retry++;
udelay(1);
};
if(retry>=200)return 1;
else retry=0;
while(!(*DS18B20_GPBDAT & (1 << 4))&&retry<240)
{
retry++;
udelay(1);
};
if(retry>=240)return 1;
return 0;
}
/*
从DS18B20读取一个位
返回值:1/0
*/
unsigned char DS18B20_Read_Bit(void) // read one bit
{
unsigned char data;
DS18B20_OUTPUT();
*DS18B20_GPBDAT &= ~(1 << 4);//输出0
udelay(2);
*DS18B20_GPBDAT |= (1 << 4);//输出1
DS18B20_INPUT()
udelay(12);
if((*DS18B20_GPBDAT & (1 << 4)))data=1;
else data=0;
udelay(50);
return data;
}
/*
从DS18B20读取一个字节
返回值:读到的数据
*/
unsigned char DS18B20_Read_Byte(void) // read one byte
{
unsigned char i,j,dat;
dat=0;
for(i=1;i<=8;i++)
{
j=DS18B20_Read_Bit();
dat=dat>>1;
if(j) //主机对总线采样的数 判断-------读数据-1就是1,否则就是0
dat|=0x80; //先收低位数据--一步一步向低位移动>>
}
return dat;
}
/*
写一个字节到DS18B20
dat:要写入的字节
*/
void DS18B20_Write_Byte(unsigned char dat)
{
unsigned char j;
unsigned char testb;
DS18B20_OUTPUT();
for(j=1;j<=8;j++)
{
testb=dat&0x01;
dat=dat>>1;
if(testb)
{
*DS18B20_GPBDAT &= ~(1 << 4);//输出0// Write 1
udelay(2);
*DS18B20_GPBDAT |= (1 << 4);//输出1
udelay(60);
}
else
{
*DS18B20_GPBDAT &= ~(1 << 4);//输出0// Write 0
udelay(60);
*DS18B20_GPBDAT |= (1 << 4);//输出1
udelay(2);
}
}
}
/*
从ds18b20得到温度值
精度:0.1C
返回值:温度值 (-550~1250)
*/
short DS18B20_Get_Temp(void)
{
unsigned short aaa;
unsigned char temp;
unsigned char TL,TH;
DS18B20_OUTPUT();
*DS18B20_GPBDAT &= ~(1 << 4);//输出0 //拉低DQ
udelay(750); //拉低750us
*DS18B20_GPBDAT |= (1 << 4);//输出1 //DQ=1
udelay(15); //15US
DS18B20_Check();
DS18B20_Write_Byte(0xcc);
DS18B20_Write_Byte(0x44);
DS18B20_OUTPUT();
*DS18B20_GPBDAT &= ~(1 << 4);//输出0 //拉低DQ
udelay(750); //拉低750us
*DS18B20_GPBDAT |= (1 << 4);//输出1 //DQ=1
udelay(15); //15US
DS18B20_Check();
DS18B20_Write_Byte(0xcc);// skip rom
DS18B20_Write_Byte(0xbe);// convert
TL=DS18B20_Read_Byte(); // LSB
TH=DS18B20_Read_Byte(); // MSB
aaa=((unsigned short)TH<<8)|TL;
return aaa;
}
/*
杂项字符设备注册示例----->DS18B20
*/
static int tiny4412_open(struct inode *my_inode, struct file *my_file)
{
/*映射物理地址*/
DS18B20_GPBCON=ioremap(0x11400040,4);
DS18B20_GPBDAT=ioremap(0x11400044,4);
printk("DS18B20初始化成功!\r\n");
/*设置ds18b20为输出模式*/
*DS18B20_GPBCON &= ~(0xf << 4 * 4);
*DS18B20_GPBCON |= (0x1 << 4 * 4);
return 0;
}
static int tiny4412_release(struct inode *my_inode, struct file *my_file)
{
/*释放虚拟地址*/
iounmap(DS18B20_GPBCON);
iounmap(DS18B20_GPBDAT);
printk("DS18B20释放成功\r\n");
return 0;
}
static ssize_t tiny4412_read(struct file *my_file, char __user *buf, size_t len, loff_t *loff)
{
/*读取温度信息*/
short temp=DS18B20_Get_Temp();
copy_to_user(buf,&temp,2); //拷贝温度至应用层
return 0;
}
static ssize_t tiny4412_write(struct file *my_file, const char __user *buf, size_t len, loff_t *loff)
{
return 0;
}
/*文件操作集合*/
static struct file_operations tiny4412_fops=
{
.open=tiny4412_open,
.read=tiny4412_read,
.write=tiny4412_write,
.release=tiny4412_release
};
/*
核心结构体
*/
static struct miscdevice tiny4412_misc=
{
.minor=MISC_DYNAMIC_MINOR, /*自动分配次设备号*/
.name="DS18B20", /*设备文件,指定/dev/生成的文件名称*/
.fops=&tiny4412_fops
};
static int __init DS18B20_dev_init(void)
{
/*杂项设备注册*/
misc_register(&tiny4412_misc);
return 0;
}
static void __exit DS18B20_dev_exit(void)
{
/*杂项设备注销*/
misc_deregister(&tiny4412_misc);
}
module_init(DS18B20_dev_init);
module_exit(DS18B20_dev_exit);
MODULE_LICENSE("GPL");
版权声明: 本文为 InfoQ 作者【DS小龙哥】的原创文章。
原文链接:【http://xie.infoq.cn/article/6b032e39b7b7901407e986577】。文章转载请联系作者。
DS小龙哥
之所以觉得累,是因为说的比做的多。 2022.01.06 加入
熟悉C/C++、51单片机、STM32、Linux应用开发、Linux驱动开发、音视频开发、QT开发. 目前已经完成的项目涉及音视频、物联网、智能家居、工业控制领域
评论