水表集中抄表器单片机实现方案

一、系统总体架构

复制代码
┌─────────────────────────────────────────────────────────┐
│                  水表集中抄表器                           │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐  │
│  │  下行采集层  │    │  数据处理层  │    │  上行通信层  │  │
│  │             │    │             │    │             │  │
│  │ • MBus总线  │    │ • 协议解析  │    │ • GPRS/4G   │  │
│  │ • RS485接口 │    │ • 数据存储  │    │ • WiFi/LoRa │  │
│  │ • 脉冲采集  │    │ • 异常处理  │    │ • NB-IoT    │  │
│  │ • 红外通信  │    │ • 远程升级  │    │ • 以太网    │  │
│  └─────────────┘    └─────────────┘    └─────────────┘  │
│                                                         │
│  ┌─────────────────────────────────────────────────────┐│
│  │               核心控制单元(MCU)                     ││
│  │  • 实时调度  • 功耗管理  • 安全加密  • 看门狗        ││
│  └─────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────┘

二、单片机选型对比

方案 型号 优势 劣势 适用场景
STM32L4系列 STM32L476RG 超低功耗,丰富外设 成本中等 电池供电,长期运行
GD32L233 GD32L233KC 国产,性价比高 生态相对新 成本敏感项目
HC32L136 HC32L136KCTA 极低功耗,内置LCD 资料较少 显示需求场景
ESP32-C3 ESP32-C3-MINI 集成WiFi,RISC-V 功耗较高 市电供电,需无线

推荐方案STM32L476RG(主频80MHz,休眠<5μA,外设齐全)


三、硬件接口设计

1、下行采集接口

c 复制代码
/* 硬件资源配置 */
#define MBUS_PORT       GPIOA
#define MBUS_TX_PIN     GPIO_PIN_9    // USART1_TX
#define MBUS_RX_PIN     GPIO_PIN_10   // USART1_RX
#define MBUS_DE_PIN     GPIO_PIN_11   // RS485方向控制

#define PULSE_PORT      GPIOB
#define PULSE_CH1_PIN   GPIO_PIN_0    // 脉冲输入1
#define PULSE_CH2_PIN   GPIO_PIN_1    // 脉冲输入2
#define PULSE_CH3_PIN   GPIO_PIN_2    // 脉冲输入3
#define PULSE_CH4_PIN   GPIO_PIN_3    // 脉冲输入4

#define IR_PORT         GPIOC
#define IR_TX_PIN       GPIO_PIN_6    // 红外发射
#define IR_RX_PIN       GPIO_PIN_7    // 红外接收

2、上行通信接口

c 复制代码
/* 通信模块接口 */
#define GPRS_UART       USART2
#define WIFI_UART       USART3
#define LORA_UART       UART4
#define ETH_MAC         ETH

/* 状态指示 */
#define LED_POWER       GPIO_PIN_12   // 电源指示
#define LED_RUN         GPIO_PIN_13   // 运行指示  
#define LED_COMM        GPIO_PIN_14   // 通信指示
#define LED_FAULT       GPIO_PIN_15   // 故障指示

四、核心数据结构

1、水表设备信息

c 复制代码
#pragma pack(push, 1)
typedef struct {
    uint32_t meter_id;        // 表号(8字节BCD)
    uint8_t  meter_type;      // 表类型:01-冷水表,02-热水表
    uint8_t  protocol_type;   // 协议类型:01-CJ/T188,02-GB/T26831
    uint16_t comm_addr;       // 通信地址
    float    last_reading;    // 上次读数
    float    current_reading; // 当前读数
    uint32_t total_pulses;    // 累计脉冲数
    uint8_t  status;          // 状态:00-正常,01-倒流,02-漏水,03-拆除
    uint32_t install_time;    // 安装时间
    uint8_t  battery_level;   // 电池电量
} Meter_Info_t;
#pragma pack(pop)

