wire单总线通信
一、单总线介绍
1-Wire(单总线)是一种由Maxim Integrated(原Dallas Semiconductor)开发的串行通信协议,它允许通过单根信号线进行数据传输,同时传输时钟和数据,实现双向通信。这种协议具有节省I/O口资源、结构简单、成本低廉、便于总线扩展和维护等优点,广泛应用于温度传感器、湿度传感器、逻辑器件、时钟芯片等多种传感器和存储器中
二、DS18B20温度传感器
DS18B20采用达拉斯独家的1-Wire总线协议,使用一个控制信号实现总线通信。控制线需要一个弱上拉电阻,因为所有设备都通过三态或漏极端口(DS18B20中的DQ引脚)连接到总线。在这个总线系统中,微处理器(主设备)使用每个设备唯一的64位码来标识和寻址总线上的设备。因为每个设备都有一个唯一的码,所以可以在一个总线上寻址的设备数量实际上是无限的。
DS18B20的另一个特点是能够在没有外部电源的情况下运行。当总线为高电平时,电源通过DQ引脚通过1-Wire上拉电阻供电。高总线信号还为内部电容器(CPP)充电,然后在总线为低电平时为器件供电。这种从1-Wire总线获取电源的方法被称为“寄生电源”。作为替代方案,DS18B20
也可以由VDD上的外部电源供电
三、MEASURING TEMPERATURE(温度测量)
DS18B20的核心功能是其直接数字温度传感器。温度传感器的分辨率可由用户配置为9、10、11或12位,分别对应0.5℃、0.25℃、0.125℃和0.0625℃的增量。上电时的默认分辨率为12位DS18B20在低功耗空闲状态下上电;要启动温度测量和A到D转换,主机必须发出转换T[44h]命令。转换后,产生的热数据存储在暂存器中的2字节温度寄存器中,DS18B20返回到空闲状态。
温度数据在温度寄存器中存储为16位符号扩展的二进制补码。符号位(S)指示温度是正的还是负的:对于正数S=0,对于负数S=1。如果DS18B20配置为12位分辨率,温度寄存器中的所有位都将包含有效数据。
温度寄存器上电复位值为+85℃
对于11位分辨率,位0是未定义的。对于10位分辨率,位1和0是未定义的,对于9位分辨率,位2、1和0是未定义的。表2给出了12位分辨率转换的数字输出数据和相应温度读数的示例
三、DS18B20事务序列
The transaction sequence for accessing the DS18B20 is as follows:(访问DS18B20的事务顺序如下:)
- Step 1. Initialization(初始化)
- 1-Wire总线上的所有事务都从初始化序列开始。初始化序列由总线主站发送的复位脉冲和从站发送的存在脉冲组成。存在脉冲让总线主站知道从设备(如DS18B20)在总线上并且准备好操作
- Step 2. ROM Command(ROM命令)
- 总线主站检测到存在脉冲后,它可以发出ROM命令。这些命令对每个从设备的唯一64位ROM码进行操作,如果1-Wire总线上存在许多设备,则允许
- 主站单独选择特定设备。
- Step 3. DS18B20 Function Command(功能命令)
- 总线主站使用ROM命令寻址它希望与之通信的DS18B20后,主站可以发出DS18B20功能命令之一。
CONVERT T [44h]
此命令启动单次温度转换。转换后,生成的热数据存储在暂存器中的2字节温度寄存器中,DS18B20返回低功耗空闲状态。
READ SCRATCHPAD [BEh]
此命令允许主机读取暂存器的内容。数据传输从字节0的最低有效位开始,并继续通过暂存器,直到读取第9个字节(字节8-CRC)。如果只需要部分暂存器数据,主机可以随时发出重置以终止读取。
四、DS18B20操作时序
INITIALIZATION PROCEDURE: RESET AND PRESENCE
PULSES
与DS18B20的所有通信都以初始化序列开始,该序列由主设备的复位脉冲和DS18B20的存在脉冲组成。当DS18B20响应复位发送存在脉冲时,它向主设备表明它在总线上并准备好操作
在初始化序列中,总线主控器通过将1-Wire总线拉低至少480us来发送复位脉冲。然后总线主控器释放总线并进入接收模式(RX)。当总线释放时,5k上拉电阻将1-Wire总线拉高。当DS18B20检测到这种上升沿时,它等待15us到60us,然后通过将1-Wire总线拉低60us到240us来发送存在脉冲。
WRITE TIME SLOTS
有两种类型的写入时间槽:“Write 1”时间槽和“Write 0”时间槽。总线主控器使用“Write 1”时间槽将逻辑1写入DS18B20,使用“Write0”时间槽将逻辑0写入DS18B20。所有写入时间槽的最小持续时间为60微秒,相邻写入槽之间的最小恢复时间为1微秒。两种类型的写入时间槽均由主控器将1-Wire总线拉低来启动。
要生成一个Write 1时间槽,在将1-Wire总线拉低之后,总线主控器必须在15微秒内释放1-Wire总线。当总线被释放时,5k的上拉电阻会将总线拉高。要生成一个Write 0时间槽,在将1-Wire总线拉低之后,总线主控器必须在整个时间槽内(至少60微秒)继续将总线保持在低电平状态。
DS18B20在主机启动写入时间槽后的15us到60us的窗口中对1-Wire总线进行采样。如果在采样窗口中总线为高电平,则向DS18B20写入1。如果总线为低电平,则向DS18B20写入0。
READ TIME SLOTS
DS18B20只有在主控机发出读取时间槽时才能向主控机传输数据。因此,主控机必须在发出读取暂存器[BEh]或读取电源[B4h]命令后立即生成读取时间槽,以便DS18B20能够提供所请求的数据。此外,主控机可以在发出转换T[44h]或召回E2[B8h]命令后生成读取时间槽,以找出操作的状态,如DS18B20功能命令部分所述。所有读取时间槽的持续时间必须至少为60us,时间槽之间的恢复时间至少为1us。读取时间槽由主设备将1-Wire总线拉低至少1us,然后释放总线启动。
当主机启动读取时间槽后,DS18B20将开始在总线上传输1或0。DS18B20通过将总线保持在高电平来传输1,并通过将总线拉低来传输0。当传输0时,DS18B20将在时间槽结束时释放总线,总线将被上拉电阻拉回其高空闲状态。DS18B20的输出数据在启动读取时间槽的下降沿后对15us有效。因此,主机必须释放总线,然后从时间槽开始在15us内对总线状态进行采样。
五、GPIO控制器相关操作函数接口
ds18b20.h
#ifndef __DS18B20_HEAD__
#define __DS18B20_HEAD__
#include <stdio.h>
#include <stdint.h>
#include "../../include/imx6ull.h"
#include "../gpt/gpt.h"
#include "../uart/uart.h"
#define DS18B20_LOW_LEVEL 0
#define DS18B20_HIGH_LEVEL 1
#define DQ_OUT_MODE() (GPIO5->GDIR |= (0x1 << 2))
#define DQ_IN_MODE() (GPIO5->GDIR &= ~(0x1<<2))
extern void ds18b20_gpio_init();
extern uint_fast8_t ds18b20_read_level();
extern void ds18b20_write_bit(uint_fast8_t bit);
extern void ds18b20_write_byte(uint_fast8_t data);
extern uint_fast8_t ds18b20_read_bit();
extern uint_fast8_t ds18b20_read_byte();
extern int ds18b20_detect();
extern void ds18b20_read_roomcode();
extern void ds18b20_collect_temperature_data();
extern void test();
//输出电平状态
#define DQ_OUTPUT_LEVEL(level) do {GPIO5->DR &=~(0x1<<2);GPIO5->DR |= (level << 2);}while (0)
#endif // !__DS18B20_HEAD__
ds18b20.c
#include "ds18b20.h"
// 管脚的初始化
void ds18b20_gpio_init()
{
// 管脚初始化
//101 ALT5 — Slect mux mode: ALT5 mux port, GPIO5_IO02 of instance - gpio5
IOMUXC_SW_MUX_CTL_PAD_GPIO5_IO02 &= ~(0xf << 0);
IOMUXC_SW_MUX_CTL_PAD_GPIO5_IO02 |= (0x5 << 0);
}
// 读取DQ上的电平状态
uint_fast8_t ds18b20_read_level()
{
uint_fast8_t level;
// 从GPIO5->PSR中进行读取
level = GPIO5->PSR & (0x1 << 2);
return level ? 1 : 0;
}
void ds18b20_write_bit(uint_fast8_t bit)
{
// 先拉低然后再读取数据
/*DQ_OUT_MODE(有两种类型的写入时间槽:“Write 1”时间槽和“Write 0”时间槽。
总线主控器使用“Write 1”时间槽将逻辑1写入DS18B20,使用“Write
0”时间槽将逻辑0写入DS18B20。);*/
if (bit)
{
DQ_OUT_MODE();
DQ_OUTPUT_LEVEL(DS18B20_LOW_LEVEL);
usgpt_deplay(5);
DQ_IN_MODE();
usgpt_deplay(70);
}
else
{
DQ_OUT_MODE();
DQ_OUTPUT_LEVEL(DS18B20_LOW_LEVEL);
usgpt_deplay(80);
DQ_IN_MODE();
usgpt_deplay(40);
}
}
void ds18b20_write_byte(uint_fast8_t data)
{
int i;
for (i = 0; i < 8; i++)
{
ds18b20_write_bit((data >> i) & 1);
}
}
uint_fast8_t ds18b20_read_bit()
{
DQ_OUT_MODE();
DQ_OUTPUT_LEVEL(DS18B20_LOW_LEVEL);
usgpt_deplay(3);
DQ_IN_MODE();
usgpt_deplay(5);
uint_least8_t level = ds18b20_read_level();
usgpt_deplay(60);
return level;
}
uint_fast8_t ds18b20_read_byte()
{
uint_least8_t data = 0;
int i;
for (i = 0; i < 8; i++)
{
uint_least8_t bit = ds18b20_read_bit();
if (bit)
{
data |= (0x1 << i);
}
}
return data;
}
// 检测DS18B20温度传感器是否存在于1-Wire总线的函数ds18b20_detect()。
int ds18b20_detect()
{
DQ_OUT_MODE();
// 拉到低电平
DQ_OUTPUT_LEVEL(DS18B20_LOW_LEVEL);
usgpt_deplay(500);
// 转换模式后自动拉高
DQ_IN_MODE();
usgpt_deplay(100);
uint_fast8_t level = ds18b20_read_level();
if (level)
{
uart_printf("failure\r\n");
return -1;
}
else
{
uart_printf("success\r\n");
usgpt_deplay(500);
return 0;
}
}
void test(){
ds18b20_gpio_init();
ds18b20_detect();
}
// 读取ds18b20的room码
void ds18b20_read_roomcode()
{
// 管脚初始化
ds18b20_gpio_init();
int ret;
// 检测i_write是否存在
ret = ds18b20_detect();
if (ret == -1)
{
return;
}
ds18b20_write_byte(0x33);
uint_fast8_t id[8];
int i;
for (i = 0; i < 8; i++)
{
*(id + i) = ds18b20_read_byte();
}
// family code
uart_printf("family code:%4x\r\n", *id);
// serial number
int i1;
uart_printf("serial number:");
for (i1 = 1; i1 < 7; i1++)
{
uart_printf("CRC:%4x\r\n", *(id + 7));
}
}
// 采集温度
void ds18b20_collect_temperature_data()
{
while (1)
{
// 管脚初始化
ds18b20_gpio_init();
int ret1;
ret1 = ds18b20_detect();
if (ret1 == -1)
{
return;
}
ds18b20_write_byte(0xcc);
ds18b20_write_byte(0x44);
sgpt_deplay(1);
// 读取温度值
ds18b20_gpio_init();
int ret;
ret = ds18b20_detect();
if (ret == -1)
{
return;
}
// 主机可以通过发出SNIP ROM[CCH]命令后
ds18b20_write_byte(0xcc);
ds18b20_write_byte(0xBE);
// 读取温度值
uint_fast8_t temperature_lsb = ds18b20_read_byte();
uint_fast8_t temperature_msb = ds18b20_read_byte();
uart_printf("low:%x,high:%x\r\n", temperature_lsb, temperature_msb);
// 温度转换
// 先处理整数的情况
uint_fast8_t integer_part = (temperature_msb << 4) | (temperature_lsb >> 4);
// 处理符号位
uint_fast8_t signal = integer_part & (0x1 << 7);
if (signal)
{
integer_part = ~integer_part;
}
// 处理小数部分
uint_fast8_t decimal_part = temperature_lsb & 0xf;
if (signal)
{
decimal_part = ((~decimal_part) & 0xf) + 1;
}
// 扩大了1000倍
decimal_part *= 625;
// 打印温度
if (signal)
{
}
else
{
uart_printf("temperature:%d.%d\r\n", integer_part, decimal_part);
}
}
}
- 这是本人的学习笔记不是获利的工具,小作者会一直写下去,希望大家能多多监督我
- 文章会每攒够两篇进行更新发布(受平台原因,也是希望能让更多的人看见)
- 感谢各位的阅读希望我的文章会对诸君有所帮助