在操作DS18B20温度传感器时,单总线(1-Wire)需要根据通信阶段动态切换输入/输出模式。以下是配置建议和实现逻辑:
核心配置原则
- 输出模式:当主机(MCU)向DS18B20发送数据(复位脉冲、写命令)时
- 输入模式:当主机从DS18B20读取数据时
- 配合开漏输出:建议引脚配置为开漏模式(外部需接4.7KΩ上拉电阻)
配置步骤(以STM32 HAL库为例)
1. 引脚初始化(开漏模式)
void DS18B20_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使用PA0作为示例引脚
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 开漏输出
GPIO_InitStruct.Pull = GPIO_NOPULL; // 禁用内部上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 初始状态释放总线(高电平)
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
}
2. 模式切换宏定义
// 切换到输出模式(发送数据)
#define SET_BUS_OUTPUT() do {
GPIOA->MODER = (GPIOA->MODER & ~GPIO_MODER_MODER0) | (GPIO_MODER_MODER0_0);
} while(0)
// 切换到输入模式(接收数据)
#define SET_BUS_INPUT() do {
GPIOA->MODER &= ~GPIO_MODER_MODER0;
} while(0)
关键操作时序实现
复位脉冲(主机输出)
bool DS18B20_Reset(void) {
SET_BUS_OUTPUT(); // 配置为输出
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // 拉低总线
Delay_us(480); // 保持480μs以上
SET_BUS_INPUT(); // 切换为输入(释放总线)
Delay_us(70); // 等待15-60μs后检测应答
// 检测应答脉冲
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
Delay_us(480); // 等待从机释放总线
return true; // 复位成功
}
return false; // 无设备响应
}
写入数据位(主机输出)
void DS18B20_WriteBit(uint8_t bit) {
SET_BUS_OUTPUT(); // 配置输出
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // 拉低启动时隙
Delay_us(5); // 保持1μs以上
// 根据写入值决定总线状态
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, bit ? GPIO_PIN_SET : GPIO_PIN_RESET);
Delay_us(55); // 保持时隙
SET_BUS_INPUT(); // 释放总线
Delay_us(5); // 恢复时间
}
读取数据位(主机输入)
uint8_t DS18B20_ReadBit(void) {
uint8_t bit = 0;
SET_BUS_OUTPUT(); // 配置输出
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // 拉低启动时隙
Delay_us(2); // 保持1μs以上
SET_BUS_INPUT(); // 切换输入模式(关键!)
Delay_us(12); // 等待15μs内采样
bit = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
Delay_us(45); // 完成60μs时隙
return bit ? 1 : 0;
}
配置要点说明
开漏输出必要性:
- 允许多个设备共享总线
- 避免总线竞争时损坏器件
- 必须外接4.7KΩ上拉电阻
切换时机:
- 输出→输入:发送命令后准备接收数据前
- 输入→输出:检测到总线空闲需要发起通信时
时序精度:
- 使用微秒级延时函数(推荐SysTick或定时器实现)
- 严格遵循DS18B20时序要求(典型时隙60μs)
典型读取流程
float DS18B20_ReadTemp(void) {
uint8_t temp[2] = {0};
DS18B20_Reset();
DS18B20_WriteByte(0xCC); // 跳过ROM
DS18B20_WriteByte(0x44); // 启动转换
Delay_ms(750); // 等待转换完成
DS18B20_Reset();
DS18B20_WriteByte(0xCC);
DS18B20_WriteByte(0xBE); // 读暂存器
// 读取数据(期间自动切换输入/输出)
for(int i=0; i<2; i++) {
temp[i] = DS18B20_ReadByte();
}
return (temp[1]<<8 | temp[0]) * 0.0625; // 转换为℃
}
重要提示:实际开发中应添加超时判断和校验机制(CRC校验),并考虑多设备并存的场景。建议参考Maxim官方文档《AN126:1-Wire通信时序指南》优化时序控制。