2、抄表数据包

c 复制代码
typedef struct {
    uint8_t  cmd;             // 命令字
    uint16_t seq;            // 序列号
    uint8_t  meter_count;    // 表数量
    Meter_Info_t meters[32];  // 表数据数组
    uint16_t crc;            // CRC校验
} Meter_Data_Packet_t;

五、MBus总线通信实现

1、MBus协议帧格式(CJ/T188)

c 复制代码
/* CJ/T188协议帧 */
typedef struct {
    uint8_t  start;           // 起始符 0x68
    uint8_t  addr[7];         // 地址域
    uint8_t  ctrl;            // 控制码
    uint8_t  length;          // 数据长度
    uint8_t  data[252];       // 数据域
    uint8_t  cs;              // 校验和
    uint8_t  end;             // 结束符 0x16
} CJ188_Frame_t;

2、MBus主机驱动

c 复制代码
#include "stm32l4xx_hal.h"

#define MBUS_BAUDRATE        2400
#define MBUS_TIMEOUT_MS      3000

UART_HandleTypeDef huart1;
TIM_HandleTypeDef htim2;

/* MBus初始化 */
void MBus_Init(void)
{
    huart1.Instance = USART1;
    huart1.Init.BaudRate = MBUS_BAUDRATE;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_TX_RX;
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    HAL_UART_Init(&huart1);
    
    /* 定时器用于超时检测 */
    HTIM2_Init();
}

/* MBus发送数据 */
HAL_StatusTypeDef MBus_SendFrame(uint8_t *data, uint16_t len)
{
    /* 使能发送 */
    HAL_GPIO_WritePin(MBUS_PORT, MBUS_DE_PIN, GPIO_PIN_SET);
    
    /* 发送数据 */
    HAL_StatusTypeDef ret = HAL_UART_Transmit(&huart1, data, len, 1000);
    
    /* 等待发送完成 */
    while(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TC) == RESET);
    
    /* 切换到接收 */
    HAL_GPIO_WritePin(MBUS_PORT, MBUS_DE_PIN, GPIO_PIN_RESET);
    
    return ret;
}

/* MBus接收数据 */
HAL_StatusTypeDef MBus_ReceiveFrame(uint8_t *buffer, uint16_t *len)
{
    uint32_t start_time = HAL_GetTick();
    uint16_t recv_len = 0;
    
    while((HAL_GetTick() - start_time) < MBUS_TIMEOUT_MS) {
        if(HAL_UART_Receive(&huart1, &buffer[recv_len], 1, 100) == HAL_OK) {
            recv_len++;
            if(buffer[recv_len-1] == 0x16 && recv_len > 10) {
                *len = recv_len;
                return HAL_OK;
            }
        }
    }
    
    *len = 0;
    return HAL_TIMEOUT;
}

3、水表抄表核心函数

c 复制代码
/* 抄读单个水表 */
Meter_Info_t Read_Single_Meter(uint32_t meter_id)
{
    Meter_Info_t meter_info = {0};
    CJ188_Frame_t tx_frame, rx_frame;
    uint8_t buffer[256];
    uint16_t len;
    
    /* 构建抄表命令帧 */
    tx_frame.start = 0x68;
    memcpy(tx_frame.addr, &meter_id, 4);
    tx_frame.ctrl = 0x01;  // 读数据命令
    tx_frame.length = 0x02;
    tx_frame.data[0] = 0x90;  // 当前累积流量
    tx_frame.data[1] = 0x1F;
    tx_frame.cs = Calculate_CS(&tx_frame);
    tx_frame.end = 0x16;
    
    /* 发送并接收 */
    MBus_SendFrame((uint8_t*)&tx_frame, sizeof(tx_frame));
    if(MBus_ReceiveFrame(buffer, &len) == HAL_OK) {
        Parse_Meter_Response(buffer, &meter_info);
    }
    
    return meter_info;
}

