单片机:实现IO口单线通讯(附带源码)
单片机:实现IO口单线通讯
1. 项目背景与目标
在嵌入式系统中,单线通讯(One-Wire)是一种常见的串行通讯协议,广泛用于低速设备间的数据传输。通过单一的信号线传输数据,通常还需要与地(GND)共享一条公共线,且通讯的实现方式通常非常简单。常见的应用包括温度传感器(如DS18B20)、EEPROM存储器、RFID设备等。
本项目的目标是通过单片机的GPIO引脚实现IO口的单线通讯协议,使单片机能够与支持单线通讯的设备(如DS18B20温度传感器)进行数据交换。
2. 硬件设计
2.1 硬件组件
- 单片机:例如STM32、8051、AVR等,本项目假设使用STM32单片机。
- 单线通讯设备:如DS18B20温度传感器,支持通过单一的线与单片机进行通讯。
- 外部电源:为单片机和单线设备提供必要的电源。
- 电阻:通常单线通讯协议需要一个上拉电阻来确保信号稳定,通常使用4.7kΩ的电阻。
2.2 硬件连接
-
单线通讯引脚连接:
- 将DS18B20的单线数据引脚连接到STM32的一个GPIO引脚(例如PA0),该引脚用于收发数据。
- DS18B20的VDD引脚连接到3.3V电源,GND引脚连接到地(GND)。
- 在数据引脚上串联一个4.7kΩ的上拉电阻,将其连接到3.3V电源。
-
单片机电源:通过外部电源或USB提供单片机的工作电压。
3. 软件设计
3.1 单线通讯协议
单线通讯协议基于以下几个核心操作:
- 初始化通信:通过向设备发送“复位脉冲”来初始化设备。
- 发送和接收数据:通过数据线上的电平变化来传输数据,1代表高电平,0代表低电平。
- 时序控制:单线通讯协议需要严格的时序控制,确保主机和从机同步。
3.2 程序设计思路
- 初始化IO口:将用于单线通讯的GPIO配置为开漏输出,并启用上拉电阻。
- 发送和接收数据:通过精确控制GPIO口的电平变化,实现数据的发送和接收。
- 数据协议实现:实现对单线通讯协议的支持,如设备复位、命令发送、数据读取等。
- 时序控制:在数据传输过程中严格控制时间,确保信号的准确传递。
3.3 代码实现
以下是基于STM32单片机实现单线通讯协议的代码框架。假设我们使用DS18B20温度传感器来演示数据交换。
#include "stm32f4xx_hal.h"
// 定义单线通讯的GPIO引脚
#define OW_PIN GPIO_PIN_0
#define OW_PORT GPIOA
// 延时函数
void delay_us(uint32_t us) {
while (us--) {
__NOP();
}
}
// 初始化单线通讯引脚
void OW_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置单线引脚(PA0)为推挽输出模式
GPIO_InitStruct.Pin = OW_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 开漏输出
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上拉或下拉电阻
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(OW_PORT, &GPIO_InitStruct);
// 设置单线引脚为上拉电阻
HAL_GPIO_WritePin(OW_PORT, OW_PIN, GPIO_PIN_SET);
}
// 发送1位数据
void OW_WriteBit(uint8_t bit) {
if (bit) {
HAL_GPIO_WritePin(OW_PORT, OW_PIN, GPIO_PIN_RESET); // 拉低
delay_us(10); // 保持低电平10us
HAL_GPIO_WritePin(OW_PORT, OW_PIN, GPIO_PIN_SET); // 拉高
delay_us(50); // 高电平维持
} else {
HAL_GPIO_WritePin(OW_PORT, OW_PIN, GPIO_PIN_RESET); // 拉低
delay_us(50); // 保持低电平
HAL_GPIO_WritePin(OW_PORT, OW_PIN, GPIO_PIN_SET); // 拉高
delay_us(10); // 高电平维持
}
}
// 读取1位数据
uint8_t OW_ReadBit(void) {
uint8_t bit = 0;
HAL_GPIO_WritePin(OW_PORT, OW_PIN, GPIO_PIN_RESET); // 拉低
delay_us(2); // 保持低电平2us
HAL_GPIO_WritePin(OW_PORT, OW_PIN, GPIO_PIN_SET); // 拉高
delay_us(10); // 等待数据准备
// 读取数据
if (HAL_GPIO_ReadPin(OW_PORT, OW_PIN) == GPIO_PIN_SET) {
bit = 1;
} else {
bit = 0;
}
delay_us(40); // 等待数据稳定
return bit;
}
// 发送字节
void OW_WriteByte(uint8_t byte) {
for (uint8_t i = 0; i < 8; i++) {
OW_WriteBit(byte & 0x01);
byte >>= 1;
}
}
// 读取字节
uint8_t OW_ReadByte(void) {
uint8_t byte = 0;
for (uint8_t i = 0; i < 8; i++) {
byte |= (OW_ReadBit() << i);
}
return byte;
}
// 复位单线设备
uint8_t OW_Reset(void) {
uint8_t presence = 0;
// 发送复位信号
HAL_GPIO_WritePin(OW_PORT, OW_PIN, GPIO_PIN_RESET);
delay_us(500); // 拉低500us
HAL_GPIO_WritePin(OW_PORT, OW_PIN, GPIO_PIN_SET);
delay_us(50); // 拉高后等待50us
// 设备是否响应
if (HAL_GPIO_ReadPin(OW_PORT, OW_PIN) == GPIO_PIN_RESET) {
presence = 1; // 设备存在
}
delay_us(500); // 等待
return presence;
}
// 读取DS18B20温度
void DS18B20_ReadTemperature(void) {
uint8_t temp_LSB, temp_MSB;
// 复位设备并检查响应
if (OW_Reset()) {
// 向设备发送ROM命令(跳过设备地址)
OW_WriteByte(0xCC); // Skip ROM command
OW_WriteByte(0x44); // Convert T command
// 等待温度转换完成
HAL_Delay(1000);
// 向设备发送ROM命令(跳过设备地址)
OW_WriteByte(0xCC); // Skip ROM command
OW_WriteByte(0xBE); // Read Scratchpad command
// 读取温度数据
temp_LSB = OW_ReadByte();
temp_MSB = OW_ReadByte();
// 处理温度数据
int16_t temperature = ((int16_t)temp_MSB << 8) | temp_LSB;
float celsius = temperature * 0.0625; // DS18B20的温度转换公式
// 输出温度
printf("Temperature: %.2f °C\n", celsius);
} else {
printf("Device not found.\n");
}
}
int main(void) {
HAL_Init(); // 初始化HAL库
OW_Init(); // 初始化单线通讯引脚
while (1) {
DS18B20_ReadTemperature(); // 读取温度
HAL_Delay(1000); // 延时1秒
}
}
3.4 代码解释
-
IO口初始化:
OW_Init()
函数用于初始化GPIO引脚,配置为开漏输出模式,并启用上拉电阻。该引脚用于与单线设备进行通讯。-
数据读写:
OW_WriteBit()
和OW_ReadBit()
分别用于发送和读取1位数据。OW_WriteByte()
和OW_ReadByte()
用于发送和读取一个字节的数据,按照单线通讯协议逐位进行传输。-
复位与设备响应:
OW_Reset()
函数用于复位单线设备,发送复位脉冲并等待设备响应。如果设备存在,则返回1,否则返回0。-
温度读取:
DS18B20_ReadTemperature()
函数通过单线通讯协议读取DS18B20的温度数据。先发送转换命令,等待温度转换完成,然后读取温度数据。-
主循环:
- 在主循环中,每1秒读取一次温度并显示在串口终端。
4. 总结
通过本项目实现了基于单片机的单线通讯协议,使用STM32单片机与DS18B20温度传感器进行了通讯。通过GPIO口控制时序,实现了数据的发送与接收。此项目为后续复杂的嵌入式单线设备通讯提供了基础框架,可以扩展到更多单线设备(如内存模块、传感器等)的支持。
作者:Katie。