• DS18B20驱动编写--杂项设备框架注册


    DS18B20驱动编写–杂项设备框架注册

      设备驱动最通俗的解释就是“驱使硬件设备行动”。驱动与底层硬件直接打交道,按照硬件设备的具体工作方式,读写设备的寄存器,完成设备的轮询、中断处理、DMA通信,进行物理内存向虚拟内存的映射等,最终让通信设备能收发数据,让显示设备能显示文字和画面,让存储设备能记录文件和数据。
      驱动程序是应用层和硬件层的连接桥梁,应用层只管完成应用逻辑开发和界面设计,驱动层则处理硬件配置,实现应用层相关接口函数。
      杂项设备:字符设备类的一种,杂项设备主设备号为10。

    1.DS18B20简介

      DS18B20是Dallas半导体公司生产的数字温度传感器,是世界上第一片支持"一线总线"接口的温度传感器。测量温度范围为-55℃ ~ +125℃,精度为±0.5℃。分辨率为9 ~ 12位。支持3V ~ 5.5V输入电压。抗干扰能力强。
      每一个DSl820包括一个唯一的64位长的序号,该序号值存放在 DSl820 内部的 ROM(只读存贮器)中。开始8位是产品类型编码(DSl820 编码均为 10H) ,接着的 48位是每个器件唯一的序号,最后 8 位是前面 56 位的CRC(循环冗余校验)码。
    在这里插入图片描述

    引脚说明
    GND
    DQ数字信号脚
    VDD电源脚3V~5.5V

      DS18B20以9位数字量形式反应器件的温度值。
      DS18B20采用单总线通讯,与CPU之间只需要DQ脚相连,再和CPU之间共地即可。每一个DS18B20都有唯一的64位光刻ROM,因此可以在一根数据线上接多个DS18B20模块。

    • 单总线协议特性
    • 总线协议:一个数据线可挂载多个设备(DS18B20通过64位光刻ROM区分设备);
    • 半双工通讯:数据线上同一时间只能发送或者接收数据;

    2.DS18B20驱动时序

    2.1 发送复位脉冲和检测存在信号

    在这里插入图片描述
    在这里插入图片描述
      DS18B20初始化过程首先需要发送复位脉冲:至少480us的低电平信号。接下来释放总线,DS18B20开始返回存在信号:60~240us的低电平。最后释放总线,模块初始化完成。

    /*发送复位信号,检测存在脉冲*/
    static u8 ds18b20_CheckRst(void)
    {
    	u8 time=0;
    	DS18B20_OUT_MODE();/*输出模式*/
    	DS18B20_OUT(0);/*总线拉低*/
    	udelay(600);/*至少480us低电平*/
    	DS18B20_OUT(1);/*释放总线,恢复为空闲电平*/
    	udelay(15);
    	DS18B20_INPUT_MODE();/*输入模式*/
    	while(DS18B20_IN)
    	{
    		time++;
    		udelay(1);
    		if(time>=100)return 1;/*等待存在脉冲失败*/
    	}
    	time=0;
    	while(!DS18B20_IN)
    	{
    		time++;
    		udelay(1);
    		if(time>=250)return 2;//模块出错
    	}
    	return 0;	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    2.2 写一位数据时序

    在这里插入图片描述
      要实现写一字节数据,则首先要实现的是写一位数据时序。分为写1和写0。首先是总线拉低,产生写间隙(至少1us)。接着往数据线DQ上写入0或者1,周期时间为60us,最后释放总线(总线拉高,至少1us),至此,写数据完成。

    DS18B20_OUT_MODE();/*输出模式*/
    DS18B20_OUT(0);//总线拉低,产生写间隙时间
    udelay(2);
    if(dat&0x01)DS18B20_OUT(1);
    else DS18B20_OUT(0);
    udelay(60);//写周期时间
    DS18B20_OUT(1);//释放总线
    udelay(2);
    dat>>=1;//继续发送下一位数据
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.3 读一位数据时序

    在这里插入图片描述
      读数据首先需要主机产生读间隙:总线拉低,至少1us的低电平信号。接着释放总线,在15us内进行数据读取,读数据周期时间为60us,最后释放总线:总线拉高,至少1us时间。 至此,读一位数据完成。

    DS18B20_OUT_MODE();/*输出模式*/
    DS18B20_OUT(0);//总线拉低,产生读间隙时间
    udelay(2);
    DS18B20_INPUT_MODE();//配置为输入模式
    udelay(12);//等待数据到来
    data>>=1;
    if(DS18B20_IN)data|=0x80;
    udelay(50);//读数据时间
    DS18B20_OUT(1);//恢复总线为空闲电平
    udelay(2);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.DS18B20相关命令

    • 跳转指令0xCC
        这条指令允许控制器不需要提供64位光刻ROM就使用存储器操作命令,在总线上仅有一个DS18B20时使用,若有多个则会产生冲突。
    • 启动一次温度转换0x44
        此命令完成一次温度转换。执行此命令后,DS18B20保持等待状态。若总线在这条命令发送后跟着读间隙,而DS18B20正处于数据转换,则会输出一个0,若温度转换完成,则会输出1。若使用寄生电源,总线必须在这条命令发完后拉高总线,保存500ms。
    • 读取一次数据0xBE
        此命令用于读取暂存器中的内容,可连续读取9个字节数据。若只想读取温度数据,则只需要读取前两个字节即可。
      在这里插入图片描述
    • 读ROM 0x33
        此命令可以读取DS18B20的64位光刻ROM数据,此命令仅能在总线上一个设备的时候使用。
    • 匹配ROM 0x55
        此命令可以实现和DS18B20的ROM进行匹配,只有和DS1820的64位光刻ROM完全匹配才能响应后面存储器命令。此命令用于当总线上不止一个设备时使用。
    • 搜索ROM 0xF0
        当系统第一次启动时,无法确认总线上有多少个设备或者该设备的光刻ROM,搜索光刻ROM可以让控制器通过排除法识别总线上的所有设备的64位光刻ROM。

    4.DS18B20采集一次温度步骤

      采用外部电源供电,且总线上仅有一个DS18B20模块
    在这里插入图片描述

    5.编写DS18B20驱动,通过杂项设备注册

    • 开发平台

    开发平台:Ubuntu18.04
    编译器:arm-linux-gcc
    硬件平台:tiny4412基于Cortex-A9 4核1.5GHZ
    开发板内核:Linux3.5

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include 
    #include 
    #include 
    
    static unsigned int ds18b20_gpio=EXYNOS4_GPB(4);//GPB_4
    #define DS18B20_OUT_MODE() s3c_gpio_cfgpin(ds18b20_gpio,S3C_GPIO_OUTPUT)/*输出模式*/
    #define DS18B20_INPUT_MODE()  s3c_gpio_cfgpin(ds18b20_gpio,S3C_GPIO_INPUT)/*输入模式*/
    #define DS18B20_OUT(x)   gpio_set_value(ds18b20_gpio,(x))
    #define DS18B20_IN   		gpio_get_value(ds18b20_gpio)
    /*发送复位信号,检测存在脉冲*/
    static u8 ds18b20_CheckRst(void)
    {
    	u8 time=0;
    	DS18B20_OUT_MODE();/*输出模式*/
    	DS18B20_OUT(0);/*总线拉低*/
    	udelay(600);/*至少480us低电平*/
    	DS18B20_OUT(1);/*释放总线,恢复为空闲电平*/
    	udelay(15);
    	DS18B20_INPUT_MODE();/*输入模式*/
    	while(DS18B20_IN)
    	{
    		time++;
    		udelay(1);
    		if(time>=100)return 1;/*等待存在脉冲失败*/
    	}
    	time=0;
    	while(!DS18B20_IN)
    	{
    		time++;
    		udelay(1);
    		if(time>=250)return 2;//模块出错
    	}
    	return 0;	
    }
    /*写一个字节函数*/
    static void ds18b20_writeDat(u8 dat)
    {
    	int i=0;
    	DS18B20_OUT_MODE();/*输出模式*/
    	for(i=0;i<8;i++)
    	{
    		DS18B20_OUT(0);//总线拉低,产生写间隙时间
    		udelay(2);
    		if(dat&0x01)DS18B20_OUT(1);
    		else DS18B20_OUT(0);
    		udelay(60);//写周期时间
    		DS18B20_OUT(1);//释放总线
    		udelay(2);
    		dat>>=1;//继续发送下一位数据
    	}
    }
    /*读取1字节数据*/
    static u8 ds18b20_readDat(void)
    {
    	int i=0;
    	u8 data=0;
    	for(i=0;i<8;i++)
    	{
    		DS18B20_OUT_MODE();/*输出模式*/
    		DS18B20_OUT(0);//总线拉低,产生读间隙时间
    		udelay(2);
    		DS18B20_INPUT_MODE();//配置为输入模式
    		udelay(12);//等待数据到来
    		data>>=1;
    		if(DS18B20_IN)data|=0x80;
    		udelay(50);//读数据时间
    		DS18B20_OUT(1);//恢复总线为空闲电平
    		udelay(2);
    	}
    	return data;
    }
    
    /*获取一次温度数据*/
    static u16 ds18b20_GetTemp(void)
    {
    	u8 L,H;
    	u16 temp;
    	if(ds18b20_CheckRst())return 0xffff;
    	ds18b20_writeDat(0xcc);
    	ds18b20_writeDat(0x44);
    	while(ds18b20_readDat()!=0xff);/*等待温度转换完成*/
    	if(ds18b20_CheckRst())return 0xffff;
    	ds18b20_writeDat(0xcc);
    	ds18b20_writeDat(0xbe);/*读取一次温度*/
    	L=ds18b20_readDat();
    	H=ds18b20_readDat();
    	temp=H<<8|L;
    	return temp;
    }
    static int ds18b20_open(struct inode *inode, struct file *file)
    {
    	printk("open函数调用成功\n");
    	if(ds18b20_CheckRst())
    	{
    		printk("DS18B20初始化失败\n");
    	}
    	return 0;
    }
    static int ds18b20_release(struct inode *inode, struct file *file)
    {
    	printk("release函数调用成功");
    	return 0;
    }
    long ds18b20_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    {
    	int dir=_IOC_DIR(cmd);/*数据读写方向,00无参数,10用户层读,01用户层写,11可读写*/
    	int type=_IOC_TYPE(cmd);/*魔术,标志符*/
    	int size=_IOC_SIZE(cmd);/*arg的字节数*/
    	printk("dir=%d,type=%c,size=%d\n",dir,type,size);
    	int ret;
    	u16 temp=ds18b20_GetTemp();/*获取一次温度*/
    	ret=copy_to_user((void *)arg, &temp,size);
    	return 4-ret;
    }
    static  struct file_operations ds18b20_fops=
    {
    	.open			=ds18b20_open,
    	.release		=ds18b20_release,
    	.unlocked_ioctl	=ds18b20_ioctl
    };
    
    /*杂项设备结构体*/
    static struct miscdevice ds18b20_drv=
    {
    	.minor	=MISC_DYNAMIC_MINOR,/*255,有内核自动分配*/
    	.name	="ds18b20",//设备节点名字
    	.fops	=&ds18b20_fops,//文件操作集合
    };
    static int __init wbyq_ds18b20_init(void)
    {
    	/*1.GPIO注销*/
    	gpio_free(ds18b20_gpio);
    	/*2.注册GPIO*/
    	gpio_request(ds18b20_gpio,"DS18B20");
    	/*配置GPIO模式*/
    	s3c_gpio_cfgpin(ds18b20_gpio,S3C_GPIO_OUTPUT);
    	gpio_set_value(ds18b20_gpio,1);/*上拉*/
    	/*注册杂项设备*/
    	misc_register(&ds18b20_drv);
    	return 0;
    	
    }
    /*驱动释放*/
    static void __exit wbyq_ds18b20_cleanup(void)
    {
        printk("驱动出口,驱动注销成功\n");
    	/*注销杂项设备*/
    	misc_deregister(&ds18b20_drv);
    	/*注销GPIO*/
    	gpio_free(ds18b20_gpio);
    }
    module_init(wbyq_ds18b20_init);//驱动入口函数
    module_exit(wbyq_ds18b20_cleanup);//驱动出口函数
    
    MODULE_LICENSE("GPL");//驱动注册协议
    MODULE_AUTHOR("it_ashui");
    MODULE_DESCRIPTION("Exynos4 ds18b20 Driver");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165

    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    学会这 29 个 函数,你就是 Pandas 专家
    【C++】之模板初阶
    2022年0702 第八课 JAVA中的数据结构重中的集合
    ASP.NET Core 6框架揭秘实例演示[32]:错误页面的N种呈现方式
    【计算机网络】【《计算机网络·自顶向下方法(原书第7版)》笔记】第三章:运输层
    JAVA登录界面
    5. kafka单机版本
    《深入浅出.NET框架设计与实现》笔记6.2——ASP.NET Core应用程序多种运行模式之二——IIS 服务承载
    Java 面试题 —— 强类型语言和弱类型语言的区别
    钉钉内嵌H5遇到的一些问题
  • 原文地址:https://blog.csdn.net/weixin_44453694/article/details/126770763