单片机:实现IO口单线通讯(附带源码)

单片机:实现IO口单线通讯

1. 项目背景与目标

在嵌入式系统中,单线通讯(One-Wire)是一种常见的串行通讯协议,广泛用于低速设备间的数据传输。通过单一的信号线传输数据,通常还需要与地(GND)共享一条公共线,且通讯的实现方式通常非常简单。常见的应用包括温度传感器(如DS18B20)、EEPROM存储器、RFID设备等。

本项目的目标是通过单片机的GPIO引脚实现IO口的单线通讯协议,使单片机能够与支持单线通讯的设备(如DS18B20温度传感器)进行数据交换。

2. 硬件设计
2.1 硬件组件
  1. 单片机:例如STM32、8051、AVR等,本项目假设使用STM32单片机。
  2. 单线通讯设备:如DS18B20温度传感器,支持通过单一的线与单片机进行通讯。
  3. 外部电源:为单片机和单线设备提供必要的电源。
  4. 电阻:通常单线通讯协议需要一个上拉电阻来确保信号稳定,通常使用4.7kΩ的电阻。
2.2 硬件连接
  1. 单线通讯引脚连接

  2. 将DS18B20的单线数据引脚连接到STM32的一个GPIO引脚(例如PA0),该引脚用于收发数据。
  3. DS18B20的VDD引脚连接到3.3V电源,GND引脚连接到地(GND)。
  4. 在数据引脚上串联一个4.7kΩ的上拉电阻,将其连接到3.3V电源。
  5. 单片机电源:通过外部电源或USB提供单片机的工作电压。

3. 软件设计
3.1 单线通讯协议

单线通讯协议基于以下几个核心操作:

  1. 初始化通信:通过向设备发送“复位脉冲”来初始化设备。
  2. 发送和接收数据:通过数据线上的电平变化来传输数据,1代表高电平,0代表低电平。
  3. 时序控制:单线通讯协议需要严格的时序控制,确保主机和从机同步。
3.2 程序设计思路
  1. 初始化IO口:将用于单线通讯的GPIO配置为开漏输出,并启用上拉电阻。
  2. 发送和接收数据:通过精确控制GPIO口的电平变化,实现数据的发送和接收。
  3. 数据协议实现:实现对单线通讯协议的支持,如设备复位、命令发送、数据读取等。
  4. 时序控制:在数据传输过程中严格控制时间,确保信号的准确传递。
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 代码解释
  1. IO口初始化

  2. OW_Init()函数用于初始化GPIO引脚,配置为开漏输出模式,并启用上拉电阻。该引脚用于与单线设备进行通讯。
  3. 数据读写

  4. OW_WriteBit()OW_ReadBit()分别用于发送和读取1位数据。
  5. OW_WriteByte()OW_ReadByte()用于发送和读取一个字节的数据,按照单线通讯协议逐位进行传输。
  6. 复位与设备响应

  7. OW_Reset()函数用于复位单线设备,发送复位脉冲并等待设备响应。如果设备存在,则返回1,否则返回0。
  8. 温度读取

  9. DS18B20_ReadTemperature()函数通过单线通讯协议读取DS18B20的温度数据。先发送转换命令,等待温度转换完成,然后读取温度数据。
  10. 主循环

  11. 在主循环中,每1秒读取一次温度并显示在串口终端。
4. 总结

通过本项目实现了基于单片机的单线通讯协议,使用STM32单片机与DS18B20温度传感器进行了通讯。通过GPIO口控制时序,实现了数据的发送与接收。此项目为后续复杂的嵌入式单线设备通讯提供了基础框架,可以扩展到更多单线设备(如内存模块、传感器等)的支持。

作者:Katie。

物联沃分享整理
物联沃-IOTWORD物联网 » 单片机:实现IO口单线通讯(附带源码)

发表回复