/* 批量抄表 */
void Batch_Read_Meters(void)
{
    uint8_t meter_count = Get_Total_Meter_Count();
    
    for(uint8_t i = 0; i < meter_count; i++) {
        uint32_t meter_id = Get_Meter_ID(i);
        Meter_Info_t meter_data = Read_Single_Meter(meter_id);
        
        /* 存储到Flash */
        Save_Meter_Data(&meter_data);
        
        /* 异常检测 */
        Check_Meter_Exception(&meter_data);
        
        /* 延时避免总线冲突 */
        HAL_Delay(100);
    }
}

六、脉冲计数实现(备用方案)

c 复制代码
/* 脉冲计数器 */
typedef struct {
    uint32_t pulse_count;     // 脉冲计数
    uint32_t last_time;       // 上次脉冲时间
    float    flow_rate;       // 瞬时流量
    uint8_t  pulse_per_liter; // 每升脉冲数
} Pulse_Counter_t;

Pulse_Counter_t pulse_counters[4];

/* 外部中断回调 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    uint32_t current_time = HAL_GetTick();
    
    if(GPIO_Pin == PULSE_CH1_PIN) {
        pulse_counters[0].pulse_count++;
        
        /* 计算瞬时流量 */
        uint32_t time_diff = current_time - pulse_counters[0].last_time;
        if(time_diff > 0) {
            pulse_counters[0].flow_rate = 
                (3600000.0f / time_diff) / pulse_counters[0].pulse_per_liter;
        }
        pulse_counters[0].last_time = current_time;
    }
}

七、数据存储与管理

1、Flash存储结构

c 复制代码
/* Flash存储布局 */
#define FLASH_BASE_ADDR    0x08080000
#define METER_DATA_ADDR    (FLASH_BASE_ADDR + 0x0000)
#define SYSTEM_CONFIG_ADDR (FLASH_BASE_ADDR + 0x4000)
#define LOG_DATA_ADDR      (FLASH_BASE_ADDR + 0x6000)

/* 系统配置 */
typedef struct {
    uint32_t device_id;           // 设备ID
    uint8_t  network_mode;        // 网络模式:01-GPRS,02-WiFi,03-NB-IoT
    uint8_t  read_interval;       // 抄表间隔(小时)
    uint8_t  upload_interval;     // 上传间隔(小时)
    uint8_t  alarm_enable;        // 报警使能
    uint16_t low_flow_threshold;  // 小流量阈值(L/h)
    uint16_t leak_time_threshold; // 漏水判断时间(分钟)
} System_Config_t;

2、数据压缩存储

c 复制代码
/* 日冻结数据压缩存储 */
typedef struct {
    uint16_t date;           // 日期:YYYYMMDD格式
    uint32_t total_flow;     // 当日总流量(L)
    uint16_t max_flow;       // 最大瞬时流量(L/h)
    uint16_t avg_flow;       // 平均流量(L/h)
    uint8_t  alarm_flags;    // 报警标志
} Daily_Freeze_Data_t;

/* 存储到Flash */
void Save_Daily_Freeze(Daily_Freeze_Data_t *data)
{
    uint32_t addr = LOG_DATA_ADDR + 
                   (Get_Days_Since_Install() * sizeof(Daily_Freeze_Data_t));
    
    HAL_FLASH_Unlock();
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr, *(uint64_t*)data);
    HAL_FLASH_Lock();
}

八、通信协议实现

1、与平台通信协议

c 复制代码
/* 上报数据包 */
typedef struct {
    uint8_t  header[4];      // "MTR\xAA"
    uint32_t device_id;     // 集中器ID
    uint32_t timestamp;     // 时间戳
    uint16_t data_len;      // 数据长度
    uint8_t  data[1024];    // 压缩数据
    uint16_t crc16;         // CRC16校验
} Upload_Packet_t;

/* 数据压缩上传 */
void Upload_Meter_Data(void)
{
    Upload_Packet_t packet = {0};
    
    packet.header[0] = 'M'; packet.header[1] = 'T';
    packet.header[2] = 'R'; packet.header[3] = 0xAA;
    packet.device_id = system_config.device_id;
    packet.timestamp = Get_RTC_Time();
    
    /* 压缩当天数据 */
    Compress_Daily_Data(packet.data, &packet.data_len);
    
    /* 计算CRC */
    packet.crc16 = CRC16_Calculate(packet.data, packet.data_len);
    
    /* 通过GPRS上传 */
    GPRS_Send_Data((uint8_t*)&packet, sizeof(packet));
}

2、远程控制指令

c 复制代码
/* 远程控制命令 */
typedef enum {
    CMD_READ_METER = 0x01,      // 即时抄表
    CMD_SET_INTERVAL = 0x02,    // 设置抄表间隔
    CMD_REBOOT = 0x03,          // 重启设备
    CMD_FACTORY_RESET = 0x04,   // 恢复出厂设置
    CMD_FIRMWARE_UPDATE = 0x05, // 固件升级
    CMD_CALIBRATE = 0x06        // 校准参数
} Remote_Cmd_t;

/* 命令处理 */
void Process_Remote_Command(uint8_t *cmd_data, uint16_t len)
{
    Remote_Cmd_t cmd = (Remote_Cmd_t)cmd_data[0];
    
    switch(cmd) {
        case CMD_READ_METER:
            Immediate_Read_All_Meters();
            break;
        case CMD_SET_INTERVAL:
            Set_Read_Interval(cmd_data[1]);
            break;
        case CMD_REBOOT:
            NVIC_SystemReset();
            break;
        case CMD_FIRMWARE_UPDATE:
            Start_Firmware_Update(&cmd_data[1], len-1);
            break;
        default:
            Send_Error_Response(0x01);  // 未知命令
            break;
    }
}

九、低功耗管理

1、功耗模式设计

c 复制代码
/* 功耗模式 */
typedef enum {
    POWER_ACTIVE = 0,      // 活跃模式:全速运行
    POWER_SLEEP = 1,       // 睡眠模式:CPU停止,外设运行
    POWER_STOP = 2,        // 停止模式:大部分外设停止
    POWER_STANDBY = 3      // 待机模式:最低功耗
} Power_Mode_t;

/* 动态功耗管理 */
void Power_Management(void)
{
    static uint32_t last_activity = 0;
    uint32_t current_time = HAL_GetTick();
    
    if(current_time - last_activity > 300000) {  // 5分钟无活动
        Enter_Stop_Mode();
    } else if(current_time - last_activity > 60000) {  // 1分钟无活动
        Enter_Sleep_Mode();
    }
}

/* 进入停止模式 */
void Enter_Stop_Mode(void)
{
    /* 关闭非必要外设 */
    __HAL_RCC_USART1_CLK_DISABLE();
    __HAL_RCC_TIM2_CLK_DISABLE();
    
    /* 配置唤醒源 */
    HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 30, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
    
    /* 进入STOP2模式 */
    HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
    
    /* 唤醒后恢复 */
    SystemClock_Config();
    __HAL_RCC_USART1_CLK_ENABLE();
    __HAL_RCC_TIM2_CLK_ENABLE();
}

十、异常检测与报警

c 复制代码
/* 异常检测算法 */
typedef struct {
    uint8_t reverse_flow_alarm;    // 倒流报警
    uint8_t leak_detection_alarm;   // 漏水检测
    uint8_t tamper_alarm;           // 拆卸报警
    uint8_t low_battery_alarm;      // 低电量报警
    uint8_t communication_alarm;    // 通信异常报警
} Alarm_Status_t;

/* 智能漏水检测 */
void Leak_Detection_Algorithm(void)
{
    static uint32_t leak_start_time = 0;
    static float baseline_flow = 0;
    
    float current_flow = Get_Current_Flow_Rate();
    
    /* 建立基线流量 */
    if(baseline_flow == 0) {
        baseline_flow = current_flow;
    }
    
    /* 检测持续小流量 */
    if(current_flow > system_config.low_flow_threshold && 
       current_flow < baseline_flow * 0.5f) {
        
        if(leak_start_time == 0) {
            leak_start_time = HAL_GetTick();
        } else if(HAL_GetTick() - leak_start_time > 
                  system_config.leak_time_threshold * 60000) {
            Set_Alarm_Flag(ALARM_LEAK_DETECTION);
            Send_Alarm_To_Platform(ALARM_LEAK_DETECTION);
        }
    } else {
        leak_start_time = 0;  // 重置计时
    }
}

十一、完整主程序框架

c 复制代码
int main(void)
{
    /* 系统初始化 */
    HAL_Init();
    SystemClock_Config();
    
    /* 外设初始化 */
    MBus_Init();
    Pulse_Init();
    Storage_Init();
    Communication_Init();
    RTC_Init();
    Watchdog_Init();
    
    /* 加载系统配置 */
    Load_System_Config();
    
    /* 创建任务 */
    osThreadNew(Read_Meter_Task, NULL, &read_meter_attr);
    osThreadNew(Data_Upload_Task, NULL, &upload_task_attr);
    osThreadNew(Power_Manage_Task, NULL, &power_task_attr);
    osThreadNew(Alarm_Check_Task, NULL, &alarm_task_attr);
    
    /* 启动RTOS */
    osKernelStart();
    
    while(1) {
        /* 主循环 - 低功耗等待 */
        __WFI();
    }
}

参考代码 水表集中抄表器单片机实现 www.youwenfan.com/contentcsv/72707.html

十二、测试验证要点

测试项目 测试方法 合格标准
MBus通信稳定性 连续抄表1000次 成功率>99.5%
功耗测试 电池供电72小时 平均电流<50μA
数据存储可靠性 断电重启测试 数据无丢失
网络重连能力 断网恢复测试 30秒内重连
异常检测准确性 模拟各种故障 误报率<1%

十三、成本与BOM建议

器件 型号 单价 备注
MCU STM32L476RG ¥18 主控制器
MBus收发器 TSS721A ¥12 MBus接口
电源管理 TPS61099 ¥6 升压/降压
存储 W25Q128 ¥8 Flash存储
通信模块 EC200S ¥45 4G Cat.1
外壳 定制 ¥15 IP65防护
相关推荐
iCxhust11 小时前
8086 Proteus 8253制作跑表
单片机·嵌入式硬件·proteus·微机原理·8088单板机
xiangw@GZ11 小时前
HDI 高密度互连板阶数的深度理解
服务器·单片机·嵌入式硬件
@残梦12 小时前
200、stm32定义缓冲区用在DMA上时,需要谨记4字节地址对齐规则
stm32·单片机·嵌入式硬件
深圳市尚想信息技术有限公司13 小时前
HTW1000 烧录器/仿真器 TENX(十速)/海速芯 MCU在线/串联烧录器 单片机开发 嵌入式系统应用
单片机·烧录器·单片机开发·tenx·十速·海速芯
嵌入式-老费14 小时前
esp开发与应用(驱动步进电机)
单片机·嵌入式硬件
PegasusYu14 小时前
STM32 SPI 访问配置霍尔磁编码器KTH7823
stm32·单片机·嵌入式硬件
ji1985944314 小时前
STM32 舵机控制程序(基于标准外设库)
stm32·单片机·嵌入式硬件
星夜夏空9914 小时前
STM32单片机学习(27) —— SPI相关概念
stm32·单片机·学习
Hello:CodeWorld15 小时前
PCIe(PCI Express)技术详解:架构、演进与实践
linux·嵌入式硬件·express