Compare commits
No commits in common. "main" and "v1.4.0" have entirely different histories.
|
|
@ -1,27 +0,0 @@
|
||||||
# 旧版本工程与手册已移除(历史见 git tag v1.1.0~v1.4.0、v1.4-chinese)
|
|
||||||
*.zip
|
|
||||||
|
|
||||||
# 调试日志
|
|
||||||
debug.txt
|
|
||||||
|
|
||||||
# Keil 编译产物(keilkill.bat 可清理,不入库)
|
|
||||||
Object/
|
|
||||||
OutputBin/
|
|
||||||
Log/
|
|
||||||
*.crf
|
|
||||||
*.d
|
|
||||||
*.o
|
|
||||||
*.axf
|
|
||||||
*.hex
|
|
||||||
*.lnp
|
|
||||||
*.lst
|
|
||||||
*.map
|
|
||||||
*.sct
|
|
||||||
*.dep
|
|
||||||
*.htm
|
|
||||||
*.bak
|
|
||||||
*.scvd
|
|
||||||
*.uvguix.*
|
|
||||||
JLinkSettings.ini
|
|
||||||
JLinkLog.txt
|
|
||||||
STM32F103rb_bootloader/
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,95 +0,0 @@
|
||||||
#include "adc.h"
|
|
||||||
#include "hlw8032.h"
|
|
||||||
#include "oled.h"
|
|
||||||
#include "main.h"
|
|
||||||
#include "network.h"
|
|
||||||
#include "led.h"
|
|
||||||
#include "reset_log.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
ADC_HandleTypeDef hadc1;
|
|
||||||
|
|
||||||
void adcInit(void)
|
|
||||||
{
|
|
||||||
ADC_ChannelConfTypeDef sConfig = {0};
|
|
||||||
hadc1.Instance = ADC1;
|
|
||||||
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
|
|
||||||
hadc1.Init.ContinuousConvMode = DISABLE;
|
|
||||||
hadc1.Init.DiscontinuousConvMode = DISABLE;
|
|
||||||
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
|
|
||||||
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
|
|
||||||
hadc1.Init.NbrOfConversion = 1;
|
|
||||||
if (HAL_ADC_Init(&hadc1) != HAL_OK) Error_Handler();
|
|
||||||
|
|
||||||
sConfig.Channel = ADC_CHANNEL_0;
|
|
||||||
sConfig.Rank = ADC_REGULAR_RANK_1;
|
|
||||||
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
|
|
||||||
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t getAdc(void)
|
|
||||||
{
|
|
||||||
uint16_t adc_value = 0;
|
|
||||||
HAL_ADC_Start(&hadc1);
|
|
||||||
if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK) {
|
|
||||||
adc_value = HAL_ADC_GetValue(&hadc1);
|
|
||||||
}
|
|
||||||
HAL_ADC_Stop(&hadc1);
|
|
||||||
return adc_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_batter(void)
|
|
||||||
{
|
|
||||||
static _Bool powoff = 0;
|
|
||||||
uint16_t adc_value = getAdc();
|
|
||||||
float batter_valtage = adc_value * (3.3 / 4096.0);
|
|
||||||
batter_valtage = batter_valtage * 11.0;
|
|
||||||
batter_valtage += 0.6;
|
|
||||||
batter_valtage = 100 * (batter_valtage - 10.8) / (12.6 - 10.8);
|
|
||||||
/* 限幅 0~100%:法拉电容放电时 3.3V 基准下跌,ADC 读数失真可能算出负数 */
|
|
||||||
if (batter_valtage < 0) batter_valtage = 0;
|
|
||||||
if (batter_valtage > 100) batter_valtage = 100;
|
|
||||||
|
|
||||||
/* 电量 <10% 基本等同外部 12V 断开。法拉电容(5.5F)续航有限,
|
|
||||||
* 首次低于阈值立即 50ms 快速复核 2 次(约 100ms 出结果),仍低马上按断电处理:
|
|
||||||
* 优先把掉电消息发出去,再做本地记录,最后留 500ms 让模组把报文真正发完 */
|
|
||||||
if (batter_valtage < 10 && powoff == 0) {
|
|
||||||
uint8_t confirm = 1;
|
|
||||||
for (int k = 0; k < 2; k++) {
|
|
||||||
HAL_Delay(50);
|
|
||||||
adc_value = getAdc();
|
|
||||||
float v = adc_value * (3.3 / 4096.0);
|
|
||||||
v = v * 11.0 + 0.6;
|
|
||||||
v = 100 * (v - 10.8) / (12.6 - 10.8);
|
|
||||||
if (v >= 10) { confirm = 0; break; }
|
|
||||||
}
|
|
||||||
if (!confirm) return;
|
|
||||||
|
|
||||||
/* 1. 先发掉电消息(等模组回 OK,最多 ~1.5s/次,最多试 3 次;
|
|
||||||
* 3 次都发不出也照常记录复位,不能因发送失败就不记录) */
|
|
||||||
{
|
|
||||||
static uint8_t pub_try = 0;
|
|
||||||
if (NET_publish_power_wait(0) != 0 && ++pub_try < 3) return;
|
|
||||||
pub_try = 0;
|
|
||||||
}
|
|
||||||
powoff = 1;
|
|
||||||
log_info("> 电量低: %d%%", (int)batter_valtage);
|
|
||||||
|
|
||||||
/* 2. 屏幕提示掉电事件,展示 3 秒(法拉电容续航足够) */
|
|
||||||
oled_clear();
|
|
||||||
oled_locate(1, 0);
|
|
||||||
oled_print("发生掉电事件!");
|
|
||||||
|
|
||||||
hlw8032_save(); /* 本地记录:电量、断电原因(都很快) */
|
|
||||||
reset_log_power_loss();
|
|
||||||
|
|
||||||
/* 3. 3 秒后熄屏(这段延时也顺便给了模组发报文的空口时间),随后复位 */
|
|
||||||
HAL_Delay(3000);
|
|
||||||
oled_off();
|
|
||||||
NVIC_SystemReset();
|
|
||||||
/* BootLoader 会检测主电源,12V 未恢复前不跳 APP,防止外设没上电时程序乱跑 */
|
|
||||||
}
|
|
||||||
/* LED1: 低电量时常亮 */
|
|
||||||
if (powoff) LED1_ON; else LED1_OFF;
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,60 +0,0 @@
|
||||||
#ifndef AIR780E_H
|
|
||||||
#define AIR780E_H
|
|
||||||
|
|
||||||
#include "main.h"
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/*===== 4G 模组引脚定义 (Air780E 复位控制, 低电平有效) =====*/
|
|
||||||
#define CAT_RST_PIN GPIO_PIN_4
|
|
||||||
#define CAT_RST_PORT GPIOA
|
|
||||||
|
|
||||||
#define CAT_RST_LOW() HAL_GPIO_WritePin(CAT_RST_PORT, CAT_RST_PIN, GPIO_PIN_RESET)
|
|
||||||
#define CAT_RST_HIGH() HAL_GPIO_WritePin(CAT_RST_PORT, CAT_RST_PIN, GPIO_PIN_SET)
|
|
||||||
|
|
||||||
#define CAT_RST_LOW_MS 4000 /* 拉低 4s 硬件复位 */
|
|
||||||
|
|
||||||
/*===== MQTT 服务器配置 =====*/
|
|
||||||
typedef struct {
|
|
||||||
char ClientID[20];
|
|
||||||
char Username[20];
|
|
||||||
char Passward[20];
|
|
||||||
char ServerIP[20];
|
|
||||||
uint16_t ServerPort;
|
|
||||||
char Topic[20];
|
|
||||||
} MqttConfig;
|
|
||||||
|
|
||||||
/* 默认 MQTT 参数已迁移到 main.h 的 MQTT_xxx 宏,避免多处维护不一致 */
|
|
||||||
|
|
||||||
/*===== U2_CopyBuff =====*/
|
|
||||||
#define U2_COPY_SIZE 10240
|
|
||||||
extern uint8_t U2_CopyBuff[U2_COPY_SIZE];
|
|
||||||
extern volatile uint16_t U2_CopyIndex;
|
|
||||||
extern volatile uint8_t U2_CopyFlag;
|
|
||||||
|
|
||||||
/*===== 运行状态标志 (供 main.c 判断 OTA 业务健康) =====*/
|
|
||||||
extern volatile uint8_t g_air780e_mqtt_ready;
|
|
||||||
extern volatile uint8_t g_air780e_first_publish_ok;
|
|
||||||
|
|
||||||
/*===== API =====*/
|
|
||||||
uint8_t catSendCmd(const char *cmd, const char *expect, uint8_t retry, uint8_t timeout);
|
|
||||||
int air780e_send_at(const char *cmd, const char *expect, int timeout_ms);
|
|
||||||
void bg_delay(uint32_t ms);
|
|
||||||
|
|
||||||
/* GPS 定位运行时可切:EEPROM(偏移173) 存 0/1,空白(0xFF) 用 main.h 的
|
|
||||||
* PRODUCT_WITH_GPS 编译宏做默认值。蓝牙命令 GPS 查询,GPS0/GPS1 切换(重启生效) */
|
|
||||||
extern uint8_t g_gps_on;
|
|
||||||
void gps_load_mode(void);
|
|
||||||
void gps_set_mode(uint8_t on);
|
|
||||||
#define GPS_MODE_ADDR 173 /* EEPROM GPS 开关字节(172 为产品模式) */ /* 后台切片延时(喂狗+业务轮询), WiFi 后端 AT 等待同样复用 */
|
|
||||||
int Verify_Firmware_External(uint32_t ext_addr, uint32_t size); /* 外部Flash固件头校验, WiFi OTA 复用 */
|
|
||||||
uint16_t crc16_modbus_update(uint16_t crc, uint8_t data); /* MODBUS CRC16 累加, WiFi OTA 复用 */
|
|
||||||
void air780e_init(void);
|
|
||||||
void air780e_process(void);
|
|
||||||
void air780e_trigger_ota(const char *host, int port, const char *path, const char *ota_id, uint16_t new_ver, uint16_t crc16);
|
|
||||||
int air780e_mqtt_report(const char *header, const char *ota_id, int percent_or_success); /* 返回 0=送达 */
|
|
||||||
void air780e_mqtt_result_nowait(const char *header, const char *ota_id, int val); /* 结果上报:发了就走不等OK */
|
|
||||||
int air780e_ota_confirm(void); /* 0=通知已送达 1=无挂起 -1=通知未送达(需重试) */
|
|
||||||
uint16_t ota_get_version(void);
|
|
||||||
void CAT1_printf(const char *fmt, ...);
|
|
||||||
|
|
||||||
#endif /* AIR780E_H */
|
|
||||||
|
|
@ -1,467 +0,0 @@
|
||||||
/* feed_scale.c - 485 喂料秤控制
|
|
||||||
*
|
|
||||||
* 硬件: USART2 接 RS485 磅秤传感器,PA2(TX), PA3(RX),9600-8N1
|
|
||||||
* 协议: 查询命令 {0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0xC4, 0x0B}
|
|
||||||
* 应答: 9 字节(地址1 + 功能码1 + 字节数1 + 数据4 + CRC2),重量值在数据字节[3..6],小端 float,单位 KG。
|
|
||||||
* 业务: 平台下发单次投喂量 onceWt(0.1KG 单位),继电器 1 吸合开始投喂,
|
|
||||||
* 当 起始剩余重量 - 当前重量 >= 单次投喂量 时停止。
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "feed_scale.h"
|
|
||||||
#include "relay.h"
|
|
||||||
#include "uart.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "network.h"
|
|
||||||
#include "24c02.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
/* 查询秤的 Modbus 命令 */
|
|
||||||
static const uint8_t RS485_WEIGHT_CMD[8] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0xC4, 0x0B};
|
|
||||||
|
|
||||||
/* 运行状态 */
|
|
||||||
static FeedScaleState feed_state = FEED_IDLE;
|
|
||||||
static uint16_t once_weight = 0; /* 单次投喂量,0.1KG 单位,从 EEPROM 读取 */
|
|
||||||
static uint16_t start_weight = 0; /* 本次投喂开始时的剩余重量 */
|
|
||||||
static uint16_t last_weight = 0; /* 最近一次有效净重(已去皮) */
|
|
||||||
static uint16_t last_raw_weight = 0; /* 最近一次 485 原始毛重(未去皮) */
|
|
||||||
static uint16_t reduced_weight = 0; /* 本次投喂减少的重量 */
|
|
||||||
static uint16_t zero_weight = 0; /* 去皮重量,EEPROM 掉电保持 */
|
|
||||||
|
|
||||||
/* 轮询计时 */
|
|
||||||
static uint32_t last_query_tick = 0;
|
|
||||||
static uint32_t feed_start_tick = 0; /* 投喂开始时刻,用于超时保护 */
|
|
||||||
static uint8_t first_query_sent = 0; /* 首次上电查询已发送 */
|
|
||||||
static uint32_t await_reply_tick = 0; /* 查询发出时刻,0=无等待中的查询 */
|
|
||||||
static uint8_t first_query_reported = 0; /* 首次上电有效重量已上报 */
|
|
||||||
|
|
||||||
/* 重量异常增加计数 */
|
|
||||||
static uint8_t add_weight_count = 0;
|
|
||||||
#define FEED_ADD_WEIGHT_LIMIT 20u /* 连续约 10s 重量反而增加,结束投喂 */
|
|
||||||
|
|
||||||
/* 单次投喂超时 10 分钟 */
|
|
||||||
#define FEED_TIMEOUT_MS (10u * 60u * 1000u)
|
|
||||||
|
|
||||||
/* EEPROM 中保存去皮重量和单次投喂量的偏移(在 MqttInfoStr 之外) */
|
|
||||||
#define FEED_SCALE_ZERO_ADDR 120u
|
|
||||||
#define FEED_SCALE_ONCEWT_ADDR 122u
|
|
||||||
#define FEED_SCALE_INIT_FLAG_ADDR 124u
|
|
||||||
#define FEED_SCALE_INIT_FLAG 0xA5u
|
|
||||||
|
|
||||||
/* 本地 CRC16 校验 (Modbus) */
|
|
||||||
static uint16_t crc16_modbus(const uint8_t *data, uint16_t len)
|
|
||||||
{
|
|
||||||
uint16_t crc = 0xFFFF;
|
|
||||||
for (uint16_t i = 0; i < len; i++) {
|
|
||||||
crc ^= data[i];
|
|
||||||
for (uint8_t j = 0; j < 8; j++) {
|
|
||||||
if (crc & 0x0001) {
|
|
||||||
crc = (crc >> 1) ^ 0xA001;
|
|
||||||
} else {
|
|
||||||
crc >>= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 485 原始帧 hex 打印(排查用:收到什么先原样打出来) */
|
|
||||||
static void feed_scale_dump_frame(const uint8_t *buf, uint16_t len)
|
|
||||||
{
|
|
||||||
char hex[3 * 24 + 1] = {0};
|
|
||||||
uint16_t n = len > 24 ? 24 : len;
|
|
||||||
for (uint16_t i = 0; i < n; i++) {
|
|
||||||
snprintf(hex + i * 3, 4, "%02X ", buf[i]);
|
|
||||||
}
|
|
||||||
log_info("> FEED: 485 收到 %d 字节: %s", (int)len, hex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 从 485 应答中解析重量,应答 9 字节: 01 03 04 41 97 30 33 0B F6 */
|
|
||||||
volatile uint32_t g_parse_cnt = 0; /* 解析被调次数(排障) */
|
|
||||||
|
|
||||||
static int feed_scale_parse_weight(const uint8_t *buf, uint16_t len, uint16_t *weight_100g)
|
|
||||||
{
|
|
||||||
g_parse_cnt++;
|
|
||||||
/* 485 回声(自己发出去的查询被 RX 听到):静默丢弃,不打日志不报错 */
|
|
||||||
if (len == sizeof(RS485_WEIGHT_CMD) &&
|
|
||||||
memcmp(buf, RS485_WEIGHT_CMD, sizeof(RS485_WEIGHT_CMD)) == 0) {
|
|
||||||
return -7;
|
|
||||||
}
|
|
||||||
|
|
||||||
feed_scale_dump_frame(buf, len);
|
|
||||||
|
|
||||||
/* 定位应答帧头(站号+03+字节数04):485 回声或前导垃圾可能混在帧头前面 */
|
|
||||||
int hdr = -1;
|
|
||||||
for (int i = 0; i + 8 < len; i++) {
|
|
||||||
if (buf[i] == RS485_WEIGHT_CMD[0] && buf[i+1] == 0x03 && buf[i+2] == 0x04) {
|
|
||||||
hdr = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hdr > 0) {
|
|
||||||
log_info("> FEED: 跳过前导 %d 字节(回声/干扰)", hdr);
|
|
||||||
buf += hdr;
|
|
||||||
len -= hdr;
|
|
||||||
} else if (hdr < 0 && len >= 9) {
|
|
||||||
log_warn("> FEED: 解析失败-找不到帧头 01 03 04");
|
|
||||||
return -6;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len < 9) {
|
|
||||||
log_warn("> FEED: 解析失败-帧长度过短(%d<9),可能波特率不对或线路干扰", (int)len);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (buf[0] != RS485_WEIGHT_CMD[0]) {
|
|
||||||
log_warn("> FEED: 解析失败-站号不符(收0x%02X 期望0x%02X)", buf[0], RS485_WEIGHT_CMD[0]);
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
if (buf[1] == (RS485_WEIGHT_CMD[1] | 0x80)) {
|
|
||||||
log_warn("> FEED: 解析失败-秤返回异常帧(功能码0x%02X 异常码0x%02X),寄存器地址/数量不对?", buf[1], buf[2]);
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
if (buf[1] != RS485_WEIGHT_CMD[1]) {
|
|
||||||
log_warn("> FEED: 解析失败-功能码不符(收0x%02X 期望0x%02X)", buf[1], RS485_WEIGHT_CMD[1]);
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t calc_crc = crc16_modbus(buf, 7); /* 前 7 字节参与 CRC 计算 */
|
|
||||||
uint16_t recv_crc = (uint16_t)buf[8] << 8 | buf[7]; /* 最后两字节为 CRC */
|
|
||||||
if (calc_crc != recv_crc) {
|
|
||||||
log_warn("> FEED: 解析失败-CRC 校验错误(calc=0x%04X recv=0x%04X),可能字节序/帧边界错位或干扰", calc_crc, recv_crc);
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 大端 float: 数据字节[3]=MSB, [6]=LSB,参考 APP12 解析方式 */
|
|
||||||
float fweight;
|
|
||||||
uint8_t *p = (uint8_t *)&fweight;
|
|
||||||
p[0] = buf[6];
|
|
||||||
p[1] = buf[5];
|
|
||||||
p[2] = buf[4];
|
|
||||||
p[3] = buf[3];
|
|
||||||
|
|
||||||
/* 转成 0.1KG 整数单位,限制范围 */
|
|
||||||
int32_t wt = (int32_t)(fweight * 10.0f);
|
|
||||||
if (wt < 0) wt = 0;
|
|
||||||
if (wt > FEED_SCALE_WEIGHT_MAX) wt = FEED_SCALE_WEIGHT_MAX;
|
|
||||||
*weight_100g = (uint16_t)wt;
|
|
||||||
log_info("> FEED: 485 原始毛重=%.1fKG (0.1kg=%d)", fweight, *weight_100g);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 通过 USART2 发送 485 查询命令 */
|
|
||||||
static void feed_scale_send_query(void)
|
|
||||||
{
|
|
||||||
USART2_FlushRxBuf(); /* 只清软件缓冲(残留回声/垃圾),硬件接收常开不动 */
|
|
||||||
HAL_UART_Transmit(&huart2, (uint8_t *)RS485_WEIGHT_CMD, sizeof(RS485_WEIGHT_CMD), 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 读取 485 返回帧 */
|
|
||||||
static int feed_scale_read_frame(uint8_t *buf, uint16_t max_len)
|
|
||||||
{
|
|
||||||
return (int)USART2_PollFrame(buf, max_len); /* 原子取帧,无交接状态 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 上报当前状态到平台: 开关、重量、单次投料量、去皮重量。返回 0=已发出 */
|
|
||||||
static int feed_scale_report_weight(const char *header)
|
|
||||||
{
|
|
||||||
(void)header;
|
|
||||||
/* 云平台物模型字段:weight=剩余料重(净重),zero=空载重量(去皮),onceWt=单次投料量 */
|
|
||||||
/* sw1 与 feeding 均上报继电器 1 状态,平台两个开关都能同步刷新 */
|
|
||||||
int rc = NET_publish_status(
|
|
||||||
MqttInfoStr.Relay_State[1] ? 1 : 0,
|
|
||||||
MqttInfoStr.Relay_State[1] ? 1 : 0,
|
|
||||||
MqttInfoStr.Relay_State[2] ? 1 : 0,
|
|
||||||
MqttInfoStr.Relay_State[3] ? 1 : 0,
|
|
||||||
MqttInfoStr.Relay_State[4] ? 1 : 0,
|
|
||||||
(float)zero_weight / 10.0f,
|
|
||||||
(float)once_weight / 10.0f,
|
|
||||||
(float)last_weight / 10.0f);
|
|
||||||
log_info("> FEED: 净重=%.1fKG 单次投料=%.1fKG 去皮=%.1fKG",
|
|
||||||
(float)last_weight / 10.0f,
|
|
||||||
(float)once_weight / 10.0f,
|
|
||||||
(float)zero_weight / 10.0f);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t s_last_ok_tick = 0;
|
|
||||||
static int s_last_err = 1; /* 上电默认"无应答" */
|
|
||||||
|
|
||||||
int feedScaleGetLastErr(void) { return s_last_err; }
|
|
||||||
uint32_t feedScaleGetLastOkTick(void) { return s_last_ok_tick; }
|
|
||||||
|
|
||||||
uint8_t g_feed_scale_on = 0;
|
|
||||||
|
|
||||||
/* 加载产品模式:EEPROM 空白时用编译宏做默认值 */
|
|
||||||
void feed_scale_load_mode(void)
|
|
||||||
{
|
|
||||||
uint8_t v = 0xFF;
|
|
||||||
eepromReadData(FEED_SCALE_MODE_ADDR, &v, 1);
|
|
||||||
if (v > 1) v = PRODUCT_WITH_FEED_SCALE;
|
|
||||||
g_feed_scale_on = v;
|
|
||||||
log_info("> 产品模式: %s", v ? "称重投料(带秤)" : "四路继电器(无秤)");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 蓝牙命令切换产品模式:写 EEPROM(重启后完全生效) */
|
|
||||||
void feed_scale_set_mode(uint8_t on)
|
|
||||||
{
|
|
||||||
on = on ? 1 : 0;
|
|
||||||
g_feed_scale_on = on;
|
|
||||||
eepromWriteData(FEED_SCALE_MODE_ADDR, &on, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void feedScaleInit(void)
|
|
||||||
{
|
|
||||||
uint8_t tmp[2] = {0};
|
|
||||||
|
|
||||||
/* 检测是否是空白/新刷机单片机,是则强制写入默认 1.0KG */
|
|
||||||
eepromReadData(FEED_SCALE_INIT_FLAG_ADDR, tmp, 1);
|
|
||||||
if (tmp[0] != FEED_SCALE_INIT_FLAG) {
|
|
||||||
tmp[0] = FEED_SCALE_INIT_FLAG;
|
|
||||||
eepromWriteData(FEED_SCALE_INIT_FLAG_ADDR, tmp, 1);
|
|
||||||
|
|
||||||
zero_weight = 10; /* 默认去皮 1.0KG */
|
|
||||||
once_weight = 10; /* 默认单次投料 1.0KG */
|
|
||||||
|
|
||||||
tmp[0] = (uint8_t)(zero_weight & 0xFF);
|
|
||||||
tmp[1] = (uint8_t)(zero_weight >> 8);
|
|
||||||
eepromWriteData(FEED_SCALE_ZERO_ADDR, tmp, 2);
|
|
||||||
|
|
||||||
tmp[0] = (uint8_t)(once_weight & 0xFF);
|
|
||||||
tmp[1] = (uint8_t)(once_weight >> 8);
|
|
||||||
eepromWriteData(FEED_SCALE_ONCEWT_ADDR, tmp, 2);
|
|
||||||
|
|
||||||
log_info("> FEED: 首次初始化, 设置默认 去皮=1.0KG, 单次投料=1.0KG");
|
|
||||||
} else {
|
|
||||||
eepromReadData(FEED_SCALE_ZERO_ADDR, tmp, 2);
|
|
||||||
zero_weight = (uint16_t)tmp[0] | ((uint16_t)tmp[1] << 8);
|
|
||||||
if (zero_weight == 0 || zero_weight > FEED_SCALE_WEIGHT_MAX) {
|
|
||||||
zero_weight = 10; /* 默认去皮 1.0KG */
|
|
||||||
}
|
|
||||||
|
|
||||||
eepromReadData(FEED_SCALE_ONCEWT_ADDR, tmp, 2);
|
|
||||||
once_weight = (uint16_t)tmp[0] | ((uint16_t)tmp[1] << 8);
|
|
||||||
if (once_weight == 0 || once_weight > FEED_SCALE_WEIGHT_MAX) {
|
|
||||||
once_weight = 10; /* 默认单次投料 1.0KG */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_feed_scale_on) {
|
|
||||||
log_info("> FEED: 初始化, 去皮=%.1fKG, 单次投料=%.1fKG",
|
|
||||||
(float)zero_weight / 10.0f, (float)once_weight / 10.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void feedScaleSetZero(uint16_t zero_100g)
|
|
||||||
{
|
|
||||||
if (zero_100g > FEED_SCALE_WEIGHT_MAX) return;
|
|
||||||
zero_weight = zero_100g;
|
|
||||||
|
|
||||||
/* 平台下发新空载重量后,用最新原始毛重重新计算剩余料重 */
|
|
||||||
if (last_raw_weight >= zero_weight) {
|
|
||||||
last_weight = last_raw_weight - zero_weight;
|
|
||||||
} else {
|
|
||||||
last_weight = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t tmp[2] = { (uint8_t)(zero_weight & 0xFF), (uint8_t)(zero_weight >> 8) };
|
|
||||||
eepromWriteData(FEED_SCALE_ZERO_ADDR, tmp, 2);
|
|
||||||
log_info("> FEED: 设置去皮=%.1fKG, 重算净重=%.1fKG",
|
|
||||||
(float)zero_weight / 10.0f, (float)last_weight / 10.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void feedScaleSetOnceWeight(uint16_t once_wt_100g)
|
|
||||||
{
|
|
||||||
if (once_wt_100g > FEED_SCALE_WEIGHT_MAX) return;
|
|
||||||
once_weight = once_wt_100g;
|
|
||||||
uint8_t tmp[2] = { (uint8_t)(once_weight & 0xFF), (uint8_t)(once_weight >> 8) };
|
|
||||||
eepromWriteData(FEED_SCALE_ONCEWT_ADDR, tmp, 2);
|
|
||||||
log_info("> FEED: 设置单次投料=%.1fKG", (float)once_weight / 10.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t feedScaleGetOnceWeight(void)
|
|
||||||
{
|
|
||||||
return once_weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t feedScaleGetWeight(void)
|
|
||||||
{
|
|
||||||
return last_weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t feedScaleGetZero(void)
|
|
||||||
{
|
|
||||||
return zero_weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 获取最近一次 485 原始毛重(未去皮,0.1KG 单位) */
|
|
||||||
uint16_t feedScaleGetRawWeight(void)
|
|
||||||
{
|
|
||||||
return last_raw_weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
FeedScaleState feedScaleGetState(void)
|
|
||||||
{
|
|
||||||
return feed_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t feedScaleGetReduced(void)
|
|
||||||
{
|
|
||||||
return reduced_weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
void feedScaleStart(uint16_t once_wt_100g)
|
|
||||||
{
|
|
||||||
if (once_wt_100g < FEED_SCALE_ONCEWT_MIN || once_wt_100g > FEED_SCALE_ONCEWT_MAX) {
|
|
||||||
log_warn("> FEED: 单次投料量无效=%.1fKG", (float)once_wt_100g / 10.0f);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (feed_state == FEED_RUNNING) {
|
|
||||||
log_warn("> FEED: 正在投喂中, 忽略本次请求");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (first_query_reported == 0) {
|
|
||||||
log_warn("> FEED: 秤未就绪, 无法开始投喂");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (last_weight == 0) {
|
|
||||||
log_warn("> FEED: 剩余重量为 0, 无法开始投喂");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
once_weight = once_wt_100g;
|
|
||||||
start_weight = last_weight; /* 取最近一次重量作为起始重量 */
|
|
||||||
reduced_weight = 0;
|
|
||||||
add_weight_count = 0;
|
|
||||||
feed_start_tick = HAL_GetTick();
|
|
||||||
feed_state = FEED_RUNNING;
|
|
||||||
|
|
||||||
relaySet(1, 1);
|
|
||||||
eepromWriteData(102, &MqttInfoStr.Relay_State[0], 7);
|
|
||||||
log_info("> FEED: 开始投喂 单次=%.1fKG, 起始=%.1fKG, 继电器1=开",
|
|
||||||
(float)once_weight / 10.0f, (float)start_weight / 10.0f);
|
|
||||||
|
|
||||||
/* 投喂开始时立即上报一次状态 */
|
|
||||||
feed_scale_report_weight("iot.prop.post");
|
|
||||||
}
|
|
||||||
|
|
||||||
void feedScaleStop(void)
|
|
||||||
{
|
|
||||||
if (feed_state == FEED_IDLE) return;
|
|
||||||
relaySet(1, 0);
|
|
||||||
eepromWriteData(102, &MqttInfoStr.Relay_State[0], 7);
|
|
||||||
feed_state = FEED_DONE;
|
|
||||||
log_info("> FEED: 停止投喂, 已减少=%.1fKG", (float)reduced_weight / 10.0f);
|
|
||||||
relayRequestReport();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void feed_scale_finish_error(const char *reason)
|
|
||||||
{
|
|
||||||
relaySet(1, 0);
|
|
||||||
eepromWriteData(102, &MqttInfoStr.Relay_State[0], 7);
|
|
||||||
feed_state = FEED_ERROR;
|
|
||||||
log_warn("> FEED: 异常停止, %s", reason);
|
|
||||||
relayRequestReport();
|
|
||||||
}
|
|
||||||
|
|
||||||
void feedScaleProcess(void)
|
|
||||||
{
|
|
||||||
if (!g_feed_scale_on) return;
|
|
||||||
|
|
||||||
uint8_t rx_buf[32];
|
|
||||||
int len;
|
|
||||||
uint16_t cur_weight;
|
|
||||||
int32_t diff;
|
|
||||||
|
|
||||||
/* 原子取帧在 feed_scale_read_frame 内完成,无需再 Tick */
|
|
||||||
|
|
||||||
/* 空闲时 10s 查询一次; 投喂时 1s 查询一次;
|
|
||||||
不累计无响应计数,手动测试时漏回也不会一直刷超时 */
|
|
||||||
uint32_t query_interval = (feed_state == FEED_RUNNING) ? FEED_SCALE_QUERY_RUN_MS : FEED_SCALE_QUERY_IDLE_MS;
|
|
||||||
|
|
||||||
/* 首次查询延迟 3s:避开上电窗口(BLE 配置/网络初始化/首次上报都在抢资源),
|
|
||||||
* 该窗口内发出的首个应答容易被撞坏 */
|
|
||||||
if (HAL_GetTick() < 3000u) return;
|
|
||||||
|
|
||||||
if (first_query_sent == 0 || (HAL_GetTick() - last_query_tick) >= query_interval) {
|
|
||||||
last_query_tick = HAL_GetTick();
|
|
||||||
feed_scale_send_query();
|
|
||||||
await_reply_tick = HAL_GetTick(); /* 标记等待应答,1s 无帧打日志 */
|
|
||||||
if (first_query_sent == 0) {
|
|
||||||
first_query_sent = 1;
|
|
||||||
log_info("> FEED: 首次查询已发送");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 查询发出后 1s 仍无有效帧:打日志(不自动补发) */
|
|
||||||
if (await_reply_tick && (HAL_GetTick() - await_reply_tick) > 1000u) {
|
|
||||||
await_reply_tick = 0;
|
|
||||||
log_warn("> FEED: 查询无应答(秤未接/线序/波特率/站号不对)");
|
|
||||||
s_last_err = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 读取 485 返回 */
|
|
||||||
len = feed_scale_read_frame(rx_buf, sizeof(rx_buf));
|
|
||||||
if (len > 0) {
|
|
||||||
int prc = feed_scale_parse_weight(rx_buf, len, &cur_weight);
|
|
||||||
if (prc == 0) { await_reply_tick = 0; }
|
|
||||||
if (prc != -7) { /* -7=回声帧:不影响状态显示 */
|
|
||||||
s_last_err = (prc == 0) ? 0 : (prc == -1 ? 2 : (prc == -2 ? 3 : (prc == -4 ? 4 : (prc == -6 ? 6 : 5))));
|
|
||||||
}
|
|
||||||
if (prc == 0) s_last_ok_tick = HAL_GetTick();
|
|
||||||
if (prc == 0) {
|
|
||||||
last_raw_weight = cur_weight; /* 保存原始毛重 */
|
|
||||||
last_weight = cur_weight > zero_weight ? (cur_weight - zero_weight) : 0; /* 净重 */
|
|
||||||
if (!first_query_reported) {
|
|
||||||
/* 首次上电: 查询到一次有效重量后上报平台。
|
|
||||||
* 发布成功才置标志——发布被丢弃(AT忙)时下轮查询继续重试,
|
|
||||||
* 避免重启后平台重量一直是 0 直到下次碰巧发出去 */
|
|
||||||
if (feed_scale_report_weight("iot.prop.post") == 0) {
|
|
||||||
first_query_reported = 1;
|
|
||||||
log_info("> FEED: 首次重量已上报");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (feed_state == FEED_RUNNING) {
|
|
||||||
/* 投喂期间:每次有效重量都上报平台,保证实时 */
|
|
||||||
feed_scale_report_weight("iot.prop.post");
|
|
||||||
|
|
||||||
/* 计算减少量,去零后比较 */
|
|
||||||
diff = (int32_t)start_weight - (int32_t)last_weight;
|
|
||||||
if (diff < 0) diff = -diff;
|
|
||||||
reduced_weight = (uint16_t)diff;
|
|
||||||
log_info("> FEED: 投喂中 起始=%.1f 当前=%.1f 已减=%.1f 单次=%.1f",
|
|
||||||
(float)start_weight / 10.0f, (float)last_weight / 10.0f,
|
|
||||||
(float)reduced_weight / 10.0f, (float)once_weight / 10.0f);
|
|
||||||
|
|
||||||
/* 条件1: 达到/超过单次投喂量,停止 */
|
|
||||||
if (reduced_weight >= once_weight) {
|
|
||||||
log_info("> FEED: 达到目标投喂量, 停止");
|
|
||||||
feedScaleStop();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 条件2: 重量反而增加,可能有人在加料,连续超过阈值后结束 */
|
|
||||||
if (last_weight > start_weight) {
|
|
||||||
add_weight_count++;
|
|
||||||
if (add_weight_count >= FEED_ADD_WEIGHT_LIMIT) {
|
|
||||||
feed_scale_finish_error("重量反增");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
add_weight_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 条件3: 投喂超时 5 分钟 */
|
|
||||||
if (HAL_GetTick() - feed_start_tick > FEED_TIMEOUT_MS) {
|
|
||||||
feed_scale_finish_error("投喂超时");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 清理已完成状态,让外部模块在下一循环前处理 */
|
|
||||||
if (feed_state == FEED_DONE || feed_state == FEED_ERROR) {
|
|
||||||
/* 状态保持一次,由 relay 状态机上报 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
#ifndef __FEED_SCALE_H
|
|
||||||
#define __FEED_SCALE_H
|
|
||||||
|
|
||||||
#include "main.h"
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/* 单次投喂量有效范围: 0 ~ 500.0 KG, 内部以 0.1 KG 为单位保存 */
|
|
||||||
#define FEED_SCALE_ONCEWT_MIN 1 /* 0.1 KG */
|
|
||||||
#define FEED_SCALE_ONCEWT_MAX 5000 /* 500.0 KG */
|
|
||||||
#define FEED_SCALE_WEIGHT_MAX 5000 /* 500.0 KG */
|
|
||||||
|
|
||||||
/* 产品形态运行时可切换:EEPROM(偏移172) 存 0/1,空白(0xFF) 时用
|
|
||||||
* main.h 的 PRODUCT_WITH_FEED_SCALE 编译宏做默认值。
|
|
||||||
* 1=接入磅秤(智能水产投料机),称重定量自动投喂;
|
|
||||||
* 0=不接磅秤(基础四路继电器),feeding 指令直接控制继电器 1。
|
|
||||||
* 蓝牙命令 SCALE 查询,SCALE0/SCALE1 切换(写入后自动重启生效) */
|
|
||||||
extern uint8_t g_feed_scale_on;
|
|
||||||
void feed_scale_load_mode(void); /* app_init 早期调用,加载产品模式 */
|
|
||||||
void feed_scale_set_mode(uint8_t on); /* 写 EEPROM 并更新全局(重启后完全生效) */
|
|
||||||
|
|
||||||
#define FEED_SCALE_MODE_ADDR 172 /* EEPROM 产品模式字节(164~171 为电能累计脉冲) */
|
|
||||||
|
|
||||||
/* 485 读秤状态(OLED 显示用):
|
|
||||||
* 0=正常有数据 1=一直无应答 2=帧过短 3=CRC错 4=秤异常帧 5=站号/功能码不符 */
|
|
||||||
int feedScaleGetLastErr(void);
|
|
||||||
uint32_t feedScaleGetLastOkTick(void); /* 最后一次有效重量的时刻 */
|
|
||||||
|
|
||||||
/* 投喂状态 */
|
|
||||||
typedef enum {
|
|
||||||
FEED_IDLE = 0, /* 空闲 */
|
|
||||||
FEED_RUNNING, /* 投喂中: 继电器吸合,正在读485 */
|
|
||||||
FEED_DONE, /* 投喂完成 */
|
|
||||||
FEED_ERROR /* 异常结束(485失联/重量反而增加等) */
|
|
||||||
} FeedScaleState;
|
|
||||||
|
|
||||||
/* 喂料秤模块 API */
|
|
||||||
void feedScaleInit(void);
|
|
||||||
void feedScaleProcess(void);
|
|
||||||
|
|
||||||
/* 触发/停止投喂 */
|
|
||||||
void feedScaleStart(uint16_t once_wt_100g);
|
|
||||||
void feedScaleStop(void);
|
|
||||||
|
|
||||||
/* 设置/获取单次投喂量(0.1KG 单位),设置后持久化到 EEPROM */
|
|
||||||
void feedScaleSetOnceWeight(uint16_t once_wt_100g);
|
|
||||||
uint16_t feedScaleGetOnceWeight(void);
|
|
||||||
|
|
||||||
/* 设置秤去皮重量(0.1KG 单位),并持久化到 EEPROM */
|
|
||||||
void feedScaleSetZero(uint16_t zero_100g);
|
|
||||||
|
|
||||||
/* 获取当前/最近一次解析出的重量(0.1KG 单位) */
|
|
||||||
uint16_t feedScaleGetWeight(void);
|
|
||||||
|
|
||||||
/* 获取最近一次 485 原始毛重(未去皮,0.1KG 单位) */
|
|
||||||
uint16_t feedScaleGetRawWeight(void);
|
|
||||||
|
|
||||||
/* 获取当前去皮重量(0.1KG 单位) */
|
|
||||||
uint16_t feedScaleGetZero(void);
|
|
||||||
|
|
||||||
/* 获取投喂状态 */
|
|
||||||
FeedScaleState feedScaleGetState(void);
|
|
||||||
|
|
||||||
/* 获取已减少重量(0.1KG 单位),投喂结束后有效 */
|
|
||||||
uint16_t feedScaleGetReduced(void);
|
|
||||||
|
|
||||||
/* 485 轮询间隔: 空闲 30s, 投喂中 1s */
|
|
||||||
#define FEED_SCALE_QUERY_IDLE_MS 30000u
|
|
||||||
#define FEED_SCALE_QUERY_RUN_MS 1000u
|
|
||||||
|
|
||||||
#endif /* __FEED_SCALE_H */
|
|
||||||
|
|
@ -1,241 +0,0 @@
|
||||||
/* hlw8032.c - HLW8032 电量计量芯片驱动(UART5 只收,4800 8E1)
|
|
||||||
*
|
|
||||||
* 帧格式(24 字节,每 50ms 一帧,主动上报):
|
|
||||||
* [0] 状态寄存器 State REG 0x55=正常 / 0xAA=修调失效 / 0xFx=有寄存器溢出
|
|
||||||
* [1] 检测寄存器 Check REG 固定 0x5A
|
|
||||||
* [2:5] 电压参数寄存器(本模块不用)
|
|
||||||
* [5:8] 电压寄存器(不用)
|
|
||||||
* [8:11] 电流参数寄存器(不用)
|
|
||||||
* [11:14]电流寄存器(不用)
|
|
||||||
* [14:17]功率参数寄存器 PowerPar 芯片出厂修调常数,算电量要用
|
|
||||||
* [17:20]功率寄存器(不用)
|
|
||||||
* [20] 数据更新寄存器 bit7 = PF 溢出翻转标志
|
|
||||||
* [21:23]PF 寄存器 16bit 脉冲计数,溢出清零同时 bit7 翻转
|
|
||||||
* [23] 校验和 [2]~[22] 共 21 字节相加取低 8 位
|
|
||||||
*
|
|
||||||
* 手册电量公式:
|
|
||||||
* 1 度电脉冲数 = (1/PowerPar) × (1/(电压系数×电流系数)) × 10^9 × 3600
|
|
||||||
* 电量(kWh) = 总脉冲数 × PowerPar × 电压系数 × 电流系数 / (10^9 × 3600)
|
|
||||||
* 电压/电流系数在 main.h(HLW_U_COEF / HLW_I_COEF),由采样电阻和互感器变比决定。
|
|
||||||
*
|
|
||||||
* 溢出处理:PF 寄存器只有 16 位,溢出时 bit7 翻转。本驱动记住 bit7 上次值,
|
|
||||||
* 每帧比对,翻转一次说明溢出一次(+65536)。50ms 一帧 vs 溢出最快也要数小时,
|
|
||||||
* 不可能在一帧间隔内翻两次,不会漏。
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "hlw8032.h"
|
|
||||||
#include "uart.h"
|
|
||||||
#include "24c02.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define HLW_FRAME_LEN 24
|
|
||||||
#define HLW_OVERFLOW_PULSES 65536u
|
|
||||||
#define HLW_EEP_ADDR 164 /* EEPROM 164~171: uint64 累计脉冲数(132~157 蓝牙缓存, 160 日志等级) */
|
|
||||||
|
|
||||||
static uint8_t s_frame[HLW_FRAME_LEN];
|
|
||||||
static uint8_t s_idx = 0; /* 帧同步状态:0=等首字节 1=等 0x5A >=2=收集中 */
|
|
||||||
|
|
||||||
static uint64_t s_total_pulses = 0; /* 累计脉冲(含 EEPROM 恢复的历史值) */
|
|
||||||
static uint64_t s_saved_pulses = 0; /* 上次已写入 EEPROM 的值 */
|
|
||||||
static uint32_t s_power_par = 0; /* 功率参数寄存器(芯片常数,收到帧后才有) */
|
|
||||||
static uint32_t s_u_par = 0, s_u_reg = 0; /* 电压参数/电压寄存器(显示电压用) */
|
|
||||||
static uint32_t s_i_par = 0, s_i_reg = 0; /* 电流参数/电流寄存器 */
|
|
||||||
static uint32_t s_p_reg = 0; /* 功率寄存器 */
|
|
||||||
static float s_u_f = 0, s_i_f = 0, s_p_f = 0; /* 电压/电流/功率 EMA 滤波值 */
|
|
||||||
|
|
||||||
static uint32_t s_k = 0; /* 本次上电以来 bit7 翻转(PF 溢出)次数 */
|
|
||||||
static uint8_t s_last_bit7 = 0;
|
|
||||||
static uint32_t s_last_chip = 0; /* 上一帧芯片侧总脉冲 k*65536+n */
|
|
||||||
static uint8_t s_synced = 0; /* 已确认同步(连续有效帧) */
|
|
||||||
static uint8_t s_streak = 0; /* 连续有效帧计数(防噪声假同步) */
|
|
||||||
static uint32_t s_last_valid_tick = 0; /* 上一有效帧时刻(帧间隔校验用) */
|
|
||||||
static uint32_t s_last_frame_tick = 0;
|
|
||||||
|
|
||||||
/*==== 帧校验与电量累计(中断上下文执行,保持轻量)====*/
|
|
||||||
static void hlw8032_parse(const uint8_t *f)
|
|
||||||
{
|
|
||||||
uint8_t sum = 0;
|
|
||||||
for (uint8_t i = 2; i <= 22; i++) sum += f[i];
|
|
||||||
if (sum != f[23]) return; /* 校验失败丢帧 */
|
|
||||||
|
|
||||||
uint32_t power_par = ((uint32_t)f[14] << 16) | ((uint32_t)f[15] << 8) | f[16];
|
|
||||||
if (power_par == 0) return; /* 参数寄存器异常,帧不可用 */
|
|
||||||
s_power_par = power_par;
|
|
||||||
|
|
||||||
/* 电压/电流/功率原始寄存器(调试用,每分钟日志显示) */
|
|
||||||
s_u_par = ((uint32_t)f[2] << 16) | ((uint32_t)f[3] << 8) | f[4];
|
|
||||||
s_u_reg = ((uint32_t)f[5] << 16) | ((uint32_t)f[6] << 8) | f[7];
|
|
||||||
s_i_par = ((uint32_t)f[8] << 16) | ((uint32_t)f[9] << 8) | f[10];
|
|
||||||
s_i_reg = ((uint32_t)f[11] << 16) | ((uint32_t)f[12] << 8) | f[13];
|
|
||||||
s_p_reg = ((uint32_t)f[17] << 16) | ((uint32_t)f[18] << 8) | f[19];
|
|
||||||
|
|
||||||
uint8_t bit7 = f[20] & 0x80;
|
|
||||||
uint16_t pf = ((uint16_t)f[21] << 8) | f[22];
|
|
||||||
|
|
||||||
if (!s_synced) {
|
|
||||||
/* 首帧只建立基准,不累计增量:
|
|
||||||
* 单片机重启而芯片一直在跑时,寄存器里攒的脉冲(几秒,约零点几 Wh)舍弃 */
|
|
||||||
s_last_bit7 = bit7;
|
|
||||||
s_k = 0;
|
|
||||||
} else if (bit7 != s_last_bit7) {
|
|
||||||
s_k++; /* PF 溢出一次 */
|
|
||||||
s_last_bit7 = bit7;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 防噪声:真芯片 50ms 一帧稳定到达,要求连续 3 帧且间隔 20~150ms 才算同步。
|
|
||||||
* PD2 悬空时的电磁干扰偶发也能凑出校验和正确的假帧(约 1/256),
|
|
||||||
* 但不可能连续 3 帧都卡在 50ms 节奏上 */
|
|
||||||
{
|
|
||||||
uint32_t now = HAL_GetTick();
|
|
||||||
uint32_t gap = now - s_last_valid_tick;
|
|
||||||
s_last_valid_tick = now;
|
|
||||||
if (gap >= 20 && gap <= 150) {
|
|
||||||
if (s_streak < 3) s_streak++;
|
|
||||||
} else {
|
|
||||||
s_streak = 1;
|
|
||||||
}
|
|
||||||
if (s_streak < 3) return; /* 未确认同步:帧内容一律不采用 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* EMA 滑动平均(系数 1/8,约 0.4s 响应):压空载噪声跳变,
|
|
||||||
* 真实负载变化延迟 <0.5s,不影响使用 */
|
|
||||||
{
|
|
||||||
float u = s_u_reg ? (float)((double)s_u_par / (double)s_u_reg * HLW_U_COEF) : 0.0f;
|
|
||||||
float a = s_i_reg ? (float)((double)s_i_par / (double)s_i_reg * HLW_I_COEF) : 0.0f;
|
|
||||||
float w = s_p_reg ? (float)((double)s_power_par / (double)s_p_reg * HLW_U_COEF * HLW_I_COEF) : 0.0f;
|
|
||||||
s_u_f += (u - s_u_f) * 0.125f;
|
|
||||||
s_i_f += (a - s_i_f) * 0.125f;
|
|
||||||
s_p_f += (w - s_p_f) * 0.125f;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t chip_total = s_k * HLW_OVERFLOW_PULSES + pf;
|
|
||||||
if (s_synced) {
|
|
||||||
/* 正常单调递增;变小说明芯片断电重启过(寄存器清零),从当前值重新累计 */
|
|
||||||
uint32_t delta = (chip_total >= s_last_chip) ? (chip_total - s_last_chip) : chip_total;
|
|
||||||
s_total_pulses += delta;
|
|
||||||
}
|
|
||||||
s_last_chip = chip_total;
|
|
||||||
if (!s_synced) {
|
|
||||||
s_synced = 1;
|
|
||||||
log_info("> 电能: HLW8032 在线, 功率参数=%lu", (unsigned long)s_power_par);
|
|
||||||
}
|
|
||||||
s_last_frame_tick = HAL_GetTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== UART5 中断逐字节喂入:首字节同步 + 0x5A 二次确认 + 收满 24 字节解析 ====*/
|
|
||||||
void hlw8032_rx_byte(uint8_t b)
|
|
||||||
{
|
|
||||||
if (s_idx == 0) {
|
|
||||||
/* 首字节候选:0x55 / 0xAA / 0xFx */
|
|
||||||
if (b == 0x55 || b == 0xAA || (b & 0xF0) == 0xF0) {
|
|
||||||
s_frame[0] = b;
|
|
||||||
s_idx = 1;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (s_idx == 1) {
|
|
||||||
if (b == 0x5A) {
|
|
||||||
s_frame[1] = b;
|
|
||||||
s_idx = 2;
|
|
||||||
} else {
|
|
||||||
/* 第二字节不是 0x5A:伪同步,但它本身可能是真首字节 */
|
|
||||||
s_idx = 0;
|
|
||||||
if (b == 0x55 || b == 0xAA || (b & 0xF0) == 0xF0) {
|
|
||||||
s_frame[0] = b;
|
|
||||||
s_idx = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
s_frame[s_idx++] = b;
|
|
||||||
if (s_idx >= HLW_FRAME_LEN) {
|
|
||||||
s_idx = 0;
|
|
||||||
hlw8032_parse(s_frame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 初始化:EEPROM 恢复累计 + UART5 起收 ====*/
|
|
||||||
void hlw8032_init(void)
|
|
||||||
{
|
|
||||||
uint64_t v = 0;
|
|
||||||
eepromReadData(HLW_EEP_ADDR, &v, sizeof(v));
|
|
||||||
if (v == 0xFFFFFFFFFFFFFFFFULL) v = 0; /* 空白 EEPROM(出厂 0xFF) */
|
|
||||||
s_total_pulses = v;
|
|
||||||
s_saved_pulses = v;
|
|
||||||
|
|
||||||
MX_UART5_UART_Init();
|
|
||||||
UART5_StartRx();
|
|
||||||
if (s_total_pulses) {
|
|
||||||
log_info("> 电能: HLW8032 就绪 (UART5 PD2), 历史脉冲 %lu%09lu",
|
|
||||||
(unsigned long)(s_total_pulses / 1000000000ULL),
|
|
||||||
(unsigned long)(s_total_pulses % 1000000000ULL));
|
|
||||||
} else {
|
|
||||||
log_info("> 电能: HLW8032 就绪 (UART5 PD2)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 累计电量 kWh ====*/
|
|
||||||
float hlw8032_get_energy_kwh(void)
|
|
||||||
{
|
|
||||||
if (!s_power_par) return 0.0f;
|
|
||||||
return (float)((double)s_total_pulses * (double)s_power_par *
|
|
||||||
(double)HLW_U_COEF * (double)HLW_I_COEF / 3.6e12);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 电压/电流/功率(手册:有效值 = 参数寄存器/寄存器 × 系数)====*/
|
|
||||||
float hlw8032_get_voltage_v(void)
|
|
||||||
{
|
|
||||||
if (!s_synced) return 0.0f;
|
|
||||||
return s_u_f;
|
|
||||||
}
|
|
||||||
|
|
||||||
float hlw8032_get_current_a(void)
|
|
||||||
{
|
|
||||||
if (!s_synced) return 0.0f;
|
|
||||||
float a = s_i_f - HLW_I_ZERO_A; /* 减零点偏移 */
|
|
||||||
return a > 0.0f ? a : 0.0f; /* 钳位到 0 */
|
|
||||||
}
|
|
||||||
|
|
||||||
float hlw8032_get_power_w(void)
|
|
||||||
{
|
|
||||||
if (!s_synced) return 0.0f;
|
|
||||||
if (s_p_f < HLW_P_DEAD_W) return 0.0f; /* 死区:空载噪声底显示 0 */
|
|
||||||
return s_p_f - HLW_P_ZERO_W;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 在线判断:1s 内收到过有效帧 ====*/
|
|
||||||
uint8_t hlw8032_online(void)
|
|
||||||
{
|
|
||||||
return s_synced && (HAL_GetTick() - s_last_frame_tick) < 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 立即写 EEPROM(断电预警/计划复位前调用)====*/
|
|
||||||
void hlw8032_save(void)
|
|
||||||
{
|
|
||||||
if (s_total_pulses == s_saved_pulses) return;
|
|
||||||
uint64_t v = s_total_pulses;
|
|
||||||
if (eepromWriteData(HLW_EEP_ADDR, &v, sizeof(v)) == 0) {
|
|
||||||
s_saved_pulses = s_total_pulses;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 累计电量清零(蓝牙 ENCLR 命令):清 RAM 与 EEPROM ====*/
|
|
||||||
void hlw8032_clear(void)
|
|
||||||
{
|
|
||||||
s_total_pulses = 0;
|
|
||||||
s_saved_pulses = 0;
|
|
||||||
uint64_t v = 0;
|
|
||||||
eepromWriteData(HLW_EEP_ADDR, &v, sizeof(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 1s 周期:每攒够约 0.1 kWh 写一次 EEPROM,限制擦写频率 ====*/
|
|
||||||
void hlw8032_tick(void)
|
|
||||||
{
|
|
||||||
if (!s_power_par) return;
|
|
||||||
double per_01kwh = 3.6e11 / ((double)s_power_par * HLW_U_COEF * HLW_I_COEF);
|
|
||||||
if (per_01kwh < 1.0) per_01kwh = 1.0;
|
|
||||||
if (s_total_pulses - s_saved_pulses >= (uint64_t)per_01kwh) {
|
|
||||||
hlw8032_save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
#ifndef __HLW8032_H
|
|
||||||
#define __HLW8032_H
|
|
||||||
|
|
||||||
#include "main.h"
|
|
||||||
|
|
||||||
/* HLW8032 电量计量模块
|
|
||||||
* 接线:芯片 TXD -> 单片机 PD2 (UART5_RX),光耦隔离;芯片 RX 预留不用。
|
|
||||||
* 通信:4800bps 8E1,芯片每 50ms 主动上报一帧 24 字节,只收不发。
|
|
||||||
* 本模块只取 PF 脉冲计数换算累计电量(kWh),电压/电流/功率寄存器忽略。
|
|
||||||
* 累计脉冲数持久化在 EEPROM(164~171, uint64),定期 + 断电前保存。 */
|
|
||||||
|
|
||||||
void hlw8032_init(void); /* UART5 初始化 + EEPROM 恢复累计(app_init 调用) */
|
|
||||||
void hlw8032_rx_byte(uint8_t b); /* UART5 中断逐字节喂入(uart.c 接收回调调用) */
|
|
||||||
void hlw8032_tick(void); /* 1s 周期:到阈值就把累计脉冲写 EEPROM */
|
|
||||||
void hlw8032_save(void); /* 立即写 EEPROM(断电预警/计划复位前调用) */
|
|
||||||
void hlw8032_clear(void); /* 累计电量清零并落盘(蓝牙 ENCLR 命令) */
|
|
||||||
float hlw8032_get_energy_kwh(void); /* 累计电量 kWh(未收到帧前为 0) */
|
|
||||||
float hlw8032_get_voltage_v(void); /* 电压有效值 V(调试显示用) */
|
|
||||||
float hlw8032_get_current_a(void); /* 电流有效值 A */
|
|
||||||
float hlw8032_get_power_w(void); /* 有功功率 W */
|
|
||||||
uint8_t hlw8032_online(void); /* 最近 1s 内收到过有效帧 */
|
|
||||||
|
|
||||||
#endif /* __HLW8032_H */
|
|
||||||
|
|
@ -1,499 +0,0 @@
|
||||||
/* network.c - 网络层:4G/WiFi 双通道抽象与故障切换
|
|
||||||
*
|
|
||||||
* 业务层(app_loop/relay/feed_scale)只调 NET_* 接口,不感知底层模组。
|
|
||||||
* 通道切换策略(优先顺序由 EEPROM 记忆的"上次成功联网方式"决定):
|
|
||||||
* 上次 4G 成功:4G(无卡/无信号每 10s 重试,3 次失败)→ WiFi → 回 4G…循环
|
|
||||||
* 上次 WiFi 成功:WiFi 建链一轮失败 → 4G(同上重试)→ 回 WiFi…循环
|
|
||||||
* WiFi 建链子顺序:上次是配网热点则先查已存 AP 自动连接(CIPSTATUS, 10s)
|
|
||||||
* → 固定 SSID(60s)→ SmartConfig(60s)
|
|
||||||
* 任何方式联网成功后调 NET_SaveMode(),仅方式变化时写一次 EEPROM。
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "network.h"
|
|
||||||
#include "net_wifi.h"
|
|
||||||
#include "hlw8032.h"
|
|
||||||
#include "feed_scale.h"
|
|
||||||
#include "net_wifi_ota.h"
|
|
||||||
#include "air780e.h"
|
|
||||||
#include "led.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "main.h"
|
|
||||||
#include "uart.h"
|
|
||||||
#include "relay.h"
|
|
||||||
#include "proto.h"
|
|
||||||
#include "24c02.h"
|
|
||||||
#include "FreeRTOS.h"
|
|
||||||
#include "task.h"
|
|
||||||
#include "semphr.h"
|
|
||||||
#include "stdio.h"
|
|
||||||
#include "string.h"
|
|
||||||
|
|
||||||
volatile uint8_t g_net_mqtt_ready = 0;
|
|
||||||
volatile uint8_t g_net_first_publish_ok = 0;
|
|
||||||
|
|
||||||
/* 网络发送互斥标志:OTA 下载等 AT 交互期间置位 */
|
|
||||||
volatile uint8_t g_net_tx_busy = 0;
|
|
||||||
|
|
||||||
/* 4G 无 SIM 卡标志:由 air780e.c 在初始化失败时设置 */
|
|
||||||
volatile uint8_t g_no_sim_detected = 0;
|
|
||||||
|
|
||||||
/* 4G 无信号标志:由 air780e.c 在 AT+CSQ 检测时设置 */
|
|
||||||
volatile uint8_t g_no_signal_detected = 0;
|
|
||||||
|
|
||||||
/* 当前激活的网络后端 */
|
|
||||||
/* WiFi 模块使能脚(PA6,高电平工作):4G 运行时拉低断电省电 */
|
|
||||||
#define ESP_EN_PORT GPIOA
|
|
||||||
#define ESP_EN_PIN GPIO_PIN_6
|
|
||||||
static void esp_power(uint8_t on)
|
|
||||||
{
|
|
||||||
HAL_GPIO_WritePin(ESP_EN_PORT, ESP_EN_PIN, on ? GPIO_PIN_SET : GPIO_PIN_RESET);
|
|
||||||
}
|
|
||||||
|
|
||||||
volatile net_ui_phase_t g_net_ui_phase = NET_UI_NONE;
|
|
||||||
volatile int g_net_ui_countdown = 0;
|
|
||||||
|
|
||||||
#define OTA_ID_EEP_ADDR 175 /* EEPROM 175~194:OTA 平台任务 id(确认后清 0xFF) */
|
|
||||||
|
|
||||||
static net_backend_t s_backend = NET_BACKEND_4G;
|
|
||||||
|
|
||||||
/* 上次成功联网方式(EEPROM 偏移 130,秤参数占用到 126,避开) */
|
|
||||||
#define EEPROM_NETMODE_ADDR 130
|
|
||||||
static uint8_t s_last_mode = NET_MODE_4G;
|
|
||||||
|
|
||||||
/* 无 SIM 重试多少次后切 WiFi */
|
|
||||||
#define NET_NO_SIM_SWITCH_COUNT 3
|
|
||||||
/* WiFi 一轮完整建链(已存AP→固定SSID→SmartConfig)失败后切回 4G */
|
|
||||||
#define NET_WIFI_FAIL_SWITCH_COUNT 1
|
|
||||||
|
|
||||||
/*==== 网络模组 AT 会话互斥(RTOS 版)====
|
|
||||||
* net 任务在做长 AT 会话(建链/重连/OTA)期间持有锁;
|
|
||||||
* 其他任务的 NET_publish_* 采用尝试锁,拿不到就丢弃本次发布,
|
|
||||||
* 避免业务发布与进行中的 AT 应答在串口上交织导致误判。
|
|
||||||
* 递归锁:net 任务自己调用的发布(如 net_wifi_init 里的电量上报)可重入。 */
|
|
||||||
static SemaphoreHandle_t s_net_at_mutex = NULL;
|
|
||||||
|
|
||||||
void NET_LockInit(void)
|
|
||||||
{
|
|
||||||
s_net_at_mutex = xSemaphoreCreateRecursiveMutex();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void net_at_lock(void)
|
|
||||||
{
|
|
||||||
if (s_net_at_mutex && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
|
|
||||||
xSemaphoreTakeRecursive(s_net_at_mutex, portMAX_DELAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void net_at_unlock(void)
|
|
||||||
{
|
|
||||||
if (s_net_at_mutex && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
|
|
||||||
xSemaphoreGiveRecursive(s_net_at_mutex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 尝试拿锁:0=拿到(含调度器未启动的直通),-1=被 AT 会话占用。
|
|
||||||
* 带 2s 超时而非立即放弃:net 任务每轮 process 会短暂持锁消费接收缓冲,
|
|
||||||
* 业务发布稍微等一下即可,避免因互斥频繁丢包 */
|
|
||||||
static int net_at_trylock(void)
|
|
||||||
{
|
|
||||||
if (s_net_at_mutex && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
|
|
||||||
return xSemaphoreTakeRecursive(s_net_at_mutex, pdMS_TO_TICKS(2000)) == pdTRUE ? 0 : -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
net_backend_t NET_GetActiveBackend(void)
|
|
||||||
{
|
|
||||||
return s_backend;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t NET_GetLastMode(void)
|
|
||||||
{
|
|
||||||
return s_last_mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 联网方式记忆:仅方式变化时写一次 EEPROM("只存一次") */
|
|
||||||
void NET_SaveMode(uint8_t mode)
|
|
||||||
{
|
|
||||||
if (mode == s_last_mode || mode > NET_MODE_WIFI_SMART) return;
|
|
||||||
s_last_mode = mode;
|
|
||||||
eepromWriteData(EEPROM_NETMODE_ADDR, &mode, 1);
|
|
||||||
log_info("> NET: 联网方式已保存 -> %s",
|
|
||||||
mode == NET_MODE_4G ? "4G" :
|
|
||||||
mode == NET_MODE_WIFI_FIXED ? "WiFi(固定热点)" : "WiFi(配网热点)");
|
|
||||||
}
|
|
||||||
|
|
||||||
void NET_init(void)
|
|
||||||
{
|
|
||||||
g_net_mqtt_ready = 0;
|
|
||||||
g_net_first_publish_ok = 0;
|
|
||||||
g_no_sim_detected = 0;
|
|
||||||
g_no_signal_detected = 0;
|
|
||||||
|
|
||||||
/* 读 EEPROM 记忆的上次成功联网方式:非法值(含 EEPROM 未初始化的 0xFF)按 4G */
|
|
||||||
uint8_t m = NET_MODE_4G;
|
|
||||||
eepromReadData(EEPROM_NETMODE_ADDR, &m, 1);
|
|
||||||
if (m > NET_MODE_WIFI_SMART) m = NET_MODE_4G;
|
|
||||||
s_last_mode = m;
|
|
||||||
|
|
||||||
if (m == NET_MODE_4G) {
|
|
||||||
s_backend = NET_BACKEND_4G;
|
|
||||||
esp_power(0); /* 4G 优先:关闭 WiFi 模块省电 */
|
|
||||||
net_at_lock();
|
|
||||||
air780e_init();
|
|
||||||
net_at_unlock();
|
|
||||||
|
|
||||||
if (g_no_sim_detected) {
|
|
||||||
log_warn("> 4G 无 SIM 卡,请插入 SIM 卡");
|
|
||||||
/* 不立即切 WiFi:由 NET_process 重试几次仍无卡后再切换 */
|
|
||||||
}
|
|
||||||
|
|
||||||
g_net_mqtt_ready = g_air780e_mqtt_ready;
|
|
||||||
} else {
|
|
||||||
/* 上次 WiFi 成功:WiFi 优先,NET_process 会立即发起 net_wifi_init */
|
|
||||||
log_info("> NET: 上次联网方式为 WiFi,WiFi 优先");
|
|
||||||
esp_power(1); /* WiFi 优先:使能 WiFi 模块 */
|
|
||||||
s_backend = NET_BACKEND_WIFI;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 切到 WiFi 通道:停 4G 接收(共享缓冲互斥),WiFi 建链由 NET_process 立即发起 */
|
|
||||||
static void net_switch_to_wifi(void)
|
|
||||||
{
|
|
||||||
log_warn("> NET: 切换 4G -> WiFi");
|
|
||||||
esp_power(1); /* 使能 WiFi 模块 */
|
|
||||||
USART3_StopRx();
|
|
||||||
g_net_mqtt_ready = 0;
|
|
||||||
g_net_first_publish_ok = 0;
|
|
||||||
s_backend = NET_BACKEND_WIFI;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 切回 4G 通道:停 WiFi 接收,恢复 4G 接收并重新初始化 */
|
|
||||||
static void net_switch_to_4g(void)
|
|
||||||
{
|
|
||||||
log_warn("> NET: 切换 WiFi -> 4G");
|
|
||||||
UART4_StopRx();
|
|
||||||
esp_power(0); /* 关闭 WiFi 模块省电 */
|
|
||||||
g_net_mqtt_ready = 0;
|
|
||||||
g_net_first_publish_ok = 0;
|
|
||||||
g_no_sim_detected = 0;
|
|
||||||
g_no_signal_detected = 0;
|
|
||||||
g_air780e_mqtt_ready = 0;
|
|
||||||
g_air780e_first_publish_ok = 0;
|
|
||||||
s_backend = NET_BACKEND_4G;
|
|
||||||
USART3_StartRx();
|
|
||||||
net_at_lock();
|
|
||||||
air780e_init();
|
|
||||||
net_at_unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NET_process(void)
|
|
||||||
{
|
|
||||||
static uint32_t retry_tick = 0;
|
|
||||||
static uint16_t fail_count = 0;
|
|
||||||
|
|
||||||
if (s_backend == NET_BACKEND_4G) {
|
|
||||||
/* 网络未就绪时自动重试 */
|
|
||||||
if (!g_air780e_mqtt_ready) {
|
|
||||||
if (g_no_sim_detected || g_no_signal_detected) {
|
|
||||||
/* 无 SIM 卡或无信号:每 10s 重试一次,连续多次仍不可用则切换到 WiFi 通道 */
|
|
||||||
if (retry_tick == 0 ||
|
|
||||||
(HAL_GetTick() - retry_tick) >= 10000) {
|
|
||||||
retry_tick = HAL_GetTick();
|
|
||||||
fail_count++;
|
|
||||||
log_warn("> NET: 4G 不可用 (no_sim=%d, no_signal=%d),重试初始化 %d/%d",
|
|
||||||
g_no_sim_detected, g_no_signal_detected,
|
|
||||||
fail_count, NET_NO_SIM_SWITCH_COUNT);
|
|
||||||
g_no_sim_detected = 0;
|
|
||||||
g_no_signal_detected = 0;
|
|
||||||
net_at_lock();
|
|
||||||
air780e_init();
|
|
||||||
net_at_unlock();
|
|
||||||
if (!g_no_sim_detected && !g_no_signal_detected) {
|
|
||||||
log_info("> NET: 4G SIM卡/信号正常或网络已恢复");
|
|
||||||
fail_count = 0;
|
|
||||||
} else if (fail_count >= NET_NO_SIM_SWITCH_COUNT) {
|
|
||||||
log_warn("> NET: 4G 重试 %d 次仍不可用,回退到 WiFi",
|
|
||||||
NET_NO_SIM_SWITCH_COUNT);
|
|
||||||
net_switch_to_wifi();
|
|
||||||
fail_count = 0;
|
|
||||||
retry_tick = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
retry_tick = 0;
|
|
||||||
fail_count = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
retry_tick = 0;
|
|
||||||
fail_count = 0;
|
|
||||||
NET_SaveMode(NET_MODE_4G); /* 4G 联网成功:更新方式记忆(未变化则空操作) */
|
|
||||||
}
|
|
||||||
|
|
||||||
net_at_lock();
|
|
||||||
air780e_process();
|
|
||||||
net_at_unlock();
|
|
||||||
g_net_mqtt_ready = g_air780e_mqtt_ready;
|
|
||||||
g_net_first_publish_ok = g_air780e_first_publish_ok;
|
|
||||||
} else {
|
|
||||||
/* WiFi 通道:未就绪时重新建链(首轮立即),一轮失败即切回 4G */
|
|
||||||
if (!g_wifi_mqtt_ready) {
|
|
||||||
if (retry_tick == 0 ||
|
|
||||||
(HAL_GetTick() - retry_tick) >= 10000) {
|
|
||||||
retry_tick = HAL_GetTick();
|
|
||||||
fail_count++;
|
|
||||||
log_warn("> NET: WiFi 连接尝试 %d/%d",
|
|
||||||
fail_count, NET_WIFI_FAIL_SWITCH_COUNT);
|
|
||||||
net_at_lock();
|
|
||||||
int wifi_rc = net_wifi_init();
|
|
||||||
net_at_unlock();
|
|
||||||
if (wifi_rc == 0) {
|
|
||||||
fail_count = 0;
|
|
||||||
} else if (fail_count >= NET_WIFI_FAIL_SWITCH_COUNT) {
|
|
||||||
log_warn("> NET: WiFi 连接失败,回退到 4G");
|
|
||||||
net_switch_to_4g();
|
|
||||||
fail_count = 0;
|
|
||||||
retry_tick = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
retry_tick = 0;
|
|
||||||
fail_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
net_at_lock();
|
|
||||||
net_wifi_process();
|
|
||||||
net_at_unlock();
|
|
||||||
g_net_mqtt_ready = g_wifi_mqtt_ready;
|
|
||||||
g_net_first_publish_ok = g_wifi_first_publish_ok;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int NET_publish_status(int sw1, int feeding, int sw2, int sw3, int sw4,
|
|
||||||
float weight_kg, float once_wt_kg, float zero_kg)
|
|
||||||
{
|
|
||||||
if (g_net_tx_busy) { log_info("> NET: 状态上报被丢弃 (发送忙)"); return -1; }
|
|
||||||
if (net_at_trylock() != 0) { log_info("> NET: 状态上报被丢弃 (AT 忙)"); return -1; }
|
|
||||||
/* ver 字段:固件版本号,平台据此可识别设备是否发生了 OTA 回退 */
|
|
||||||
/* sw1 与 feeding 都映射继电器 1 状态(无秤模式两者等效),
|
|
||||||
* 两个字段都上报,平台的 sw1 开关和 feeding 状态才能同步刷新 */
|
|
||||||
/* 无秤模式(PRODUCT_WITH_FEED_SCALE=0)不上报 zero/onceWt 字段 */
|
|
||||||
if (s_backend == NET_BACKEND_4G) {
|
|
||||||
if (g_feed_scale_on) {
|
|
||||||
CAT1_printf("AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22header\\22:\\22iot.prop.post\\22,\\22body\\22:{\\22ver\\22:%u,\\22weight\\22:%.1f,\\22onceWt\\22:%.1f,\\22zero\\22:%.1f,\\22sw1\\22:%d,\\22feeding\\22:%d,\\22sw2\\22:%d,\\22sw3\\22:%d,\\22sw4\\22:%d}}\"",
|
|
||||||
MqttInfoStr.ClientID, (unsigned)MqttInfoStr.Ver, zero_kg, once_wt_kg, weight_kg, sw1, feeding, sw2, sw3, sw4);
|
|
||||||
} else {
|
|
||||||
CAT1_printf("AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22header\\22:\\22iot.prop.post\\22,\\22body\\22:{\\22ver\\22:%u,\\22weight\\22:%.1f,\\22sw1\\22:%d,\\22feeding\\22:%d,\\22sw2\\22:%d,\\22sw3\\22:%d,\\22sw4\\22:%d,\\22energy\\22:%.2f}}\"",
|
|
||||||
MqttInfoStr.ClientID, (unsigned)MqttInfoStr.Ver, zero_kg, sw1, feeding, sw2, sw3, sw4, (double)hlw8032_get_energy_kwh());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
char json[256];
|
|
||||||
if (g_feed_scale_on) {
|
|
||||||
snprintf(json, sizeof(json),
|
|
||||||
"{\"header\":\"iot.prop.post\",\"body\":{\"ver\":%u,\"weight\":%.1f,\"onceWt\":%.1f,\"zero\":%.1f,\"sw1\":%d,\"feeding\":%d,\"sw2\":%d,\"sw3\":%d,\"sw4\":%d}}",
|
|
||||||
(unsigned)MqttInfoStr.Ver, zero_kg, once_wt_kg, weight_kg, sw1, feeding, sw2, sw3, sw4);
|
|
||||||
} else {
|
|
||||||
snprintf(json, sizeof(json),
|
|
||||||
"{\"header\":\"iot.prop.post\",\"body\":{\"ver\":%u,\"weight\":%.1f,\"sw1\":%d,\"feeding\":%d,\"sw2\":%d,\"sw3\":%d,\"sw4\":%d,\"energy\":%.2f}}",
|
|
||||||
(unsigned)MqttInfoStr.Ver, zero_kg, sw1, feeding, sw2, sw3, sw4, (double)hlw8032_get_energy_kwh());
|
|
||||||
}
|
|
||||||
net_wifi_publish_up(json);
|
|
||||||
}
|
|
||||||
net_at_unlock();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NET_publish_response(const char *cmd_id, int ok, const char *msg)
|
|
||||||
{
|
|
||||||
if (g_net_tx_busy) { log_info("> NET: 应答上报被丢弃 (发送忙)"); return; }
|
|
||||||
if (net_at_trylock() != 0) { log_info("> NET: 应答上报被丢弃 (AT 忙)"); return; }
|
|
||||||
const char *text = msg ? msg : (ok ? "OK" : "command failed");
|
|
||||||
if (s_backend == NET_BACKEND_4G) {
|
|
||||||
CAT1_printf("AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22id\\22:\\22%s\\22,\\22code\\22:0,\\22message\\22:\\22%s\\22}\"",
|
|
||||||
MqttInfoStr.ClientID, cmd_id, text);
|
|
||||||
} else {
|
|
||||||
char json[200];
|
|
||||||
snprintf(json, sizeof(json),
|
|
||||||
"{\"id\":\"%s\",\"code\":0,\"message\":\"%s\"}", cmd_id, text);
|
|
||||||
net_wifi_publish_up(json);
|
|
||||||
}
|
|
||||||
net_at_unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 上报代码版本字符串: ver 字段,内容即 FIRMWARE_VERSION_STR(每次联网成功报一次) */
|
|
||||||
void NET_publish_version(const char *ver)
|
|
||||||
{
|
|
||||||
if (g_net_tx_busy) { log_info("> NET: 版本上报被丢弃 (发送忙)"); return; }
|
|
||||||
if (net_at_trylock() != 0) { log_info("> NET: 版本上报被丢弃 (AT 忙)"); return; }
|
|
||||||
if (s_backend == NET_BACKEND_4G) {
|
|
||||||
CAT1_printf("AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22header\\22:\\22iot.prop.post\\22,\\22body\\22:{\\22fwver\\22:\\22%s\\22}}\"",
|
|
||||||
MqttInfoStr.ClientID, ver);
|
|
||||||
} else {
|
|
||||||
char json[96];
|
|
||||||
snprintf(json, sizeof(json),
|
|
||||||
"{\"header\":\"iot.prop.post\",\"body\":{\"fwver\":\"%s\"}}", ver);
|
|
||||||
net_wifi_publish_up(json);
|
|
||||||
}
|
|
||||||
net_at_unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 上报电量状态: on=1 正常, on=0 低电量。返回 0=已发出,-1=通道忙被丢弃 */
|
|
||||||
int NET_publish_power(int on)
|
|
||||||
{
|
|
||||||
if (g_net_tx_busy) { log_info("> NET: 电量上报被丢弃 (发送忙)"); return -1; }
|
|
||||||
if (net_at_trylock() != 0) { log_info("> NET: 电量上报被丢弃 (AT 忙)"); return -1; }
|
|
||||||
if (s_backend == NET_BACKEND_4G) {
|
|
||||||
CAT1_printf("AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22header\\22:\\22iot.prop.post\\22,\\22body\\22:{\\22pow\\22:%d}}\"",
|
|
||||||
MqttInfoStr.ClientID, on ? 1 : 0);
|
|
||||||
} else {
|
|
||||||
char json[96];
|
|
||||||
snprintf(json, sizeof(json),
|
|
||||||
"{\"header\":\"iot.prop.post\",\"body\":{\"pow\":%d}}", on ? 1 : 0);
|
|
||||||
net_wifi_publish_up(json);
|
|
||||||
}
|
|
||||||
net_at_unlock();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 断电预警专用:发 pow 并等模组回 OK(最多 ~1.5s),确认模组已受理。
|
|
||||||
* 返回 0=模组已受理,-1=通道忙/无应答。WiFi 通道 QoS0 无 OK 概念,发完即返 0 */
|
|
||||||
int NET_publish_power_wait(int on)
|
|
||||||
{
|
|
||||||
if (g_net_tx_busy) return -1;
|
|
||||||
if (net_at_trylock() != 0) return -1;
|
|
||||||
int rc = 0;
|
|
||||||
if (s_backend == NET_BACKEND_4G) {
|
|
||||||
static char cmd[160];
|
|
||||||
sprintf(cmd, "AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22header\\22:\\22iot.prop.post\\22,\\22body\\22:{\\22pow\\22:%d}}\"",
|
|
||||||
MqttInfoStr.ClientID, on ? 1 : 0);
|
|
||||||
if (catSendCmd(cmd, "OK", 1, 15) != 0) rc = -1;
|
|
||||||
} else {
|
|
||||||
char json[96];
|
|
||||||
snprintf(json, sizeof(json),
|
|
||||||
"{\"header\":\"iot.prop.post\",\"body\":{\"pow\":%d}}", on ? 1 : 0);
|
|
||||||
net_wifi_publish_up(json);
|
|
||||||
}
|
|
||||||
net_at_unlock();
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 上报累计电量+实时功率: energy(kWh) 数值 + power 字符串("220V,0.21A,46.2W"),同一条报文。
|
|
||||||
* power 物模型为字符串类型,一次性展示电压/电流/功率 */
|
|
||||||
void NET_publish_energy(float kwh, float power_w)
|
|
||||||
{
|
|
||||||
if (g_net_tx_busy) { log_info("> NET: energy 上报被丢弃 (发送忙)"); return; }
|
|
||||||
if (net_at_trylock() != 0) { log_info("> NET: energy 上报被丢弃 (AT 忙)"); return; }
|
|
||||||
char pwr[40];
|
|
||||||
snprintf(pwr, sizeof(pwr), "%dV,%.2fA,%.1fW",
|
|
||||||
(int)hlw8032_get_voltage_v(), (double)hlw8032_get_current_a(), (double)power_w);
|
|
||||||
if (s_backend == NET_BACKEND_4G) {
|
|
||||||
CAT1_printf("AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22header\\22:\\22iot.prop.post\\22,\\22body\\22:{\\22energy\\22:%.2f,\\22power\\22:\\22%s\\22}}\"",
|
|
||||||
MqttInfoStr.ClientID, kwh, pwr);
|
|
||||||
} else {
|
|
||||||
char json[128];
|
|
||||||
snprintf(json, sizeof(json),
|
|
||||||
"{\"header\":\"iot.prop.post\",\"body\":{\"energy\":%.2f,\"power\":\"%s\"}}", kwh, pwr);
|
|
||||||
net_wifi_publish_up(json);
|
|
||||||
}
|
|
||||||
log_debug("> NET: energy 已发布 %.2f %s", (double)kwh, pwr);
|
|
||||||
net_at_unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
int NET_mqtt_report(const char *header, const char *ota_id, int val)
|
|
||||||
{
|
|
||||||
if (g_net_tx_busy) { log_info("> NET: 进度/结果上报被丢弃 (发送忙)"); return -1; }
|
|
||||||
if (net_at_trylock() != 0) { log_info("> NET: 进度/结果上报被丢弃 (AT 忙)"); return -1; }
|
|
||||||
if (s_backend == NET_BACKEND_4G) {
|
|
||||||
int rc = air780e_mqtt_report(header, ota_id, val);
|
|
||||||
net_at_unlock();
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
if (!header || !ota_id || !ota_id[0]) { net_at_unlock(); return -1; }
|
|
||||||
char json[200];
|
|
||||||
if (strstr(header, "progress")) {
|
|
||||||
snprintf(json, sizeof(json),
|
|
||||||
"{\"header\":\"%s\",\"body\":{\"id\":\"%.16s\",\"progress\":%d}}",
|
|
||||||
header, ota_id, val);
|
|
||||||
} else {
|
|
||||||
snprintf(json, sizeof(json),
|
|
||||||
"{\"header\":\"%s\",\"body\":{\"id\":\"%.16s\",\"success\":%s}}",
|
|
||||||
header, ota_id, val ? "true" : "false");
|
|
||||||
}
|
|
||||||
net_wifi_publish_up(json);
|
|
||||||
net_at_unlock();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int NET_ota_confirm(void)
|
|
||||||
{
|
|
||||||
/* 两通道复用同一确认流程:无挂起 OTA 时内部直接返回;
|
|
||||||
* 其中的上报已改为 NET_mqtt_report,按当前通道路由 */
|
|
||||||
return air780e_ota_confirm();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 下行消息统一分发(从 air780e.c 上提,与模组无关)====
|
|
||||||
* msg 可能是整条 +MSUB URC(4G)或纯 JSON(WiFi),均按子串匹配处理 */
|
|
||||||
void net_dispatch_message(const char *msg)
|
|
||||||
{
|
|
||||||
/* dedup by message id */
|
|
||||||
static char last_id[20] = {0};
|
|
||||||
char cur_id[20] = {0};
|
|
||||||
proto_get_id((char*)msg, cur_id, sizeof(cur_id));
|
|
||||||
if (cur_id[0] && strcmp(cur_id, last_id) == 0) {
|
|
||||||
log_info("> MQTT: 重复消息跳过,id=%.16s", cur_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
strncpy(last_id, cur_id, sizeof(last_id)-1);
|
|
||||||
|
|
||||||
/* relay control */
|
|
||||||
if (strstr(msg, "iot.prop.set")) { relay_action((uint8_t *)msg); }
|
|
||||||
|
|
||||||
/* OTA upgrade */
|
|
||||||
if (strstr(msg, "iot.ota.upgrade.post")) {
|
|
||||||
log_info("> OTA 升级命令!");
|
|
||||||
char host[30]={0}, path[80]={0}, ota_id[20]={0}; int port = 81;
|
|
||||||
uint16_t new_ver = 0;
|
|
||||||
uint16_t crc16 = 0;
|
|
||||||
|
|
||||||
/* 解析版本号与 crc16 */
|
|
||||||
{ uint32_t v = 0; if (proto_get_u32((char*)msg, "ver", &v)) new_ver = (uint16_t)v; }
|
|
||||||
proto_get_hex16((char*)msg, "crc16", &crc16);
|
|
||||||
log_info("> OTA 版本: 当前=%u, 新版本=%u, crc16=0x%04X", MqttInfoStr.Ver, new_ver, crc16);
|
|
||||||
|
|
||||||
/* 版本号 ≤ 当前版本 → 跳过下载 (防止重复或降级) */
|
|
||||||
if (new_ver > 0 && new_ver <= MqttInfoStr.Ver) {
|
|
||||||
proto_get_id((char*)msg, ota_id, sizeof(ota_id));
|
|
||||||
log_info("> OTA: 版本相同或更低,跳过 (当前=%u, 新版本=%u)", MqttInfoStr.Ver, new_ver);
|
|
||||||
NET_mqtt_report("iot.ota.progress.post", ota_id, 10);
|
|
||||||
} else {
|
|
||||||
strncpy(host, MqttInfoStr.ServerIP, sizeof(host)-1); strcpy(path, "/firmware.bin");
|
|
||||||
proto_get_id((char*)msg, ota_id, sizeof(ota_id));
|
|
||||||
{ char fu[100]={0};
|
|
||||||
if (proto_get_str((char*)msg, "url", fu, sizeof(fu))) {
|
|
||||||
log_info("> OTA 下载地址: %s", fu); char *p=fu;
|
|
||||||
if(strncmp(p,"http://",7)==0)p+=7;
|
|
||||||
char *sl=strchr(p,'/'),*co=strchr(p,':');
|
|
||||||
if(co&&(!sl||co<sl)){ int hl=co-p; memcpy(host,p,hl); host[hl]=0;
|
|
||||||
co++; port=0; while(*co>='0'&&*co<='9'){port=port*10+(*co-'0');co++;}
|
|
||||||
if(sl)strncpy(path,sl,sizeof(path)-1); }
|
|
||||||
else if(sl){ int hl=sl-p; memcpy(host,p,hl); host[hl]=0; strncpy(path,sl,sizeof(path)-1); }
|
|
||||||
else strncpy(host,p,sizeof(host)-1);
|
|
||||||
}}
|
|
||||||
log_info("> OTA: %s:%d%s", host, port, path);
|
|
||||||
/* 平台任务 id 存 EEPROM(偏移 175):重启确认时补发 result.post 用。
|
|
||||||
* 注意:故意不用 W25Q64 OTA 信息结构(它 BL/APP 共享,改结构要重刷 BL,
|
|
||||||
* 现场设备无法远程刷 BL),EEPROM 是 APP 私有的 */
|
|
||||||
if (ota_id[0]) {
|
|
||||||
char id20[20] = {0};
|
|
||||||
strncpy(id20, ota_id, sizeof(id20) - 1);
|
|
||||||
eepromWriteData(OTA_ID_EEP_ADDR, id20, sizeof(id20));
|
|
||||||
}
|
|
||||||
/* 两条通道共用 W25Q64 双槽 + CRC + BootLoader 回退体系,仅下载传输不同 */
|
|
||||||
if (s_backend == NET_BACKEND_WIFI) {
|
|
||||||
net_wifi_ota(host, port, path, ota_id, new_ver, crc16);
|
|
||||||
} else {
|
|
||||||
air780e_trigger_ota(host, port, path, ota_id, new_ver, crc16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
#ifndef NETWORK_H
|
|
||||||
#define NETWORK_H
|
|
||||||
|
|
||||||
#include "main.h"
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/*===== 网络后端(通道)=====*/
|
|
||||||
/* 4G 与 WiFi 互斥运行:任一时刻只有一个后端激活,
|
|
||||||
* 两者共享 Rx_Buf/U2_CopyBuff(见 stm32f1xx_it.c 的 IDLE 守卫) */
|
|
||||||
typedef enum {
|
|
||||||
NET_BACKEND_4G = 0, /* Air780E, USART3, 模组内置MQTT(AT+MPUB) */
|
|
||||||
NET_BACKEND_WIFI /* ESP-01S, UART4, TCP透传+软件MQTT */
|
|
||||||
} net_backend_t;
|
|
||||||
|
|
||||||
/*===== 网络状态 =====*/
|
|
||||||
extern volatile uint8_t g_net_mqtt_ready;
|
|
||||||
extern volatile uint8_t g_net_first_publish_ok;
|
|
||||||
extern volatile uint8_t g_no_sim_detected;
|
|
||||||
extern volatile uint8_t g_no_signal_detected; /* 4G 无信号标志:air780e.c 在 AT+CSQ 检测失败时设置 */
|
|
||||||
|
|
||||||
/*===== 上次成功联网方式(EEPROM 持久化,仅方式变化时写一次)=====*/
|
|
||||||
#define NET_MODE_4G 0 /* 4G */
|
|
||||||
#define NET_MODE_WIFI_FIXED 1 /* WiFi - 代码里固定的热点 */
|
|
||||||
#define NET_MODE_WIFI_SMART 2 /* WiFi - SmartConfig 配网的热点 */
|
|
||||||
uint8_t NET_GetLastMode(void); /* 读当前记忆的方式 */
|
|
||||||
void NET_SaveMode(uint8_t mode); /* 方式变化时写 EEPROM,未变化则空操作 */
|
|
||||||
|
|
||||||
/* 网络发送互斥:置位期间 NET_publish_* 直接丢弃(OTA 下载等 AT 交互期间使用,
|
|
||||||
* 防止业务发布与进行中的 AT 指令在 USART3 上交织导致应答误判) */
|
|
||||||
extern volatile uint8_t g_net_tx_busy;
|
|
||||||
|
|
||||||
/*===== OLED 联网进度显示(net 任务各阶段更新,sys_task 屏幕读取)=====*/
|
|
||||||
typedef enum {
|
|
||||||
NET_UI_NONE = 0, /* 无进度/初始化中 */
|
|
||||||
NET_UI_4G_RESET, /* 4G 模块复位 */
|
|
||||||
NET_UI_4G_AT, /* AT 指令检测通过 */
|
|
||||||
NET_UI_4G_SIM, /* SIM 卡就绪 */
|
|
||||||
NET_UI_4G_ATTACH, /* 附着/激活网络成功 */
|
|
||||||
NET_UI_4G_MQTT, /* MQTT 连接中 */
|
|
||||||
NET_UI_4G_NOSIM, /* 无 SIM 卡 */
|
|
||||||
NET_UI_WIFI_FIXED, /* WiFi 固定热点搜索中(countdown 有效) */
|
|
||||||
NET_UI_WIFI_SMART, /* SmartConfig 配网中(countdown 有效) */
|
|
||||||
} net_ui_phase_t;
|
|
||||||
extern volatile net_ui_phase_t g_net_ui_phase;
|
|
||||||
extern volatile int g_net_ui_countdown; /* WiFi 连接/配网剩余秒数 */
|
|
||||||
|
|
||||||
/*===== API =====*/
|
|
||||||
void NET_init(void);
|
|
||||||
void NET_process(void);
|
|
||||||
void NET_LockInit(void); /* 网络 AT 会话互斥锁初始化(调度器启动前调用一次) */
|
|
||||||
net_backend_t NET_GetActiveBackend(void);
|
|
||||||
int NET_publish_status(int sw1, int feeding, int sw2, int sw3, int sw4,
|
|
||||||
float weight_kg, float once_wt_kg, float zero_kg); /* 返回 0=已发出 */
|
|
||||||
void NET_publish_response(const char *cmd_id, int ok, const char *msg);
|
|
||||||
int NET_publish_power(int on);
|
|
||||||
int NET_publish_power_wait(int on); /* 断电预警专用:等模组 OK,0=已受理 */
|
|
||||||
void NET_publish_version(const char *ver); /* 上报代码版本字符串(FIRMWARE_VERSION_STR) */ /* 返回 0=已发出,-1=通道忙被丢弃 */
|
|
||||||
void NET_publish_energy(float kwh, float power_w); /* 上报累计电量,标识符 energy,单位 kWh */
|
|
||||||
int NET_mqtt_report(const char *header, const char *ota_id, int val); /* 返回 0=送达 */
|
|
||||||
int NET_ota_confirm(void); /* 0=确认通知已送达 1=无挂起 -1=未送达(需重试) */
|
|
||||||
|
|
||||||
/* 下行消息统一分发(与模组无关):id去重 → iot.prop.set继电器控制 → iot.ota升级。
|
|
||||||
* 4G 后端切出 +MSUB 片段后调用;WiFi 后端解出 MQTT PUBLISH payload 后调用。
|
|
||||||
* msg 可以是整条 +MSUB URC 或纯 JSON,内部按子串匹配。 */
|
|
||||||
void net_dispatch_message(const char *msg);
|
|
||||||
|
|
||||||
#endif /* NETWORK_H */
|
|
||||||
|
|
@ -1,329 +0,0 @@
|
||||||
/* oled.c - 128x64 OLED + 晶联讯 GB2312 字库 IC 驱动(GPIO 模拟 SPI)
|
|
||||||
* 移植自《485模拟磅秤数据带CRC》工程 oledisp.c,引脚改为本板:
|
|
||||||
* CLK=PC12 MOSI=PC9 DC=PC8 CS1=PC7 FSO=PC6 CS2=PB15 */
|
|
||||||
|
|
||||||
#include "oled.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define OLED_CLK_PORT GPIOC
|
|
||||||
#define OLED_CLK_PIN GPIO_PIN_12
|
|
||||||
#define OLED_MOSI_PORT GPIOC
|
|
||||||
#define OLED_MOSI_PIN GPIO_PIN_9
|
|
||||||
#define OLED_DC_PORT GPIOC
|
|
||||||
#define OLED_DC_PIN GPIO_PIN_8
|
|
||||||
#define OLED_CS1_PORT GPIOC
|
|
||||||
#define OLED_CS1_PIN GPIO_PIN_7
|
|
||||||
#define OLED_FSO_PORT GPIOC
|
|
||||||
#define OLED_FSO_PIN GPIO_PIN_6
|
|
||||||
#define OLED_CS2_PORT GPIOB
|
|
||||||
#define OLED_CS2_PIN GPIO_PIN_15
|
|
||||||
#define OLED_RST_PORT GPIOB
|
|
||||||
#define OLED_RST_PIN GPIO_PIN_14 /* 复位脚,低电平复位(板内上拉,默认拉高即可) */
|
|
||||||
|
|
||||||
#define lcd_cs1(a) HAL_GPIO_WritePin(OLED_CS1_PORT, OLED_CS1_PIN, (GPIO_PinState)(a))
|
|
||||||
#define lcd_dc(a) HAL_GPIO_WritePin(OLED_DC_PORT, OLED_DC_PIN, (GPIO_PinState)(a))
|
|
||||||
#define lcd_mosi(a) HAL_GPIO_WritePin(OLED_MOSI_PORT, OLED_MOSI_PIN, (GPIO_PinState)(a))
|
|
||||||
#define lcd_clk(a) HAL_GPIO_WritePin(OLED_CLK_PORT, OLED_CLK_PIN, (GPIO_PinState)(a))
|
|
||||||
#define rom_cs2(a) HAL_GPIO_WritePin(OLED_CS2_PORT, OLED_CS2_PIN, (GPIO_PinState)(a))
|
|
||||||
#define ROM_OUT HAL_GPIO_ReadPin(OLED_FSO_PORT, OLED_FSO_PIN)
|
|
||||||
|
|
||||||
static uint8_t s_row = 0; /* 当前页地址(16px 行 × 2) */
|
|
||||||
static uint8_t s_col = 0; /* 当前列(像素) */
|
|
||||||
|
|
||||||
/*==== FSO 脚输入/输出切换(读字库时为输入)====*/
|
|
||||||
static void fso_input(void)
|
|
||||||
{
|
|
||||||
GPIO_InitTypeDef g = {0};
|
|
||||||
g.Pin = OLED_FSO_PIN;
|
|
||||||
g.Mode = GPIO_MODE_INPUT;
|
|
||||||
g.Pull = GPIO_NOPULL;
|
|
||||||
HAL_GPIO_Init(OLED_FSO_PORT, &g);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void fso_output(void)
|
|
||||||
{
|
|
||||||
GPIO_InitTypeDef g = {0};
|
|
||||||
g.Pin = OLED_FSO_PIN;
|
|
||||||
g.Mode = GPIO_MODE_OUTPUT_PP;
|
|
||||||
g.Pull = GPIO_PULLUP;
|
|
||||||
g.Speed = GPIO_SPEED_FREQ_LOW;
|
|
||||||
HAL_GPIO_Init(OLED_FSO_PORT, &g);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== GPIO 初始化 ====*/
|
|
||||||
static void oled_gpio_init(void)
|
|
||||||
{
|
|
||||||
GPIO_InitTypeDef g = {0};
|
|
||||||
|
|
||||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
|
||||||
__HAL_RCC_GPIOC_CLK_ENABLE();
|
|
||||||
|
|
||||||
/* 先置好空闲电平再开推挽,避免毛刺 */
|
|
||||||
HAL_GPIO_WritePin(OLED_CLK_PORT, OLED_CLK_PIN, GPIO_PIN_SET);
|
|
||||||
HAL_GPIO_WritePin(OLED_MOSI_PORT, OLED_MOSI_PIN, GPIO_PIN_SET);
|
|
||||||
HAL_GPIO_WritePin(OLED_DC_PORT, OLED_DC_PIN, GPIO_PIN_SET);
|
|
||||||
HAL_GPIO_WritePin(OLED_CS1_PORT, OLED_CS1_PIN, GPIO_PIN_SET);
|
|
||||||
HAL_GPIO_WritePin(OLED_CS2_PORT, OLED_CS2_PIN, GPIO_PIN_SET);
|
|
||||||
|
|
||||||
g.Mode = GPIO_MODE_OUTPUT_PP;
|
|
||||||
g.Pull = GPIO_PULLUP;
|
|
||||||
g.Speed = GPIO_SPEED_FREQ_LOW;
|
|
||||||
|
|
||||||
g.Pin = OLED_CLK_PIN; HAL_GPIO_Init(OLED_CLK_PORT, &g);
|
|
||||||
g.Pin = OLED_MOSI_PIN; HAL_GPIO_Init(OLED_MOSI_PORT, &g);
|
|
||||||
g.Pin = OLED_DC_PIN; HAL_GPIO_Init(OLED_DC_PORT, &g);
|
|
||||||
g.Pin = OLED_CS1_PIN; HAL_GPIO_Init(OLED_CS1_PORT, &g);
|
|
||||||
g.Pin = OLED_CS2_PIN; HAL_GPIO_Init(OLED_CS2_PORT, &g);
|
|
||||||
|
|
||||||
/* RST(PB14):推挽输出,默认高(板内上拉,这里也主动拉高) */
|
|
||||||
HAL_GPIO_WritePin(OLED_RST_PORT, OLED_RST_PIN, GPIO_PIN_SET);
|
|
||||||
g.Pin = OLED_RST_PIN;
|
|
||||||
g.Mode = GPIO_MODE_OUTPUT_PP;
|
|
||||||
g.Pull = GPIO_NOPULL;
|
|
||||||
g.Speed = GPIO_SPEED_FREQ_LOW;
|
|
||||||
HAL_GPIO_Init(OLED_RST_PORT, &g);
|
|
||||||
|
|
||||||
fso_output();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 写字节到屏幕(DC=0 指令 / DC=1 数据)====*/
|
|
||||||
static void oled_write(uint8_t dc, uint8_t dat)
|
|
||||||
{
|
|
||||||
lcd_dc(dc);
|
|
||||||
lcd_cs1(0);
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
lcd_clk(0);
|
|
||||||
lcd_mosi(dat & 0x80);
|
|
||||||
lcd_clk(1);
|
|
||||||
dat <<= 1;
|
|
||||||
}
|
|
||||||
lcd_dc(1);
|
|
||||||
lcd_cs1(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 设置页/列地址 ====*/
|
|
||||||
static void lcd_address(uint8_t page, uint8_t column)
|
|
||||||
{
|
|
||||||
oled_write(0, 0xb0 + column); /* 页地址 */
|
|
||||||
oled_write(0, ((page & 0xf0) >> 4) | 0x10); /* 列地址高 4 位 */
|
|
||||||
oled_write(0, (page & 0x0f)); /* 列地址低 4 位 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 关显示(黑屏,断电预警时调用)====*/
|
|
||||||
void oled_off(void)
|
|
||||||
{
|
|
||||||
oled_write(0, 0xAE); /* display off */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 全屏清屏 ====*/
|
|
||||||
void oled_clear(void)
|
|
||||||
{
|
|
||||||
lcd_cs1(0);
|
|
||||||
rom_cs2(1);
|
|
||||||
for (uint8_t i = 0; i < 8; i++) {
|
|
||||||
oled_write(0, 0xb0 + i);
|
|
||||||
oled_write(0, 0x00);
|
|
||||||
oled_write(0, 0x10);
|
|
||||||
for (uint8_t j = 0; j < 128; j++) oled_write(1, 0x00);
|
|
||||||
}
|
|
||||||
lcd_cs1(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 屏幕初始化(SSD1306 兼容序列)====*/
|
|
||||||
void oled_init(void)
|
|
||||||
{
|
|
||||||
HAL_Delay(400); /* 等屏幕/字库上电稳定 */
|
|
||||||
oled_gpio_init();
|
|
||||||
|
|
||||||
/* 硬件复位一次屏幕(RST 低 10ms),保证从确定状态开始初始化 */
|
|
||||||
HAL_GPIO_WritePin(OLED_RST_PORT, OLED_RST_PIN, GPIO_PIN_RESET);
|
|
||||||
HAL_Delay(10);
|
|
||||||
HAL_GPIO_WritePin(OLED_RST_PORT, OLED_RST_PIN, GPIO_PIN_SET);
|
|
||||||
HAL_Delay(120);
|
|
||||||
lcd_cs1(0);
|
|
||||||
rom_cs2(1);
|
|
||||||
oled_write(0, 0xAE); /* display off */
|
|
||||||
oled_write(0, 0x20); /* 寻址模式 */
|
|
||||||
oled_write(0, 0x10); /* 水平寻址 */
|
|
||||||
oled_write(0, 0xb0);
|
|
||||||
oled_write(0, 0xc8); /* COM 扫描方向 */
|
|
||||||
oled_write(0, 0x00);
|
|
||||||
oled_write(0, 0x10);
|
|
||||||
oled_write(0, 0x40); /* 起始行 */
|
|
||||||
oled_write(0, 0x81); /* 对比度 */
|
|
||||||
oled_write(0, 0xFF);
|
|
||||||
oled_write(0, 0xa1); /* 段重映射 */
|
|
||||||
oled_write(0, 0xa6); /* 正常显示 */
|
|
||||||
oled_write(0, 0xa8); /* 复用率 */
|
|
||||||
oled_write(0, 0x3F);
|
|
||||||
oled_write(0, 0xa4);
|
|
||||||
oled_write(0, 0xd3); /* 显示偏移 */
|
|
||||||
oled_write(0, 0x00);
|
|
||||||
oled_write(0, 0xd5); /* 时钟分频 */
|
|
||||||
oled_write(0, 0xf0);
|
|
||||||
oled_write(0, 0xd9); /* 预充电 */
|
|
||||||
oled_write(0, 0x22);
|
|
||||||
oled_write(0, 0xda); /* COM 硬件配置 */
|
|
||||||
oled_write(0, 0x12);
|
|
||||||
oled_write(0, 0xdb); /* VCOMH */
|
|
||||||
oled_write(0, 0x20);
|
|
||||||
oled_write(0, 0x8d); /* DC-DC */
|
|
||||||
oled_write(0, 0x14);
|
|
||||||
oled_write(0, 0xaf); /* display on */
|
|
||||||
lcd_cs1(1);
|
|
||||||
oled_clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 显示 16x16 点阵(汉字)====*/
|
|
||||||
static void display_graphic_16x16(uint8_t page, uint8_t column, const uint8_t *dp)
|
|
||||||
{
|
|
||||||
lcd_cs1(0);
|
|
||||||
rom_cs2(1);
|
|
||||||
for (uint8_t j = 2; j > 0; j--) {
|
|
||||||
lcd_address(column, page);
|
|
||||||
for (uint8_t i = 0; i < 16; i++) oled_write(1, *dp++);
|
|
||||||
page++;
|
|
||||||
}
|
|
||||||
lcd_cs1(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 显示 8x16 点阵(ASCII)====*/
|
|
||||||
static void display_graphic_8x16(uint8_t page, uint8_t column, const uint8_t *dp)
|
|
||||||
{
|
|
||||||
lcd_cs1(0);
|
|
||||||
for (uint8_t j = 2; j > 0; j--) {
|
|
||||||
lcd_address(column, page);
|
|
||||||
for (uint8_t i = 0; i < 8; i++) oled_write(1, *dp++);
|
|
||||||
page++;
|
|
||||||
}
|
|
||||||
lcd_cs1(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 字库 IC 写字节(指令/地址)====*/
|
|
||||||
static void rom_write_byte(uint8_t dat)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
lcd_mosi(dat & 0x80);
|
|
||||||
dat <<= 1;
|
|
||||||
lcd_clk(0);
|
|
||||||
lcd_clk(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 字库 IC 读一字节 ====*/
|
|
||||||
static uint8_t rom_read_byte(void)
|
|
||||||
{
|
|
||||||
uint8_t ret = 0;
|
|
||||||
lcd_clk(1);
|
|
||||||
fso_input();
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
lcd_clk(0);
|
|
||||||
ret <<= 1;
|
|
||||||
if (ROM_OUT) ret |= 1;
|
|
||||||
lcd_clk(1);
|
|
||||||
}
|
|
||||||
fso_output();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 从字库 IC 连续读 DataLen 字节 ====*/
|
|
||||||
static void rom_read_n(uint8_t ah, uint8_t am, uint8_t al, uint8_t *buf, uint8_t len)
|
|
||||||
{
|
|
||||||
rom_cs2(0);
|
|
||||||
lcd_cs1(1);
|
|
||||||
lcd_clk(0);
|
|
||||||
rom_write_byte(0x03); /* 读指令 */
|
|
||||||
rom_write_byte(ah);
|
|
||||||
rom_write_byte(am);
|
|
||||||
rom_write_byte(al);
|
|
||||||
for (uint8_t i = 0; i < len; i++) buf[i] = rom_read_byte();
|
|
||||||
rom_cs2(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 定位:row 0~3(16px 行),col8 为 8 像素单位 ====*/
|
|
||||||
void oled_locate(uint8_t row, uint8_t col8)
|
|
||||||
{
|
|
||||||
s_row = row * 2;
|
|
||||||
s_col = col8 * 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 显示 GBK 字符串(16x16 汉字 / 8x16 ASCII,编码换算见晶联讯字库手册)====*/
|
|
||||||
void oled_print(const char *s)
|
|
||||||
{
|
|
||||||
uint8_t fontbuf[32];
|
|
||||||
uint8_t y = s_row;
|
|
||||||
uint8_t x = s_col;
|
|
||||||
const uint8_t *text = (const uint8_t *)s;
|
|
||||||
uint32_t fontaddr;
|
|
||||||
|
|
||||||
s_col += strlen(s) * 8; /* 位置自加(按 8px/字符计) */
|
|
||||||
|
|
||||||
uint8_t i = 0;
|
|
||||||
while (text[i] > 0x00) {
|
|
||||||
if ((text[i] >= 0xb0 && text[i] <= 0xf7) && text[i+1] >= 0xa1) {
|
|
||||||
/* GB2312 汉字:Address = ((MSB-0xB0)*94 + (LSB-0xA1) + 846) * 32 */
|
|
||||||
fontaddr = (text[i] - 0xb0) * 94;
|
|
||||||
fontaddr += (text[i+1] - 0xa1) + 846;
|
|
||||||
fontaddr *= 32;
|
|
||||||
rom_read_n((uint8_t)(fontaddr >> 16), (uint8_t)(fontaddr >> 8),
|
|
||||||
(uint8_t)fontaddr, fontbuf, 32);
|
|
||||||
display_graphic_16x16(y, x, fontbuf);
|
|
||||||
i += 2;
|
|
||||||
x += 16;
|
|
||||||
}
|
|
||||||
else if ((text[i] >= 0xa1 && text[i] <= 0xa3) && text[i+1] >= 0xa1) {
|
|
||||||
/* GB2312 全角符号:Address = ((MSB-0xA1)*94 + (LSB-0xA1)) * 32 */
|
|
||||||
fontaddr = (text[i] - 0xa1) * 94;
|
|
||||||
fontaddr += (text[i+1] - 0xa1);
|
|
||||||
fontaddr *= 32;
|
|
||||||
rom_read_n((uint8_t)(fontaddr >> 16), (uint8_t)(fontaddr >> 8),
|
|
||||||
(uint8_t)fontaddr, fontbuf, 32);
|
|
||||||
display_graphic_16x16(y, x, fontbuf);
|
|
||||||
i += 2;
|
|
||||||
x += 16;
|
|
||||||
}
|
|
||||||
else if (text[i] >= 0x20 && text[i] <= 0x7e) {
|
|
||||||
/* ASCII 8x16:Address = (char-0x20)*16 + 0x3CF80 */
|
|
||||||
fontaddr = (text[i] - 0x20) * 16 + 0x3cf80;
|
|
||||||
rom_read_n((uint8_t)(fontaddr >> 16), (uint8_t)(fontaddr >> 8),
|
|
||||||
(uint8_t)fontaddr, fontbuf, 16);
|
|
||||||
display_graphic_8x16(y, x, fontbuf);
|
|
||||||
i += 1;
|
|
||||||
x += 8;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 当前列右移 px 像素 ====*/
|
|
||||||
void oled_xoff(uint8_t px)
|
|
||||||
{
|
|
||||||
s_col += px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 文本像素宽度:汉字/全角 16px,ASCII 8px ====*/
|
|
||||||
uint8_t oled_text_w(const char *s)
|
|
||||||
{
|
|
||||||
const uint8_t *p = (const uint8_t *)s;
|
|
||||||
uint32_t w = 0;
|
|
||||||
while (*p) {
|
|
||||||
if (p[0] >= 0xa1 && p[1] >= 0xa1) { w += 16; p += 2; }
|
|
||||||
else { w += 8; p += 1; }
|
|
||||||
}
|
|
||||||
return (uint8_t)(w > 128 ? 128 : w);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 整行显示:计算像素宽度,不足 128px 用空格补齐(覆盖上次内容)====*/
|
|
||||||
void oled_print_line(const char *s)
|
|
||||||
{
|
|
||||||
const uint8_t *p = (const uint8_t *)s;
|
|
||||||
uint32_t w = 0;
|
|
||||||
while (*p) {
|
|
||||||
if (p[0] >= 0xa1 && p[1] >= 0xa1) { w += 16; p += 2; } /* 汉字/全角 */
|
|
||||||
else { w += 8; p += 1; } /* ASCII */
|
|
||||||
}
|
|
||||||
oled_print(s);
|
|
||||||
while (w < 128) { oled_print(" "); w += 8; }
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
#ifndef __OLED_H
|
|
||||||
#define __OLED_H
|
|
||||||
|
|
||||||
#include "main.h"
|
|
||||||
|
|
||||||
/* 128x64 OLED(SSD1306 兼容指令)+ 晶联讯 GB2312 字库 IC,全部 GPIO 模拟 SPI。
|
|
||||||
* 接线:CLK=PC12 MOSI=PC9 DC=PC8 CS1=PC7(屏幕片选) FSO=PC6(字库输出,接MCU输入)
|
|
||||||
* CS2=PB15(字库片选)
|
|
||||||
* 显示字符串必须是 GB2312/GBK 编码(本工程源文件即 GBK,直接写中文字面量即可)。
|
|
||||||
* 屏幕 128x64:16 像素高的行共 4 行(row 0~3),每行 8 个汉字或 16 个 ASCII。 */
|
|
||||||
|
|
||||||
void oled_init(void); /* GPIO + 屏幕初始化(app_init 调用) */
|
|
||||||
void oled_clear(void); /* 全屏清屏 */
|
|
||||||
void oled_off(void); /* 关显示(0xAE),黑屏,断电前调用 */
|
|
||||||
void oled_locate(uint8_t row, uint8_t col8); /* 定位:row 0~3,col8 为 8 像素单位 0~15 */
|
|
||||||
void oled_xoff(uint8_t px);
|
|
||||||
uint8_t oled_text_w(const char *gbk_str); /* 文本像素宽度(汉字16/ASCII8) */ /* 在当前列基础上右移 px 像素(防烧屏偏移用) */
|
|
||||||
void oled_print(const char *gbk_str); /* 从当前位置显示 GBK 字符串 */
|
|
||||||
void oled_print_line(const char *gbk_str); /* 整行显示:不足 128px 用空格补齐覆盖旧内容 */
|
|
||||||
|
|
||||||
#endif /* __OLED_H */
|
|
||||||
|
|
@ -1,333 +0,0 @@
|
||||||
#include "relay.h"
|
|
||||||
#include "main.h"
|
|
||||||
#include "network.h"
|
|
||||||
#include "24c02.h"
|
|
||||||
#include "feed_scale.h"
|
|
||||||
#include "proto.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
/* 命令执行后状态上报标志 */
|
|
||||||
volatile uint8_t relay_report_pending = 0;
|
|
||||||
volatile uint32_t relay_report_tick = 0;
|
|
||||||
|
|
||||||
/* 继电器命令响应阶段控制:
|
|
||||||
0: 空闲
|
|
||||||
1: 已动作, 等待反馈稳定
|
|
||||||
2: 反馈已读, 已发送 response 和 status, 结束
|
|
||||||
*/
|
|
||||||
volatile uint8_t relay_cmd_stage = 0;
|
|
||||||
volatile uint32_t relay_cmd_tick = 0;
|
|
||||||
volatile uint8_t relay_cmd_ch = 0;
|
|
||||||
volatile GPIO_PinState relay_cmd_fb_before = GPIO_PIN_RESET;
|
|
||||||
char relay_cmd_id[20] = {0};
|
|
||||||
|
|
||||||
/* 运行继电器命令响应状态机,由主循环每轮调用 */
|
|
||||||
void relayCmdProcess(void)
|
|
||||||
{
|
|
||||||
GPIO_PinState fb_after;
|
|
||||||
uint8_t ch = relay_cmd_ch;
|
|
||||||
|
|
||||||
if (relay_cmd_stage == 0) return;
|
|
||||||
|
|
||||||
if (relay_cmd_stage == 1) {
|
|
||||||
/* 等待继电器反馈电路稳定 */
|
|
||||||
if (HAL_GetTick() - relay_cmd_tick < 100) return;
|
|
||||||
fb_after = relayReadFb(ch);
|
|
||||||
if (relayIsManualMode()) {
|
|
||||||
log_info("> 继电器: SW%d 动作正常(手动模式)", ch);
|
|
||||||
NET_publish_response(relay_cmd_id, 1, "OK");
|
|
||||||
} else if (RELAY_FB_ENABLE) {
|
|
||||||
if (MqttInfoStr.Relay_State[ch]) {
|
|
||||||
if (relay_cmd_fb_before == FB_INACTIVE && fb_after == FB_ACTIVE) {
|
|
||||||
log_info("> 继电器: SW%d 动作正常", ch);
|
|
||||||
NET_publish_response(relay_cmd_id, 1, "OK");
|
|
||||||
} else {
|
|
||||||
log_warn("> 继电器: SW%d 未接负载或继电器断开", ch);
|
|
||||||
NET_publish_response(relay_cmd_id, 0, "Load unpowered / relay failure !");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (relay_cmd_fb_before == FB_ACTIVE && fb_after == FB_INACTIVE) {
|
|
||||||
log_info("> 继电器: SW%d 动作正常", ch);
|
|
||||||
NET_publish_response(relay_cmd_id, 1, "OK");
|
|
||||||
} else {
|
|
||||||
log_warn("> 继电器: SW%d 未接负载或继电器断开", ch);
|
|
||||||
NET_publish_response(relay_cmd_id, 0, "Load unpowered / relay failure !");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log_info("> 继电器: SW%d 动作正常", ch);
|
|
||||||
NET_publish_response(relay_cmd_id, 1, "OK");
|
|
||||||
}
|
|
||||||
/* 参考 APP1: 命令响应后立即上报一次全状态, 让平台刷新开关状态 */
|
|
||||||
relayStatueUpdata();
|
|
||||||
relay_cmd_stage = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void relayInit(void)
|
|
||||||
{
|
|
||||||
GPIO_InitTypeDef g = {0};
|
|
||||||
|
|
||||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
|
||||||
__HAL_RCC_GPIOC_CLK_ENABLE();
|
|
||||||
|
|
||||||
/* 4 路继电器控制引脚初始化为推挽输出, 默认低电平释放 */
|
|
||||||
g.Mode = GPIO_MODE_OUTPUT_PP;
|
|
||||||
g.Pull = GPIO_NOPULL;
|
|
||||||
g.Speed = GPIO_SPEED_FREQ_LOW;
|
|
||||||
|
|
||||||
g.Pin = Relay1_Pin; HAL_GPIO_Init(Relay1_Port, &g);
|
|
||||||
g.Pin = Relay2_Pin; HAL_GPIO_Init(Relay2_Port, &g);
|
|
||||||
g.Pin = Relay3_Pin; HAL_GPIO_Init(Relay3_Port, &g);
|
|
||||||
g.Pin = Relay4_Pin; HAL_GPIO_Init(Relay4_Port, &g);
|
|
||||||
|
|
||||||
/* 4 路反馈输入引脚, 上拉 */
|
|
||||||
g.Mode = GPIO_MODE_INPUT;
|
|
||||||
g.Pull = GPIO_PULLUP;
|
|
||||||
|
|
||||||
g.Pin = Relay1_FB_Pin; HAL_GPIO_Init(Relay1_FB_Port, &g);
|
|
||||||
g.Pin = Relay2_FB_Pin; HAL_GPIO_Init(Relay2_FB_Port, &g);
|
|
||||||
g.Pin = Relay3_FB_Pin; HAL_GPIO_Init(Relay3_FB_Port, &g);
|
|
||||||
g.Pin = Relay4_FB_Pin; HAL_GPIO_Init(Relay4_FB_Port, &g);
|
|
||||||
|
|
||||||
/* PA8 手动模式检测, 下拉, 默认低电平=正常模式 */
|
|
||||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
|
||||||
g.Pin = MANUAL_MODE_Pin;
|
|
||||||
g.Mode = GPIO_MODE_INPUT;
|
|
||||||
g.Pull = GPIO_PULLDOWN;
|
|
||||||
HAL_GPIO_Init(MANUAL_MODE_Port, &g);
|
|
||||||
|
|
||||||
/* 控制 GPIO 默认全部 OFF */
|
|
||||||
Relay1_OFF; log_info("SW1:关");
|
|
||||||
Relay2_OFF; log_info("SW2:关");
|
|
||||||
Relay3_OFF; log_info("SW3:关");
|
|
||||||
Relay4_OFF; log_info("SW4:关");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 网络连接成功后, 从 EEPROM 恢复继电器上一次状态(断电记忆)。
|
|
||||||
* 无秤模式(基础四路继电器): 四路全部恢复;
|
|
||||||
* 称重投喂模式: 继电器 1 是投喂通道,不恢复且强制关闭,防止上电误投料浪费饲料 */
|
|
||||||
void relayRestoreState(void)
|
|
||||||
{
|
|
||||||
if (!g_feed_scale_on) {
|
|
||||||
if (MqttInfoStr.Relay_State[1]) { Relay1_ON; log_info("SW1:开(恢复)"); }
|
|
||||||
else { Relay1_OFF; log_info("SW1:关(恢复)"); }
|
|
||||||
} else {
|
|
||||||
MqttInfoStr.Relay_State[1] = 0;
|
|
||||||
Relay1_OFF; log_info("SW1:关(不恢复,投喂安全保护)");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MqttInfoStr.Relay_State[2]) { Relay2_ON; log_info("SW2:开(恢复)"); }
|
|
||||||
else { Relay2_OFF; log_info("SW2:关(恢复)"); }
|
|
||||||
|
|
||||||
if (MqttInfoStr.Relay_State[3]) { Relay3_ON; log_info("SW3:开(恢复)"); }
|
|
||||||
else { Relay3_OFF; log_info("SW3:关(恢复)"); }
|
|
||||||
|
|
||||||
if (MqttInfoStr.Relay_State[4]) { Relay4_ON; log_info("SW4:开(恢复)"); }
|
|
||||||
else { Relay4_OFF; log_info("SW4:关(恢复)"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 读取指定继电器反馈引脚原始电平 */
|
|
||||||
GPIO_PinState relayReadFb(uint8_t ch)
|
|
||||||
{
|
|
||||||
switch (ch) {
|
|
||||||
case 1: return HAL_GPIO_ReadPin(Relay1_FB_Port, Relay1_FB_Pin);
|
|
||||||
case 2: return HAL_GPIO_ReadPin(Relay2_FB_Port, Relay2_FB_Pin);
|
|
||||||
case 3: return HAL_GPIO_ReadPin(Relay3_FB_Port, Relay3_FB_Pin);
|
|
||||||
case 4: return HAL_GPIO_ReadPin(Relay4_FB_Port, Relay4_FB_Pin);
|
|
||||||
default: return GPIO_PIN_RESET;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 读取 PA8 手动模式输入: 1=手动模式, 0=正常模式 */
|
|
||||||
int relayIsManualMode(void)
|
|
||||||
{
|
|
||||||
return (HAL_GPIO_ReadPin(MANUAL_MODE_Port, MANUAL_MODE_Pin) == MANUAL_MODE_ACTIVE) ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 设置指定继电器开关状态: 只写 GPIO 和 Relay_State, 不碰 EEPROM/网络 */
|
|
||||||
void relaySet(uint8_t ch, uint8_t on)
|
|
||||||
{
|
|
||||||
if (ch < 1 || ch > 4) return;
|
|
||||||
MqttInfoStr.Relay_State[ch] = on ? 1 : 0;
|
|
||||||
switch (ch) {
|
|
||||||
case 1: if (on) Relay1_ON; else Relay1_OFF; break;
|
|
||||||
case 2: if (on) Relay2_ON; else Relay2_OFF; break;
|
|
||||||
case 3: if (on) Relay3_ON; else Relay3_OFF; break;
|
|
||||||
case 4: if (on) Relay4_ON; else Relay4_OFF; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 请求一次状态上报: 由主循环检测到 relay_report_pending 后延时统一上报 */
|
|
||||||
void relayRequestReport(void)
|
|
||||||
{
|
|
||||||
relay_report_tick = HAL_GetTick();
|
|
||||||
relay_report_pending = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 上报继电器当前状态到MQTT平台 */
|
|
||||||
void relayStatueUpdata(void)
|
|
||||||
{
|
|
||||||
/* 云平台物模型字段:weight=剩余料重(净重),zero=空载重量(去皮),onceWt=单次投料量 */
|
|
||||||
/* sw1 与 feeding 均上报继电器 1 状态,平台两个开关都能同步刷新 */
|
|
||||||
NET_publish_status(
|
|
||||||
MqttInfoStr.Relay_State[1] ? 1 : 0,
|
|
||||||
MqttInfoStr.Relay_State[1] ? 1 : 0,
|
|
||||||
MqttInfoStr.Relay_State[2] ? 1 : 0,
|
|
||||||
MqttInfoStr.Relay_State[3] ? 1 : 0,
|
|
||||||
MqttInfoStr.Relay_State[4] ? 1 : 0,
|
|
||||||
(float)feedScaleGetZero() / 10.0f,
|
|
||||||
(float)feedScaleGetOnceWeight() / 10.0f,
|
|
||||||
(float)feedScaleGetWeight() / 10.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 从 JSON 报文中解析控制指令并执行(取值统一走 proto 模块,按键名定位) */
|
|
||||||
void relayAction(uint8_t *data)
|
|
||||||
{
|
|
||||||
char *p;
|
|
||||||
char CmdId[20] = {0};
|
|
||||||
uint8_t ch = 0;
|
|
||||||
GPIO_PinState fb_before;
|
|
||||||
|
|
||||||
log_info("> 继电器命令: %.80s", data);
|
|
||||||
|
|
||||||
/* 支持两种 id 格式:"id":"xxx" 或 "id":xxx */
|
|
||||||
if (!proto_get_id((char *)data, CmdId, sizeof(CmdId))) {
|
|
||||||
log_warn("> 继电器: id 解析失败");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strstr((char *)data, "\"sw")) {
|
|
||||||
char swkey[4];
|
|
||||||
int on = 0;
|
|
||||||
|
|
||||||
p = strstr((char *)data, "\"sw");
|
|
||||||
ch = p[3] - '0';
|
|
||||||
/* sw1 与 feeding 指令等效, 均控制继电器 1 */
|
|
||||||
if (ch < 1 || ch > 4) { log_warn("> 继电器: sw 编号无效,仅支持 sw1~sw4"); return; }
|
|
||||||
|
|
||||||
swkey[0] = 's'; swkey[1] = 'w'; swkey[2] = (char)('0' + ch); swkey[3] = '\0';
|
|
||||||
if (!proto_get_bool((char *)data, swkey, &on)) {
|
|
||||||
log_warn("> 继电器: sw%d 值无效", ch);
|
|
||||||
NET_publish_response(CmdId, 0, "bad sw value");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 控制继电器前先读取反馈电平 */
|
|
||||||
fb_before = relayReadFb(ch);
|
|
||||||
|
|
||||||
if (g_feed_scale_on) {
|
|
||||||
/* 称重自动投喂模式下继电器 1 仅由 feeding 指令控制, sw1 直接忽略 */
|
|
||||||
if (ch == 1) {
|
|
||||||
log_warn("> 继电器: 称重投喂模式下忽略 sw1,请用 feeding 指令");
|
|
||||||
NET_publish_response(CmdId, 0, "sw1 disabled, use feeding");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
relaySet(ch, (uint8_t)on);
|
|
||||||
log_info("> SW%d %s", ch, on ? "开" : "关");
|
|
||||||
} else if (strstr((char *)data, "\"feeding\"")) {
|
|
||||||
/* 喂料/投喂指令: body.feeding.value=true 启动, value=false 停止 */
|
|
||||||
int value_true = 0;
|
|
||||||
if (!proto_get_bool((char *)data, "feeding", &value_true)) {
|
|
||||||
log_warn("> 继电器: feeding 值无效");
|
|
||||||
NET_publish_response(CmdId, 0, "bad feeding value");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ch = 1;
|
|
||||||
fb_before = relayReadFb(ch);
|
|
||||||
|
|
||||||
if (!g_feed_scale_on) {
|
|
||||||
if (value_true) {
|
|
||||||
relaySet(1, 1);
|
|
||||||
log_info("> 继电器: 手动投喂开(无秤)");
|
|
||||||
} else {
|
|
||||||
relaySet(1, 0);
|
|
||||||
log_info("> 继电器: 手动投喂关(无秤)");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (value_true) {
|
|
||||||
/* 首次上电查询到有效重量前不允许启动投喂 */
|
|
||||||
if (feedScaleGetWeight() == 0) {
|
|
||||||
log_warn("> 继电器: 拒绝启动投喂,重量未就绪");
|
|
||||||
NET_publish_response(CmdId, 0, "weight not ready");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* 优先使用本次指令中携带的 onceWt, 否则使用 EEPROM 保存的值 */
|
|
||||||
uint16_t once_wt_100g = 0;
|
|
||||||
proto_get_kg100((char *)data, "onceWt", &once_wt_100g);
|
|
||||||
if (once_wt_100g == 0) {
|
|
||||||
once_wt_100g = feedScaleGetOnceWeight();
|
|
||||||
} else {
|
|
||||||
feedScaleSetOnceWeight(once_wt_100g);
|
|
||||||
}
|
|
||||||
if (once_wt_100g == 0) {
|
|
||||||
log_warn("> 继电器: 无 onceWt 单次投喂量,已忽略");
|
|
||||||
NET_publish_response(CmdId, 0, "no onceWt");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
log_info("> 继电器: 开始投喂,onceWt=%.1fKG", (float)once_wt_100g / 10.0f);
|
|
||||||
feedScaleStart(once_wt_100g);
|
|
||||||
} else {
|
|
||||||
log_info("> 继电器: 停止投喂");
|
|
||||||
feedScaleStop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (proto_locate((char *)data, "onceWt")) {
|
|
||||||
if (!g_feed_scale_on) {
|
|
||||||
/* 无秤模式:无单次投喂量概念,拒绝设置 */
|
|
||||||
log_warn("> 继电器: 拒绝设置 onceWt(无秤)");
|
|
||||||
NET_publish_response(CmdId, 0, "no scale on this device");
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
/* 单独设置单次投喂量, 持久化到 EEPROM */
|
|
||||||
uint16_t once_wt_100g = 0;
|
|
||||||
proto_get_kg100((char *)data, "onceWt", &once_wt_100g);
|
|
||||||
if (once_wt_100g == 0 || once_wt_100g > 5000) {
|
|
||||||
log_warn("> 继电器: onceWt 无效");
|
|
||||||
NET_publish_response(CmdId, 0, "invalid onceWt");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
feedScaleSetOnceWeight(once_wt_100g);
|
|
||||||
NET_publish_response(CmdId, 1, "OK");
|
|
||||||
relayStatueUpdata();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (proto_locate((char *)data, "zero")) {
|
|
||||||
if (!g_feed_scale_on) {
|
|
||||||
/* 无秤模式:无去皮/空载重量概念,拒绝设置 */
|
|
||||||
log_warn("> 继电器: 拒绝设置 zero(无秤)");
|
|
||||||
NET_publish_response(CmdId, 0, "no scale on this device");
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
/* 平台下发空载重量/去皮值,直接解析 value 并保存 */
|
|
||||||
uint16_t zero_100g = 0;
|
|
||||||
proto_get_kg100((char *)data, "zero", &zero_100g);
|
|
||||||
if (zero_100g == 0 || zero_100g > 5000) {
|
|
||||||
log_warn("> 继电器: zero 无效");
|
|
||||||
NET_publish_response(CmdId, 0, "invalid zero");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
feedScaleSetZero(zero_100g);
|
|
||||||
NET_publish_response(CmdId, 1, "OK");
|
|
||||||
relayStatueUpdata();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log_warn("> 继电器: 未知指令(无 sw/feeding/zero)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 保存继电器状态到EEPROM, 掉电后恢复 */
|
|
||||||
eepromWriteData(102, &MqttInfoStr.Relay_State[0], 7);
|
|
||||||
|
|
||||||
/* 启动非阻塞反馈检测与响应流程 */
|
|
||||||
relay_cmd_stage = 1;
|
|
||||||
relay_cmd_tick = HAL_GetTick();
|
|
||||||
relay_cmd_ch = ch;
|
|
||||||
relay_cmd_fb_before = fb_before;
|
|
||||||
memset(relay_cmd_id, 0, sizeof(relay_cmd_id));
|
|
||||||
memcpy(relay_cmd_id, CmdId, sizeof(relay_cmd_id) - 1);
|
|
||||||
}
|
|
||||||
|
|
@ -1,485 +0,0 @@
|
||||||
/* app_loop.c - FreeRTOS 任务调度封装
|
|
||||||
*
|
|
||||||
* 任务划分(裸机主循环轮询项拆分而来):
|
|
||||||
* net_task(优先级3, 栈3KB) - 网络状态机 NET_process:4G/WiFi 建链、收发、OTA 下载
|
|
||||||
* app_task(优先级2, 栈2KB) - 业务:继电器命令/反馈状态机、投喂秤、状态上报、OTA 确认
|
|
||||||
* sys_task(优先级1, 栈1KB) - 系统:LED、电量、蓝牙调试命令、运行时长打印
|
|
||||||
* idle 钩子 - 喂 IWDG(只要调度器正常,看门狗就不会断粮;
|
|
||||||
* 有任务死循环占 CPU 时 idle 饿死 → 复位,正是想要的行为)
|
|
||||||
*
|
|
||||||
* 共享资源互斥:
|
|
||||||
* g_net_at_mutex - NET_publish_* 与长 AT 会话(建链/重连)互斥,拿锁失败直接丢弃
|
|
||||||
* s_storage_mutex - EEPROM(软件I2C) / W25Q64(软件SPI) 跨任务访问互斥(storage_lock)
|
|
||||||
* log 输出 - log.c 内部互斥,多任务日志不交织
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "app_loop.h"
|
|
||||||
#include "app_common.h"
|
|
||||||
#include "user_cmd.h"
|
|
||||||
#include "relay.h"
|
|
||||||
#include "network.h"
|
|
||||||
#include "led.h"
|
|
||||||
#include "adc.h"
|
|
||||||
#include "feed_scale.h"
|
|
||||||
#include "iic.h"
|
|
||||||
#include "24c02.h"
|
|
||||||
#include "uart.h"
|
|
||||||
#include "log_cn.h"
|
|
||||||
#include "ble_at.h"
|
|
||||||
#include "hlw8032.h"
|
|
||||||
#include "oled.h"
|
|
||||||
#include "reset_log.h"
|
|
||||||
#include "../W25Q64/w25q64.h"
|
|
||||||
|
|
||||||
#include "FreeRTOS.h"
|
|
||||||
#include "task.h"
|
|
||||||
#include "semphr.h"
|
|
||||||
|
|
||||||
/*==== 存储总线互斥锁(EEPROM/W25Q64 共用一把递归锁)====*/
|
|
||||||
static SemaphoreHandle_t s_storage_mutex = NULL;
|
|
||||||
|
|
||||||
void storage_lock_init(void)
|
|
||||||
{
|
|
||||||
s_storage_mutex = xSemaphoreCreateRecursiveMutex();
|
|
||||||
}
|
|
||||||
|
|
||||||
void storage_lock(void)
|
|
||||||
{
|
|
||||||
if (s_storage_mutex && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
|
|
||||||
xSemaphoreTakeRecursive(s_storage_mutex, portMAX_DELAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void storage_unlock(void)
|
|
||||||
{
|
|
||||||
if (s_storage_mutex && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
|
|
||||||
xSemaphoreGiveRecursive(s_storage_mutex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 业务模块初始化(调度器启动前调用,与裸机版一致)====*/
|
|
||||||
void app_init(void)
|
|
||||||
{
|
|
||||||
log_info("");
|
|
||||||
log_info("* * * * * * * * * * * * * * * * * *");
|
|
||||||
log_info("* 版本: %s (RTOS) *", FIRMWARE_VERSION_STR);
|
|
||||||
log_info("* * * * * * * By Helei * * * *");
|
|
||||||
|
|
||||||
/* EEPROM 中的 MQTT/版本配置 */
|
|
||||||
iicInit();
|
|
||||||
eepromReadInfo();
|
|
||||||
log_load_level(); /* 恢复保存的日志等级 */
|
|
||||||
log_info("> 服务器: %s:%d", MqttInfoStr.ServerIP, MqttInfoStr.ServerPort);
|
|
||||||
log_info("> 客户端ID: %s", MqttInfoStr.ClientID);
|
|
||||||
log_info("> 用户名: %s", MqttInfoStr.Username);
|
|
||||||
log_info("> 版本序号: %u", MqttInfoStr.Ver);
|
|
||||||
|
|
||||||
/* W25Q64 外部 Flash,用于 OTA 固件暂存 */
|
|
||||||
W25Q64_Init();
|
|
||||||
|
|
||||||
/* 生成本次重启记录(原因+上轮运行时长)并打印最近 10 条 */
|
|
||||||
reset_log_boot();
|
|
||||||
|
|
||||||
/* 继电器 GPIO(控制输出 + 反馈输入) */
|
|
||||||
relayInit();
|
|
||||||
|
|
||||||
/* 产品模式(带秤/无秤,EEPROM 可切,默认跟编译宏) */
|
|
||||||
feed_scale_load_mode();
|
|
||||||
gps_load_mode(); /* GPS 定位开关(EEPROM 173,蓝牙 GPS0/1 切换) */
|
|
||||||
|
|
||||||
/* RS485 喂料秤控制模块 */
|
|
||||||
feedScaleInit();
|
|
||||||
|
|
||||||
/* LED(LED2 心跳同时兼任外部看门狗 TPL5010 喂狗) */
|
|
||||||
ledInit();
|
|
||||||
|
|
||||||
/* ADC 电量检测 */
|
|
||||||
adcInit();
|
|
||||||
update_batter();
|
|
||||||
|
|
||||||
/* HLW8032 电能计量(UART5 PD2,只累计电量) */
|
|
||||||
hlw8032_init();
|
|
||||||
|
|
||||||
|
|
||||||
/* OLED 屏幕(128x64 + GB2312 字库 IC) */
|
|
||||||
oled_init();
|
|
||||||
if (g_feed_scale_on) { oled_locate(1, 1); oled_print("智能水产投料机"); }
|
|
||||||
else { oled_locate(1, 1); oled_print("继电器控制终端"); }
|
|
||||||
oled_locate(2, 2); oled_print("V" FIRMWARE_VERSION_STR " RTOS");
|
|
||||||
log_info("> OLED: 初始化完成");
|
|
||||||
|
|
||||||
/* 网络模块初始化:按 EEPROM 记忆的上次成功方式优先(4G 或 WiFi) */
|
|
||||||
NET_init();
|
|
||||||
|
|
||||||
/* 启动各路串口接收 */
|
|
||||||
USART2_StartRx(); /* RS485 喂料秤 */
|
|
||||||
USART3_StartRx(); /* 4G MQTT 消息,DMA+IDLE */
|
|
||||||
USART1_StartRx(); /* 蓝牙调试命令 */
|
|
||||||
|
|
||||||
/* 上电把 HLK-B40 蓝牙名称设置为 MQTT ClientID,多台设备可区分 */
|
|
||||||
ble_at_init();
|
|
||||||
ble_setup_name();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== net_task:网络状态机(4G/WiFi 建链、收发、OTA)====*/
|
|
||||||
static void net_task(void *arg)
|
|
||||||
{
|
|
||||||
(void)arg;
|
|
||||||
for (;;) {
|
|
||||||
NET_process();
|
|
||||||
vTaskDelay(5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== app_task:业务逻辑(继电器/秤/上报/OTA 确认)====*/
|
|
||||||
static void app_task(void *arg)
|
|
||||||
{
|
|
||||||
(void)arg;
|
|
||||||
|
|
||||||
/* 主循环状态变量 */
|
|
||||||
static uint8_t ota_confirmed = 0;
|
|
||||||
static uint8_t ota_simulate_fail = 0; /* =1测试:模拟 OTA 确认失败(验证回退) */
|
|
||||||
static uint8_t ota_fail_logged = 0;
|
|
||||||
static uint32_t ota_confirm_tick = 0;
|
|
||||||
static uint8_t ota_report_pending = 0;
|
|
||||||
static uint32_t ota_report_tick = 0;
|
|
||||||
static uint32_t mqtt_ready_tick = 0;
|
|
||||||
static uint8_t mqtt_ready_recorded = 0;
|
|
||||||
static uint8_t mqtt_first_status_reported = 0;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
/* 记录 MQTT 首次就绪时刻 */
|
|
||||||
if (!mqtt_ready_recorded && g_net_mqtt_ready) {
|
|
||||||
mqtt_ready_recorded = 1;
|
|
||||||
mqtt_ready_tick = HAL_GetTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* MQTT 首次就绪后:先按 EEPROM 恢复继电器上次状态(断电记忆),
|
|
||||||
* 再上报一次完整状态,让平台刷新为真实状态 */
|
|
||||||
if (g_net_mqtt_ready && !mqtt_first_status_reported) {
|
|
||||||
mqtt_first_status_reported = 1;
|
|
||||||
relayRestoreState();
|
|
||||||
relayStatueUpdata();
|
|
||||||
{ /* 每次重启联网后上报一次代码版本:"1.5.0:15"(字符串版本 + EEPROM 版本序号) */
|
|
||||||
char verbuf[24];
|
|
||||||
snprintf(verbuf, sizeof(verbuf), "%s:%u",
|
|
||||||
FIRMWARE_VERSION_STR, (unsigned)MqttInfoStr.Ver);
|
|
||||||
NET_publish_version(verbuf);
|
|
||||||
}
|
|
||||||
log_info("> Main: MQTT Ready 后首次状态上报");
|
|
||||||
ota_confirm_tick = HAL_GetTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 首次发布成功后延迟 9s 再 OTA 确认(避开开机上报风暴:pow/ICCID/状态/电能) */
|
|
||||||
if (!ota_confirmed && g_net_mqtt_ready && g_net_first_publish_ok &&
|
|
||||||
(HAL_GetTick() - ota_confirm_tick) > 9000) {
|
|
||||||
if (ota_simulate_fail) {
|
|
||||||
if (!ota_fail_logged) {
|
|
||||||
ota_fail_logged = 1;
|
|
||||||
log_info("> OTA 确认跳过(模拟失败,用于回退测试)");
|
|
||||||
}
|
|
||||||
/* 不置位 ota_confirmed,让 1 分钟健康超时触发复位 */
|
|
||||||
} else {
|
|
||||||
ota_confirmed = 1;
|
|
||||||
if (NET_ota_confirm() < 0) { /* <0 才是未送达;1=无挂起 OTA 属正常 */
|
|
||||||
ota_report_pending = 1; /* 通知未送达,30s 后重试 */
|
|
||||||
ota_report_tick = HAL_GetTick();
|
|
||||||
log_warn("> OTA 确认通知未送达,稍后重试");
|
|
||||||
} else {
|
|
||||||
log_info("> OTA 已确认(MQTT Ready + 首次发布成功 + 延迟9s)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* OTA 确认通知未送达:每 30s 重试直到成功(本地确认标志已落盘,仅补平台通知) */
|
|
||||||
if (ota_report_pending && g_net_mqtt_ready &&
|
|
||||||
(HAL_GetTick() - ota_report_tick) > 30000) {
|
|
||||||
ota_report_tick = HAL_GetTick();
|
|
||||||
if (NET_ota_confirm() >= 0) {
|
|
||||||
ota_report_pending = 0;
|
|
||||||
log_info("> OTA 确认通知重试成功");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* MQTT 就绪后 1 分钟仍未首次 MPUB,主动复位 */
|
|
||||||
if (!ota_confirmed && mqtt_ready_recorded &&
|
|
||||||
(HAL_GetTick() - mqtt_ready_tick) > OTA_HEALTH_TIMEOUT_MS) {
|
|
||||||
log_warn("> OTA 健康超时: MQTT Ready 但 1 分钟内无 MPUB,复位");
|
|
||||||
reset_log_mark(RSN_OTA_HEALTH);
|
|
||||||
W25Q64_OTA_IncTrial(); /* 软件复位 BL 不计次,这里自行消耗一次试错 */
|
|
||||||
HAL_Delay(100);
|
|
||||||
NVIC_SystemReset();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 继电器命令响应状态机 */
|
|
||||||
relayCmdProcess();
|
|
||||||
|
|
||||||
/* 延迟状态上报 */
|
|
||||||
if (relay_report_pending && (HAL_GetTick() - relay_report_tick) > 100) {
|
|
||||||
relay_report_pending = 0;
|
|
||||||
relayStatueUpdata();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 喂料秤轮询与投喂保护 */
|
|
||||||
feedScaleProcess();
|
|
||||||
|
|
||||||
|
|
||||||
vTaskDelay(10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== OLED 界面刷新(1s 一次)
|
|
||||||
* 已联网:电压电流/功率电能/开关状态/网络+运行时间
|
|
||||||
* 未联网:按 g_net_ui_phase 显示 4G/WiFi 联网进度(WiFi 带倒计时)
|
|
||||||
* 防烧屏:每 10s 内容整体右移 0/2/4px 轮换 ====*/
|
|
||||||
static void oled_home_refresh(uint32_t up_ms);
|
|
||||||
|
|
||||||
/* 调度器启动前的阻塞建链阶段(app_init 里的 4G 初始化),
|
|
||||||
* sys_task 还没跑,由 bg_delay 每 500ms 调一次刷新联网进度页 */
|
|
||||||
void app_oled_presched_tick(void)
|
|
||||||
{
|
|
||||||
static uint32_t last = 0;
|
|
||||||
if (HAL_GetTick() - last < 500) return;
|
|
||||||
last = HAL_GetTick();
|
|
||||||
oled_home_refresh(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 写一整行(带防烧屏偏移):先写空格清掉左移腾出的列,再从偏移处写内容 */
|
|
||||||
static void oled_row(uint8_t row, uint8_t xoff, const char *s)
|
|
||||||
{
|
|
||||||
/* 按内容宽度钳位:内容宽就少移,保证不超出右边界 */
|
|
||||||
uint8_t w = oled_text_w(s);
|
|
||||||
if (xoff > 0 && (uint16_t)xoff + w > 128) xoff = 128 - w;
|
|
||||||
if (xoff) {
|
|
||||||
oled_locate(row, 0);
|
|
||||||
for (uint8_t c = 0; c < xoff; c += 8) oled_print(" "); /* 清空左侧腾出的列,防残留 */
|
|
||||||
oled_locate(row, 0);
|
|
||||||
}
|
|
||||||
oled_locate(row, 0);
|
|
||||||
oled_xoff(xoff);
|
|
||||||
oled_print_line(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void oled_home_refresh(uint32_t up_ms)
|
|
||||||
{
|
|
||||||
char buf[24];
|
|
||||||
/* 防烧屏:每 10s 换一档,左右往返(0→24→0,按内容宽度自动钳位) */
|
|
||||||
uint8_t phase = (uint8_t)((HAL_GetTick() / 10000u) % 8u);
|
|
||||||
uint8_t xoff = (phase <= 4u) ? phase * 6u : (8u - phase) * 6u;
|
|
||||||
|
|
||||||
if (g_net_mqtt_ready) {
|
|
||||||
/*---- 已联网界面 ----*/
|
|
||||||
/* R0: 网络 + 运行时间(天:时:分) */
|
|
||||||
uint32_t mins = up_ms / 60000u;
|
|
||||||
const char *nt = (NET_GetActiveBackend() == NET_BACKEND_WIFI) ? "WiFi" : "4G";
|
|
||||||
snprintf(buf, sizeof(buf), "%s %02u:%02u:%02u", nt,
|
|
||||||
(unsigned)(mins / 1440u),
|
|
||||||
(unsigned)((mins % 1440u) / 60u),
|
|
||||||
(unsigned)(mins % 60u));
|
|
||||||
oled_row(0, xoff, buf);
|
|
||||||
|
|
||||||
/* R1/R2: 电能参数 */
|
|
||||||
if (hlw8032_online()) {
|
|
||||||
snprintf(buf, sizeof(buf), "%.1fV %.2fA",
|
|
||||||
(double)hlw8032_get_voltage_v(), (double)hlw8032_get_current_a());
|
|
||||||
oled_row(1, xoff, buf);
|
|
||||||
snprintf(buf, sizeof(buf), "%.1fW %.2fkWh",
|
|
||||||
(double)hlw8032_get_power_w(), (double)hlw8032_get_energy_kwh());
|
|
||||||
oled_row(2, xoff, buf);
|
|
||||||
} else {
|
|
||||||
oled_row(1, xoff, "计量芯片离线");
|
|
||||||
oled_row(2, xoff, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* R3: 带秤模式显示重量(读不到显示原因),无秤模式显示开关状态 */
|
|
||||||
if (g_feed_scale_on) {
|
|
||||||
if (feedScaleGetLastErr() == 0 &&
|
|
||||||
(HAL_GetTick() - feedScaleGetLastOkTick()) < 40000u) {
|
|
||||||
snprintf(buf, sizeof(buf), "重量:%.1fKG",
|
|
||||||
(double)(feedScaleGetWeight() / 10.0f));
|
|
||||||
} else {
|
|
||||||
const char *reason;
|
|
||||||
switch (feedScaleGetLastErr()) {
|
|
||||||
case 2: reason = "帧短"; break;
|
|
||||||
case 3: reason = "CRC错"; break;
|
|
||||||
case 4: reason = "秤异常帧"; break;
|
|
||||||
case 5: reason = "站号不符"; break;
|
|
||||||
default: reason = "无应答"; break;
|
|
||||||
}
|
|
||||||
snprintf(buf, sizeof(buf), "重量:%s", reason);
|
|
||||||
}
|
|
||||||
oled_row(3, xoff, buf);
|
|
||||||
} else {
|
|
||||||
snprintf(buf, sizeof(buf), "开关:%s%s%s%s",
|
|
||||||
MqttInfoStr.Relay_State[1] ? "开" : "关",
|
|
||||||
MqttInfoStr.Relay_State[2] ? "开" : "关",
|
|
||||||
MqttInfoStr.Relay_State[3] ? "开" : "关",
|
|
||||||
MqttInfoStr.Relay_State[4] ? "开" : "关");
|
|
||||||
oled_row(3, xoff, buf);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/*---- 未联网界面:联网进度 ----*/
|
|
||||||
net_ui_phase_t ph = g_net_ui_phase;
|
|
||||||
oled_row(0, xoff, "断网");
|
|
||||||
|
|
||||||
if (ph == NET_UI_WIFI_FIXED || ph == NET_UI_WIFI_SMART ||
|
|
||||||
NET_GetActiveBackend() == NET_BACKEND_WIFI) {
|
|
||||||
if (ph == NET_UI_WIFI_FIXED) {
|
|
||||||
oled_row(1, xoff, "WiFi固定模式");
|
|
||||||
oled_row(2, xoff, "正在搜索热点");
|
|
||||||
} else if (ph == NET_UI_WIFI_SMART) {
|
|
||||||
oled_row(1, xoff, "智能配网");
|
|
||||||
oled_row(2, xoff, "请打开APP广播");
|
|
||||||
} else {
|
|
||||||
oled_row(1, xoff, "WiFi模式");
|
|
||||||
oled_row(2, xoff, "初始化中...");
|
|
||||||
}
|
|
||||||
snprintf(buf, sizeof(buf), "剩余 %ds", g_net_ui_countdown);
|
|
||||||
oled_row(3, xoff, g_net_ui_countdown > 0 ? buf : "");
|
|
||||||
} else {
|
|
||||||
/* 4G 进度:显示最近完成的阶段 */
|
|
||||||
const char *step = "初始化中";
|
|
||||||
switch (ph) {
|
|
||||||
case NET_UI_4G_RESET: step = "模块复位成功"; break;
|
|
||||||
case NET_UI_4G_AT: step = "AT指令成功"; break;
|
|
||||||
case NET_UI_4G_SIM: step = "SIM卡检测成功"; break;
|
|
||||||
case NET_UI_4G_ATTACH: step = "附着网络成功"; break;
|
|
||||||
case NET_UI_4G_MQTT: step = "连接服务器中"; break;
|
|
||||||
case NET_UI_4G_NOSIM: step = "无SIM卡!"; break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
oled_row(1, xoff, "正在等待4G联网");
|
|
||||||
oled_row(2, xoff, step);
|
|
||||||
oled_row(3, xoff, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 电能上报策略:无论功率多少,每 3 分钟上报一次(energy+power 同一条报文)。
|
|
||||||
* 串口日志固定 10s 一条 */
|
|
||||||
#define ENERGY_REPORT_INTERVAL_MS 180000u
|
|
||||||
|
|
||||||
/*==== sys_task:系统辅助(LED/电量/蓝牙命令/运行时长)====*/
|
|
||||||
static void sys_task(void *arg)
|
|
||||||
{
|
|
||||||
(void)arg;
|
|
||||||
uint32_t batt_tick = HAL_GetTick();
|
|
||||||
uint32_t batt_fast_tick = HAL_GetTick();
|
|
||||||
uint32_t up_last = HAL_GetTick();
|
|
||||||
uint32_t up_print = HAL_GetTick();
|
|
||||||
uint32_t en_print = HAL_GetTick();
|
|
||||||
uint64_t up_ms = 0;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
/* 蓝牙调试命令 */
|
|
||||||
process_usart1_command();
|
|
||||||
|
|
||||||
/* 联网指示:连接中闪烁,连上常亮(led.c activeEvents 据此控制 PA1) */
|
|
||||||
g_net_led_connected = g_net_mqtt_ready ? 1 : 0;
|
|
||||||
|
|
||||||
/* LED 心跳/联网指示 */
|
|
||||||
activeEvents();
|
|
||||||
|
|
||||||
/* 断电检测 50ms 快轮询(PA0 掉电要抢在法拉电容耗尽前发消息) */
|
|
||||||
if (HAL_GetTick() - batt_fast_tick >= 50) {
|
|
||||||
batt_fast_tick = HAL_GetTick();
|
|
||||||
update_batter();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 运行秒数 1s 节流更新 */
|
|
||||||
if (HAL_GetTick() - batt_tick >= 1000) {
|
|
||||||
batt_tick = HAL_GetTick();
|
|
||||||
hlw8032_tick(); /* 攒够约 0.1kWh 写一次 EEPROM */
|
|
||||||
oled_home_refresh(up_ms); /* OLED 界面 1s 刷新 */
|
|
||||||
reset_log_tick((uint32_t)(up_ms / 1000)); /* 共享 RAM 更新运行秒数,热复位不丢 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 运行时长心跳:联网后每 60s 串口输出一次(天/时/分);
|
|
||||||
* 未联网(配网/重连中)不打印,避免干扰配网倒计时日志。
|
|
||||||
* 64 位累计,HAL_GetTick 约 49 天回绕也不受影响 */
|
|
||||||
{
|
|
||||||
uint32_t now = HAL_GetTick();
|
|
||||||
up_ms += (uint32_t)(now - up_last); /* 32位差值天然容忍回绕 */
|
|
||||||
up_last = now;
|
|
||||||
if (now - up_print >= 60000) {
|
|
||||||
up_print = now;
|
|
||||||
if (g_net_mqtt_ready) {
|
|
||||||
uint32_t mins = (uint32_t)(up_ms / 60000);
|
|
||||||
log_info(FMT_UPTIME,
|
|
||||||
(unsigned)(mins / 1440),
|
|
||||||
(unsigned)((mins % 1440) / 60),
|
|
||||||
(unsigned)(mins % 60));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 电能:串口日志固定 10s 一条;上报为联网后首次一条,之后固定每 3 分钟一条 */
|
|
||||||
if (g_net_mqtt_ready && hlw8032_online() &&
|
|
||||||
(HAL_GetTick() - en_print) >= 10000u) {
|
|
||||||
en_print = HAL_GetTick();
|
|
||||||
static uint8_t energy_first_reported = 0;
|
|
||||||
static uint32_t energy_report_tick = 0;
|
|
||||||
float kwh = hlw8032_get_energy_kwh();
|
|
||||||
float pw = hlw8032_get_power_w();
|
|
||||||
uint8_t report = 0;
|
|
||||||
if (!energy_first_reported) {
|
|
||||||
report = 1;
|
|
||||||
energy_first_reported = 1;
|
|
||||||
} else if ((HAL_GetTick() - energy_report_tick) >= ENERGY_REPORT_INTERVAL_MS) {
|
|
||||||
report = 1;
|
|
||||||
}
|
|
||||||
if (report) {
|
|
||||||
energy_report_tick = HAL_GetTick();
|
|
||||||
NET_publish_energy(kwh, pw);
|
|
||||||
}
|
|
||||||
log_info(FMT_ENERGY, (double)kwh,
|
|
||||||
(double)hlw8032_get_voltage_v(),
|
|
||||||
(double)hlw8032_get_current_a(),
|
|
||||||
(double)pw);
|
|
||||||
}
|
|
||||||
|
|
||||||
vTaskDelay(20);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== FreeRTOS 钩子 ====*/
|
|
||||||
|
|
||||||
/* idle 钩子:喂独立看门狗。有任务死循环占 CPU 时 idle 饿死 → IWDG 复位 */
|
|
||||||
void vApplicationIdleHook(void)
|
|
||||||
{
|
|
||||||
IWDG_Feed();
|
|
||||||
}
|
|
||||||
|
|
||||||
void vApplicationMallocFailedHook(void)
|
|
||||||
{
|
|
||||||
log_error("> RTOS: malloc 失败(堆耗尽)");
|
|
||||||
taskDISABLE_INTERRUPTS();
|
|
||||||
for (;;) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
|
|
||||||
{
|
|
||||||
(void)xTask;
|
|
||||||
log_error("> RTOS: 任务 %s 栈溢出", pcTaskName);
|
|
||||||
taskDISABLE_INTERRUPTS();
|
|
||||||
for (;;) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 任务创建与调度器启动(main 调用,不再返回)====*/
|
|
||||||
void app_rtos_start(void)
|
|
||||||
{
|
|
||||||
/* 互斥锁先于任务创建 */
|
|
||||||
log_lock_init();
|
|
||||||
storage_lock_init();
|
|
||||||
NET_LockInit();
|
|
||||||
|
|
||||||
xTaskCreate(net_task, "net", 768, NULL, 3, NULL); /* 栈 3KB:AT/OTA 有大的 sprintf 缓冲 */
|
|
||||||
xTaskCreate(app_task, "app", 512, NULL, 2, NULL); /* 栈 2KB */
|
|
||||||
xTaskCreate(sys_task, "sys", 512, NULL, 1, NULL); /* 栈 2KB:电能日志一行 4 个浮点格式化很吃栈 */
|
|
||||||
|
|
||||||
vTaskStartScheduler();
|
|
||||||
|
|
||||||
/* 不会走到这里(堆不足创建失败才会) */
|
|
||||||
log_error("> RTOS: 调度器启动失败");
|
|
||||||
for (;;) { }
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
#ifndef __APP_LOOP_H
|
|
||||||
#define __APP_LOOP_H
|
|
||||||
|
|
||||||
/* FreeRTOS 任务调度封装 */
|
|
||||||
void app_init(void); /* 业务模块初始化(横幅/外设/网络/串口接收) */
|
|
||||||
void app_rtos_start(void); /* 创建任务并启动调度器(不再返回) */
|
|
||||||
void app_oled_presched_tick(void); /* 调度器启动前阻塞建链期间的屏幕进度刷新(bg_delay 调用,500ms 节流) */
|
|
||||||
|
|
||||||
/* 存储总线互斥(EEPROM 软件I2C 与 W25Q64 软件SPI 跨任务共用) */
|
|
||||||
void storage_lock_init(void);
|
|
||||||
void storage_lock(void);
|
|
||||||
void storage_unlock(void);
|
|
||||||
|
|
||||||
#endif /* __APP_LOOP_H */
|
|
||||||
|
|
@ -1,226 +0,0 @@
|
||||||
/* reset_log.c - 重启记录(原因 + 上次运行时长)
|
|
||||||
*
|
|
||||||
* 存储:W25Q64 0x100000 处一个 4KB 扇区,10 条固定槽位环形覆盖;
|
|
||||||
* 写满一圈擦一次扇区(复位事件稀疏,寿命可忽略)。
|
|
||||||
* 运行时间:保存在 BOOT_SHARE 共享 RAM,每秒更新,热复位(看门狗/软件)不丢;
|
|
||||||
* 冷启动(断电)时 RAM 失效,运行时间由断电瞬间的预警记录提供
|
|
||||||
* (PA0 电量检测 <10% 视为外部 12V 断开,法拉电容续航约 30s 足够写完)。
|
|
||||||
* 原因码:RCC->CSR 由 BL 放入 BOOT_SHARE 传给 APP(BL 读完即清);
|
|
||||||
* 软件复位的细分原因由各复位点 reset_log_mark() 预先写入。
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "reset_log.h"
|
|
||||||
#include "boot_share.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "main.h"
|
|
||||||
#include "app_loop.h"
|
|
||||||
#include "hlw8032.h"
|
|
||||||
#include "../W25Q64/w25q64.h"
|
|
||||||
#include "string.h"
|
|
||||||
|
|
||||||
#define RST_LOG_ADDR 0x100000u /* 避开 OTA 区(0~0x81FFF) */
|
|
||||||
#define RST_LOG_SLOTS 10
|
|
||||||
#define RST_REC_MAGIC 0x5253544Cu /* 'RSTL' */
|
|
||||||
#define RST_REC_SIZE 32u
|
|
||||||
|
|
||||||
/* 每条记录独占一个 4KB 扇区:NOR Flash 只能 1->0 翻位,同扇区覆写必须先整擦,
|
|
||||||
* 若 10 条挤一个扇区,回卷擦除会一次丢掉 9 条旧记录;
|
|
||||||
* 一条一扇区才能实现真正的"第 11 条只覆盖最旧那条"。W25Q64 有 8MB,用 10 个扇区无压力 */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t magic; /* RST_REC_MAGIC */
|
|
||||||
uint32_t seq; /* 递增序号,大者新 */
|
|
||||||
uint8_t reason; /* RSN_x */
|
|
||||||
uint8_t boot_seen; /* 0xFF=新记录;断电预警记录被下次开机消费后置 0x00(位翻转免擦除) */
|
|
||||||
uint8_t rsvd[2];
|
|
||||||
uint32_t uptime_sec; /* 上次运行时长;0xFFFFFFFF=未知(冷启动 RAM 丢失) */
|
|
||||||
uint16_t crc16; /* 前 14 字节累加和 */
|
|
||||||
uint8_t pad[16];
|
|
||||||
} rst_rec_t;
|
|
||||||
|
|
||||||
static const char *reason_str(uint8_t r)
|
|
||||||
{
|
|
||||||
switch (r) {
|
|
||||||
case RSN_POWER_ON: return "上电/意外掉电";
|
|
||||||
case RSN_PIN_RESET: return "按键复位";
|
|
||||||
case RSN_WATCHDOG: return "卡死看门狗溢出";
|
|
||||||
case RSN_KICKED: return "被平台踢下线";
|
|
||||||
case RSN_PING_TIMEOUT: return "心跳超时(服务器无响应)";
|
|
||||||
case RSN_NO_SERVICE: return "4G无网络服务";
|
|
||||||
case RSN_NO_SIM: return "无SIM卡";
|
|
||||||
case RSN_OTA_UPDATE: return "OTA升级完成";
|
|
||||||
case RSN_OTA_HEALTH: return "OTA健康检查超时";
|
|
||||||
case RSN_4G_FAIL: return "4G初始化失败";
|
|
||||||
case RSN_CMD_RESET: return "串口RESET命令";
|
|
||||||
case RSN_CMD_OTA: return "串口OTA命令";
|
|
||||||
case RSN_CMD_RECOVERY: return "串口RECOVERY命令";
|
|
||||||
case RSN_POWER_LOSS: return "外部断电(法拉电容记录)";
|
|
||||||
case RSN_4G_MODULE: return "4G模组异常自重启";
|
|
||||||
case RSN_OTA_FAIL: return "OTA失败中止";
|
|
||||||
case RSN_SOFT_GENERIC: return "软件复位(未标记)";
|
|
||||||
default: return "未知";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t rec_sum(const rst_rec_t *r)
|
|
||||||
{
|
|
||||||
const uint8_t *p = (const uint8_t *)r;
|
|
||||||
uint16_t s = 0;
|
|
||||||
for (int i = 0; i < 14; i++) s += p[i]; /* magic+seq+reason+boot_seen+rsvd+uptime */
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int rec_valid(const rst_rec_t *r)
|
|
||||||
{
|
|
||||||
return r->magic == RST_REC_MAGIC && r->crc16 == rec_sum(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rec_read(uint8_t slot, rst_rec_t *r)
|
|
||||||
{
|
|
||||||
W25Q64_Read(RST_LOG_ADDR + (uint32_t)slot * W25Q64_SECTOR_SIZE, (uint8_t *)r, RST_REC_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rec_write(uint8_t slot, rst_rec_t *r)
|
|
||||||
{
|
|
||||||
r->crc16 = rec_sum(r);
|
|
||||||
/* 每条独占一扇区:覆写前先擦除该扇区(NOR 只能 1->0 翻位) */
|
|
||||||
W25Q64_EraseRegion(RST_LOG_ADDR + (uint32_t)slot * W25Q64_SECTOR_SIZE, W25Q64_SECTOR_SIZE);
|
|
||||||
W25Q64_WriteBuffer(RST_LOG_ADDR + (uint32_t)slot * W25Q64_SECTOR_SIZE, (const uint8_t *)r, RST_REC_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 找最新记录:返回槽位;无有效记录返回 -1 */
|
|
||||||
static int find_newest(uint32_t *seq_out)
|
|
||||||
{
|
|
||||||
rst_rec_t r;
|
|
||||||
int best = -1;
|
|
||||||
uint32_t best_seq = 0;
|
|
||||||
for (uint8_t i = 0; i < RST_LOG_SLOTS; i++) {
|
|
||||||
rec_read(i, &r);
|
|
||||||
if (rec_valid(&r) && (best < 0 || (int32_t)(r.seq - best_seq) > 0)) {
|
|
||||||
best = i;
|
|
||||||
best_seq = r.seq;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (seq_out) *seq_out = best_seq;
|
|
||||||
return best;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 追加一条记录(自动环形覆盖最旧) */
|
|
||||||
static void append_record(uint8_t reason, uint32_t uptime_sec)
|
|
||||||
{
|
|
||||||
uint32_t seq = 0;
|
|
||||||
int newest = find_newest(&seq);
|
|
||||||
int slot = (newest < 0) ? 0 : (newest + 1) % RST_LOG_SLOTS;
|
|
||||||
|
|
||||||
rst_rec_t r;
|
|
||||||
memset(&r, 0xFF, sizeof(r));
|
|
||||||
r.magic = RST_REC_MAGIC;
|
|
||||||
r.seq = seq + 1;
|
|
||||||
r.reason = reason;
|
|
||||||
r.boot_seen = 0xFF;
|
|
||||||
r.uptime_sec = uptime_sec;
|
|
||||||
rec_write((uint8_t)slot, &r);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 主动复位前标记细分原因(共享 RAM,热复位后 APP 开机读取) */
|
|
||||||
void reset_log_mark(uint8_t reason)
|
|
||||||
{
|
|
||||||
hlw8032_save(); /* 计划内复位前把累计电量落 EEPROM(无变化时空操作) */
|
|
||||||
BOOT_SHARE->pending_reason = reason;
|
|
||||||
boot_share_store();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 每秒更新运行时长到共享 RAM(热复位不丢) */
|
|
||||||
void reset_log_tick(uint32_t uptime_s)
|
|
||||||
{
|
|
||||||
BOOT_SHARE->uptime_sec = uptime_s;
|
|
||||||
boot_share_store();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 外部断电预警(PA0 低电量 = 12V 断开,法拉电容续航中):立即写断电记录 */
|
|
||||||
void reset_log_power_loss(void)
|
|
||||||
{
|
|
||||||
uint32_t up = boot_share_valid() ? BOOT_SHARE->uptime_sec : 0xFFFFFFFFu;
|
|
||||||
uint32_t m = up / 60;
|
|
||||||
log_warn("> 断电预警:外部 12V 断开,记录运行时间 %u天%02u小时%02u分%02u秒",
|
|
||||||
(unsigned)(m / 1440), (unsigned)((m % 1440) / 60), (unsigned)(m % 60), (unsigned)(up % 60));
|
|
||||||
append_record(RSN_POWER_LOSS, up);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 开机:根据 BL 传来的 CSR + 预留原因码生成本次记录,并打印最近 10 条 ====*/
|
|
||||||
void reset_log_boot(void)
|
|
||||||
{
|
|
||||||
uint8_t reason = RSN_UNKNOWN;
|
|
||||||
uint32_t up = 0xFFFFFFFFu;
|
|
||||||
int skip_new = 0;
|
|
||||||
|
|
||||||
if (boot_share_valid()) {
|
|
||||||
uint32_t csr = BOOT_SHARE->csr_flags;
|
|
||||||
up = BOOT_SHARE->uptime_sec; /* 热复位时这就是上轮运行时长 */
|
|
||||||
|
|
||||||
if ((csr & RCC_CSR_SFTRSTF) && BOOT_SHARE->pending_reason != 0) {
|
|
||||||
reason = (uint8_t)BOOT_SHARE->pending_reason;
|
|
||||||
} else if (csr & RCC_CSR_SFTRSTF) {
|
|
||||||
reason = RSN_SOFT_GENERIC;
|
|
||||||
} else if (csr & RCC_CSR_IWDGRSTF) {
|
|
||||||
reason = RSN_WATCHDOG;
|
|
||||||
} else if (csr & RCC_CSR_WWDGRSTF) {
|
|
||||||
reason = RSN_WATCHDOG;
|
|
||||||
} else if (csr & RCC_CSR_PINRSTF) {
|
|
||||||
reason = RSN_PIN_RESET;
|
|
||||||
} else if (csr & RCC_CSR_PORRSTF) {
|
|
||||||
reason = RSN_POWER_ON;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 断电预警记录消费(不限复位类型):断电时已预写记录,本次开机直接消费它,
|
|
||||||
* 不再新建(断电后可能先经历一次软件复位+BL 等待,再到真正冷启动) */
|
|
||||||
{
|
|
||||||
uint32_t seq;
|
|
||||||
int newest = find_newest(&seq);
|
|
||||||
if (newest >= 0) {
|
|
||||||
rst_rec_t last;
|
|
||||||
rec_read((uint8_t)newest, &last);
|
|
||||||
if (last.reason == RSN_POWER_LOSS && last.boot_seen == 0xFF) {
|
|
||||||
last.boot_seen = 0x00;
|
|
||||||
rec_write((uint8_t)newest, &last); /* 位翻转 1->0 免擦除 */
|
|
||||||
skip_new = 1;
|
|
||||||
log_info("> 上次为断电关机(已记录)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!skip_new) {
|
|
||||||
append_record(reason, up);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 本次开机初始化共享 RAM 状态(清预留原因,运行时长重新计) */
|
|
||||||
BOOT_SHARE->pending_reason = 0;
|
|
||||||
BOOT_SHARE->uptime_sec = 0;
|
|
||||||
BOOT_SHARE->magic = BOOT_SHARE_MAGIC;
|
|
||||||
boot_share_store();
|
|
||||||
|
|
||||||
/* 打印最近 10 条(新→旧),显示序号固定 1~10([1] 最新,写满后覆盖最旧) */
|
|
||||||
log_info("> 历史重启记录(新→旧):");
|
|
||||||
uint32_t newest_seq;
|
|
||||||
int newest = find_newest(&newest_seq);
|
|
||||||
if (newest < 0) {
|
|
||||||
log_info(" (无记录)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < RST_LOG_SLOTS; i++) {
|
|
||||||
int slot = (newest - i + RST_LOG_SLOTS) % RST_LOG_SLOTS;
|
|
||||||
rst_rec_t r;
|
|
||||||
rec_read((uint8_t)slot, &r);
|
|
||||||
if (!rec_valid(&r)) continue;
|
|
||||||
if (r.uptime_sec == 0xFFFFFFFFu) {
|
|
||||||
log_info(" [%d] 重启原因: %s 运行时间: 未知",
|
|
||||||
i + 1, reason_str(r.reason));
|
|
||||||
} else {
|
|
||||||
uint32_t m = r.uptime_sec / 60;
|
|
||||||
log_info(" [%d] 重启原因: %s 运行时间: %u天%02u小时%02u分",
|
|
||||||
i + 1, reason_str(r.reason),
|
|
||||||
(unsigned)(m / 1440), (unsigned)((m % 1440) / 60), (unsigned)(m % 60));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
#include "system.h"
|
|
||||||
#include "uart.h"
|
|
||||||
#include "relay.h"
|
|
||||||
|
|
||||||
DMA_HandleTypeDef hdma_usart3_rx;
|
|
||||||
DMA_HandleTypeDef hdma_uart4_rx;
|
|
||||||
|
|
||||||
static IWDG_HandleTypeDef hiwdg; /* 独立看门狗句柄,本模块内私有 */
|
|
||||||
|
|
||||||
/* LSI(~40kHz) / 256分频 / 重载4095 → 超时约26s(LSI 漂移下最短约17s) */
|
|
||||||
static void MX_IWDG_Init(void)
|
|
||||||
{
|
|
||||||
hiwdg.Instance = IWDG;
|
|
||||||
hiwdg.Init.Prescaler = IWDG_PRESCALER_256;
|
|
||||||
hiwdg.Init.Reload = 4095;
|
|
||||||
if (HAL_IWDG_Init(&hiwdg) != HAL_OK) Error_Handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 喂狗:主循环和 app_bg_service(长阻塞等待间隙)都必须调用 */
|
|
||||||
void IWDG_Feed(void)
|
|
||||||
{
|
|
||||||
HAL_IWDG_Refresh(&hiwdg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 基础硬件初始化一条龙:HAL/时钟/GPIO/DMA/UART/看门狗
|
|
||||||
* @note BootLoader 跳转前已启动 IWDG,这里尽早接管喂狗;
|
|
||||||
* 之后 NET_init 等长阻塞流程通过 bg_delay → app_bg_service 喂狗
|
|
||||||
*/
|
|
||||||
void Board_Init(void)
|
|
||||||
{
|
|
||||||
HAL_Init();
|
|
||||||
SystemClock_Config();
|
|
||||||
MX_GPIO_Init();
|
|
||||||
MX_DMA_Init();
|
|
||||||
MX_USART1_UART_Init();
|
|
||||||
MX_USART2_UART_Init(); /* RS485 喂料秤 */
|
|
||||||
MX_USART3_UART_Init(); /* 4G Air780E */
|
|
||||||
MX_UART4_UART_Init(); /* WiFi ESP-01S */
|
|
||||||
MX_IWDG_Init();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SystemClock_Config(void)
|
|
||||||
{
|
|
||||||
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
|
|
||||||
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
|
|
||||||
|
|
||||||
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
|
|
||||||
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
|
|
||||||
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
|
|
||||||
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
|
|
||||||
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
|
|
||||||
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
|
|
||||||
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
|
|
||||||
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) Error_Handler();
|
|
||||||
|
|
||||||
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
|
|
||||||
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
|
|
||||||
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
|
|
||||||
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
|
|
||||||
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
|
|
||||||
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) Error_Handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MX_DMA_Init(void)
|
|
||||||
{
|
|
||||||
__HAL_RCC_DMA1_CLK_ENABLE();
|
|
||||||
__HAL_RCC_DMA2_CLK_ENABLE();
|
|
||||||
HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 0, 0);
|
|
||||||
HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
|
|
||||||
HAL_NVIC_SetPriority(DMA2_Channel3_IRQn, 0, 0);
|
|
||||||
HAL_NVIC_EnableIRQ(DMA2_Channel3_IRQn);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MX_GPIO_Init(void)
|
|
||||||
{
|
|
||||||
GPIO_InitTypeDef g = {0};
|
|
||||||
|
|
||||||
__HAL_RCC_GPIOD_CLK_ENABLE();
|
|
||||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
|
||||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
|
||||||
__HAL_RCC_GPIOC_CLK_ENABLE();
|
|
||||||
|
|
||||||
/* 继电器为高电平吸合,上电瞬间必须立即置低,防止浮空导致误吸合 */
|
|
||||||
HAL_GPIO_WritePin(Relay1_Port, Relay1_Pin, GPIO_PIN_RESET);
|
|
||||||
HAL_GPIO_WritePin(Relay2_Port, Relay2_Pin, GPIO_PIN_RESET);
|
|
||||||
HAL_GPIO_WritePin(Relay3_Port, Relay3_Pin, GPIO_PIN_RESET);
|
|
||||||
HAL_GPIO_WritePin(Relay4_Port, Relay4_Pin, GPIO_PIN_RESET);
|
|
||||||
|
|
||||||
g.Mode = GPIO_MODE_OUTPUT_PP;
|
|
||||||
g.Pull = GPIO_NOPULL;
|
|
||||||
g.Speed = GPIO_SPEED_FREQ_LOW;
|
|
||||||
|
|
||||||
g.Pin = Relay1_Pin; HAL_GPIO_Init(Relay1_Port, &g);
|
|
||||||
g.Pin = Relay2_Pin; HAL_GPIO_Init(Relay2_Port, &g);
|
|
||||||
g.Pin = Relay3_Pin; HAL_GPIO_Init(Relay3_Port, &g);
|
|
||||||
g.Pin = Relay4_Pin; HAL_GPIO_Init(Relay4_Port, &g);
|
|
||||||
|
|
||||||
/* CAT_PWR(PC4): 4G 模块开机脚,开漏输出,拉低=模块开机(上电直接拉低即可) */
|
|
||||||
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_RESET);
|
|
||||||
g.Mode = GPIO_MODE_OUTPUT_OD;
|
|
||||||
g.Pin = GPIO_PIN_4;
|
|
||||||
HAL_GPIO_Init(GPIOC, &g);
|
|
||||||
|
|
||||||
/* ESP_EN(PA6): WiFi 模块使能,高电平有效。默认关闭省电,
|
|
||||||
* 由 network.c 按通道开关(4G 运行时拉低断电,切 WiFi 才拉高) */
|
|
||||||
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);
|
|
||||||
g.Mode = GPIO_MODE_OUTPUT_PP;
|
|
||||||
g.Pin = GPIO_PIN_6;
|
|
||||||
HAL_GPIO_Init(GPIOA, &g);
|
|
||||||
|
|
||||||
/* BT_RST(PA7): 蓝牙模块复位脚,低电平=正常工作,高电平=复位,推挽输出默认拉低 */
|
|
||||||
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);
|
|
||||||
g.Pin = GPIO_PIN_7;
|
|
||||||
HAL_GPIO_Init(GPIOA, &g);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Error_Handler(void)
|
|
||||||
{
|
|
||||||
/* 初始化失败:停在这里,若 IWDG 已启动则由看门狗复位 */
|
|
||||||
__disable_irq();
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,459 +0,0 @@
|
||||||
#include "uart.h"
|
|
||||||
#include "network.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "hlw8032.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
UART_HandleTypeDef huart1;
|
|
||||||
UART_HandleTypeDef huart2;
|
|
||||||
UART_HandleTypeDef huart3;
|
|
||||||
UART_HandleTypeDef huart4;
|
|
||||||
UART_HandleTypeDef huart5;
|
|
||||||
DMA_HandleTypeDef hdma_usart2_rx;
|
|
||||||
|
|
||||||
uint16_t Rx_Len = 0;
|
|
||||||
uint8_t Rx_Buf[Rx_Max] = {0};
|
|
||||||
|
|
||||||
volatile uint8_t uart1_rx_ready = 0;
|
|
||||||
uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE] = {0};
|
|
||||||
|
|
||||||
/* 诊断计数:UART4 高吞吐接收健康状况(OTA 排障用) */
|
|
||||||
volatile uint32_t g_uart4_rxcplt_cnt = 0; /* DMA 填满重启次数 */
|
|
||||||
volatile uint32_t g_uart4_ore_cnt = 0; /* 溢出错误次数 */
|
|
||||||
|
|
||||||
volatile uint8_t uart2_rx_ready = 0;
|
|
||||||
uint8_t uart2_rx_buf[UART2_RX_BUF_SIZE] = {0};
|
|
||||||
|
|
||||||
static uint8_t uart1_rx_byte = 0;
|
|
||||||
static uint8_t uart1_cmd_buf[UART1_RX_BUF_SIZE] = {0};
|
|
||||||
static uint8_t uart1_cmd_index = 0;
|
|
||||||
static uint32_t uart1_last_rx_tick = 0;
|
|
||||||
|
|
||||||
static uint8_t uart5_rx_byte = 0;
|
|
||||||
static uint8_t uart2_cmd_buf[UART2_RX_BUF_SIZE] = {0};
|
|
||||||
static uint8_t uart2_cmd_index = 0;
|
|
||||||
static uint32_t uart2_last_rx_tick = 0;
|
|
||||||
|
|
||||||
/* USART2 DMA 接收(485 秤):DMA 填 dma_buf,线路静默产生 IDLE 中断即为一帧 */
|
|
||||||
uint8_t uart2_dma_buf[64];
|
|
||||||
uint8_t uart2_frame_buf[64];
|
|
||||||
volatile uint16_t uart2_frame_len = 0;
|
|
||||||
volatile uint8_t uart2_frame_ready = 0;
|
|
||||||
|
|
||||||
#define UART1_CMD_TIMEOUT_MS 100u /* 无换行时,字符间隔超过该时间视为命令结束 */
|
|
||||||
#define UART2_CMD_TIMEOUT_MS 20u /* 485 返回帧较短,20ms 视为帧结束 */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void MX_USART1_UART_Init(void)
|
|
||||||
{
|
|
||||||
huart1.Instance = USART1;
|
|
||||||
huart1.Init.BaudRate = 115200;
|
|
||||||
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;
|
|
||||||
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
|
|
||||||
if (HAL_UART_Init(&huart1) != HAL_OK) Error_Handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MX_USART2_UART_Init(void)
|
|
||||||
{
|
|
||||||
huart2.Instance = USART2;
|
|
||||||
huart2.Init.BaudRate = 9600;
|
|
||||||
huart2.Init.WordLength = UART_WORDLENGTH_8B;
|
|
||||||
huart2.Init.StopBits = UART_STOPBITS_1;
|
|
||||||
huart2.Init.Parity = UART_PARITY_NONE;
|
|
||||||
huart2.Init.Mode = UART_MODE_TX_RX;
|
|
||||||
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
|
|
||||||
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
|
|
||||||
if (HAL_UART_Init(&huart2) != HAL_OK) Error_Handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MX_USART3_UART_Init(void)
|
|
||||||
{
|
|
||||||
huart3.Instance = USART3;
|
|
||||||
huart3.Init.BaudRate = 115200; /* 上电默认 115200(安全档),cat_connect_network 里再自适应切 921600 */
|
|
||||||
huart3.Init.WordLength = UART_WORDLENGTH_8B;
|
|
||||||
huart3.Init.StopBits = UART_STOPBITS_1;
|
|
||||||
huart3.Init.Parity = UART_PARITY_NONE;
|
|
||||||
huart3.Init.Mode = UART_MODE_TX_RX;
|
|
||||||
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
|
|
||||||
huart3.Init.OverSampling = UART_OVERSAMPLING_16;
|
|
||||||
if (HAL_UART_Init(&huart3) != HAL_OK) Error_Handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* UART4: WiFi ESP-01S 模块 (PC10=TX -> ESP_RXD, PC11=RX <- ESP_TXD)
|
|
||||||
* 上电初始 115200(ESP-01S AT 固件出厂默认),
|
|
||||||
* esp_reset 里探测成功后会用 AT+UART_DEF 固定为 WIFI_BAUD_TARGET */
|
|
||||||
void MX_UART4_UART_Init(void)
|
|
||||||
{
|
|
||||||
huart4.Instance = UART4;
|
|
||||||
huart4.Init.BaudRate = 115200;
|
|
||||||
huart4.Init.WordLength = UART_WORDLENGTH_8B;
|
|
||||||
huart4.Init.StopBits = UART_STOPBITS_1;
|
|
||||||
huart4.Init.Parity = UART_PARITY_NONE;
|
|
||||||
huart4.Init.Mode = UART_MODE_TX_RX;
|
|
||||||
huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
|
|
||||||
huart4.Init.OverSampling = UART_OVERSAMPLING_16;
|
|
||||||
if (HAL_UART_Init(&huart4) != HAL_OK) Error_Handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* UART5: HLW8032 计量芯片 (PD2=RX <- 芯片 TXD),4800 8E1。
|
|
||||||
* 芯片只发不收,TX(PC12) 不配置;8 数据位+偶校验在 F1 上要按 9 位帧长配置 */
|
|
||||||
void MX_UART5_UART_Init(void)
|
|
||||||
{
|
|
||||||
huart5.Instance = UART5;
|
|
||||||
huart5.Init.BaudRate = 4800;
|
|
||||||
huart5.Init.WordLength = UART_WORDLENGTH_9B;
|
|
||||||
huart5.Init.StopBits = UART_STOPBITS_1;
|
|
||||||
huart5.Init.Parity = UART_PARITY_EVEN;
|
|
||||||
huart5.Init.Mode = UART_MODE_RX;
|
|
||||||
huart5.Init.HwFlowCtl = UART_HWCONTROL_NONE;
|
|
||||||
huart5.Init.OverSampling = UART_OVERSAMPLING_16;
|
|
||||||
if (HAL_UART_Init(&huart5) != HAL_OK) Error_Handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UART5_StartRx(void)
|
|
||||||
{
|
|
||||||
HAL_UART_Receive_IT(&huart5, &uart5_rx_byte, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void USART3_StartRx(void)
|
|
||||||
{
|
|
||||||
/* 避免重复启动 DMA: 若已在接收则先停止再启动 */
|
|
||||||
if (huart3.RxState != HAL_UART_STATE_READY) {
|
|
||||||
HAL_UART_DMAStop(&huart3);
|
|
||||||
}
|
|
||||||
HAL_UART_Receive_DMA(&huart3, Rx_Buf, Rx_Max);
|
|
||||||
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 停止 USART3 DMA 接收:WiFi/4G 互斥复用 Rx_Buf,切 WiFi 通道时必须先停 4G 接收 */
|
|
||||||
void USART3_StopRx(void)
|
|
||||||
{
|
|
||||||
__HAL_UART_DISABLE_IT(&huart3, UART_IT_IDLE);
|
|
||||||
HAL_UART_DMAStop(&huart3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 启动 UART4 DMA+IDLE 接收:与 USART3 复用同一块 Rx_Buf(两通道互斥,靠 StartRx/StopRx 保证) */
|
|
||||||
void UART4_StartRx(void)
|
|
||||||
{
|
|
||||||
if (huart4.RxState != HAL_UART_STATE_READY) {
|
|
||||||
HAL_UART_DMAStop(&huart4);
|
|
||||||
}
|
|
||||||
HAL_UART_Receive_DMA(&huart4, Rx_Buf, Rx_Max);
|
|
||||||
__HAL_UART_ENABLE_IT(&huart4, UART_IT_IDLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UART4_StopRx(void)
|
|
||||||
{
|
|
||||||
__HAL_UART_DISABLE_IT(&huart4, UART_IT_IDLE);
|
|
||||||
HAL_UART_DMAStop(&huart4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 运行中切换 UART4 波特率:只重写 BRR 寄存器,不动 GPIO/NVIC/DMA 配置。
|
|
||||||
* UART4 挂在 APB1(36MHz) 上 */
|
|
||||||
void UART4_SetBaudRate(uint32_t baud)
|
|
||||||
{
|
|
||||||
if (huart4.RxState != HAL_UART_STATE_READY) {
|
|
||||||
HAL_UART_DMAStop(&huart4);
|
|
||||||
}
|
|
||||||
huart4.Init.BaudRate = baud;
|
|
||||||
UART4->BRR = UART_BRR_SAMPLING16(36000000u, baud);
|
|
||||||
__HAL_UART_CLEAR_PEFLAG(&huart4);
|
|
||||||
__HAL_UART_CLEAR_FEFLAG(&huart4);
|
|
||||||
__HAL_UART_CLEAR_NEFLAG(&huart4);
|
|
||||||
__HAL_UART_CLEAR_OREFLAG(&huart4);
|
|
||||||
__HAL_UART_CLEAR_IDLEFLAG(&huart4);
|
|
||||||
HAL_UART_Receive_DMA(&huart4, Rx_Buf, Rx_Max);
|
|
||||||
__HAL_UART_ENABLE_IT(&huart4, UART_IT_IDLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* UART 错误回调:ORE(溢出) 等错误会让 HAL 中止整个 DMA 接收
|
|
||||||
* (stm32f1xx_hal_uart.c: UART_EndRxTransfer + HAL_DMA_Abort_IT),
|
|
||||||
* 此后模块应答全丢,必须立即重新武装接收。
|
|
||||||
* 921600 高波特率下,模块连续突发数据撞上 IDLE 处理的 DMA 重启窗口
|
|
||||||
* 时容易触发 ORE,这里按当前激活的网络通道自愈。 */
|
|
||||||
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
|
|
||||||
{
|
|
||||||
if (huart->Instance == UART4) {
|
|
||||||
g_uart4_ore_cnt++;
|
|
||||||
__HAL_UART_CLEAR_OREFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_PEFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_FEFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_NEFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_IDLEFLAG(huart);
|
|
||||||
huart->ErrorCode = HAL_UART_ERROR_NONE;
|
|
||||||
if (NET_GetActiveBackend() == NET_BACKEND_WIFI) {
|
|
||||||
HAL_UART_Receive_DMA(&huart4, Rx_Buf, Rx_Max);
|
|
||||||
__HAL_UART_ENABLE_IT(&huart4, UART_IT_IDLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (huart->Instance == USART1) {
|
|
||||||
__HAL_UART_CLEAR_OREFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_PEFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_FEFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_NEFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_IDLEFLAG(huart);
|
|
||||||
huart->ErrorCode = HAL_UART_ERROR_NONE;
|
|
||||||
huart1.RxState = HAL_UART_STATE_READY; /* 关键:HAL 错误处理后 RxState 常卡 BUSY_RX,Receive_IT 会静默失败,接收永久停摆 */
|
|
||||||
HAL_UART_Receive_IT(&huart1, &uart1_rx_byte, 1); /* 蓝牙/调试口被 ORE 弄挂后立即恢复接收 */
|
|
||||||
}
|
|
||||||
else if (huart->Instance == USART2) {
|
|
||||||
__HAL_UART_CLEAR_OREFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_PEFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_FEFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_NEFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_IDLEFLAG(huart);
|
|
||||||
huart->ErrorCode = HAL_UART_ERROR_NONE;
|
|
||||||
huart2.RxState = HAL_UART_STATE_READY;
|
|
||||||
HAL_UART_Receive_DMA(&huart2, uart2_dma_buf, sizeof(uart2_dma_buf)); /* RS485 秤口 DMA 自愈 */
|
|
||||||
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
|
|
||||||
}
|
|
||||||
else if (huart->Instance == UART5) {
|
|
||||||
__HAL_UART_CLEAR_OREFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_PEFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_FEFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_NEFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_IDLEFLAG(huart);
|
|
||||||
huart->ErrorCode = HAL_UART_ERROR_NONE;
|
|
||||||
huart5.RxState = HAL_UART_STATE_READY; /* 关键:HAL 错误处理后 RxState 常卡 BUSY_RX,Receive_IT 会静默失败,接收永久停摆 */
|
|
||||||
HAL_UART_Receive_IT(&huart5, &uart5_rx_byte, 1); /* HLW8032 口同样自愈 */
|
|
||||||
}
|
|
||||||
else if (huart->Instance == USART3) {
|
|
||||||
__HAL_UART_CLEAR_OREFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_PEFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_FEFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_NEFLAG(huart);
|
|
||||||
__HAL_UART_CLEAR_IDLEFLAG(huart);
|
|
||||||
huart->ErrorCode = HAL_UART_ERROR_NONE;
|
|
||||||
if (NET_GetActiveBackend() == NET_BACKEND_4G) {
|
|
||||||
HAL_UART_Receive_DMA(&huart3, Rx_Buf, Rx_Max);
|
|
||||||
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 运行中切换 USART3 波特率:只重写 BRR 寄存器,不动 GPIO/NVIC/DMA 配置。
|
|
||||||
* USART3 挂在 APB1(36MHz) 上 */
|
|
||||||
void USART3_SetBaudRate(uint32_t baud)
|
|
||||||
{
|
|
||||||
if (huart3.RxState != HAL_UART_STATE_READY) {
|
|
||||||
HAL_UART_DMAStop(&huart3);
|
|
||||||
}
|
|
||||||
huart3.Init.BaudRate = baud;
|
|
||||||
USART3->BRR = UART_BRR_SAMPLING16(36000000u, baud);
|
|
||||||
__HAL_UART_CLEAR_PEFLAG(&huart3);
|
|
||||||
__HAL_UART_CLEAR_FEFLAG(&huart3);
|
|
||||||
__HAL_UART_CLEAR_NEFLAG(&huart3);
|
|
||||||
__HAL_UART_CLEAR_OREFLAG(&huart3);
|
|
||||||
__HAL_UART_CLEAR_IDLEFLAG(&huart3);
|
|
||||||
HAL_UART_Receive_DMA(&huart3, Rx_Buf, Rx_Max);
|
|
||||||
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void USART1_StartRx(void)
|
|
||||||
{
|
|
||||||
uart1_rx_ready = 0;
|
|
||||||
uart1_cmd_index = 0;
|
|
||||||
uart1_last_rx_tick = 0;
|
|
||||||
memset(uart1_cmd_buf, 0, sizeof(uart1_cmd_buf));
|
|
||||||
memset(uart1_rx_buf, 0, sizeof(uart1_rx_buf));
|
|
||||||
HAL_UART_Receive_IT(&huart1, &uart1_rx_byte, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void USART2_StartRx(void)
|
|
||||||
{
|
|
||||||
uart2_rx_ready = 0;
|
|
||||||
uart2_cmd_index = 0;
|
|
||||||
uart2_last_rx_tick = 0;
|
|
||||||
memset(uart2_cmd_buf, 0, sizeof(uart2_cmd_buf));
|
|
||||||
memset(uart2_rx_buf, 0, sizeof(uart2_rx_buf));
|
|
||||||
uart2_frame_ready = 0;
|
|
||||||
uart2_frame_len = 0;
|
|
||||||
HAL_UART_Receive_DMA(&huart2, uart2_dma_buf, sizeof(uart2_dma_buf));
|
|
||||||
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 清 USART2 软件接收缓冲(不动硬件)。
|
|
||||||
* 485 查询前调用,丢弃缓冲里残留的回声/垃圾,让应答帧从干净状态开始。
|
|
||||||
* 注意:不要在这里 AbortReceive/重挂中断——实测会把接收搞死,
|
|
||||||
* 硬件接收从开机起常开(ISR 逐字节重挂 + ORE 自愈),不需要动 */
|
|
||||||
uint16_t USART2_GetRxIndex(void) { return uart2_cmd_index; } /* 排障用 */
|
|
||||||
|
|
||||||
void USART2_FlushRxBuf(void)
|
|
||||||
{
|
|
||||||
if (uart2_cmd_index > 0) {
|
|
||||||
char hex[3 * 24 + 1] = {0};
|
|
||||||
uint16_t n = uart2_cmd_index < 24 ? uart2_cmd_index : 24;
|
|
||||||
for (uint16_t i = 0; i < n; i++) snprintf(hex + i * 3, 4, "%02X ", uart2_cmd_buf[i]);
|
|
||||||
log_warn("> UART2: 清缓冲丢弃 %d 字节: %s", (int)uart2_cmd_index, hex);
|
|
||||||
}
|
|
||||||
uart2_rx_ready = 0;
|
|
||||||
uart2_cmd_index = 0;
|
|
||||||
/* 兜底:接收中断意外停摆时(ORE 后状态卡 BUSY_RX)拉起来。不 Abort,不动正常状态 */
|
|
||||||
if (!(USART2->CR1 & USART_CR1_RXNEIE)) {
|
|
||||||
__HAL_UART_CLEAR_OREFLAG(&huart2);
|
|
||||||
huart2.ErrorCode = HAL_UART_ERROR_NONE;
|
|
||||||
huart2.RxState = HAL_UART_STATE_READY;
|
|
||||||
HAL_UART_Receive_DMA(&huart2, uart2_dma_buf, sizeof(uart2_dma_buf));
|
|
||||||
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 从 USART2 接收缓冲区取出一帧(20ms 无新字节结束),返回帧长度,0 表示暂无完整帧 */
|
|
||||||
uint16_t USART2_GetFrame(uint8_t *out, uint16_t out_size)
|
|
||||||
{
|
|
||||||
if (uart2_rx_ready) {
|
|
||||||
uart2_rx_ready = 0;
|
|
||||||
uint16_t len = uart2_cmd_index < out_size ? uart2_cmd_index : out_size - 1;
|
|
||||||
memcpy(out, uart2_cmd_buf, len);
|
|
||||||
out[len] = '\0';
|
|
||||||
uart2_cmd_index = 0;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 中断接收回调:把单个字符追加到命令缓冲区,回车换行结束;
|
|
||||||
* 无换行时,字符间隔超过 UART1_CMD_TIMEOUT_MS 自动提交 */
|
|
||||||
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
|
|
||||||
{
|
|
||||||
if (huart->Instance == USART1) {
|
|
||||||
uint8_t ch = uart1_rx_byte;
|
|
||||||
if (ch == '\r' || ch == '\n') {
|
|
||||||
if (uart1_cmd_index > 0) {
|
|
||||||
uart1_cmd_buf[uart1_cmd_index] = '\0';
|
|
||||||
memcpy(uart1_rx_buf, uart1_cmd_buf, uart1_cmd_index + 1);
|
|
||||||
uart1_rx_ready = 1;
|
|
||||||
uart1_cmd_index = 0;
|
|
||||||
}
|
|
||||||
} else if (uart1_cmd_index < UART1_RX_BUF_SIZE - 1) {
|
|
||||||
uart1_cmd_buf[uart1_cmd_index++] = ch;
|
|
||||||
uart1_last_rx_tick = HAL_GetTick();
|
|
||||||
}
|
|
||||||
HAL_UART_Receive_IT(&huart1, &uart1_rx_byte, 1);
|
|
||||||
}
|
|
||||||
else if (huart->Instance == USART2) {
|
|
||||||
/* DMA(NORMAL) 填满 64B:垃圾洪泛场景,把内容当一帧提交并重启接收 */
|
|
||||||
HAL_UART_DMAStop(&huart2);
|
|
||||||
if (!uart2_frame_ready) {
|
|
||||||
memcpy(uart2_frame_buf, uart2_dma_buf, sizeof(uart2_dma_buf));
|
|
||||||
uart2_frame_len = sizeof(uart2_dma_buf);
|
|
||||||
uart2_frame_ready = 1;
|
|
||||||
}
|
|
||||||
HAL_UART_Receive_DMA(&huart2, uart2_dma_buf, sizeof(uart2_dma_buf));
|
|
||||||
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
|
|
||||||
}
|
|
||||||
else if (huart->Instance == UART5) {
|
|
||||||
hlw8032_rx_byte(uart5_rx_byte);
|
|
||||||
HAL_UART_Receive_IT(&huart5, &uart5_rx_byte, 1);
|
|
||||||
}
|
|
||||||
else if (huart->Instance == UART4) {
|
|
||||||
/* DMA(NORMAL) 填满 Rx_Buf:连续数据流没有空闲间隙、IDLE 不触发,
|
|
||||||
* 若不在这里搬运并重启,接收将永久停摆(WiFi OTA 大流量下载必现) */
|
|
||||||
g_uart4_rxcplt_cnt++;
|
|
||||||
if (NET_GetActiveBackend() == NET_BACKEND_WIFI &&
|
|
||||||
(U2_CopyIndex + Rx_Max) < (U2_COPY_SIZE - 1)) {
|
|
||||||
memcpy(U2_CopyBuff + U2_CopyIndex, Rx_Buf, Rx_Max);
|
|
||||||
U2_CopyIndex += Rx_Max;
|
|
||||||
U2_CopyBuff[U2_CopyIndex] = '\0';
|
|
||||||
U2_CopyFlag = 1;
|
|
||||||
}
|
|
||||||
__HAL_UART_CLEAR_IDLEFLAG(&huart4);
|
|
||||||
HAL_UART_Receive_DMA(&huart4, Rx_Buf, Rx_Max);
|
|
||||||
__HAL_UART_ENABLE_IT(&huart4, UART_IT_IDLE);
|
|
||||||
}
|
|
||||||
else if (huart->Instance == USART3) {
|
|
||||||
/* 同理:4G 侧理论上也可能遇到连续无间隙数据流 */
|
|
||||||
if (NET_GetActiveBackend() == NET_BACKEND_4G &&
|
|
||||||
(U2_CopyIndex + Rx_Max) < (U2_COPY_SIZE - 1)) {
|
|
||||||
memcpy(U2_CopyBuff + U2_CopyIndex, Rx_Buf, Rx_Max);
|
|
||||||
U2_CopyIndex += Rx_Max;
|
|
||||||
U2_CopyBuff[U2_CopyIndex] = '\0';
|
|
||||||
U2_CopyFlag = 1;
|
|
||||||
}
|
|
||||||
__HAL_UART_CLEAR_IDLEFLAG(&huart3);
|
|
||||||
HAL_UART_Receive_DMA(&huart3, Rx_Buf, Rx_Max);
|
|
||||||
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 原子取帧:20ms 静默判帧完整 → 直接拷出并清缓冲,一步完成。
|
|
||||||
* 替代旧的 Tick(置标志)+GetFrame(取帧) 两步交接——两步之间被查询/补发的
|
|
||||||
* 清缓冲打断时会丢帧(485 丢帧根因) */
|
|
||||||
uint16_t USART2_PollFrame(uint8_t *out, uint16_t out_size)
|
|
||||||
{
|
|
||||||
if (uart2_frame_ready) {
|
|
||||||
/* 排障:帧提交时打印内容 */
|
|
||||||
char hex[3 * 24 + 1] = {0};
|
|
||||||
uint16_t n0 = uart2_frame_len < 24 ? uart2_frame_len : 24;
|
|
||||||
for (uint16_t k = 0; k < n0; k++) snprintf(hex + k * 3, 4, "%02X ", uart2_frame_buf[k]);
|
|
||||||
log_info("> UART2: 帧提交 %d 字节: %s", (int)uart2_frame_len, hex);
|
|
||||||
uint16_t len = uart2_frame_len < out_size ? uart2_frame_len : out_size - 1;
|
|
||||||
memcpy(out, uart2_frame_buf, len);
|
|
||||||
out[len] = 0;
|
|
||||||
uart2_frame_ready = 0;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 在 USART2_GetFrame 被轮询前,20ms 无新字节自动提交帧 */
|
|
||||||
void USART2_Tick(void)
|
|
||||||
{
|
|
||||||
if (uart2_cmd_index > 0 &&
|
|
||||||
(HAL_GetTick() - uart2_last_rx_tick) > UART2_CMD_TIMEOUT_MS) {
|
|
||||||
/* 排障:帧提交时打印内容(485 丢帧定位) */
|
|
||||||
char hex[3 * 24 + 1] = {0};
|
|
||||||
uint16_t n = uart2_cmd_index < 24 ? uart2_cmd_index : 24;
|
|
||||||
for (uint16_t i = 0; i < n; i++) snprintf(hex + i * 3, 4, "%02X ", uart2_cmd_buf[i]);
|
|
||||||
log_info("> UART2: 帧提交 %d 字节: %s", (int)uart2_cmd_index, hex);
|
|
||||||
uart2_rx_ready = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 不区分大小写的字符串比较 */
|
|
||||||
static int uart_strcasecmp(const char *a, const char *b)
|
|
||||||
{
|
|
||||||
while (*a && *b) {
|
|
||||||
char ca = (char)toupper((unsigned char)*a);
|
|
||||||
char cb = (char)toupper((unsigned char)*b);
|
|
||||||
if (ca != cb) return ca - cb;
|
|
||||||
a++;
|
|
||||||
b++;
|
|
||||||
}
|
|
||||||
return (char)toupper((unsigned char)*a) - (char)toupper((unsigned char)*b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 检查 USART1 是否收到指定命令(不区分大小写),检查后清除 ready */
|
|
||||||
int USART1_CheckCommand(const char *cmd)
|
|
||||||
{
|
|
||||||
const char *got = USART1_GetCommand();
|
|
||||||
if (!got) return 0;
|
|
||||||
return (uart_strcasecmp(got, cmd) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 获取当前接收到的命令字符串(用于自定义解析)。
|
|
||||||
* 支持两种结束方式:收到 \r/\n 立即结束;无换行时字符间隔超过 100ms 自动结束。 */
|
|
||||||
const char *USART1_GetCommand(void)
|
|
||||||
{
|
|
||||||
if (uart1_rx_ready) {
|
|
||||||
uart1_rx_ready = 0;
|
|
||||||
return (const char *)uart1_rx_buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uart1_cmd_index > 0 &&
|
|
||||||
(HAL_GetTick() - uart1_last_rx_tick) > UART1_CMD_TIMEOUT_MS) {
|
|
||||||
uart1_cmd_buf[uart1_cmd_index] = '\0';
|
|
||||||
memcpy(uart1_rx_buf, uart1_cmd_buf, uart1_cmd_index + 1);
|
|
||||||
uart1_cmd_index = 0;
|
|
||||||
return (const char *)uart1_rx_buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
#ifndef __UART_H
|
|
||||||
#define __UART_H
|
|
||||||
|
|
||||||
#include "main.h"
|
|
||||||
|
|
||||||
#define UART1_RX_BUF_SIZE 64
|
|
||||||
#define UART2_RX_BUF_SIZE 64
|
|
||||||
|
|
||||||
extern UART_HandleTypeDef huart1;
|
|
||||||
extern UART_HandleTypeDef huart2; /* RS485 喂料秤 */
|
|
||||||
extern UART_HandleTypeDef huart3; /* 4G Air780E */
|
|
||||||
extern UART_HandleTypeDef huart4; /* WiFi ESP-01S (PC10=TX, PC11=RX) */
|
|
||||||
extern UART_HandleTypeDef huart5; /* HLW8032 计量芯片 (PD2=RX, 只收) */
|
|
||||||
extern volatile uint8_t uart1_rx_ready;
|
|
||||||
extern uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];
|
|
||||||
|
|
||||||
extern volatile uint8_t uart2_rx_ready;
|
|
||||||
extern uint8_t uart2_rx_buf[UART2_RX_BUF_SIZE];
|
|
||||||
|
|
||||||
extern volatile uint32_t g_uart4_rxcplt_cnt; /* DMA 填满重启次数(诊断) */
|
|
||||||
extern volatile uint32_t g_uart4_ore_cnt; /* 溢出错误次数(诊断) */
|
|
||||||
|
|
||||||
void MX_USART1_UART_Init(void);
|
|
||||||
void MX_USART2_UART_Init(void);
|
|
||||||
void MX_USART3_UART_Init(void);
|
|
||||||
void MX_UART4_UART_Init(void);
|
|
||||||
void MX_UART5_UART_Init(void);
|
|
||||||
void USART3_StartRx(void); /* 启动 USART3 DMA+IDLE 接收 */
|
|
||||||
void USART3_StopRx(void); /* 停止 USART3 DMA 接收(切 WiFi 通道时调用,释放共享 Rx_Buf) */
|
|
||||||
void USART3_SetBaudRate(uint32_t baud); /* 运行中切换 USART3 波特率 */
|
|
||||||
void UART4_StartRx(void); /* 启动 UART4 DMA+IDLE 接收(与 USART3 复用 Rx_Buf,互斥使用) */
|
|
||||||
void UART4_StopRx(void); /* 停止 UART4 DMA 接收 */
|
|
||||||
void UART4_SetBaudRate(uint32_t baud); /* 运行中切换 UART4 波特率 */
|
|
||||||
void USART1_StartRx(void); /* 启动 USART1 中断接收 */
|
|
||||||
void USART2_StartRx(void); /* 启动 USART2 中断接收 */
|
|
||||||
void UART5_StartRx(void); /* 启动 UART5 中断接收(HLW8032) */
|
|
||||||
void USART2_Tick(void); /* 触发 USART2 帧超时自动提交 */
|
|
||||||
uint16_t USART2_GetFrame(uint8_t *out, uint16_t out_size);
|
|
||||||
uint16_t USART2_PollFrame(uint8_t *out, uint16_t out_size); /* 原子取帧(判静默+拷出一步完成) */
|
|
||||||
void USART2_FlushRxBuf(void);
|
|
||||||
extern volatile uint32_t g_uart2_rx_byte_cnt;
|
|
||||||
uint16_t USART2_GetRxIndex(void); /* USART2 收字节计数(485 排障) */ /* 清 USART2 软件接收缓冲(485 查询前调用,不动硬件) */ /* 从 USART2 取一帧原始字节 */
|
|
||||||
int USART1_CheckCommand(const char *cmd); /* 检查是否收到指定命令(不区分大小写) */
|
|
||||||
const char *USART1_GetCommand(void); /* 获取当前接收到的命令字符串 */
|
|
||||||
|
|
||||||
extern volatile uint32_t g_wp_pc;
|
|
||||||
extern uint32_t g_wp_ring[16];
|
|
||||||
extern uint8_t g_wp_ring_idx;
|
|
||||||
void dbg_watchpoint_uart2_idx(void);
|
|
||||||
void dbg_wp_rearm(void);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,206 +0,0 @@
|
||||||
/* ble_at.c - HLK-B40 蓝牙透传模块 AT 配置
|
|
||||||
*
|
|
||||||
* 接线:模块 PC5(功能按键输入) 接单片机 PB0。
|
|
||||||
* 进 AT 模式:PB0 拉低 1s 后释放(手册要求 0.5~3s);
|
|
||||||
* 回透传模式:AT 模式下发送 AT+TS=1。
|
|
||||||
* 上电默认透传模式。
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ble_at.h"
|
|
||||||
#include "uart.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "main.h"
|
|
||||||
#include "system.h"
|
|
||||||
#include "24c02.h"
|
|
||||||
#include "stdio.h"
|
|
||||||
#include "string.h"
|
|
||||||
|
|
||||||
#define BLE_PC5_PORT GPIOB
|
|
||||||
#define BLE_PC5_PIN GPIO_PIN_0
|
|
||||||
/* BT_RST(PA7):高电平复位模块,默认低电平工作(system.c 已初始化为推挽低) */
|
|
||||||
#define BLE_RST_PORT GPIOA
|
|
||||||
#define BLE_RST_PIN GPIO_PIN_7
|
|
||||||
#define BLE_RST_HIGH() HAL_GPIO_WritePin(BLE_RST_PORT, BLE_RST_PIN, GPIO_PIN_SET)
|
|
||||||
#define BLE_RST_LOW() HAL_GPIO_WritePin(BLE_RST_PORT, BLE_RST_PIN, GPIO_PIN_RESET)
|
|
||||||
#define BLE_PC5_LOW() HAL_GPIO_WritePin(BLE_PC5_PORT, BLE_PC5_PIN, GPIO_PIN_RESET)
|
|
||||||
#define BLE_PC5_HIGH() HAL_GPIO_WritePin(BLE_PC5_PORT, BLE_PC5_PIN, GPIO_PIN_SET)
|
|
||||||
|
|
||||||
/* EEPROM 缓存区:已配置好的蓝牙名(132~157,26B,避开其它占用) */
|
|
||||||
#define BLE_CACHE_ADDR 132
|
|
||||||
#define BLE_POWER_ADDR 158 /* EEPROM:已配置的发射功率(132~157 名称缓存, 160 日志等级) */
|
|
||||||
#define BLE_CACHE_LEN 26
|
|
||||||
|
|
||||||
/* PB0 开漏输出:释放时由模块 PC5 内部上拉保持高电平 */
|
|
||||||
void ble_at_init(void)
|
|
||||||
{
|
|
||||||
GPIO_InitTypeDef g = {0};
|
|
||||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
|
||||||
BLE_PC5_HIGH();
|
|
||||||
g.Pin = BLE_PC5_PIN;
|
|
||||||
g.Mode = GPIO_MODE_OUTPUT_OD;
|
|
||||||
g.Pull = GPIO_NOPULL;
|
|
||||||
g.Speed = GPIO_SPEED_FREQ_LOW;
|
|
||||||
HAL_GPIO_Init(BLE_PC5_PORT, &g);
|
|
||||||
BLE_PC5_HIGH();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ble_delay(uint32_t ms)
|
|
||||||
{
|
|
||||||
while (ms >= 100) {
|
|
||||||
HAL_Delay(100);
|
|
||||||
IWDG_Feed();
|
|
||||||
ms -= 100;
|
|
||||||
}
|
|
||||||
if (ms) HAL_Delay(ms);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 清空 USART1 待处理行 */
|
|
||||||
static void ble_flush_rx(void)
|
|
||||||
{
|
|
||||||
while (USART1_GetCommand() != NULL) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 发送 AT 命令并等待期望应答(按行匹配子串),0=成功 */
|
|
||||||
static int ble_at_cmd(const char *cmd, const char *expect, int timeout_ms)
|
|
||||||
{
|
|
||||||
char line[64];
|
|
||||||
int n = snprintf(line, sizeof(line), "%s\r\n", cmd);
|
|
||||||
ble_flush_rx();
|
|
||||||
HAL_UART_Transmit(&huart1, (uint8_t *)line, n, 500);
|
|
||||||
|
|
||||||
int t = timeout_ms / 50;
|
|
||||||
while (t-- > 0) {
|
|
||||||
ble_delay(50);
|
|
||||||
const char *resp = USART1_GetCommand();
|
|
||||||
if (resp) {
|
|
||||||
if (strstr(resp, expect)) {
|
|
||||||
log_info("> BLE AT: %s", resp);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (strstr(resp, "ERROR")) {
|
|
||||||
log_warn("> BLE AT 应答错误: %s", resp);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log_warn("> BLE AT 超时: %s", cmd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 上电执行一次:进 AT 模式 → 蓝牙名称查询/设置为 BLE_NAME → 需要时重启模块生效。
|
|
||||||
* 先查询后设置:名字已正确则整个流程跳过(不留 AT 窗口,避免 APP 数据撞进 AT 命令) */
|
|
||||||
void ble_setup_name(void)
|
|
||||||
{
|
|
||||||
char cmd[48];
|
|
||||||
|
|
||||||
/* EEPROM 缓存命中(名称+功率都对)则跳过整个 AT 流程(零窗口,蓝牙 APP 不断联) */
|
|
||||||
char cache[BLE_CACHE_LEN] = {0};
|
|
||||||
eepromReadData(BLE_CACHE_ADDR, cache, BLE_CACHE_LEN);
|
|
||||||
cache[BLE_CACHE_LEN - 1] = 0;
|
|
||||||
uint8_t pwr_cache = 0xFF;
|
|
||||||
eepromReadData(BLE_POWER_ADDR, &pwr_cache, 1);
|
|
||||||
if (strcmp(cache, BLE_NAME) == 0 && pwr_cache == BLE_TX_POWER) {
|
|
||||||
log_info("> BLE: 配置已缓存(%s/功率%d),跳过设置", BLE_NAME, BLE_TX_POWER);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 先硬复位一次模块(PA7 拉高 100ms),防止模块状态异常时 PC5 进不了 AT 模式。
|
|
||||||
* 只有缓存未命中(要改配置)才会走到这里,正常开机零窗口不受影响 */
|
|
||||||
BLE_RST_HIGH();
|
|
||||||
ble_delay(100);
|
|
||||||
BLE_RST_LOW();
|
|
||||||
ble_delay(1500); /* 等模块重启完成 */
|
|
||||||
|
|
||||||
/* AT 窗口内静音日志:UART1 与模块共用,日志字节会污染模块的 AT 命令解析 */
|
|
||||||
log_mute(1);
|
|
||||||
|
|
||||||
/* 进 AT 模式并查询当前名称:无应答则重新拉 PC5 再试,最多 3 轮 */
|
|
||||||
const char *cur = NULL;
|
|
||||||
for (int round = 0; round < 3 && !cur; round++) {
|
|
||||||
BLE_PC5_LOW();
|
|
||||||
ble_delay(1100); /* 拉低 1s 进 AT 模式 */
|
|
||||||
BLE_PC5_HIGH();
|
|
||||||
ble_delay(600); /* 等模块切换到 AT 模式 */
|
|
||||||
|
|
||||||
ble_flush_rx();
|
|
||||||
HAL_UART_Transmit(&huart1, (uint8_t *)"AT+NAME=?\r\n", 12, 500);
|
|
||||||
int t = 30;
|
|
||||||
while (t-- > 0 && !cur) {
|
|
||||||
ble_delay(50);
|
|
||||||
cur = USART1_GetCommand();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cur) {
|
|
||||||
log_mute(0);
|
|
||||||
log_warn("> BLE: 3 轮 AT 均无应答(模块未接/被手机占用/PC5 未拉低)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strstr(cur, BLE_NAME) && pwr_cache == BLE_TX_POWER) {
|
|
||||||
/* 名称与功率都正确:直接回透传,不动任何设置 */
|
|
||||||
ble_at_cmd("AT+TS=1", "OK", 1500);
|
|
||||||
log_mute(0);
|
|
||||||
log_info("> BLE: 名称已是 %s,无需设置", BLE_NAME);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (strstr(cur, BLE_NAME)) {
|
|
||||||
/* 名称对但功率变了:只重设发射功率 */
|
|
||||||
snprintf(cmd, sizeof(cmd), "AT+RFPOWER=%d", BLE_TX_POWER);
|
|
||||||
ble_at_cmd(cmd, "OK", 1500);
|
|
||||||
ble_at_cmd("AT+REBOOT=1", "OK", 1500);
|
|
||||||
uint8_t p = BLE_TX_POWER;
|
|
||||||
eepromWriteData(BLE_POWER_ADDR, &p, 1);
|
|
||||||
log_mute(0);
|
|
||||||
log_info("> BLE: 发射功率已设置为 %d", BLE_TX_POWER);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 需要设置:发 AT+NAME(最多重试 3 次)。
|
|
||||||
* 注意:本模块设置类参数重启后才生效,当场回查查到的仍是旧名,
|
|
||||||
* 不能用作校验——改完直接重启,下次开机的查询才是真正的校验 */
|
|
||||||
int ok = -1;
|
|
||||||
for (int i = 0; i < 3 && ok != 0; i++) {
|
|
||||||
snprintf(cmd, sizeof(cmd), "AT+NAME=%s", BLE_NAME);
|
|
||||||
ok = ble_at_cmd(cmd, "OK", 1500);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ok == 0) {
|
|
||||||
#if BLE_UUID_CUSTOM
|
|
||||||
/* 顺带设置透传 GATT UUID(FFE0/FFE1),通用 BLE 串口 APP 可连 */
|
|
||||||
snprintf(cmd, sizeof(cmd), "AT+UUIDS=%s", BLE_UUID_SERVICE);
|
|
||||||
ble_at_cmd(cmd, "OK", 1500);
|
|
||||||
snprintf(cmd, sizeof(cmd), "AT+UUIDR=%s", BLE_UUID_TX);
|
|
||||||
ble_at_cmd(cmd, "OK", 1500);
|
|
||||||
snprintf(cmd, sizeof(cmd), "AT+UUIDW=%s", BLE_UUID_RX);
|
|
||||||
ble_at_cmd(cmd, "OK", 1500);
|
|
||||||
#endif
|
|
||||||
/* 顺带设置发射功率 */
|
|
||||||
snprintf(cmd, sizeof(cmd), "AT+RFPOWER=%d", BLE_TX_POWER);
|
|
||||||
ble_at_cmd(cmd, "OK", 1500);
|
|
||||||
|
|
||||||
/* 手册说明:设置类参数重启后才生效,重启模块使新名称生效;
|
|
||||||
* 模块重启后默认就是透传模式,无需再发 AT+TS=1 */
|
|
||||||
for (int i = 0; i < 2; i++) {
|
|
||||||
if (ble_at_cmd("AT+REBOOT=1", "OK", 1500) == 0) break;
|
|
||||||
}
|
|
||||||
eepromWriteData(BLE_CACHE_ADDR, (void *)BLE_NAME, strlen(BLE_NAME) + 1);
|
|
||||||
{
|
|
||||||
uint8_t p = BLE_TX_POWER;
|
|
||||||
eepromWriteData(BLE_POWER_ADDR, &p, 1);
|
|
||||||
}
|
|
||||||
log_mute(0);
|
|
||||||
log_info("> BLE: 名称/UUID/功率已设置完成");
|
|
||||||
} else {
|
|
||||||
ble_at_cmd("AT+TS=1", "OK", 1500); /* 确保回到透传 */
|
|
||||||
log_mute(0);
|
|
||||||
log_warn("> BLE: 设置名称失败(模块未接或应答异常)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 强制重新执行蓝牙设置(蓝牙 BLESET 命令触发):清缓存后重跑 */
|
|
||||||
void ble_force_setup(void)
|
|
||||||
{
|
|
||||||
eepromWriteData(BLE_CACHE_ADDR, "", 1);
|
|
||||||
ble_setup_name();
|
|
||||||
}
|
|
||||||
|
|
@ -1,179 +0,0 @@
|
||||||
/* user_cmd.c - 蓝牙串口调试命令处理
|
|
||||||
*
|
|
||||||
* 通过 USART1 接收简单文本命令,用于现场调试:
|
|
||||||
* RESET/OTA - 复位整机
|
|
||||||
* RECOVERY - WiFi模块 AT+RESTORE 忘掉已存热点(测试SmartConfig) + 复位
|
|
||||||
* INFO - 打印 MQTT 配置信息
|
|
||||||
* STATUS - 打印继电器状态
|
|
||||||
* R1ON ~ R4OFF - 单路继电器开关
|
|
||||||
* ALLON / ALLOFF - 全部继电器开关
|
|
||||||
* HELP - 显示支持命令
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "user_cmd.h"
|
|
||||||
#include "ble_at.h"
|
|
||||||
#include "app_common.h"
|
|
||||||
#include "relay.h"
|
|
||||||
#include "24c02.h"
|
|
||||||
#include "uart.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "network.h"
|
|
||||||
#include "net_wifi.h"
|
|
||||||
#include "esp8266.h"
|
|
||||||
#include "reset_log.h"
|
|
||||||
#include "feed_scale.h"
|
|
||||||
#include "air780e.h"
|
|
||||||
#include "hlw8032.h"
|
|
||||||
|
|
||||||
/* 本地命令字符串不区分大小写比较 */
|
|
||||||
static int cmd_is(const char *a, const char *b)
|
|
||||||
{
|
|
||||||
while (*a && *b) {
|
|
||||||
char ca = *a, cb = *b;
|
|
||||||
if (ca >= 'A' && ca <= 'Z') ca += 'a' - 'A';
|
|
||||||
if (cb >= 'A' && cb <= 'Z') cb += 'a' - 'A';
|
|
||||||
if (ca != cb) return 0;
|
|
||||||
a++; b++;
|
|
||||||
}
|
|
||||||
return (*a == 0 && *b == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 通过蓝牙串口直接控制单个继电器,并保存/上报状态 */
|
|
||||||
static void relay_set(uint8_t ch, uint8_t on)
|
|
||||||
{
|
|
||||||
if (ch < 1 || ch > 4) return;
|
|
||||||
|
|
||||||
relaySet(ch, on);
|
|
||||||
log_info("> BT: SW%d %s", ch, on ? "开" : "关");
|
|
||||||
eepromWriteData(102, &MqttInfoStr.Relay_State[0], 7);
|
|
||||||
relayRequestReport();
|
|
||||||
}
|
|
||||||
|
|
||||||
void userCmdInit(void)
|
|
||||||
{
|
|
||||||
/* 无需额外初始化 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 处理 USART1 蓝牙调试命令 */
|
|
||||||
void process_usart1_command(void)
|
|
||||||
{
|
|
||||||
const char *cmd = USART1_GetCommand();
|
|
||||||
if (!cmd) return;
|
|
||||||
|
|
||||||
/* 产品模式查询/切换:SCALE 查询,SCALE0=四路继电器(无秤),SCALE1=称重投料(带秤),切换后自动重启生效 */
|
|
||||||
if (cmd_is(cmd, "SCALE")) {
|
|
||||||
log_info("> BT: 产品模式 %s", g_feed_scale_on ? "SCALE1 称重投料(带秤)" : "SCALE0 四路继电器(无秤)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (cmd_is(cmd, "SCALE0") || cmd_is(cmd, "SCALE1")) {
|
|
||||||
uint8_t on = (cmd[5] == '1') ? 1 : 0;
|
|
||||||
if (on == g_feed_scale_on) {
|
|
||||||
log_info("> BT: 已是该模式,无需切换");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
feed_scale_set_mode(on);
|
|
||||||
log_warn("> BT: 产品模式已切到 %s,重启生效...", on ? "称重投料(带秤)" : "四路继电器(无秤)");
|
|
||||||
reset_log_mark(RSN_CMD_RESET);
|
|
||||||
HAL_Delay(200);
|
|
||||||
NVIC_SystemReset();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* GPS 定位开关:GPS 查询,GPS0=关闭,GPS1=开启,切换后自动重启生效 */
|
|
||||||
if (cmd_is(cmd, "GPS")) {
|
|
||||||
log_info("> BT: GPS 定位 %s", g_gps_on ? "开启(GPS1)" : "关闭(GPS0)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (cmd_is(cmd, "GPS0") || cmd_is(cmd, "GPS1")) {
|
|
||||||
uint8_t on = (cmd[3] == '1') ? 1 : 0;
|
|
||||||
if (on == g_gps_on) {
|
|
||||||
log_info("> BT: 已是该状态,无需切换");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
gps_set_mode(on);
|
|
||||||
log_warn("> BT: GPS 定位已切到 %s,重启生效...", on ? "开启" : "关闭");
|
|
||||||
reset_log_mark(RSN_CMD_RESET);
|
|
||||||
HAL_Delay(200);
|
|
||||||
NVIC_SystemReset();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 累计电能清零(EEPROM 同步落盘,无需重启) */
|
|
||||||
if (cmd_is(cmd, "ENCLR")) {
|
|
||||||
hlw8032_clear();
|
|
||||||
log_warn("> BT: 累计电能已清零");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmd_is(cmd, "RESET")) {
|
|
||||||
log_warn("> USART1: 收到 RESET 命令,正在重启...");
|
|
||||||
reset_log_mark(RSN_CMD_RESET);
|
|
||||||
HAL_Delay(100);
|
|
||||||
NVIC_SystemReset();
|
|
||||||
}
|
|
||||||
else if (cmd_is(cmd, "INFO")) {
|
|
||||||
log_info("> BT INFO: 版本=%u, 服务器=%s:%d, 客户端=%s",
|
|
||||||
MqttInfoStr.Ver, MqttInfoStr.ServerIP,
|
|
||||||
MqttInfoStr.ServerPort, MqttInfoStr.ClientID);
|
|
||||||
}
|
|
||||||
else if (cmd_is(cmd, "R1ON")) { relay_set(1, 1); }
|
|
||||||
else if (cmd_is(cmd, "R1OFF")) { relay_set(1, 0); }
|
|
||||||
else if (cmd_is(cmd, "R2ON")) { relay_set(2, 1); }
|
|
||||||
else if (cmd_is(cmd, "R2OFF")) { relay_set(2, 0); }
|
|
||||||
else if (cmd_is(cmd, "R3ON")) { relay_set(3, 1); }
|
|
||||||
else if (cmd_is(cmd, "R3OFF")) { relay_set(3, 0); }
|
|
||||||
else if (cmd_is(cmd, "R4ON")) { relay_set(4, 1); }
|
|
||||||
else if (cmd_is(cmd, "R4OFF")) { relay_set(4, 0); }
|
|
||||||
else if (cmd_is(cmd, "ALLON")) {
|
|
||||||
relay_set(1, 1); relay_set(2, 1); relay_set(3, 1); relay_set(4, 1);
|
|
||||||
}
|
|
||||||
else if (cmd_is(cmd, "ALLOFF")) {
|
|
||||||
relay_set(1, 0); relay_set(2, 0); relay_set(3, 0); relay_set(4, 0);
|
|
||||||
}
|
|
||||||
else if (cmd_is(cmd, "STATUS")) {
|
|
||||||
log_info("> BT STATUS: SW1=%s, SW2=%s, SW3=%s, SW4=%s",
|
|
||||||
MqttInfoStr.Relay_State[1] ? "开" : "关",
|
|
||||||
MqttInfoStr.Relay_State[2] ? "开" : "关",
|
|
||||||
MqttInfoStr.Relay_State[3] ? "开" : "关",
|
|
||||||
MqttInfoStr.Relay_State[4] ? "开" : "关");
|
|
||||||
}
|
|
||||||
else if (cmd_is(cmd, "OTA")) {
|
|
||||||
log_info("> BT OTA: 重启进入 BootLoader 检查升级");
|
|
||||||
reset_log_mark(RSN_CMD_OTA);
|
|
||||||
HAL_Delay(100);
|
|
||||||
NVIC_SystemReset();
|
|
||||||
}
|
|
||||||
else if (cmd_is(cmd, "RECOVERY")) {
|
|
||||||
/* WiFi 模块 AT+RESTORE 恢复出厂(忘掉已保存的热点),用于测试 SmartConfig;
|
|
||||||
* 仅 WiFi 通道激活时才操作模块(UART4 与 4G 共享接收缓冲,
|
|
||||||
* 4G 模式下动 UART4 会干扰 4G 数据),随后整机复位走完整重连流程 */
|
|
||||||
if (NET_GetActiveBackend() == NET_BACKEND_WIFI) {
|
|
||||||
log_warn("> BT RECOVERY: 已安排 WiFi 恢复出厂设置"); g_wifi_restore_req = 1;
|
|
||||||
} else {
|
|
||||||
log_warn("> BT RECOVERY: 4G 模式,不操作 WiFi 模块,仅重启");
|
|
||||||
reset_log_mark(RSN_CMD_RECOVERY);
|
|
||||||
HAL_Delay(100);
|
|
||||||
NVIC_SystemReset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (cmd_is(cmd, "BLESET")) {
|
|
||||||
log_info("> BT: 强制重新设置蓝牙名称/UUID");
|
|
||||||
ble_force_setup();
|
|
||||||
}
|
|
||||||
else if (cmd_is(cmd, "LOGTRACE")) { log_set_level(LOG_TRACE); log_info("> BT: 日志等级 TRACE"); }
|
|
||||||
else if (cmd_is(cmd, "LOGDEBUG")) { log_set_level(LOG_DEBUG); log_info("> BT: 日志等级 DEBUG"); }
|
|
||||||
else if (cmd_is(cmd, "LOGINFO")) { log_set_level(LOG_INFO); log_info("> BT: 日志等级 INFO"); }
|
|
||||||
else if (cmd_is(cmd, "LOGWARN")) { log_set_level(LOG_WARN); log_info("> BT: 日志等级 WARN"); }
|
|
||||||
else if (cmd_is(cmd, "LOGERROR")) { log_set_level(LOG_ERROR); log_info("> BT: 日志等级 ERROR"); }
|
|
||||||
else if (cmd_is(cmd, "LOGFATAL")) { log_set_level(LOG_FATAL); log_info("> BT: 日志等级 FATAL"); }
|
|
||||||
else if (cmd_is(cmd, "LOG")) {
|
|
||||||
static const char *lvs[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"};
|
|
||||||
log_info("> BT: 当前日志等级 %s", lvs[(int)log_get_level()]);
|
|
||||||
}
|
|
||||||
else if (cmd_is(cmd, "HELP")) {
|
|
||||||
log_info("BT CMD: RESET, INFO, STATUS, R1ON/R1OFF~R4ON/R4OFF, ALLON, ALLOFF, OTA, RECOVERY, BLESET, SCALE, SCALE0, SCALE1, GPS, GPS0, GPS1, ENCLR, LOG, LOGTRACE, LOGDEBUG, LOGINFO, LOGWARN, LOGERROR, LOGFATAL, HELP");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log_warn("> BT: 未知命令 %s", cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,404 +0,0 @@
|
||||||
/* esp8266.c - ESP-01S (AT固件) 驱动
|
|
||||||
*
|
|
||||||
* 移植自参考工程《4路继电器控制带WIFI和4G V3.4 WIFI无回弹》HARDWARE/WIFI/wifi.c,
|
|
||||||
* 由标准库改为 HAL 库,串口由 USART2 改为 UART4(PC10/PC11),复位脚改为 PA5。
|
|
||||||
*
|
|
||||||
* 接收缓冲:与 4G 后端共享 U2_CopyBuff(WiFi 后端激活时由 UART4 IDLE 中断追加,
|
|
||||||
* 见 stm32f1xx_it.c 的 UART4_IRQHandler,两通道互斥由 network.c 切换时
|
|
||||||
* StopRx/StartRx 保证)。
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "esp8266.h"
|
|
||||||
#include "air780e.h" /* U2_CopyBuff / bg_delay */
|
|
||||||
#include "network.h"
|
|
||||||
#include "uart.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "led.h"
|
|
||||||
#include "system.h"
|
|
||||||
#include "stdio.h"
|
|
||||||
#include "string.h"
|
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
/*==== 接收缓冲操作 ====*/
|
|
||||||
void esp_rx_clear(void)
|
|
||||||
{
|
|
||||||
U2_CopyIndex = 0;
|
|
||||||
U2_CopyBuff[0] = 0;
|
|
||||||
U2_CopyFlag = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 二进制安全的子串查找(strstr 遇 0x00 会失明) */
|
|
||||||
static uint8_t *esp_memfind(uint8_t *hay, uint16_t hay_len, const char *needle)
|
|
||||||
{
|
|
||||||
uint16_t nlen = strlen(needle);
|
|
||||||
if (hay_len < nlen) return NULL;
|
|
||||||
for (uint16_t i = 0; i <= hay_len - nlen; i++) {
|
|
||||||
if (memcmp(hay + i, needle, nlen) == 0) return hay + i;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 等待接收缓冲中出现关键字,期间切片喂狗/跑后台任务。
|
|
||||||
* 二进制安全匹配;出现 "ERROR" 立即失败返回,不等超时。返回 0=等到 */
|
|
||||||
static int esp_wait(const char *expect, int timeout_ms)
|
|
||||||
{
|
|
||||||
int t = timeout_ms / 100;
|
|
||||||
while (t-- > 0) {
|
|
||||||
bg_delay(100);
|
|
||||||
if (U2_CopyFlag) {
|
|
||||||
if (esp_memfind(U2_CopyBuff, U2_CopyIndex, expect)) return 0;
|
|
||||||
if (expect[0] != 'E' && esp_memfind(U2_CopyBuff, U2_CopyIndex, "ERROR")) return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 带倒计时日志的长等待:每秒打印一次剩余秒数,便于观察进度。
|
|
||||||
* 出现 "ERROR"/"FAIL" 立即失败返回。返回 0=等到 */
|
|
||||||
static int esp_wait_countdown(const char *expect, int timeout_s)
|
|
||||||
{
|
|
||||||
int remain = timeout_s * 10; /* 100ms 计数 */
|
|
||||||
int sec = timeout_s;
|
|
||||||
while (remain-- > 0) {
|
|
||||||
bg_delay(100);
|
|
||||||
if (U2_CopyFlag) {
|
|
||||||
if (esp_memfind(U2_CopyBuff, U2_CopyIndex, expect)) {
|
|
||||||
log_info("> WiFi: 等待完成 (剩 %ds)", sec);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (esp_memfind(U2_CopyBuff, U2_CopyIndex, "ERROR") ||
|
|
||||||
esp_memfind(U2_CopyBuff, U2_CopyIndex, "FAIL")) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (remain % 10 == 0 && sec > 0) {
|
|
||||||
g_net_ui_countdown = sec; /* OLED 显示用 */
|
|
||||||
log_info("> WiFi: 倒计时 %ds", sec--);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 透传/AT 数据发送:UART4,末尾自动加\r\n ====*/
|
|
||||||
static void ESP_printf(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
static uint32_t last_tx_tick = 0;
|
|
||||||
char buf[256];
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
/* 任意两条 AT 指令之间至少间隔 30ms,与 4G 侧一致 */
|
|
||||||
while (HAL_GetTick() - last_tx_tick < 30) {
|
|
||||||
HAL_Delay(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
HAL_UART_Transmit(&huart4, (uint8_t *)buf, strlen(buf), 500);
|
|
||||||
HAL_UART_Transmit(&huart4, (uint8_t *)"\r\n", 2, 500);
|
|
||||||
last_tx_tick = HAL_GetTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 透传模式原始数据发送(MQTT 二进制报文,不加\r\n) */
|
|
||||||
void esp_send(const uint8_t *data, uint16_t len)
|
|
||||||
{
|
|
||||||
HAL_UART_Transmit(&huart4, (uint8_t *)data, len, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== GPIO ====*/
|
|
||||||
void esp_gpio_init(void)
|
|
||||||
{
|
|
||||||
GPIO_InitTypeDef g = {0};
|
|
||||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
|
||||||
ESP_RST_HIGH(); /* 默认拉高,模块正常运行 */
|
|
||||||
g.Pin = ESP_RST_PIN;
|
|
||||||
g.Mode = GPIO_MODE_OUTPUT_PP;
|
|
||||||
g.Pull = GPIO_NOPULL;
|
|
||||||
g.Speed = GPIO_SPEED_FREQ_LOW;
|
|
||||||
HAL_GPIO_Init(ESP_RST_PORT, &g);
|
|
||||||
ESP_RST_HIGH();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 硬件复位 + 波特率同步 ====
|
|
||||||
* 拉低复位脚 500ms 后,先按 115200 探测(出厂/RESTORE 后的模块),
|
|
||||||
* 通则用 AT+UART_DEF 固定为 WIFI_BAUD_TARGET(掉电保存)并切换;
|
|
||||||
* 不通说明模块已是目标波特率,直接切换使用。 */
|
|
||||||
int esp_reset(void)
|
|
||||||
{
|
|
||||||
log_info("> WiFi: ESP 复位...");
|
|
||||||
|
|
||||||
/* 关闭中断/DMA,防止模块复位期间乱码触发中断风暴(同 4G cat_reset 思路) */
|
|
||||||
HAL_NVIC_DisableIRQ(UART4_IRQn);
|
|
||||||
HAL_NVIC_DisableIRQ(DMA2_Channel3_IRQn);
|
|
||||||
HAL_UART_DMAStop(&huart4);
|
|
||||||
DMA2_Channel3->CCR &= ~DMA_CCR_EN;
|
|
||||||
while (DMA2_Channel3->CCR & DMA_CCR_EN);
|
|
||||||
DMA2->IFCR = DMA_IFCR_CGIF3;
|
|
||||||
__HAL_UART_CLEAR_IDLEFLAG(&huart4);
|
|
||||||
__HAL_UART_CLEAR_OREFLAG(&huart4);
|
|
||||||
__HAL_UART_CLEAR_PEFLAG(&huart4);
|
|
||||||
__HAL_UART_CLEAR_FEFLAG(&huart4);
|
|
||||||
__HAL_UART_CLEAR_NEFLAG(&huart4);
|
|
||||||
esp_rx_clear();
|
|
||||||
|
|
||||||
ESP_RST_LOW();
|
|
||||||
bg_delay(500);
|
|
||||||
ESP_RST_HIGH();
|
|
||||||
bg_delay(1500); /* 等模块完成启动(boot log 输出完毕) */
|
|
||||||
|
|
||||||
/* 复位完成后重新启动 DMA+IDLE 接收 */
|
|
||||||
esp_rx_clear();
|
|
||||||
UART4_SetBaudRate(WIFI_BAUD_DEFAULT);
|
|
||||||
HAL_NVIC_EnableIRQ(DMA2_Channel3_IRQn);
|
|
||||||
HAL_NVIC_EnableIRQ(UART4_IRQn);
|
|
||||||
|
|
||||||
/* 先按 115200 探测 */
|
|
||||||
if (esp_send_cmd("AT", "OK", 1500) == 0) {
|
|
||||||
/* 通了:固定为目标波特率(AT+UART_DEF 掉电保存) */
|
|
||||||
if (esp_send_cmd("AT+UART_DEF=921600,8,1,0,0", "OK", 3000) == 0) {
|
|
||||||
UART4_SetBaudRate(WIFI_BAUD_TARGET);
|
|
||||||
bg_delay(200);
|
|
||||||
if (esp_send_cmd("AT", "OK", 1500) == 0) {
|
|
||||||
log_info("> WiFi: 波特率 -> 921600");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* 目标波特率通信失败:回滚为 115200,维持可用 */
|
|
||||||
log_warn("> WiFi: 921600 不可用, 回退到 115200");
|
|
||||||
UART4_SetBaudRate(WIFI_BAUD_DEFAULT);
|
|
||||||
bg_delay(100);
|
|
||||||
esp_send_cmd("AT+UART_DEF=115200,8,1,0,0", "OK", 2000);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* 固件不支持 UART_DEF 等:维持 115200 也能用 */
|
|
||||||
log_warn("> WiFi: UART_DEF 失败, 保持 115200");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 115200 不通:模块可能已是目标波特率 */
|
|
||||||
UART4_SetBaudRate(WIFI_BAUD_TARGET);
|
|
||||||
if (esp_send_cmd("AT", "OK", 1500) == 0) {
|
|
||||||
log_info("> WiFi: 波特率已是 921600");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_warn("> WiFi: 所有波特率均无 AT 应答");
|
|
||||||
UART4_SetBaudRate(WIFI_BAUD_DEFAULT);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 通用 AT 指令:发送→清缓冲→等关键字 ====*/
|
|
||||||
|
|
||||||
/* 接收缓冲十六进制转储(诊断用):strstr 遇 0x00 会失明,
|
|
||||||
* 字符串打印看不出缓冲里实际收到了什么 */
|
|
||||||
/* 接收缓冲内容转储(诊断用):按文本打印,不可打印字节显示为 '.' */
|
|
||||||
static void esp_dump_rx(const char *tag)
|
|
||||||
{
|
|
||||||
char text[65];
|
|
||||||
uint16_t len = U2_CopyIndex < 64 ? U2_CopyIndex : 64;
|
|
||||||
for (uint16_t i = 0; i < len; i++) {
|
|
||||||
uint8_t c = U2_CopyBuff[i];
|
|
||||||
text[i] = (c >= 32 && c <= 126) ? (char)c : '.';
|
|
||||||
}
|
|
||||||
text[len] = '\0';
|
|
||||||
log_warn("> WiFi 接收[%s] idx=%u: %s", tag, U2_CopyIndex, len ? text : "(empty)");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 通用 AT 指令:发送→清缓冲→等关键字 ====
|
|
||||||
* 注意清缓冲在发送【之后】:清掉上一条应答的迟到尾巴(如 CWJAP 的
|
|
||||||
* "WIFI GOT IP" 之后姗姗来迟的 OK)和命令回显,防止 esp_wait 匹配到
|
|
||||||
* 陈旧内容造成"假成功"(曾导致 AT+CIPMODE=1 实际失败却被判定 OK)。
|
|
||||||
* DMA+IDLE 架构下这样做是安全的:发送完成的瞬间模块应答还在路上,
|
|
||||||
* 在途字节留在 Rx_Buf 里,IDLE 后才会追加进 U2_CopyBuff。 */
|
|
||||||
int esp_send_cmd(const char *cmd, const char *expect, int timeout_ms)
|
|
||||||
{
|
|
||||||
ESP_printf("%s", cmd);
|
|
||||||
esp_rx_clear();
|
|
||||||
if (esp_wait(expect, timeout_ms) != 0) {
|
|
||||||
log_warn("> WiFi AT 超时: %s", cmd);
|
|
||||||
esp_dump_rx(cmd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 查询模块是否已自动连上保存的热点(SmartConfig 配过的 AP)====
|
|
||||||
* 主动查 AT+CIPSTATUS(STATUS:2=已拿到 IP),无被动等待的时序竞争。
|
|
||||||
* 注:模块保存的 AP 上电 2~3s 即连上,其 "WIFI GOT IP" 打印会被波特率
|
|
||||||
* 同步阶段的清缓冲擦掉,所以不能靠等打印判断。 */
|
|
||||||
int esp_saved_ap_connected(int timeout_s)
|
|
||||||
{
|
|
||||||
log_info("> WiFi: 检查已保存热点的自动连接 (%ds)", timeout_s);
|
|
||||||
for (int i = 0; i < timeout_s; i++) {
|
|
||||||
ESP_printf("AT+CIPSTATUS");
|
|
||||||
esp_rx_clear();
|
|
||||||
if (esp_wait("STATUS:2", 1000) == 0) {
|
|
||||||
log_info("> WiFi: 已连接保存的热点");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log_warn("> WiFi: 无已保存的热点");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 连接固定 WiFi(代码里保存的 SSID/PASS),超时见 WIFI_JOIN_TIMEOUT_S ====*/
|
|
||||||
int esp_join_fixed_ap(void)
|
|
||||||
{
|
|
||||||
char cmd[96];
|
|
||||||
|
|
||||||
g_net_ui_phase = NET_UI_WIFI_FIXED;
|
|
||||||
|
|
||||||
log_info("> WiFi: 连接固定热点 '%s' (%ds)", WIFI_SSID, WIFI_JOIN_TIMEOUT_S);
|
|
||||||
|
|
||||||
/* 保持 CWAUTOCONN=1(默认):连接成功的AP会被模块保存,
|
|
||||||
* 之后重连可优先等自动连接(见 esp_wait_got_ip) */
|
|
||||||
snprintf(cmd, sizeof(cmd), "AT+CWJAP=\"%s\",\"%s\"", WIFI_SSID, WIFI_PASS);
|
|
||||||
ESP_printf("%s", cmd);
|
|
||||||
esp_rx_clear();
|
|
||||||
if (esp_wait_countdown("WIFI GOT IP", WIFI_JOIN_TIMEOUT_S) != 0) {
|
|
||||||
/* 提前结束 = 模块回了 FAIL/ERROR(密码错误等);数满倒计时 = 真超时(热点不在) */
|
|
||||||
log_warn("> WiFi: 固定热点连接失败或超时");
|
|
||||||
esp_dump_rx("CWJAP");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
log_info("> WiFi: 固定热点已连接");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== Smart Config (ESP-Touch) 智能配网,60s 倒计时 ====
|
|
||||||
* 手机 APP(ESP-Touch) 广播 WiFi 密码;成功后 AP 信息由模块自动保存,
|
|
||||||
* 之后重连可直接 AT+CWJAP 用已存热点。 */
|
|
||||||
int esp_smartconfig(void)
|
|
||||||
{
|
|
||||||
log_info("> WiFi: SmartConfig 开始 (%ds), 请用 ESP-Touch APP", WIFI_SMART_TIMEOUT_S);
|
|
||||||
g_net_ui_phase = NET_UI_WIFI_SMART;
|
|
||||||
g_net_led_smartconfig = 1; /* NET_LED 100ms 快闪提示配网模式 */
|
|
||||||
|
|
||||||
if (esp_send_cmd("AT+CWSTARTSMART", "OK", 5000) != 0) { g_net_led_smartconfig = 0; return -1; }
|
|
||||||
|
|
||||||
esp_rx_clear();
|
|
||||||
if (esp_wait_countdown("connected", WIFI_SMART_TIMEOUT_S) != 0) {
|
|
||||||
log_warn("> WiFi: SmartConfig 超时");
|
|
||||||
esp_send_cmd("AT+CWSTOPSMART", "OK", 2000);
|
|
||||||
g_net_led_smartconfig = 0;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
log_info("> WiFi: SmartConfig 已获取热点");
|
|
||||||
esp_send_cmd("AT+CWSTOPSMART", "OK", 2000);
|
|
||||||
g_net_led_smartconfig = 0;
|
|
||||||
|
|
||||||
/* 配网成功后模块自动连接热点。主动查 CIPSTATUS(STATUS:2=已拿到IP),
|
|
||||||
* 不能等 "WIFI GOT IP" 打印——它可能在 CWSTOPSMART 交互期间被清缓冲擦掉 */
|
|
||||||
if (esp_saved_ap_connected(10) != 0) {
|
|
||||||
log_warn("> WiFi: SmartConfig 未获取到 IP");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
log_info("> WiFi: SmartConfig 已连接热点");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 退出透传模式(OTA 换连接前用)====
|
|
||||||
* "+++" 前后各需 1s 静默 guard time,且不能带 \r\n;
|
|
||||||
* 回命令态后顺手关掉当前 TCP 连接(失败忽略,后续 esp_reset 会兜底) */
|
|
||||||
int esp_exit_transparent(void)
|
|
||||||
{
|
|
||||||
bg_delay(1100);
|
|
||||||
esp_send((uint8_t *)"+++", 3);
|
|
||||||
bg_delay(1100);
|
|
||||||
if (esp_send_cmd("AT", "OK", 2000) != 0) {
|
|
||||||
log_warn("> WiFi: 退出透传无 AT 应答");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
esp_send_cmd("AT+CIPCLOSE", "OK", 2000);
|
|
||||||
log_info("> WiFi: 退出透传成功");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 恢复出厂设置:AT+RESTORE,模块忘掉已保存的热点(用于测试 SmartConfig)====
|
|
||||||
* 注意 1:RESTORE 后模块自动重启且波特率回到出厂 115200,
|
|
||||||
* 这里发送后主动把 UART4 切回 115200 探测确认。
|
|
||||||
* 注意 2:WiFi 已连上时模块处于 TCP 透传态,直接发 AT 会被当作 TCP 数据
|
|
||||||
* 发往服务器(模块永远收不到),必须先 "+++" 退透传回命令态! */
|
|
||||||
int esp_factory_restore(void)
|
|
||||||
{
|
|
||||||
log_info("> WiFi: AT+RESTORE (忘记已保存热点)");
|
|
||||||
esp_exit_transparent(); /* 透传中先退回命令态;非透传时调用无害 */
|
|
||||||
esp_rx_clear();
|
|
||||||
ESP_printf("AT+RESTORE");
|
|
||||||
bg_delay(2500); /* 等模块执行 RESTORE 并完成重启 */
|
|
||||||
UART4_SetBaudRate(WIFI_BAUD_DEFAULT);
|
|
||||||
if (esp_send_cmd("AT", "OK", 2000) != 0) {
|
|
||||||
log_warn("> WiFi: 波特率复位后 RESTORE 无应答");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
log_info("> WiFi: RESTORE 完成, 已保存热点已清除");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*==== 连接 TCP 服务器并进入透传模式 ====*/
|
|
||||||
int esp_enter_transparent(const char *ip, uint16_t port)
|
|
||||||
{
|
|
||||||
char cmd[96];
|
|
||||||
|
|
||||||
log_info("> WiFi: 进入透传 %s:%u", ip, (unsigned)port);
|
|
||||||
|
|
||||||
/* 整体可重试一次:CIPSEND 收到 ERROR 多为 CIPMODE 未生效或连接状态残留 */
|
|
||||||
for (int attempt = 0; attempt < 2; attempt++) {
|
|
||||||
if (attempt > 0) {
|
|
||||||
log_warn("> WiFi: 透传重试");
|
|
||||||
bg_delay(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 先清掉可能残留的旧 TCP 连接(上一轮失败没关的连接会让
|
|
||||||
* CIPMODE/CIPMUX 设置报 "link is builded" ERROR);
|
|
||||||
* 无连接时 CIPCLOSE 回 ERROR,忽略结果 */
|
|
||||||
esp_send_cmd("AT+CIPCLOSE", "OK", 1500);
|
|
||||||
|
|
||||||
/* CIPMUX=0 必须在 CIPMODE=1 之前:透传模式只在单连接下可设置 */
|
|
||||||
if (esp_send_cmd("AT+CIPMUX=0", "OK", 2000) != 0) continue;
|
|
||||||
if (esp_send_cmd("AT+CIPMODE=1", "OK", 2000) != 0) continue;
|
|
||||||
|
|
||||||
snprintf(cmd, sizeof(cmd), "AT+CIPSTART=\"TCP\",\"%s\",%u", ip, (unsigned)port);
|
|
||||||
ESP_printf("%s", cmd);
|
|
||||||
esp_rx_clear();
|
|
||||||
if (esp_wait("CONNECT", WIFI_TCP_TIMEOUT_MS) != 0) {
|
|
||||||
log_warn("> WiFi: TCP 连接失败");
|
|
||||||
esp_dump_rx("CIPSTART");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
log_info("> WiFi: TCP 已连接, 接收: %.100s", U2_CopyBuff);
|
|
||||||
|
|
||||||
/* 等 CIPSTART 的 OK 收尾(先匹配到 CONNECT 时 OK 可能还没回来),
|
|
||||||
* 避免模块 busy 导致 CIPSEND 无响应 */
|
|
||||||
esp_wait("OK", 2000);
|
|
||||||
bg_delay(300);
|
|
||||||
|
|
||||||
/* 进透传:等 ">" */
|
|
||||||
ESP_printf("AT+CIPSEND");
|
|
||||||
esp_rx_clear();
|
|
||||||
if (esp_wait(">", 10000) == 0) {
|
|
||||||
esp_rx_clear();
|
|
||||||
log_info("> WiFi: 透传模式成功");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_warn("> WiFi: CIPSEND 无 '>'");
|
|
||||||
esp_dump_rx("CIPSEND");
|
|
||||||
/* 发 AT 探测:有 OK=仍在命令态;无应答=固件已自动进透传 */
|
|
||||||
ESP_printf("AT");
|
|
||||||
esp_rx_clear();
|
|
||||||
if (esp_wait("OK", 2000) != 0) {
|
|
||||||
log_warn("> WiFi: 无 AT 应答, 假定已进入透传");
|
|
||||||
esp_rx_clear();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* 仍在命令态:查 CIPMODE 实际值做诊断 */
|
|
||||||
if (esp_send_cmd("AT+CIPMODE?", "+CIPMODE:1", 2000) == 0) {
|
|
||||||
log_warn("> WiFi: CIPMODE 仍为 1, 连接可能被服务器关闭");
|
|
||||||
} else {
|
|
||||||
log_warn("> WiFi: CIPMODE 已还原或从未设置成功");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log_warn("> WiFi: 进入透传失败");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
#ifndef ESP8266_H
|
|
||||||
#define ESP8266_H
|
|
||||||
|
|
||||||
#include "main.h"
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/*===== 用户配置区 =====*/
|
|
||||||
/* 固定 WiFi 凭据:优先尝试连接的热点;连不上(60s)则进 Smart Config 智能配网 */
|
|
||||||
#define WIFI_SSID "OrayBox-6B0E" /* TODO: 填入固定 WiFi 名称 */
|
|
||||||
#define WIFI_PASS "nky20192006" /* TODO: 填入固定 WiFi 密码 */
|
|
||||||
|
|
||||||
|
|
||||||
//#define WIFI_SSID "TP_Link-01" /* TODO: 填入固定 WiFi 名称 */
|
|
||||||
//#define WIFI_PASS "zxcvbnmm112" /* TODO: 填入固定 WiFi 密码 */
|
|
||||||
|
|
||||||
//#define WIFI_SSID "TP_Link" /* TODO: 填入固定 WiFi 名称 */
|
|
||||||
//#define WIFI_PASS "zxcvbnm888" /* TODO: 填入固定 WiFi 密码 */
|
|
||||||
|
|
||||||
|
|
||||||
/* 超时配置 */
|
|
||||||
#define WIFI_JOIN_TIMEOUT_S 40 /* 固定 WiFi 连接超时(秒) */
|
|
||||||
#define WIFI_SMART_TIMEOUT_S 100 /* Smart Config 配网超时(秒) */
|
|
||||||
#define WIFI_TCP_TIMEOUT_MS 10000 /* TCP 连接服务器超时 */
|
|
||||||
|
|
||||||
/* 波特率配置:模块出厂/RESTORE 后为 115200,
|
|
||||||
* 初始化时探测并固定为 WIFI_BAUD_TARGET(AT+UART_DEF 掉电保存),
|
|
||||||
* 提高吞吐(为 WiFi OTA 预留);切换失败自动维持 115200 */
|
|
||||||
#define WIFI_BAUD_DEFAULT 115200u
|
|
||||||
#define WIFI_BAUD_TARGET 921600u
|
|
||||||
|
|
||||||
/*===== ESP-01S 复位脚 (ESP_RST -> PA5) =====*/
|
|
||||||
#define ESP_RST_PIN GPIO_PIN_5
|
|
||||||
#define ESP_RST_PORT GPIOA
|
|
||||||
#define ESP_RST_LOW() HAL_GPIO_WritePin(ESP_RST_PORT, ESP_RST_PIN, GPIO_PIN_RESET)
|
|
||||||
#define ESP_RST_HIGH() HAL_GPIO_WritePin(ESP_RST_PORT, ESP_RST_PIN, GPIO_PIN_SET)
|
|
||||||
|
|
||||||
/*===== API =====*/
|
|
||||||
void esp_gpio_init(void); /* PA5 推挽输出初始化(默认拉高) */
|
|
||||||
int esp_reset(void); /* 硬件复位+波特率同步(->921600), 0=成功 */
|
|
||||||
int esp_send_cmd(const char *cmd, const char *expect, int timeout_ms); /* 发AT等关键字, 0=成功 */
|
|
||||||
int esp_join_fixed_ap(void); /* 连固定SSID(60s), 0=成功 */
|
|
||||||
int esp_saved_ap_connected(int timeout_s); /* CIPSTATUS查已存AP自动连接, 0=已连 */
|
|
||||||
int esp_smartconfig(void); /* ESP-Touch智能配网(60s), 0=成功 */
|
|
||||||
int esp_enter_transparent(const char *ip, uint16_t port); /* 连TCP并进透传, 0=成功 */
|
|
||||||
int esp_exit_transparent(void); /* "+++" 退透传+CIPCLOSE, 0=成功 */
|
|
||||||
int esp_factory_restore(void); /* AT+RESTORE 恢复出厂(忘掉已存AP), 0=成功 */
|
|
||||||
void esp_send(const uint8_t *data, uint16_t len); /* 透传模式原始数据发送 */
|
|
||||||
void esp_rx_clear(void); /* 清空共享接收缓冲 */
|
|
||||||
|
|
||||||
#endif /* ESP8266_H */
|
|
||||||
|
|
@ -1,138 +0,0 @@
|
||||||
#ifndef __MAIN_H
|
|
||||||
#define __MAIN_H
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "stm32f1xx_hal.h"
|
|
||||||
|
|
||||||
/* USER CODE BEGIN Includes */
|
|
||||||
#include "air780e.h"
|
|
||||||
#include "log.h"
|
|
||||||
/* USER CODE END Includes */
|
|
||||||
|
|
||||||
void Error_Handler(void);
|
|
||||||
|
|
||||||
/* USER CODE BEGIN Private defines */
|
|
||||||
#define Rx_Max 8192 /* USART3 DMA 接收缓冲:OTA 分块 3KB + 头部/尾部突发约 3.1KB,留足余量 */
|
|
||||||
extern uint16_t Rx_Len;
|
|
||||||
extern uint8_t Rx_Buf[Rx_Max];
|
|
||||||
extern UART_HandleTypeDef huart3;
|
|
||||||
|
|
||||||
/* EEPROM配置魔数: 自动检测EEPROM是否已初始化 */
|
|
||||||
#define EEPROM_MAGIC 0xDEADBEEF// 0xDE123456//0xDEADBEEF
|
|
||||||
|
|
||||||
/*===== 产品型号配置 =====
|
|
||||||
* 同一套板子适配两种产品,编译前在这里二选一:
|
|
||||||
* 1 = 智能水产投料机:接 RS485 磅秤,feeding 指令按 onceWt 定量自动投喂,
|
|
||||||
* 带重量反增/超时保护,sw1 指令被忽略(投喂只能走 feeding)
|
|
||||||
* 0 = 基础四路继电器:不接磅秤,feeding/sw1 指令直接开关投喂继电器 1,
|
|
||||||
* 由平台侧自行计时控制
|
|
||||||
*/
|
|
||||||
/*===== 固件版本号(开机横幅打印用,改版本只动这里) =====*/
|
|
||||||
#define FIRMWARE_VERSION_STR "1.5.11"
|
|
||||||
|
|
||||||
#define PRODUCT_WITH_FEED_SCALE 1 /* 默认带秤(EEPROM 172 可改,蓝牙 SCALE0/1 切换) */
|
|
||||||
|
|
||||||
/*===== GPS 定位配置 =====
|
|
||||||
* 同一套固件适配是否带 GNSS 的模组,编译前在这里二选一:
|
|
||||||
* 1 = 启用北斗/GPS 定位周期上报(仅 4G 通道,需 Air780EG 模组)
|
|
||||||
* 0 = 不获取不上传(纯 Air780E 无 GNSS 时用,不产生任何 GPS 相关 AT 交互)
|
|
||||||
*/
|
|
||||||
#define PRODUCT_WITH_GPS 1
|
|
||||||
|
|
||||||
/*===== MQTT 默认配置 =====*/
|
|
||||||
////锦鲤塘增氧投料机-02
|
|
||||||
//#define MQTT_CLIENT_ID "3eb3d1e51846f296"
|
|
||||||
//#define MQTT_USERNAME "766990788193702677"
|
|
||||||
//#define MQTT_PASSWORD "a0207984565267a5"
|
|
||||||
|
|
||||||
////测试平台的ID - 01
|
|
||||||
//#define MQTT_CLIENT_ID "4fbaf68ca378b72b"
|
|
||||||
//#define MQTT_USERNAME "698227263545354343"
|
|
||||||
//#define MQTT_PASSWORD "b612c2839c135934"
|
|
||||||
|
|
||||||
//RTOS版-投料机-无蠕变磅秤-01
|
|
||||||
#define MQTT_CLIENT_ID "46c080c385c3652e"
|
|
||||||
#define MQTT_USERNAME "766990788193702677"
|
|
||||||
#define MQTT_PASSWORD "6ae9622b7034326b"
|
|
||||||
|
|
||||||
////RTOS版-投料机-无蠕变磅秤-03
|
|
||||||
//#define MQTT_CLIENT_ID "5791a8579d459290"
|
|
||||||
//#define MQTT_USERNAME "766990788193702677"
|
|
||||||
//#define MQTT_PASSWORD "5f6cd7748ec9db35"
|
|
||||||
|
|
||||||
#define MQTT_SERVER_IP "58.17.14.95"
|
|
||||||
#define MQTT_SERVER_PORT 1880
|
|
||||||
#define MQTT_TOPIC "/iot/data/down/"
|
|
||||||
|
|
||||||
/*===== 蓝牙名称配置 =====
|
|
||||||
* 上电时通过 AT 命令设置 HLK-B40 的蓝牙名称(掉电保存),
|
|
||||||
* 多台设备现场用名字区分;手机列表大约只显示前 10 位 */
|
|
||||||
#define BLE_NAME "NKY-BT-01"
|
|
||||||
|
|
||||||
/*===== 蓝牙发射功率(AT+RFPOWER) =====
|
|
||||||
* 1~18 共 18 级,出厂默认 8。多台设备同场所时调小(如 1~3),
|
|
||||||
* 缩短可搜索距离,避免互相认错设备。模块内保存,改宏后下次开机自动重设一次 */
|
|
||||||
#define BLE_TX_POWER 1
|
|
||||||
|
|
||||||
/*===== HLW8032 电量计量系数(UART5 PD2 接收,只上报累计电量) =====
|
|
||||||
* 电压通道: ZMPT107-1(2mA:2mA),初级 220V 串 100K,次级并 49.9R
|
|
||||||
* -> 电压系数 = (100K/49.9)/1000 = 2.004
|
|
||||||
* 电流通道: CT 100A/100mA(1000:1),输出并 2R -> 等效采样电阻 0.002R
|
|
||||||
* -> 电流系数 = 1/(0.002*1000) = 0.5
|
|
||||||
* 改电阻/互感器只需改这两个宏(公式见 HLW8032 手册电量计算一节) */
|
|
||||||
#define HLW_U_COEF 2.004f
|
|
||||||
#define HLW_I_COEF 0.5f
|
|
||||||
/* 电流零点偏移(A):空载时电流通道的零漂读数,显示时减去并钳位到 0。
|
|
||||||
* 换板子/换 CT 后空载看一下读数,按实际调整 */
|
|
||||||
#define HLW_I_ZERO_A 0.02f
|
|
||||||
/* 功率零漂处理:空载噪声底约 0.4~2W 且会跳动,单纯减偏移压不住尖峰。
|
|
||||||
* 读数低于死区直接显示 0,高于死区再减去偏移 */
|
|
||||||
#define HLW_P_DEAD_W 2.0f /* 功率死区(W):低于此值显示 0 */
|
|
||||||
#define HLW_P_ZERO_W 0.5f /* 功率偏移(W):超过死区后减去 */
|
|
||||||
|
|
||||||
/*===== 蓝牙 GATT UUID 配置(透传服务)=====
|
|
||||||
* BLE_UUID_CUSTOM = 1:上电把三个 UUID 设为下面的值(重启模块生效)。
|
|
||||||
* = 0:保持模块出厂 UUID(fff0/fff1/fff2),仅海凌科自家 APP 可用
|
|
||||||
* 当前配置:FFE0/FFE1(HM-10/JDY-31 同款,收发共用 FFE1 单特征),
|
|
||||||
* 常见 BLE 串口 APP 都能连。Nordic NUS 备选:6e400001/6e400003/6e400002 */
|
|
||||||
#define BLE_UUID_CUSTOM 1
|
|
||||||
#define BLE_UUID_SERVICE "0000ffe000001000800000805f9b34fb" /* FFE0 服务 */
|
|
||||||
#define BLE_UUID_TX "0000ffe100001000800000805f9b34fb" /* FFE1 模块->手机 */
|
|
||||||
#define BLE_UUID_RX "0000ffe100001000800000805f9b34fb" /* FFE1 手机->模块(同特征双向) */
|
|
||||||
|
|
||||||
/* MQTT配置结构体 (存储在AT24C02 EEPROM中) */
|
|
||||||
typedef struct {
|
|
||||||
char ClientID[20];
|
|
||||||
char Username[20];
|
|
||||||
char Passward[20];
|
|
||||||
char ServerIP[20];
|
|
||||||
char Topic[20];
|
|
||||||
uint16_t ServerPort;
|
|
||||||
uint8_t Relay_State[7]; /* [1..4]继电器状态, [0]保留 */
|
|
||||||
uint32_t Magic; /* 魔数, 检测EEPROM是否已初始化 */
|
|
||||||
uint16_t Ver; /* OTA固件版本号, 断电保持 */
|
|
||||||
} MqttInfo_Str;
|
|
||||||
|
|
||||||
#define MQTT_STRUCT_LEN sizeof(MqttInfo_Str)
|
|
||||||
|
|
||||||
/*===== W25Q64 外部 Flash 引脚(软件 SPI) =====*/
|
|
||||||
#define W25Q64_CS_PIN GPIO_PIN_4
|
|
||||||
#define W25Q64_CS_PORT GPIOB
|
|
||||||
#define W25Q64_CLK_PIN GPIO_PIN_3
|
|
||||||
#define W25Q64_CLK_PORT GPIOB
|
|
||||||
#define W25Q64_DO_PIN GPIO_PIN_5 /* Flash DO -> MCU 输入 */
|
|
||||||
#define W25Q64_DO_PORT GPIOB
|
|
||||||
#define W25Q64_DI_PIN GPIO_PIN_15 /* MCU DI -> Flash 输入 */
|
|
||||||
#define W25Q64_DI_PORT GPIOA
|
|
||||||
|
|
||||||
extern MqttInfo_Str MqttInfoStr;
|
|
||||||
/* USER CODE END Private defines */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __MAIN_H */
|
|
||||||
|
|
@ -1,984 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
|
||||||
<ProjectOpt xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="project_optx.xsd">
|
|
||||||
|
|
||||||
<SchemaVersion>1.0</SchemaVersion>
|
|
||||||
|
|
||||||
<Header>### uVision Project, (C) Keil Software</Header>
|
|
||||||
|
|
||||||
<Extensions>
|
|
||||||
<cExt>*.c</cExt>
|
|
||||||
<aExt>*.s*; *.src; *.a*</aExt>
|
|
||||||
<oExt>*.obj; *.o</oExt>
|
|
||||||
<lExt>*.lib</lExt>
|
|
||||||
<tExt>*.txt; *.h; *.inc</tExt>
|
|
||||||
<pExt>*.plm</pExt>
|
|
||||||
<CppX>*.cpp</CppX>
|
|
||||||
<nMigrate>0</nMigrate>
|
|
||||||
</Extensions>
|
|
||||||
|
|
||||||
<DaveTm>
|
|
||||||
<dwLowDateTime>0</dwLowDateTime>
|
|
||||||
<dwHighDateTime>0</dwHighDateTime>
|
|
||||||
</DaveTm>
|
|
||||||
|
|
||||||
<Target>
|
|
||||||
<TargetName>STM32F103rb_App1</TargetName>
|
|
||||||
<ToolsetNumber>0x4</ToolsetNumber>
|
|
||||||
<ToolsetName>ARM-ADS</ToolsetName>
|
|
||||||
<TargetOption>
|
|
||||||
<CLKADS>12000000</CLKADS>
|
|
||||||
<OPTTT>
|
|
||||||
<gFlags>1</gFlags>
|
|
||||||
<BeepAtEnd>1</BeepAtEnd>
|
|
||||||
<RunSim>0</RunSim>
|
|
||||||
<RunTarget>1</RunTarget>
|
|
||||||
<RunAbUc>0</RunAbUc>
|
|
||||||
</OPTTT>
|
|
||||||
<OPTHX>
|
|
||||||
<HexSelection>1</HexSelection>
|
|
||||||
<FlashByte>65535</FlashByte>
|
|
||||||
<HexRangeLowAddress>0</HexRangeLowAddress>
|
|
||||||
<HexRangeHighAddress>0</HexRangeHighAddress>
|
|
||||||
<HexOffset>0</HexOffset>
|
|
||||||
</OPTHX>
|
|
||||||
<OPTLEX>
|
|
||||||
<PageWidth>79</PageWidth>
|
|
||||||
<PageLength>66</PageLength>
|
|
||||||
<TabStop>8</TabStop>
|
|
||||||
<ListingPath></ListingPath>
|
|
||||||
</OPTLEX>
|
|
||||||
<ListingPage>
|
|
||||||
<CreateCListing>1</CreateCListing>
|
|
||||||
<CreateAListing>1</CreateAListing>
|
|
||||||
<CreateLListing>1</CreateLListing>
|
|
||||||
<CreateIListing>0</CreateIListing>
|
|
||||||
<AsmCond>1</AsmCond>
|
|
||||||
<AsmSymb>1</AsmSymb>
|
|
||||||
<AsmXref>0</AsmXref>
|
|
||||||
<CCond>1</CCond>
|
|
||||||
<CCode>0</CCode>
|
|
||||||
<CListInc>0</CListInc>
|
|
||||||
<CSymb>0</CSymb>
|
|
||||||
<LinkerCodeListing>0</LinkerCodeListing>
|
|
||||||
</ListingPage>
|
|
||||||
<OPTXL>
|
|
||||||
<LMap>1</LMap>
|
|
||||||
<LComments>1</LComments>
|
|
||||||
<LGenerateSymbols>1</LGenerateSymbols>
|
|
||||||
<LLibSym>1</LLibSym>
|
|
||||||
<LLines>1</LLines>
|
|
||||||
<LLocSym>1</LLocSym>
|
|
||||||
<LPubSym>1</LPubSym>
|
|
||||||
<LXref>0</LXref>
|
|
||||||
<LExpSel>0</LExpSel>
|
|
||||||
</OPTXL>
|
|
||||||
<OPTFL>
|
|
||||||
<tvExp>1</tvExp>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<IsCurrentTarget>1</IsCurrentTarget>
|
|
||||||
</OPTFL>
|
|
||||||
<CpuCode>18</CpuCode>
|
|
||||||
<Books>
|
|
||||||
<Book>
|
|
||||||
<Number>0</Number>
|
|
||||||
<Title>Base Board Schematics (MCBSTM32E)</Title>
|
|
||||||
<Path>C:\Keil_v5\ARM\PACK\Keil\STM32F1xx_DFP\1.0.5\Documents\mcbstm32e-base-board-schematics.pdf</Path>
|
|
||||||
</Book>
|
|
||||||
<Book>
|
|
||||||
<Number>1</Number>
|
|
||||||
<Title>Display Board Schematics (MCBSTM32E)</Title>
|
|
||||||
<Path>C:\Keil_v5\ARM\PACK\Keil\STM32F1xx_DFP\1.0.5\Documents\mcbstm32e-display-board-schematics.pdf</Path>
|
|
||||||
</Book>
|
|
||||||
<Book>
|
|
||||||
<Number>2</Number>
|
|
||||||
<Title>User Manual (MCBSTM32E)</Title>
|
|
||||||
<Path>C:\Keil_v5\ARM\PACK\Keil\STM32F1xx_DFP\1.0.5\Documents\mcbstm32e.chm</Path>
|
|
||||||
</Book>
|
|
||||||
<Book>
|
|
||||||
<Number>3</Number>
|
|
||||||
<Title>MCBSTM32E Evaluation Board Web Page (MCBSTM32E)</Title>
|
|
||||||
<Path>http://www.keil.com/mcbstm32e/</Path>
|
|
||||||
</Book>
|
|
||||||
</Books>
|
|
||||||
<DebugOpt>
|
|
||||||
<uSim>0</uSim>
|
|
||||||
<uTrg>1</uTrg>
|
|
||||||
<sLdApp>1</sLdApp>
|
|
||||||
<sGomain>1</sGomain>
|
|
||||||
<sRbreak>1</sRbreak>
|
|
||||||
<sRwatch>1</sRwatch>
|
|
||||||
<sRmem>1</sRmem>
|
|
||||||
<sRfunc>1</sRfunc>
|
|
||||||
<sRbox>1</sRbox>
|
|
||||||
<tLdApp>1</tLdApp>
|
|
||||||
<tGomain>1</tGomain>
|
|
||||||
<tRbreak>1</tRbreak>
|
|
||||||
<tRwatch>1</tRwatch>
|
|
||||||
<tRmem>1</tRmem>
|
|
||||||
<tRfunc>1</tRfunc>
|
|
||||||
<tRbox>1</tRbox>
|
|
||||||
<tRtrace>1</tRtrace>
|
|
||||||
<sRSysVw>1</sRSysVw>
|
|
||||||
<tRSysVw>1</tRSysVw>
|
|
||||||
<sRunDeb>0</sRunDeb>
|
|
||||||
<sLrtime>0</sLrtime>
|
|
||||||
<nTsel>6</nTsel>
|
|
||||||
<sDll></sDll>
|
|
||||||
<sDllPa></sDllPa>
|
|
||||||
<sDlgDll></sDlgDll>
|
|
||||||
<sDlgPa></sDlgPa>
|
|
||||||
<sIfile></sIfile>
|
|
||||||
<tDll></tDll>
|
|
||||||
<tDllPa></tDllPa>
|
|
||||||
<tDlgDll></tDlgDll>
|
|
||||||
<tDlgPa></tDlgPa>
|
|
||||||
<tIfile></tIfile>
|
|
||||||
<pMon>Segger\JL2CM3.dll</pMon>
|
|
||||||
</DebugOpt>
|
|
||||||
<TargetDriverDllRegistry>
|
|
||||||
<SetRegEntry>
|
|
||||||
<Number>0</Number>
|
|
||||||
<Key>JL2CM3</Key>
|
|
||||||
<Name>-U20090928 -O78 -S0 -A0 -C0 -JU1 -JI127.0.0.1 -JP0 -RST0 -N00("ARM CoreSight SW-DP") -D00(1BA01477) -L00(0) -TO18 -TC10000000 -TP21 -TDS8007 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -TB1 -TFE0 -FO15 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_512.FLM -FS08000000 -FL080000 -FP0($$Device:STM32F103RC$Flash\STM32F10x_512.FLM)</Name>
|
|
||||||
</SetRegEntry>
|
|
||||||
<SetRegEntry>
|
|
||||||
<Number>0</Number>
|
|
||||||
<Key>CMSIS_AGDI</Key>
|
|
||||||
<Name>-X"ATK CMSIS-DAP-Debugger" -UATK-05022018 -O206 -S0 -C0 -P00 -N00("ARM CoreSight SW-DP") -D00(1BA01477) -L00(0) -FO15 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_512 -FS08000000 -FL080000 -FP0($$Device:STM32F103RC$Flash\STM32F10x_512.FLM)</Name>
|
|
||||||
</SetRegEntry>
|
|
||||||
<SetRegEntry>
|
|
||||||
<Number>0</Number>
|
|
||||||
<Key>ARMRTXEVENTFLAGS</Key>
|
|
||||||
<Name>-L70 -Z18 -C0 -M0 -T1</Name>
|
|
||||||
</SetRegEntry>
|
|
||||||
<SetRegEntry>
|
|
||||||
<Number>0</Number>
|
|
||||||
<Key>DLGTARM</Key>
|
|
||||||
<Name>(1010=-1,-1,-1,-1,0)(1007=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0)(1009=-1,-1,-1,-1,0)</Name>
|
|
||||||
</SetRegEntry>
|
|
||||||
<SetRegEntry>
|
|
||||||
<Number>0</Number>
|
|
||||||
<Key>ARMDBGFLAGS</Key>
|
|
||||||
<Name></Name>
|
|
||||||
</SetRegEntry>
|
|
||||||
<SetRegEntry>
|
|
||||||
<Number>0</Number>
|
|
||||||
<Key>DLGUARM</Key>
|
|
||||||
<Name>(105=-1,-1,-1,-1,0)</Name>
|
|
||||||
</SetRegEntry>
|
|
||||||
<SetRegEntry>
|
|
||||||
<Number>0</Number>
|
|
||||||
<Key>UL2CM3</Key>
|
|
||||||
<Name>UL2CM3(-S0 -C0 -P0 ) -FN1 -FC1000 -FD20000000 -FF0STM32F10x_512 -FL080000 -FS08000000 -FP0($$Device:STM32F103RC$Flash\STM32F10x_512.FLM)</Name>
|
|
||||||
</SetRegEntry>
|
|
||||||
<SetRegEntry>
|
|
||||||
<Number>0</Number>
|
|
||||||
<Key>ST-LINKIII-KEIL_SWO</Key>
|
|
||||||
<Name>-U066CFF373436424E43133952 -O2254 -SF4000 -C0 -A0 -I0 -HNlocalhost -HP7184 -P1 -N00("ARM CoreSight SW-DP") -D00(1BA01477) -L00(0) -TO18 -TC10000000 -TP21 -TDS8004 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO15 -FD20000000 -FC800 -FN1 -FF0STM32F10x_128 -FS08000000 -FL020000</Name>
|
|
||||||
</SetRegEntry>
|
|
||||||
</TargetDriverDllRegistry>
|
|
||||||
<Breakpoint/>
|
|
||||||
<WatchWindow1>
|
|
||||||
<Ww>
|
|
||||||
<count>0</count>
|
|
||||||
<WinNumber>1</WinNumber>
|
|
||||||
<ItemText>Rx_Buf</ItemText>
|
|
||||||
</Ww>
|
|
||||||
<Ww>
|
|
||||||
<count>1</count>
|
|
||||||
<WinNumber>1</WinNumber>
|
|
||||||
<ItemText>temp_buf</ItemText>
|
|
||||||
</Ww>
|
|
||||||
</WatchWindow1>
|
|
||||||
<MemoryWindow2>
|
|
||||||
<Mm>
|
|
||||||
<WinNumber>2</WinNumber>
|
|
||||||
<SubType>0</SubType>
|
|
||||||
<ItemText>0x0800F000</ItemText>
|
|
||||||
<AccSizeX>1</AccSizeX>
|
|
||||||
</Mm>
|
|
||||||
</MemoryWindow2>
|
|
||||||
<Tracepoint>
|
|
||||||
<THDelay>0</THDelay>
|
|
||||||
</Tracepoint>
|
|
||||||
<DebugFlag>
|
|
||||||
<trace>0</trace>
|
|
||||||
<periodic>1</periodic>
|
|
||||||
<aLwin>0</aLwin>
|
|
||||||
<aCover>0</aCover>
|
|
||||||
<aSer1>0</aSer1>
|
|
||||||
<aSer2>0</aSer2>
|
|
||||||
<aPa>0</aPa>
|
|
||||||
<viewmode>1</viewmode>
|
|
||||||
<vrSel>0</vrSel>
|
|
||||||
<aSym>0</aSym>
|
|
||||||
<aTbox>0</aTbox>
|
|
||||||
<AscS1>0</AscS1>
|
|
||||||
<AscS2>0</AscS2>
|
|
||||||
<AscS3>0</AscS3>
|
|
||||||
<aSer3>0</aSer3>
|
|
||||||
<eProf>0</eProf>
|
|
||||||
<aLa>0</aLa>
|
|
||||||
<aPa1>0</aPa1>
|
|
||||||
<AscS4>0</AscS4>
|
|
||||||
<aSer4>0</aSer4>
|
|
||||||
<StkLoc>0</StkLoc>
|
|
||||||
<TrcWin>0</TrcWin>
|
|
||||||
<newCpu>0</newCpu>
|
|
||||||
<uProt>0</uProt>
|
|
||||||
</DebugFlag>
|
|
||||||
<LintExecutable></LintExecutable>
|
|
||||||
<LintConfigFile></LintConfigFile>
|
|
||||||
<bLintAuto>0</bLintAuto>
|
|
||||||
</TargetOption>
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
<Group>
|
|
||||||
<GroupName>Application/MDK-ARM</GroupName>
|
|
||||||
<tvExp>1</tvExp>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<cbSel>0</cbSel>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>1</GroupNumber>
|
|
||||||
<FileNumber>1</FileNumber>
|
|
||||||
<FileType>2</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>.\RTE\Device\STM32F103RC\startup_stm32f10x_hd.s</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>startup_stm32f10x_hd.s</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
</Group>
|
|
||||||
|
|
||||||
<Group>
|
|
||||||
<GroupName>Application/User</GroupName>
|
|
||||||
<tvExp>1</tvExp>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<cbSel>0</cbSel>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>2</GroupNumber>
|
|
||||||
<FileNumber>2</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Src/main.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>main.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>2</GroupNumber>
|
|
||||||
<FileNumber>3</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Src/stm32f1xx_it.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_it.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>2</GroupNumber>
|
|
||||||
<FileNumber>4</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Src/stm32f1xx_hal_msp.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_msp.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>2</GroupNumber>
|
|
||||||
<FileNumber>5</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Src/stm32f1xx_hal_timebase_tim.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_timebase_tim.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
</Group>
|
|
||||||
|
|
||||||
<Group>
|
|
||||||
<GroupName>Middlewares/FreeRTOS</GroupName>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<cbSel>0</cbSel>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>3</GroupNumber>
|
|
||||||
<FileNumber>6</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Middlewares/FreeRTOS/Source/tasks.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>tasks.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>3</GroupNumber>
|
|
||||||
<FileNumber>7</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Middlewares/FreeRTOS/Source/queue.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>queue.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>3</GroupNumber>
|
|
||||||
<FileNumber>8</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Middlewares/FreeRTOS/Source/list.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>list.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>3</GroupNumber>
|
|
||||||
<FileNumber>9</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Middlewares/FreeRTOS/Source/portable/RVDS/ARM_CM3/port.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>port.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>3</GroupNumber>
|
|
||||||
<FileNumber>10</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Middlewares/FreeRTOS/Source/portable/MemMang/heap_4.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>heap_4.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
</Group>
|
|
||||||
|
|
||||||
<Group>
|
|
||||||
<GroupName>Drivers/STM32F1xx_HAL_Driver</GroupName>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<cbSel>0</cbSel>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>4</GroupNumber>
|
|
||||||
<FileNumber>11</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio_ex.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_gpio_ex.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>4</GroupNumber>
|
|
||||||
<FileNumber>12</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_tim.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>4</GroupNumber>
|
|
||||||
<FileNumber>13</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim_ex.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_tim_ex.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>4</GroupNumber>
|
|
||||||
<FileNumber>14</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_uart.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_uart.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>4</GroupNumber>
|
|
||||||
<FileNumber>15</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>4</GroupNumber>
|
|
||||||
<FileNumber>16</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rcc.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_rcc.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>4</GroupNumber>
|
|
||||||
<FileNumber>17</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rcc_ex.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_rcc_ex.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>4</GroupNumber>
|
|
||||||
<FileNumber>18</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_gpio.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>4</GroupNumber>
|
|
||||||
<FileNumber>19</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_dma.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_dma.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>4</GroupNumber>
|
|
||||||
<FileNumber>20</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_cortex.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_cortex.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>4</GroupNumber>
|
|
||||||
<FileNumber>21</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_pwr.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_pwr.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>4</GroupNumber>
|
|
||||||
<FileNumber>22</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_flash.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_flash.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>4</GroupNumber>
|
|
||||||
<FileNumber>23</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_flash_ex.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_flash_ex.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>4</GroupNumber>
|
|
||||||
<FileNumber>24</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_exti.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_exti.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>4</GroupNumber>
|
|
||||||
<FileNumber>25</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_iwdg.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_iwdg.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>4</GroupNumber>
|
|
||||||
<FileNumber>26</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_adc.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>stm32f1xx_hal_adc.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
</Group>
|
|
||||||
|
|
||||||
<Group>
|
|
||||||
<GroupName>Drivers/CMSIS</GroupName>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<cbSel>0</cbSel>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>5</GroupNumber>
|
|
||||||
<FileNumber>27</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>../Src/system_stm32f1xx.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>system_stm32f1xx.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
</Group>
|
|
||||||
|
|
||||||
<Group>
|
|
||||||
<GroupName>m_app</GroupName>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<cbSel>0</cbSel>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>6</GroupNumber>
|
|
||||||
<FileNumber>28</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\m_app\proto.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>proto.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
</Group>
|
|
||||||
|
|
||||||
<Group>
|
|
||||||
<GroupName>HardWare</GroupName>
|
|
||||||
<tvExp>1</tvExp>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<cbSel>0</cbSel>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>29</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\CAR\air780e.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>air780e.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>30</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\NETWORK\network.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>network.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>31</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\NETWORK\net_wifi.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>net_wifi.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>32</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\NETWORK\net_wifi_ota.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>net_wifi_ota.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>33</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\WIFI\esp8266.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>esp8266.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>34</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\WIFI\mqtt_client.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>mqtt_client.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>35</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\RELAY\relay.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>relay.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>36</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\LOG\log.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>log.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>37</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\LOG\log_cn.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>log_cn.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>38</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\24C02\24c02.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>24c02.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>39</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\IIC\iic.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>iic.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>40</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\ADC\adc.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>adc.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>41</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\LED\led.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>led.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>42</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\UART\uart.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>uart.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>43</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\FEED_SCALE\feed_scale.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>feed_scale.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>44</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\USER_CMD\user_cmd.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>user_cmd.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>45</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\USER_CMD\ble_at.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>ble_at.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>46</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\SYSINIT\system.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>system.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>47</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\SYSINIT\reset_log.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>reset_log.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>48</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\SYSINIT\app_loop.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>app_loop.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>49</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\W25Q64\w25q64.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>w25q64.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>50</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\HLW8032\hlw8032.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>hlw8032.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>7</GroupNumber>
|
|
||||||
<FileNumber>51</FileNumber>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\HardWare\OLED\oled.c</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>oled.c</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
</Group>
|
|
||||||
|
|
||||||
<Group>
|
|
||||||
<GroupName>Doc</GroupName>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<cbSel>0</cbSel>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<File>
|
|
||||||
<GroupNumber>8</GroupNumber>
|
|
||||||
<FileNumber>52</FileNumber>
|
|
||||||
<FileType>5</FileType>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<Focus>0</Focus>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<bDave2>0</bDave2>
|
|
||||||
<PathWithFileName>..\README.md</PathWithFileName>
|
|
||||||
<FilenameWithoutPath>README.md</FilenameWithoutPath>
|
|
||||||
<RteFlg>0</RteFlg>
|
|
||||||
<bShared>0</bShared>
|
|
||||||
</File>
|
|
||||||
</Group>
|
|
||||||
|
|
||||||
<Group>
|
|
||||||
<GroupName>::CMSIS</GroupName>
|
|
||||||
<tvExp>0</tvExp>
|
|
||||||
<tvExpOptDlg>0</tvExpOptDlg>
|
|
||||||
<cbSel>0</cbSel>
|
|
||||||
<RteFlg>1</RteFlg>
|
|
||||||
</Group>
|
|
||||||
|
|
||||||
</ProjectOpt>
|
|
||||||
|
|
@ -1,751 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
|
||||||
<Project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="project_projx.xsd">
|
|
||||||
|
|
||||||
<SchemaVersion>2.1</SchemaVersion>
|
|
||||||
|
|
||||||
<Header>### uVision Project, (C) Keil Software</Header>
|
|
||||||
|
|
||||||
<Targets>
|
|
||||||
<Target>
|
|
||||||
<TargetName>STM32F103rb_App1</TargetName>
|
|
||||||
<ToolsetNumber>0x4</ToolsetNumber>
|
|
||||||
<ToolsetName>ARM-ADS</ToolsetName>
|
|
||||||
<TargetOption>
|
|
||||||
<TargetCommonOption>
|
|
||||||
<Device>STM32F103RC</Device>
|
|
||||||
<Vendor>STMicroelectronics</Vendor>
|
|
||||||
<PackID>Keil.STM32F1xx_DFP.1.0.5</PackID>
|
|
||||||
<PackURL>http://www.keil.com/pack/</PackURL>
|
|
||||||
<Cpu>IROM(0x08008000,0x38000) IRAM(0x20000000,0xC000) CPUTYPE("Cortex-M3") CLOCK(12000000) ELITTLE</Cpu>
|
|
||||||
<FlashUtilSpec></FlashUtilSpec>
|
|
||||||
<StartupFile></StartupFile>
|
|
||||||
<FlashDriverDll>UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_512 -FS08000000 -FL080000 -FP0($$Device:STM32F103RC$Flash\STM32F10x_512.FLM))</FlashDriverDll>
|
|
||||||
<DeviceId>0</DeviceId>
|
|
||||||
<RegisterFile>$$Device:STM32F103RC$Device\Include\stm32f10x.h</RegisterFile>
|
|
||||||
<MemoryEnv></MemoryEnv>
|
|
||||||
<Cmp></Cmp>
|
|
||||||
<Asm></Asm>
|
|
||||||
<Linker></Linker>
|
|
||||||
<OHString></OHString>
|
|
||||||
<InfinionOptionDll></InfinionOptionDll>
|
|
||||||
<SLE66CMisc></SLE66CMisc>
|
|
||||||
<SLE66AMisc></SLE66AMisc>
|
|
||||||
<SLE66LinkerMisc></SLE66LinkerMisc>
|
|
||||||
<SFDFile>$$Device:STM32F103RC$SVD\STM32F103xx.svd</SFDFile>
|
|
||||||
<bCustSvd>0</bCustSvd>
|
|
||||||
<UseEnv>0</UseEnv>
|
|
||||||
<BinPath></BinPath>
|
|
||||||
<IncludePath></IncludePath>
|
|
||||||
<LibPath></LibPath>
|
|
||||||
<RegisterFilePath></RegisterFilePath>
|
|
||||||
<DBRegisterFilePath></DBRegisterFilePath>
|
|
||||||
<TargetStatus>
|
|
||||||
<Error>0</Error>
|
|
||||||
<ExitCodeStop>0</ExitCodeStop>
|
|
||||||
<ButtonStop>0</ButtonStop>
|
|
||||||
<NotGenerated>0</NotGenerated>
|
|
||||||
<InvalidFlash>1</InvalidFlash>
|
|
||||||
</TargetStatus>
|
|
||||||
<OutputDirectory>..\Object\</OutputDirectory>
|
|
||||||
<OutputName>STM32F103rb_App1</OutputName>
|
|
||||||
<CreateExecutable>1</CreateExecutable>
|
|
||||||
<CreateLib>0</CreateLib>
|
|
||||||
<CreateHexFile>1</CreateHexFile>
|
|
||||||
<DebugInformation>1</DebugInformation>
|
|
||||||
<BrowseInformation>1</BrowseInformation>
|
|
||||||
<ListingPath></ListingPath>
|
|
||||||
<HexFormatSelection>1</HexFormatSelection>
|
|
||||||
<Merge32K>0</Merge32K>
|
|
||||||
<CreateBatchFile>0</CreateBatchFile>
|
|
||||||
<BeforeCompile>
|
|
||||||
<RunUserProg1>0</RunUserProg1>
|
|
||||||
<RunUserProg2>1</RunUserProg2>
|
|
||||||
<UserProg1Name></UserProg1Name>
|
|
||||||
<UserProg2Name></UserProg2Name>
|
|
||||||
<UserProg1Dos16Mode>0</UserProg1Dos16Mode>
|
|
||||||
<UserProg2Dos16Mode>0</UserProg2Dos16Mode>
|
|
||||||
<nStopU1X>0</nStopU1X>
|
|
||||||
<nStopU2X>0</nStopU2X>
|
|
||||||
</BeforeCompile>
|
|
||||||
<BeforeMake>
|
|
||||||
<RunUserProg1>0</RunUserProg1>
|
|
||||||
<RunUserProg2>1</RunUserProg2>
|
|
||||||
<UserProg1Name></UserProg1Name>
|
|
||||||
<UserProg2Name></UserProg2Name>
|
|
||||||
<UserProg1Dos16Mode>0</UserProg1Dos16Mode>
|
|
||||||
<UserProg2Dos16Mode>0</UserProg2Dos16Mode>
|
|
||||||
<nStopB1X>0</nStopB1X>
|
|
||||||
<nStopB2X>0</nStopB2X>
|
|
||||||
</BeforeMake>
|
|
||||||
<AfterMake>
|
|
||||||
<RunUserProg1>1</RunUserProg1>
|
|
||||||
<RunUserProg2>1</RunUserProg2>
|
|
||||||
<UserProg1Name>cmd /c if not exist ..\OutputBin mkdir ..\OutputBin</UserProg1Name>
|
|
||||||
<UserProg2Name>C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin --output ..\OutputBin\STM32F103rb_App1.bin ..\Object\STM32F103rb_App1.axf</UserProg2Name>
|
|
||||||
<UserProg1Dos16Mode>0</UserProg1Dos16Mode>
|
|
||||||
<UserProg2Dos16Mode>0</UserProg2Dos16Mode>
|
|
||||||
<nStopA1X>0</nStopA1X>
|
|
||||||
<nStopA2X>0</nStopA2X>
|
|
||||||
</AfterMake>
|
|
||||||
<SelectedForBatchBuild>0</SelectedForBatchBuild>
|
|
||||||
<SVCSIdString></SVCSIdString>
|
|
||||||
</TargetCommonOption>
|
|
||||||
<CommonProperty>
|
|
||||||
<UseCPPCompiler>0</UseCPPCompiler>
|
|
||||||
<RVCTCodeConst>0</RVCTCodeConst>
|
|
||||||
<RVCTZI>0</RVCTZI>
|
|
||||||
<RVCTOtherData>0</RVCTOtherData>
|
|
||||||
<ModuleSelection>0</ModuleSelection>
|
|
||||||
<IncludeInBuild>1</IncludeInBuild>
|
|
||||||
<AlwaysBuild>0</AlwaysBuild>
|
|
||||||
<GenerateAssemblyFile>0</GenerateAssemblyFile>
|
|
||||||
<AssembleAssemblyFile>0</AssembleAssemblyFile>
|
|
||||||
<PublicsOnly>0</PublicsOnly>
|
|
||||||
<StopOnExitCode>3</StopOnExitCode>
|
|
||||||
<CustomArgument></CustomArgument>
|
|
||||||
<IncludeLibraryModules></IncludeLibraryModules>
|
|
||||||
<ComprImg>0</ComprImg>
|
|
||||||
</CommonProperty>
|
|
||||||
<DllOption>
|
|
||||||
<SimDllName>SARMCM3.DLL</SimDllName>
|
|
||||||
<SimDllArguments> -REMAP</SimDllArguments>
|
|
||||||
<SimDlgDll>DCM.DLL</SimDlgDll>
|
|
||||||
<SimDlgDllArguments>-pCM3</SimDlgDllArguments>
|
|
||||||
<TargetDllName>SARMCM3.DLL</TargetDllName>
|
|
||||||
<TargetDllArguments></TargetDllArguments>
|
|
||||||
<TargetDlgDll>TCM.DLL</TargetDlgDll>
|
|
||||||
<TargetDlgDllArguments>-pCM3</TargetDlgDllArguments>
|
|
||||||
</DllOption>
|
|
||||||
<DebugOption>
|
|
||||||
<OPTHX>
|
|
||||||
<HexSelection>1</HexSelection>
|
|
||||||
<HexRangeLowAddress>0</HexRangeLowAddress>
|
|
||||||
<HexRangeHighAddress>0</HexRangeHighAddress>
|
|
||||||
<HexOffset>0</HexOffset>
|
|
||||||
<Oh166RecLen>16</Oh166RecLen>
|
|
||||||
</OPTHX>
|
|
||||||
<Simulator>
|
|
||||||
<UseSimulator>0</UseSimulator>
|
|
||||||
<LoadApplicationAtStartup>1</LoadApplicationAtStartup>
|
|
||||||
<RunToMain>1</RunToMain>
|
|
||||||
<RestoreBreakpoints>1</RestoreBreakpoints>
|
|
||||||
<RestoreWatchpoints>1</RestoreWatchpoints>
|
|
||||||
<RestoreMemoryDisplay>1</RestoreMemoryDisplay>
|
|
||||||
<RestoreFunctions>1</RestoreFunctions>
|
|
||||||
<RestoreToolbox>1</RestoreToolbox>
|
|
||||||
<LimitSpeedToRealTime>0</LimitSpeedToRealTime>
|
|
||||||
<RestoreSysVw>1</RestoreSysVw>
|
|
||||||
</Simulator>
|
|
||||||
<Target>
|
|
||||||
<UseTarget>1</UseTarget>
|
|
||||||
<LoadApplicationAtStartup>1</LoadApplicationAtStartup>
|
|
||||||
<RunToMain>1</RunToMain>
|
|
||||||
<RestoreBreakpoints>1</RestoreBreakpoints>
|
|
||||||
<RestoreWatchpoints>1</RestoreWatchpoints>
|
|
||||||
<RestoreMemoryDisplay>1</RestoreMemoryDisplay>
|
|
||||||
<RestoreFunctions>1</RestoreFunctions>
|
|
||||||
<RestoreToolbox>1</RestoreToolbox>
|
|
||||||
<RestoreTracepoints>1</RestoreTracepoints>
|
|
||||||
<RestoreSysVw>1</RestoreSysVw>
|
|
||||||
</Target>
|
|
||||||
<RunDebugAfterBuild>0</RunDebugAfterBuild>
|
|
||||||
<TargetSelection>6</TargetSelection>
|
|
||||||
<SimDlls>
|
|
||||||
<CpuDll></CpuDll>
|
|
||||||
<CpuDllArguments></CpuDllArguments>
|
|
||||||
<PeripheralDll></PeripheralDll>
|
|
||||||
<PeripheralDllArguments></PeripheralDllArguments>
|
|
||||||
<InitializationFile></InitializationFile>
|
|
||||||
</SimDlls>
|
|
||||||
<TargetDlls>
|
|
||||||
<CpuDll></CpuDll>
|
|
||||||
<CpuDllArguments></CpuDllArguments>
|
|
||||||
<PeripheralDll></PeripheralDll>
|
|
||||||
<PeripheralDllArguments></PeripheralDllArguments>
|
|
||||||
<InitializationFile></InitializationFile>
|
|
||||||
<Driver>Segger\JL2CM3.dll</Driver>
|
|
||||||
</TargetDlls>
|
|
||||||
</DebugOption>
|
|
||||||
<Utilities>
|
|
||||||
<Flash1>
|
|
||||||
<UseTargetDll>1</UseTargetDll>
|
|
||||||
<UseExternalTool>0</UseExternalTool>
|
|
||||||
<RunIndependent>0</RunIndependent>
|
|
||||||
<UpdateFlashBeforeDebugging>1</UpdateFlashBeforeDebugging>
|
|
||||||
<Capability>1</Capability>
|
|
||||||
<DriverSelection>4096</DriverSelection>
|
|
||||||
</Flash1>
|
|
||||||
<bUseTDR>1</bUseTDR>
|
|
||||||
<Flash2>BIN\UL2CM3.DLL</Flash2>
|
|
||||||
<Flash3>"" ()</Flash3>
|
|
||||||
<Flash4></Flash4>
|
|
||||||
<pFcarmOut></pFcarmOut>
|
|
||||||
<pFcarmGrp></pFcarmGrp>
|
|
||||||
<pFcArmRoot></pFcArmRoot>
|
|
||||||
<FcArmLst>0</FcArmLst>
|
|
||||||
</Utilities>
|
|
||||||
<TargetArmAds>
|
|
||||||
<ArmAdsMisc>
|
|
||||||
<GenerateListings>0</GenerateListings>
|
|
||||||
<asHll>1</asHll>
|
|
||||||
<asAsm>1</asAsm>
|
|
||||||
<asMacX>1</asMacX>
|
|
||||||
<asSyms>1</asSyms>
|
|
||||||
<asFals>1</asFals>
|
|
||||||
<asDbgD>1</asDbgD>
|
|
||||||
<asForm>1</asForm>
|
|
||||||
<ldLst>0</ldLst>
|
|
||||||
<ldmm>1</ldmm>
|
|
||||||
<ldXref>1</ldXref>
|
|
||||||
<BigEnd>0</BigEnd>
|
|
||||||
<AdsALst>1</AdsALst>
|
|
||||||
<AdsACrf>1</AdsACrf>
|
|
||||||
<AdsANop>0</AdsANop>
|
|
||||||
<AdsANot>0</AdsANot>
|
|
||||||
<AdsLLst>1</AdsLLst>
|
|
||||||
<AdsLmap>1</AdsLmap>
|
|
||||||
<AdsLcgr>1</AdsLcgr>
|
|
||||||
<AdsLsym>1</AdsLsym>
|
|
||||||
<AdsLszi>1</AdsLszi>
|
|
||||||
<AdsLtoi>1</AdsLtoi>
|
|
||||||
<AdsLsun>1</AdsLsun>
|
|
||||||
<AdsLven>1</AdsLven>
|
|
||||||
<AdsLsxf>1</AdsLsxf>
|
|
||||||
<RvctClst>0</RvctClst>
|
|
||||||
<GenPPlst>0</GenPPlst>
|
|
||||||
<AdsCpuType>"Cortex-M3"</AdsCpuType>
|
|
||||||
<RvctDeviceName></RvctDeviceName>
|
|
||||||
<mOS>0</mOS>
|
|
||||||
<uocRom>0</uocRom>
|
|
||||||
<uocRam>0</uocRam>
|
|
||||||
<hadIROM>1</hadIROM>
|
|
||||||
<hadIRAM>1</hadIRAM>
|
|
||||||
<hadXRAM>0</hadXRAM>
|
|
||||||
<uocXRam>0</uocXRam>
|
|
||||||
<RvdsVP>0</RvdsVP>
|
|
||||||
<hadIRAM2>0</hadIRAM2>
|
|
||||||
<hadIROM2>0</hadIROM2>
|
|
||||||
<StupSel>8</StupSel>
|
|
||||||
<useUlib>0</useUlib>
|
|
||||||
<EndSel>0</EndSel>
|
|
||||||
<uLtcg>0</uLtcg>
|
|
||||||
<RoSelD>3</RoSelD>
|
|
||||||
<RwSelD>3</RwSelD>
|
|
||||||
<CodeSel>0</CodeSel>
|
|
||||||
<OptFeed>0</OptFeed>
|
|
||||||
<NoZi1>0</NoZi1>
|
|
||||||
<NoZi2>0</NoZi2>
|
|
||||||
<NoZi3>0</NoZi3>
|
|
||||||
<NoZi4>0</NoZi4>
|
|
||||||
<NoZi5>0</NoZi5>
|
|
||||||
<Ro1Chk>0</Ro1Chk>
|
|
||||||
<Ro2Chk>0</Ro2Chk>
|
|
||||||
<Ro3Chk>0</Ro3Chk>
|
|
||||||
<Ir1Chk>1</Ir1Chk>
|
|
||||||
<Ir2Chk>0</Ir2Chk>
|
|
||||||
<Ra1Chk>0</Ra1Chk>
|
|
||||||
<Ra2Chk>0</Ra2Chk>
|
|
||||||
<Ra3Chk>0</Ra3Chk>
|
|
||||||
<Im1Chk>1</Im1Chk>
|
|
||||||
<Im2Chk>0</Im2Chk>
|
|
||||||
<OnChipMemories>
|
|
||||||
<Ocm1>
|
|
||||||
<Type>0</Type>
|
|
||||||
<StartAddress>0x0</StartAddress>
|
|
||||||
<Size>0x0</Size>
|
|
||||||
</Ocm1>
|
|
||||||
<Ocm2>
|
|
||||||
<Type>0</Type>
|
|
||||||
<StartAddress>0x0</StartAddress>
|
|
||||||
<Size>0x0</Size>
|
|
||||||
</Ocm2>
|
|
||||||
<Ocm3>
|
|
||||||
<Type>0</Type>
|
|
||||||
<StartAddress>0x0</StartAddress>
|
|
||||||
<Size>0x0</Size>
|
|
||||||
</Ocm3>
|
|
||||||
<Ocm4>
|
|
||||||
<Type>0</Type>
|
|
||||||
<StartAddress>0x0</StartAddress>
|
|
||||||
<Size>0x0</Size>
|
|
||||||
</Ocm4>
|
|
||||||
<Ocm5>
|
|
||||||
<Type>0</Type>
|
|
||||||
<StartAddress>0x0</StartAddress>
|
|
||||||
<Size>0x0</Size>
|
|
||||||
</Ocm5>
|
|
||||||
<Ocm6>
|
|
||||||
<Type>0</Type>
|
|
||||||
<StartAddress>0x0</StartAddress>
|
|
||||||
<Size>0x0</Size>
|
|
||||||
</Ocm6>
|
|
||||||
<IRAM>
|
|
||||||
<Type>0</Type>
|
|
||||||
<StartAddress>0x20000000</StartAddress>
|
|
||||||
<Size>0xc000</Size>
|
|
||||||
</IRAM>
|
|
||||||
<IROM>
|
|
||||||
<Type>1</Type>
|
|
||||||
<StartAddress>0x8008000</StartAddress>
|
|
||||||
<Size>0x38000</Size>
|
|
||||||
</IROM>
|
|
||||||
<XRAM>
|
|
||||||
<Type>0</Type>
|
|
||||||
<StartAddress>0x0</StartAddress>
|
|
||||||
<Size>0x0</Size>
|
|
||||||
</XRAM>
|
|
||||||
<OCR_RVCT1>
|
|
||||||
<Type>1</Type>
|
|
||||||
<StartAddress>0x0</StartAddress>
|
|
||||||
<Size>0x0</Size>
|
|
||||||
</OCR_RVCT1>
|
|
||||||
<OCR_RVCT2>
|
|
||||||
<Type>1</Type>
|
|
||||||
<StartAddress>0x0</StartAddress>
|
|
||||||
<Size>0x0</Size>
|
|
||||||
</OCR_RVCT2>
|
|
||||||
<OCR_RVCT3>
|
|
||||||
<Type>1</Type>
|
|
||||||
<StartAddress>0x0</StartAddress>
|
|
||||||
<Size>0x0</Size>
|
|
||||||
</OCR_RVCT3>
|
|
||||||
<OCR_RVCT4>
|
|
||||||
<Type>1</Type>
|
|
||||||
<StartAddress>0x8008000</StartAddress>
|
|
||||||
<Size>0x1c000</Size>
|
|
||||||
</OCR_RVCT4>
|
|
||||||
<OCR_RVCT5>
|
|
||||||
<Type>1</Type>
|
|
||||||
<StartAddress>0x0</StartAddress>
|
|
||||||
<Size>0x0</Size>
|
|
||||||
</OCR_RVCT5>
|
|
||||||
<OCR_RVCT6>
|
|
||||||
<Type>0</Type>
|
|
||||||
<StartAddress>0x0</StartAddress>
|
|
||||||
<Size>0x0</Size>
|
|
||||||
</OCR_RVCT6>
|
|
||||||
<OCR_RVCT7>
|
|
||||||
<Type>0</Type>
|
|
||||||
<StartAddress>0x0</StartAddress>
|
|
||||||
<Size>0x0</Size>
|
|
||||||
</OCR_RVCT7>
|
|
||||||
<OCR_RVCT8>
|
|
||||||
<Type>0</Type>
|
|
||||||
<StartAddress>0x0</StartAddress>
|
|
||||||
<Size>0x0</Size>
|
|
||||||
</OCR_RVCT8>
|
|
||||||
<OCR_RVCT9>
|
|
||||||
<Type>0</Type>
|
|
||||||
<StartAddress>0x20000000</StartAddress>
|
|
||||||
<Size>0xc000</Size>
|
|
||||||
</OCR_RVCT9>
|
|
||||||
<OCR_RVCT10>
|
|
||||||
<Type>0</Type>
|
|
||||||
<StartAddress>0x0</StartAddress>
|
|
||||||
<Size>0x0</Size>
|
|
||||||
</OCR_RVCT10>
|
|
||||||
</OnChipMemories>
|
|
||||||
<RvctStartVector></RvctStartVector>
|
|
||||||
</ArmAdsMisc>
|
|
||||||
<Cads>
|
|
||||||
<interw>1</interw>
|
|
||||||
<Optim>4</Optim>
|
|
||||||
<oTime>0</oTime>
|
|
||||||
<SplitLS>0</SplitLS>
|
|
||||||
<OneElfS>1</OneElfS>
|
|
||||||
<Strict>0</Strict>
|
|
||||||
<EnumInt>0</EnumInt>
|
|
||||||
<PlainCh>0</PlainCh>
|
|
||||||
<Ropi>0</Ropi>
|
|
||||||
<Rwpi>0</Rwpi>
|
|
||||||
<wLevel>2</wLevel>
|
|
||||||
<uThumb>0</uThumb>
|
|
||||||
<uSurpInc>0</uSurpInc>
|
|
||||||
<uC99>1</uC99>
|
|
||||||
<useXO>0</useXO>
|
|
||||||
<v6Lang>1</v6Lang>
|
|
||||||
<v6LangP>1</v6LangP>
|
|
||||||
<VariousControls>
|
|
||||||
<MiscControls>--no_multibyte_chars</MiscControls>
|
|
||||||
<Define>USE_HAL_DRIVER,STM32F103xE</Define>
|
|
||||||
<Undefine></Undefine>
|
|
||||||
<IncludePath>../Inc;../Drivers/STM32F1xx_HAL_Driver/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy;../Drivers/CMSIS/Device/ST/STM32F1xx/Include;../Drivers/CMSIS/Include;..\m_app;..\HardWare\CAR;..\HardWare\RELAY;..\HardWare\LOG;..\HardWare\IIC;..\HardWare\24C02;..\HardWare\ADC;..\HardWare\LED;..\HardWare\SYSINIT;..\HardWare\UART;..\HardWare\W25Q64;..\HardWare\NETWORK;..\HardWare\WIFI;..\HardWare\MQTT;..\HardWare\FEED_SCALE;..\HardWare\USER_CMD;..\HardWare\HLW8032;..\HardWare\OLED;..\Middlewares\FreeRTOS\Source\include;..\Middlewares\FreeRTOS\Source\portable\RVDS\ARM_CM3</IncludePath>
|
|
||||||
</VariousControls>
|
|
||||||
</Cads>
|
|
||||||
<Aads>
|
|
||||||
<interw>1</interw>
|
|
||||||
<Ropi>0</Ropi>
|
|
||||||
<Rwpi>0</Rwpi>
|
|
||||||
<thumb>0</thumb>
|
|
||||||
<SplitLS>0</SplitLS>
|
|
||||||
<SwStkChk>0</SwStkChk>
|
|
||||||
<NoWarn>0</NoWarn>
|
|
||||||
<uSurpInc>0</uSurpInc>
|
|
||||||
<useXO>0</useXO>
|
|
||||||
<VariousControls>
|
|
||||||
<MiscControls></MiscControls>
|
|
||||||
<Define></Define>
|
|
||||||
<Undefine></Undefine>
|
|
||||||
<IncludePath></IncludePath>
|
|
||||||
</VariousControls>
|
|
||||||
</Aads>
|
|
||||||
<LDads>
|
|
||||||
<umfTarg>1</umfTarg>
|
|
||||||
<Ropi>0</Ropi>
|
|
||||||
<Rwpi>0</Rwpi>
|
|
||||||
<noStLib>0</noStLib>
|
|
||||||
<RepFail>1</RepFail>
|
|
||||||
<useFile>0</useFile>
|
|
||||||
<TextAddressRange>0x08005000</TextAddressRange>
|
|
||||||
<DataAddressRange>0x20000000</DataAddressRange>
|
|
||||||
<pXoBase></pXoBase>
|
|
||||||
<ScatterFile>STM32F103rb_App1\STM32F103rb_App1.sct</ScatterFile>
|
|
||||||
<IncludeLibs></IncludeLibs>
|
|
||||||
<IncludeLibsPath></IncludeLibsPath>
|
|
||||||
<Misc></Misc>
|
|
||||||
<LinkerInputFile></LinkerInputFile>
|
|
||||||
<DisabledWarnings></DisabledWarnings>
|
|
||||||
</LDads>
|
|
||||||
</TargetArmAds>
|
|
||||||
</TargetOption>
|
|
||||||
<Groups>
|
|
||||||
<Group>
|
|
||||||
<GroupName>Application/MDK-ARM</GroupName>
|
|
||||||
<Files>
|
|
||||||
<File>
|
|
||||||
<FileName>startup_stm32f10x_hd.s</FileName>
|
|
||||||
<FileType>2</FileType>
|
|
||||||
<FilePath>.\RTE\Device\STM32F103RC\startup_stm32f10x_hd.s</FilePath>
|
|
||||||
</File>
|
|
||||||
</Files>
|
|
||||||
</Group>
|
|
||||||
<Group>
|
|
||||||
<GroupName>Application/User</GroupName>
|
|
||||||
<Files>
|
|
||||||
<File>
|
|
||||||
<FileName>main.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Src/main.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_it.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Src/stm32f1xx_it.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal_msp.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Src/stm32f1xx_hal_msp.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal_timebase_tim.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Src/stm32f1xx_hal_timebase_tim.c</FilePath>
|
|
||||||
</File>
|
|
||||||
</Files>
|
|
||||||
</Group>
|
|
||||||
<Group>
|
|
||||||
<GroupName>Middlewares/FreeRTOS</GroupName>
|
|
||||||
<Files>
|
|
||||||
<File>
|
|
||||||
<FileName>tasks.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Middlewares/FreeRTOS/Source/tasks.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>queue.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Middlewares/FreeRTOS/Source/queue.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>list.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Middlewares/FreeRTOS/Source/list.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>port.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Middlewares/FreeRTOS/Source/portable/RVDS/ARM_CM3/port.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>heap_4.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Middlewares/FreeRTOS/Source/portable/MemMang/heap_4.c</FilePath>
|
|
||||||
</File>
|
|
||||||
</Files>
|
|
||||||
</Group>
|
|
||||||
<Group>
|
|
||||||
<GroupName>Drivers/STM32F1xx_HAL_Driver</GroupName>
|
|
||||||
<Files>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal_gpio_ex.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio_ex.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal_tim.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal_tim_ex.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim_ex.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal_uart.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_uart.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal_rcc.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rcc.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal_rcc_ex.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rcc_ex.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal_gpio.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal_dma.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_dma.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal_cortex.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_cortex.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal_pwr.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_pwr.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal_flash.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_flash.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal_flash_ex.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_flash_ex.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal_exti.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_exti.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal_iwdg.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_iwdg.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>stm32f1xx_hal_adc.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_adc.c</FilePath>
|
|
||||||
</File>
|
|
||||||
</Files>
|
|
||||||
</Group>
|
|
||||||
<Group>
|
|
||||||
<GroupName>Drivers/CMSIS</GroupName>
|
|
||||||
<Files>
|
|
||||||
<File>
|
|
||||||
<FileName>system_stm32f1xx.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>../Src/system_stm32f1xx.c</FilePath>
|
|
||||||
</File>
|
|
||||||
</Files>
|
|
||||||
</Group>
|
|
||||||
<Group>
|
|
||||||
<GroupName>m_app</GroupName>
|
|
||||||
<Files>
|
|
||||||
<File>
|
|
||||||
<FileName>proto.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\m_app\proto.c</FilePath>
|
|
||||||
</File>
|
|
||||||
</Files>
|
|
||||||
</Group>
|
|
||||||
<Group>
|
|
||||||
<GroupName>HardWare</GroupName>
|
|
||||||
<Files>
|
|
||||||
<File>
|
|
||||||
<FileName>air780e.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\CAR\air780e.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>network.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\NETWORK\network.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>net_wifi.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\NETWORK\net_wifi.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>net_wifi_ota.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\NETWORK\net_wifi_ota.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>esp8266.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\WIFI\esp8266.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>mqtt_client.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\WIFI\mqtt_client.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>relay.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\RELAY\relay.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>log.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\LOG\log.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>log_cn.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\LOG\log_cn.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>24c02.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\24C02\24c02.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>iic.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\IIC\iic.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>adc.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\ADC\adc.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>led.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\LED\led.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>uart.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\UART\uart.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>feed_scale.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\FEED_SCALE\feed_scale.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>user_cmd.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\USER_CMD\user_cmd.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>ble_at.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\USER_CMD\ble_at.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>system.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\SYSINIT\system.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>reset_log.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\SYSINIT\reset_log.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>app_loop.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\SYSINIT\app_loop.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>w25q64.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\W25Q64\w25q64.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>hlw8032.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\HLW8032\hlw8032.c</FilePath>
|
|
||||||
</File>
|
|
||||||
<File>
|
|
||||||
<FileName>oled.c</FileName>
|
|
||||||
<FileType>1</FileType>
|
|
||||||
<FilePath>..\HardWare\OLED\oled.c</FilePath>
|
|
||||||
</File>
|
|
||||||
</Files>
|
|
||||||
</Group>
|
|
||||||
<Group>
|
|
||||||
<GroupName>Doc</GroupName>
|
|
||||||
<Files>
|
|
||||||
<File>
|
|
||||||
<FileName>README.md</FileName>
|
|
||||||
<FileType>5</FileType>
|
|
||||||
<FilePath>..\README.md</FilePath>
|
|
||||||
</File>
|
|
||||||
</Files>
|
|
||||||
</Group>
|
|
||||||
<Group>
|
|
||||||
<GroupName>::CMSIS</GroupName>
|
|
||||||
</Group>
|
|
||||||
</Groups>
|
|
||||||
</Target>
|
|
||||||
</Targets>
|
|
||||||
|
|
||||||
<RTE>
|
|
||||||
<apis/>
|
|
||||||
<components>
|
|
||||||
<component Cclass="CMSIS" Cgroup="CORE" Cvendor="ARM" Cversion="3.40.0" condition="CMSIS Core">
|
|
||||||
<package name="CMSIS" schemaVersion="1.3" url="http://www.keil.com/pack/" vendor="ARM" version="4.2.0"/>
|
|
||||||
<targetInfos>
|
|
||||||
<targetInfo name="STM32F103rb_App1"/>
|
|
||||||
</targetInfos>
|
|
||||||
</component>
|
|
||||||
</components>
|
|
||||||
<files>
|
|
||||||
<file attr="config" category="header" name="RTE_Driver\Config\RTE_Device.h">
|
|
||||||
<instance index="0" removed="1">RTE\Device\STM32F103RC\RTE_Device.h</instance>
|
|
||||||
<component Cclass="Device" Cgroup="Startup" Cvendor="Keil" Cversion="1.0.0" condition="STM32F1xx CMSIS Device"/>
|
|
||||||
<package name="STM32F1xx_DFP" schemaVersion="1.2" url="http://www.keil.com/pack/" vendor="Keil" version="1.0.5"/>
|
|
||||||
<targetInfos/>
|
|
||||||
</file>
|
|
||||||
<file attr="config" category="source" condition="STM32F1xx HD" name="Device\Source\ARM\startup_stm32f10x_hd.s">
|
|
||||||
<instance index="0" removed="1">RTE\Device\STM32F103RC\startup_stm32f10x_hd.s</instance>
|
|
||||||
<component Cclass="Device" Cgroup="Startup" Cvendor="Keil" Cversion="1.0.0" condition="STM32F1xx CMSIS Device"/>
|
|
||||||
<package name="STM32F1xx_DFP" schemaVersion="1.2" url="http://www.keil.com/pack/" vendor="Keil" version="1.0.5"/>
|
|
||||||
<targetInfos/>
|
|
||||||
</file>
|
|
||||||
<file attr="config" category="source" name="Device\Source\system_stm32f10x.c">
|
|
||||||
<instance index="0" removed="1">RTE\Device\STM32F103RC\system_stm32f10x.c</instance>
|
|
||||||
<component Cclass="Device" Cgroup="Startup" Cvendor="Keil" Cversion="1.0.0" condition="STM32F1xx CMSIS Device"/>
|
|
||||||
<package name="STM32F1xx_DFP" schemaVersion="1.2" url="http://www.keil.com/pack/" vendor="Keil" version="1.0.5"/>
|
|
||||||
<targetInfos/>
|
|
||||||
</file>
|
|
||||||
</files>
|
|
||||||
</RTE>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
|
|
@ -1,483 +0,0 @@
|
||||||
# 智能水产投料机 V1.5.0(FreeRTOS · 串口日志中文版)
|
|
||||||
|
|
||||||
> **本版本基于 FreeRTOS 实时操作系统**(V10.0.1,net/app/sys 三任务架构),
|
|
||||||
> 串口日志为中文输出(**GBK 编码**,串口工具/蓝牙 APP 字符集请选 GBK)。
|
|
||||||
|
|
||||||
## 工程概述
|
|
||||||
|
|
||||||
本工程基于 **STM32F103RCT6**(兼容 GD32F103RCT6)+ **STM32Cube HAL** + **FreeRTOS V10.0.1** + **Keil MDK**,实现设备的控制、联网与投喂。
|
|
||||||
|
|
||||||
- **BootLoader**:`STM32F103_BootLoader/`,负责 OTA 固件烧录、试运行计数与失败回滚、主电源门禁
|
|
||||||
- **应用固件**:`STM32F103_App1/`,当前运行版本 V1.5.0(FreeRTOS 版,全中文串口日志)
|
|
||||||
- **双网络通道**:4G(Air780E/EG) 优先,WiFi(ESP-01S) 兜底,见下文"双通道网络"
|
|
||||||
- **蓝牙调试**:HLK-B40 BLE 透传模块(USART1),上电自动配置名称/GATT UUID,见下文"蓝牙模块配置"
|
|
||||||
|
|
||||||
> 注意:Keil 工程必须带编译选项 `--no_multibyte_chars`(App1 与 BootLoader 均已配置),
|
|
||||||
> 使 ARMCC 不解读多字节字符,中文字符串(GBK/UTF-8 均可)字节原样透传输出。
|
|
||||||
> `missing closing quote` 编译错误。
|
|
||||||
|
|
||||||
## FreeRTOS 任务架构
|
|
||||||
|
|
||||||
| 任务 | 优先级 | 栈 | 职责 |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| net_task | 3 | 3KB | 网络状态机 `NET_process`:4G/WiFi 建链与切换、MQTT 收发、GPS 轮询、OTA 下载 |
|
|
||||||
| app_task | 2 | 2KB | 业务:继电器命令/反馈状态机、断电记忆恢复、投喂秤轮询与保护、状态上报、OTA 确认与健康检查 |
|
|
||||||
| sys_task | 1 | 1KB | 系统:LED、电量(1s)、蓝牙调试命令、Uptime 打印(联网后每 60s) |
|
|
||||||
| idle 钩子 | - | - | 喂 IWDG(任务死循环占 CPU → idle 饿死 → 复位,正是期望行为) |
|
|
||||||
|
|
||||||
关键设计:
|
|
||||||
|
|
||||||
- **HAL 时基 = TIM2**(1ms 中断,`Src/stm32f1xx_hal_timebase_tim.c`,注意 `HAL_TIM_PeriodElapsedCallback` 里必须 `HAL_IncTick()`),SysTick 归 FreeRTOS 当 RTOS tick;it.c 不再定义 SVC/PendSV/SysTick 三个 Handler(port.c 提供,经 FreeRTOSConfig.h 宏映射)
|
|
||||||
- **bg_delay 改造**:调度器运行中 = `vTaskDelay` 让出;调度器未启动(开机初始化)= HAL_Delay + 喂狗
|
|
||||||
- **互斥锁三把**(调度器启动前全部为空操作直通):
|
|
||||||
- `s_net_at_mutex`(network.c):net 任务长 AT 会话(建链/重连/OTA)持锁,其他任务 `NET_publish_*` 尝试锁、拿不到丢弃,防 AT 应答交织
|
|
||||||
- `s_storage_mutex`(app_loop.c `storage_lock`):EEPROM(软件I2C) 与 W25Q64(软件SPI) 跨任务互斥,递归锁
|
|
||||||
- `s_log_mutex`(log.c):日志输出不交织
|
|
||||||
- **内存预算**(48KB RAM):裸机存量 ZI ~36.3KB + FreeRTOS 堆 10KB(configTOTAL_HEAP_SIZE)≈ 46.3KB;Flash ~67KB/224KB 分区。中断优先级维持原值(UART/DMA 0~3 级,不调 RTOS API,低于 5 级 syscall 阈值无冲突)
|
|
||||||
|
|
||||||
## 产品型号配置
|
|
||||||
|
|
||||||
同一套板子适配两种产品,编译前在 `Inc/main.h` 顶部通过一个宏二选一:
|
|
||||||
|
|
||||||
```c
|
|
||||||
#define PRODUCT_WITH_FEED_SCALE 0
|
|
||||||
```
|
|
||||||
|
|
||||||
| 取值 | 产品 | 投喂行为 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `1` | 智能水产投料机(接 RS485 磅秤) | `feeding` 按 `onceWt` 定量自动投喂,带重量反增/超时保护;`sw1` 指令被忽略 |
|
|
||||||
| `0` | 基础四路继电器(不接磅秤) | `feeding` / `sw1` 直接开关投喂继电器 1,由平台侧自行计时 |
|
|
||||||
|
|
||||||
## 硬件接口
|
|
||||||
|
|
||||||
| 接口 | 用途 | 引脚 | 参数 |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| USART1 | 蓝牙串口调试 / 日志输出 | PA9/PA10 | 115200-8N1 |
|
|
||||||
| USART2 | RS485 喂料秤 | PA2/PA3 | 9600-8N1 |
|
|
||||||
| USART3 | 4G Air780E/EG MQTT+GNSS | PB10/PB11 | 921600-8N1(见下文波特率自适应) |
|
|
||||||
| UART4 | WiFi ESP-01S | PC10/PC11 | 115200→921600-8N1(见下文 WiFi 波特率同步) |
|
|
||||||
| UART5 | HLW8032 电能计量 | PD2(RX) | 4800-8E1,只收,芯片每 50ms 主动上报 |
|
|
||||||
| ESP_RST | WiFi 模块复位 | PA5 | 低电平 500ms 复位 |
|
|
||||||
| CAT_PWR | 4G 模块开机 | PC4 | 开漏输出,拉低=模块开机(上电即拉低) |
|
|
||||||
| ESP_EN | WiFi 模块使能 | PA6 | 推挽输出,高电平=使能;**默认关闭,4G 运行时拉低断电省电,切 WiFi 才拉高** |
|
|
||||||
| BT_MODE | 蓝牙 AT/透传切换 | PB0 | 开漏输出,拉低 1s 进 AT 模式(ble_at.c 控制) |
|
|
||||||
| BT_RST | 蓝牙模块复位 | PA7 | 推挽输出,低电平=工作(默认),高电平=复位 |
|
|
||||||
| OLED 屏幕 | 128x64 + GB2312 字库 IC | CLK=PC12 MOSI=PC9 DC=PC8 CS1=PC7 FSO=PC6 CS2=PB15 | GPIO 模拟 SPI,显示电压/电流/功率/电能/继电器/网络 |
|
|
||||||
| Relay1 | 投喂电机控制 | PB7 | 高电平吸合 |
|
|
||||||
| Relay2~4 | 备用控制 | PB8/PC0/PC2 | 高电平吸合 |
|
|
||||||
| I2C(软件) | AT24C02 配置存储 | PA11/PA12 | 页写入 |
|
|
||||||
| SPI(软件) | W25Q64 外部 Flash | PB3/PB4/PA15/PB5 | OTA 固件暂存 |
|
|
||||||
| ADC1 | 电池电压检测 | PA0 | 低电量上报 `pow:0` |
|
|
||||||
| PA8 | 手动模式检测 | PA8 | 高电平=手动模式 |
|
|
||||||
| PA1 | NET_LED 联网指示 | PA1 | 低电平点亮,见下表 |
|
|
||||||
| PC13 | LED2 心跳 / TPL5010 喂狗 | PC13 | 每 1s 翻转(见下文看门狗) |
|
|
||||||
|
|
||||||
### 指示灯含义
|
|
||||||
|
|
||||||
| 灯 | 状态 | 含义 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| NET_LED(PA1) | 100ms 快闪 | SmartConfig 智能配网中,请用 ESP-Touch APP 广播密码 |
|
|
||||||
| NET_LED(PA1) | 500ms 慢闪 | 联网中(4G/WiFi 建链、重连) |
|
|
||||||
| NET_LED(PA1) | 常亮 | 已联网(MQTT 就绪) |
|
|
||||||
| LED2(PC13) | 1s 心跳闪烁 | 系统正常运行(兼任 TPL5010 外置看门狗喂狗) |
|
|
||||||
|
|
||||||
## 看门狗(双狗架构)
|
|
||||||
|
|
||||||
| 看门狗 | 超时 | 喂狗点 | 作用 |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| IWDG(片内独立看门狗) | ~26s(LSI 漂移下最短 ~17s) | APP 由 FreeRTOS **idle 钩子**喂养(`vApplicationIdleHook`);BL 的擦除/拷贝循环 | 固件卡死 → 复位;BL 启动,OTA 试运行期计一次试错 |
|
|
||||||
| TPL5010(外置硬件狗) | 7 分钟 | PC13(LED2) 电平翻转:APP `activeEvents()` 每秒翻转;BL 进入时及长循环中翻转 | MCU 完全失控(含 IWDG 失效)时的最后兜底 |
|
|
||||||
|
|
||||||
注意:LED2 同时承担运行指示和外部狗喂狗,**修改 LED2 闪烁逻辑时必须保持周期性翻转**,否则 7 分钟后整板复位。
|
|
||||||
|
|
||||||
## 重启记录与断电保护
|
|
||||||
|
|
||||||
### 重启记录(reset_log.c)
|
|
||||||
|
|
||||||
- 开机时("固件版本"日志后)打印最近 **10 条**重启记录:`重启原因 + 上次运行时长`
|
|
||||||
- **存储**:W25Q64 `0x100000` 一个 4KB 扇区,10 条 32B 记录环形覆盖(序号+原因码+运行秒数+CRC16),写满一圈擦一次,断电不丢
|
|
||||||
- **原因来源**:`RCC->CSR` 由 BootLoader 读入后放共享 RAM(`BOOT_SHARE`,地址 `0x2000BF00`,热复位保持)传给 APP;软件复位的细分原因由各复位点 `reset_log_mark()` 预写(被平台踢下线/心跳超时/4G无服务/无SIM/OTA升级/OTA健康超时/4G初始化失败/串口RESET/OTA/RECOVERY命令/4G模组自重启等)
|
|
||||||
- **运行时长**:每秒写入共享 RAM(热复位不丢),看门狗/软件复位后精确到秒
|
|
||||||
|
|
||||||
### 断电保护(法拉电容 + PA0 检测)
|
|
||||||
|
|
||||||
- APP:PA0 电量检测改为 **50ms 快轮询**(原 1s),<10%(=外部 12V 断开)立即 50ms 快速复核 2 次(检测+确认全程 ~150ms)→ **先发 `pow:0` 后打日志** → 写电能/断电记录 → 留 500ms 让模组把报文发完 → 复位。发送通道忙(如 GPS 轮询占锁)时不锁存、下个周期重试直到发出或电容耗尽。电量百分比做了 0~100% 限幅(电容放电时 3.3V 基准下跌会失真)
|
|
||||||
- BootLoader:跳转 APP 前读 PA0(寄存器直读 ADC),主电源未恢复(<约 11V)则每 500ms 检测原地等待,**不跳 APP**——防止单片机靠电容低压运行 APP 而外设没电的乱态;12V 恢复后正常放行
|
|
||||||
- 断电记录只被下次成功启动消费一次(`boot_seen` 位翻转标记),一次掉电 = 一条记录,BL 等待期间不产生记录
|
|
||||||
- BL 找不到有效 APP(如只刷了 BL):打印提示并每 3s 主动复位重试,刷入 APP 即恢复
|
|
||||||
|
|
||||||
## 电能计量(HLW8032)
|
|
||||||
|
|
||||||
只计量**累计消耗电量**,上报平台标识符 `energy`(单位 kWh,两位小数)。
|
|
||||||
|
|
||||||
- **硬件**:HLW8032(5V 供电)经 B0505S 隔离电源 + 光耦隔离,芯片 TXD 接 PD2(UART5_RX);电压采样 ZMPT107-1(2mA:2mA,初级 220V 串 100K,次级并 49.9Ω),电流采样 CT 100A/100mA(1000:1,输出并 2Ω),三个模拟脚各对地 33nF
|
|
||||||
- **通信**:4800bps 8E1(F1 上按 9 位帧长 + 偶校验配置),芯片每 50ms 主动上报 24 字节帧,只收不发;帧同步 = 首字节 0x55/0xAA/0xFx + 第二字节 0x5A + 校验和([2]~[22] 相加低 8 位)
|
|
||||||
- **电量算法**(手册公式):`kWh = 总脉冲 × 功率参数寄存器 × 电压系数 × 电流系数 / (10^9×3600)`;PF 寄存器 16 位溢出由数据更新寄存器 bit7 翻转计数(`总脉冲 = k×65536 + PF`),50ms 一帧不可能漏翻转;芯片断电重启(计数值倒退)自动重新基准
|
|
||||||
- **系数宏**:`main.h` 的 `HLW_U_COEF`(=100K/49.9/1000=2.004)、`HLW_I_COEF`(=1/(0.002×1000)=0.5)——改电阻/互感器只动这两个宏
|
|
||||||
- **防噪声假同步**:要求连续 3 帧且间隔 20~150ms 才认定在线(PD2 悬空时电磁干扰偶发也能凑出校验和正确的假帧,但不可能连续卡在 50ms 节奏上)
|
|
||||||
- **滤波与零漂**:电压/电流/功率经 EMA 滑动平均(系数 1/8,约 0.4s 响应)压空载跳变;电流再减零点偏移 `HLW_I_ZERO_A`(0.02A) 钳位到 0;功率死区 `HLW_P_DEAD_W`(2W) 以下显示 0、超出再减 `HLW_P_ZERO_W`(0.5W)——宏都在 main.h
|
|
||||||
- **持久化**:累计脉冲数(uint64)存 EEPROM 偏移 164~171;每攒约 0.1kWh 写一次(限制擦写),所有计划内复位(`reset_log_mark` 覆盖的复位点)复位前写一次,断电预警(法拉电容续航内)再写一次——除突然死机外基本不丢电量
|
|
||||||
- **上报策略**:无论功率多少,每 3 分钟上报一次 `{"energy":%.2f,"power":%.1f}`;继电器开关切换随状态大报文携带 energy。串口电能日志固定 10s 一条。间隔宏 `ENERGY_REPORT_INTERVAL_MS` 在 app_loop.c
|
|
||||||
- **调试日志**:芯片在线时每 10s 打印一条(`电能: x.xxkWh 电压: xxx.xV 电流: x.xxA 功率: xxxx.xW`),与上报节奏无关
|
|
||||||
|
|
||||||
## OLED 屏幕显示(128x64 + GB2312 字库 IC)
|
|
||||||
|
|
||||||
- 移植自《485模拟磅秤数据带CRC》的 oledisp 驱动,GPIO 模拟 SPI(屏幕 CS1 + 字库 CS2 两路片选,字库数据从 FSO 脚读回)
|
|
||||||
- 字符串直接写中文(工程源文件即 GBK 编码,字库 IC 按 GB2312 地址换算取字模)
|
|
||||||
- **已联网界面**(sys_task 每 1s 刷新):第 0 行 网络 + 运行时间(`WiFi 00:02:35`,天:时:分),第 1 行 电压/电流,第 2 行 功率/电能(计量芯片离线时显示"计量芯片离线"),第 3 行 继电器状态中文(`开关:开开关关`)
|
|
||||||
- **未联网界面**:只显示联网进度,不显示电能/开关——4G 模式显示"正在等待4G联网"+ 最近完成阶段(模块复位成功/AT指令成功/SIM卡检测成功/附着网络成功/连接服务器中/无SIM卡,状态由 `g_net_ui_phase` 从 air780e.c 各阶段更新);WiFi 固定热点显示"WiFi固定模式 正在搜索热点"+倒计时,SmartConfig 显示"智能配网 请打开APP广播"+倒计时(倒计时由 esp_wait_countdown 写入 `g_net_ui_countdown`)
|
|
||||||
- 开机欢迎页按产品模式显示标题(带秤=`智能水产投料机`,无秤=`继电器控制终端`)+ `V1.5.0 RTOS`;4G 阻塞建链期间由 bg_delay 钩子每 500ms 刷进度页(调度器启动前 sys_task 未运行,曾一直卡在欢迎页)
|
|
||||||
|
|
||||||
## 双通道网络(4G 优先,WiFi 兜底)
|
|
||||||
|
|
||||||
网络层 `HardWare/NETWORK/network.c` 对业务层屏蔽底层模组差异,业务只调 `NET_*` 接口;4G 后端(`CAR/air780e.c`,模组内置 MQTT)与 WiFi 后端(`NETWORK/net_wifi.c`,TCP 透传 + 软件 MQTT)同构,运行时互斥切换。
|
|
||||||
|
|
||||||
### 通道切换状态机
|
|
||||||
|
|
||||||
EEPROM(偏移 130)记忆**上次成功联网方式**(`4G` / `WiFi固定热点` / `WiFi配网热点`),仅方式变化时写一次;开机优先跑上次成功的方式:
|
|
||||||
|
|
||||||
```text
|
|
||||||
上次 4G 成功:4G(无 SIM/无信号每 10s 重试,3 次失败)→ WiFi → 回 4G…循环
|
|
||||||
上次 WiFi 成功:WiFi 一轮建链失败 → 4G(同上重试)→ 回 WiFi…循环
|
|
||||||
|
|
||||||
WiFi 建链子顺序(net_wifi_init,阻塞式,等待期间 vTaskDelay 让出 CPU):
|
|
||||||
① 上次是配网热点:AT+CIPSTATUS 查已存 AP 自动连接(10s,STATUS:2=已拿到IP)
|
|
||||||
② AT+CWJAP 连代码里固定的 SSID/PASS(esp8266.h 的 WIFI_SSID/WIFI_PASS,60s 倒计时)
|
|
||||||
③ ESP-Touch SmartConfig 智能配网(60s 倒计时,配到的热点由模块保存)
|
|
||||||
全部失败 → 回 4G
|
|
||||||
WiFi 运行:30s MQTT PINGREQ 心跳,连续 2 帧无应答(约 60s)判死;TCP 断开(CLOSED)/心跳超时 → 打印原因并直接整机复位重连
|
|
||||||
(CLOSED=服务器主动断开/被平台踢下线;ping 超时=服务器无响应/假在线,最坏检测延迟 ~30s)
|
|
||||||
```
|
|
||||||
|
|
||||||
> 注:已存 AP 的判断用 `AT+CIPSTATUS` 主动查询而非被动等 `WIFI GOT IP` 打印——
|
|
||||||
> 后者在波特率同步阶段就会被清缓冲擦掉,永远等不到。
|
|
||||||
|
|
||||||
### WiFi 通道实现要点
|
|
||||||
|
|
||||||
- **TCP 透传 + 软件 MQTT**:ESP-01S 用 `AT+CIPMODE=1` + `AT+CIPSEND` 进透传,MQTT 3.1.1 协议栈在 MCU 软件实现(`HardWare/WIFI/mqtt_client.c`),不用 AT+MQTT
|
|
||||||
- **WiFi 波特率同步**:ESP-01S 出厂/RESTORE 后 115200,初始化时探测并用 `AT+UART_DEF=921600` 固化(掉电保存),MCU 侧 `UART4_SetBaudRate()` 同步;失败自动回滚维持 115200
|
|
||||||
- **接收缓冲共享**:WiFi 与 4G 互斥运行,共用 `Rx_Buf`/`U2_CopyBuff`(`stm32f1xx_it.c` 两路 IDLE 均带激活后端守卫),RAM 增量 <2KB
|
|
||||||
- **AT 应答严格匹配**:发送后才清接收缓冲(防上一条应答迟到尾巴造成"假成功")、二进制安全匹配、收到 `ERROR`/`FAIL` 立即失败;进透传前先 `AT+CIPCLOSE` 清残留连接、`CIPMUX=0` 先于 `CIPMODE=1`
|
|
||||||
- **ORE 自愈**:921600 高波特率下 UART 溢出会让 HAL 中止 DMA 接收,`HAL_UART_ErrorCallback` 里按当前激活通道立即重新武装;DMA 填满(连续流无 IDLE 间隙)时在 `HAL_UART_RxCpltCallback` 里搬运并重启,防接收停摆
|
|
||||||
- **WiFi 通道 OTA**:支持,透传 TCP 上软件实现 HTTP Range 分块下载,见下文"OTA 升级"章节
|
|
||||||
|
|
||||||
## 4G 波特率自适应(115200 → 921600)
|
|
||||||
|
|
||||||
- Air780E 出厂/自适应模式只覆盖 9600~115200;921600 必须用 `AT+IPR` 固定。
|
|
||||||
- APP 联网时**永远先按 115200 探测**(避免向自适应侦测窗口发送其他波特率的乱码):
|
|
||||||
- 通 → 发 `AT+IPR=921600` + `AT&W` 固化,切换到 921600(日志出现 `baud -> 921600`);
|
|
||||||
- 不通(模块已固化 921600)→ 直接切 921600 连接。
|
|
||||||
- 切换由 `USART3_SetBaudRate()` 运行中重写 BRR 完成,不影响 GPIO/DMA/NVIC。
|
|
||||||
|
|
||||||
## 4G 信号检测(AT+CSQ)
|
|
||||||
|
|
||||||
- 网络附着(CGATT)成功后查询一次 `AT+CSQ`,rssi(0~31) 转百分比串口输出:`> 4G: signal 77% (rssi=24)`
|
|
||||||
- 无信号(rssi=99 或 <2):置 `g_no_signal_detected`,与无 SIM 走同一套重试/回退 WiFi 逻辑
|
|
||||||
- 信号弱(<20%):告警但继续联网
|
|
||||||
|
|
||||||
## GPS 定位上报(仅 4G 通道,Air780EG)
|
|
||||||
|
|
||||||
**开关**:运行时可切(与产品模式同机制)——EEPROM(173) 存 0/1,空白时用 `Inc/main.h` 的 `PRODUCT_WITH_GPS` 宏做默认值;蓝牙命令 `GPS` 查询、`GPS0` 关 / `GPS1` 开,切换后自动重启生效。关闭后不进行任何 GPS 相关 AT 交互(纯 Air780E 用)。
|
|
||||||
|
|
||||||
参考工程《air780EG-V1.1加入北斗GPS数据》移植实现(`air780e.c` 的 `air780e_gps_poll()`)。
|
|
||||||
|
|
||||||
### 工作流程
|
|
||||||
|
|
||||||
1. **GNSS 初始化**:4G 拨号(`AT+SAPBR=1,1`)成功后执行 `AT+CGNSPWR=1`(开 GNSS 电源)+ `AT+CGNSAID=31,1,1,1`(A-GPS 辅助定位)。非 EG 版本模组回 ERROR 时仅告警并关闭轮询(`s_gnss_present=0`),不影响联网
|
|
||||||
2. **周期查询**:联网 60 秒后首次查询(`GPS_FIRST_DELAY_MS`,留搜星时间),之后每 5 分钟一次(`GPS_REPORT_INTERVAL_MS`)`AT+CGNSINF`
|
|
||||||
3. **解析**:`+CGNSINF: run,fix,UTC,lat,lon,...`,手工按逗号切分(不依赖 sscanf scanset)。**定位无效(fix≠1)跳过本次上报**(室内常见),不发空串
|
|
||||||
4. **坐标转换**:WGS-84 → GCJ-02(见下节)
|
|
||||||
5. **上报**:模组内置 MQTT `AT+MPUB`,topic `/iot/data/up/<ClientID>`
|
|
||||||
|
|
||||||
### 上报格式
|
|
||||||
|
|
||||||
云平台字段标识符 **`GPS`**,**经度在前、纬度在后**(高德/GeoJSON 习惯):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{"header":"iot.prop.post","body":{"GPS":"115.924092,28.679470"}}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 坐标系转换(WGS-84 → GCJ-02)
|
|
||||||
|
|
||||||
GPS 模块输出 WGS-84 原始坐标,**直接输入高德地图会有 100~600m 加密偏移**(国内地图强制使用 GCJ-02"火星坐标系")。固件在上报前用公开标准算法完成转换(`wgs84_to_gcj02()`,三角函数近似,中国境外自动跳过),上报的坐标可直接输入高德地图/坐标拾取器。
|
|
||||||
|
|
||||||
> 注意:若改用百度地图,需要再转一层 GCJ-02 → BD-09。
|
|
||||||
|
|
||||||
### 保护机制
|
|
||||||
|
|
||||||
- OTA 下载期间(`g_net_tx_busy`)禁止 GPS 查询插队,避免污染 HTTP 数据流
|
|
||||||
- 查询等待期间有平台下行命令(`+MSUB:`)插队时主动让行,不吞继电器指令
|
|
||||||
|
|
||||||
### 使用注意
|
|
||||||
|
|
||||||
- GPS 天线需能看到天空:室内基本无法定位(`> GPS: no fix, skip` 属正常),窗边/阳台通常可定,室外最佳;冷启动首次定位约 1~3 分钟
|
|
||||||
- 验证:串口日志 `> GPS: 115.924092,28.679470`,或直接看平台 `GPS` 字段
|
|
||||||
|
|
||||||
## OTA 升级
|
|
||||||
|
|
||||||
### 流程
|
|
||||||
|
|
||||||
```text
|
|
||||||
平台下发 iot.ota.upgrade.post(url, crc16, ver)
|
|
||||||
→ APP: 模块硬复位 → HTTP HEAD 拿大小 → 擦 W25Q64 槽 B
|
|
||||||
→ 3072 字节/块分段下载(AT+HTTPPARA BREAK/BREAKEND + HTTPACTION + HTTPREAD)
|
|
||||||
→ 每块写 W25Q64 并回读校验,全程累计 CRC16
|
|
||||||
→ 整包 CRC + 栈顶/复位向量校验 → 可靠上报 result.post(等 OK + 重试)
|
|
||||||
→ 写 OTA 信息区(状态 DOWNLOADED) → 复位进 BootLoader
|
|
||||||
BootLoader:
|
|
||||||
→ 擦除备份槽 → 备份当前 APP1 到槽 A(失败则中止更新,留在旧版本)
|
|
||||||
→ 擦 APP1 → 写入新固件 → CRC + 向量校验 → 状态 INSTALLED → 跳转
|
|
||||||
新 APP 试运行:
|
|
||||||
→ MQTT 就绪 + 首次发布成功 + 3s → 上报 confirm.post
|
|
||||||
→ 确认标志写 W25Q64(回读验证 + 3 次重试)→ 版本号存 EEPROM
|
|
||||||
→ 下次启动 BL 清理 OTA 标志,升级完成
|
|
||||||
```
|
|
||||||
|
|
||||||
### Flash 分区
|
|
||||||
|
|
||||||
内部 Flash(256KB):
|
|
||||||
|
|
||||||
| 区域 | 地址 | 大小 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| BootLoader | `0x08000000` | 32KB |
|
|
||||||
| APP1 运行区 | `0x08008000` | 224KB |
|
|
||||||
|
|
||||||
W25Q64 外部 Flash(8MB):
|
|
||||||
|
|
||||||
| 区域 | 地址 | 用途 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 槽 A | `0x000000` (256KB) | 当前固件备份(回滚镜像) |
|
|
||||||
| 槽 B | `0x040000` (256KB) | 新固件下载暂存 |
|
|
||||||
| 信息区 A/B | `0x080000` / `0x081000` (各 4KB) | OTA 状态双副本(magic + 序号 + checksum) |
|
|
||||||
|
|
||||||
### OTA 确认通知可靠性
|
|
||||||
|
|
||||||
- 确认通知(`iot.ota.confirm.post`)延后到首次发布成功后 **9 秒**发送,避开开机上报风暴(pow/ICCID/状态/电能挤占模组 MQTT 发送窗口)
|
|
||||||
- 本地确认标志和版本号**先于**通知落盘——通知发送失败不会导致 BL 误判回滚
|
|
||||||
- 通知未送达时每 30s 自动重试直到成功(仅补平台通知);开机 pow/ICCID 上报间隔拉开到 300ms
|
|
||||||
|
|
||||||
### 健康检查与回退
|
|
||||||
|
|
||||||
新固件"健康" = 4G 附着 → MQTT 连接 → 首次发布成功。回退判据(**3 次真实故障**才回退):
|
|
||||||
|
|
||||||
- **看门狗复位**(IWDG/WWDG,固件真的卡死)→ BL 计一次试错(读 `RCC->CSR` 判别);
|
|
||||||
- **健康超时**(MQTT 就绪 1 分钟仍无首次发布)→ APP 复位前自行计一次;
|
|
||||||
- **上电/按键/普通软件复位不计次**——现场断电、断网不会误触发回退;
|
|
||||||
- 试运行期间完全断网 → 不计次、不回退,持续等待网络恢复。
|
|
||||||
|
|
||||||
### 掉电/断网安全性
|
|
||||||
|
|
||||||
| 故障点 | 结果 |
|
|
||||||
| --- | --- |
|
|
||||||
| 下载中断网/掉电 | 信息区未更新,旧固件照常运行,平台可重推 |
|
|
||||||
| BL 备份阶段掉电 | `backup_valid=0`,重启后重新备份 |
|
|
||||||
| BL 擦写 APP1 中途掉电 | 状态 `COPYING` 视为 `DOWNLOADED`,重启后完整重做(备份不会被坏镜像覆盖) |
|
|
||||||
| 备份创建失败 | **中止更新**,留在旧版本,下次重启重试 |
|
|
||||||
| 回退过程掉电 | 重启后重新执行回退拷贝 |
|
|
||||||
|
|
||||||
### 其他要点
|
|
||||||
|
|
||||||
- **双通道均支持 OTA**:4G 用模组内置 HTTP(`AT+HTTP*` 分块);WiFi 通道由 MCU 在透传 TCP 上软件实现 HTTP **Range 分块下载**(`NETWORK/net_wifi_ota.c`):
|
|
||||||
- 流程:收 OTA 指令 → 停投喂 → `+++` 退透传(MQTT 连接被顶替,单连接限制)→ 连固件服务器 → 每 3072B 一个 `GET`(`Range: bytes=off-end`,MCU 自定节奏,防止服务器一次灌爆接收缓冲)→ 每块写 W25Q64 槽 B + 回读校验 + 累计 CRC16 → 整包 CRC + 固件头校验 → 重连 MQTT 上报 result → 写信息区复位进 BL
|
|
||||||
- 下载期间 MQTT 断开属正常,进度只在开始/结束上报;失败自动重连 MQTT 恢复业务
|
|
||||||
- OTA 触发时先强制停止投喂(断开继电器 1)
|
|
||||||
- 下载期间其余任务(秤轮询/LED/电量/蓝牙命令)由 RTOS 调度照常并发运行,业务状态上报暂时屏蔽
|
|
||||||
- 分段接收按 `+HTTPREAD: <len>` 声明长度收齐即处理,无静默等待;40KB 固件全程约 38 秒(含模块复位重连 ~15s)
|
|
||||||
|
|
||||||
## 主要功能
|
|
||||||
|
|
||||||
1. **双通道 MQTT 联网**:4G(Air780E/EG) 优先 + WiFi(ESP-01S) 兜底自动切换,见上文"双通道网络"
|
|
||||||
2. **远程控制**:平台下发开关 `sw1/sw2/sw3/sw4`、`feeding` 指令(投料机模式下 `sw1` 被忽略,投喂只能走 `feeding`)
|
|
||||||
3. **自动投喂**(`PRODUCT_WITH_FEED_SCALE = 1`):
|
|
||||||
- 平台设置单次投喂量 `onceWt`
|
|
||||||
- 平台下发 `feeding=true` 启动投喂
|
|
||||||
- 继电器 1 吸合,RS485 秤实时读取重量
|
|
||||||
- 当重量减少 ≥ `onceWt` 时自动停止,带重量反增与 10 分钟超时保护
|
|
||||||
4. **去皮/标定**:平台设置 `zero` 空载重量,设置后按最新原始重量重新计算剩余料重并立即上报
|
|
||||||
5. **GPS 定位上报**:见上文专题(仅 4G 通道)
|
|
||||||
6. **4G 信号检测**:见上文专题
|
|
||||||
7. **OTA 升级**:见上文专题(4G/WiFi 双通道均支持)
|
|
||||||
8. **蓝牙调试**:USART1 支持 `RESET/INFO/STATUS/R1ON~R4OFF/ALLON/ALLOFF/OTA/RECOVERY/HELP` 等命令
|
|
||||||
9. **掉电安全(断电记忆)**:联网成功(MQTT 首次就绪)后按 EEPROM 恢复继电器上次状态,随后全量上报让平台与电路一致
|
|
||||||
- 无秤模式(`PRODUCT_WITH_FEED_SCALE = 0`):**四路全部恢复**(含 SW1)
|
|
||||||
- 投料机模式(`PRODUCT_WITH_FEED_SCALE = 1`):**继电器 1 不恢复且上电强制断开**(防止误投料浪费饲料),继电器 2~4 恢复
|
|
||||||
|
|
||||||
## EEPROM 布局(AT24C02,256 字节)
|
|
||||||
|
|
||||||
上电由 `eepromReadInfo()` 读取并按 Magic 校验;配置损坏/未初始化时回退到 `main.h` 的编译期默认值并重新写入。
|
|
||||||
|
|
||||||
| 偏移 | 大小 | 内容 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 0~19 | 20B | `ClientID` MQTT 客户端 ID |
|
|
||||||
| 20~39 | 20B | `Username` |
|
|
||||||
| 40~59 | 20B | `Passward` |
|
|
||||||
| 60~79 | 20B | `ServerIP` |
|
|
||||||
| 80~99 | 20B | `Topic`(下行主题前缀 `/iot/data/down/`) |
|
|
||||||
| 100~101 | 2B | `ServerPort` |
|
|
||||||
| 102~108 | 7B | `Relay_State[7]`:[1..4] 四路继电器掉电记忆状态,[0] 保留,[5][6] 未用 |
|
|
||||||
| 112~115 | 4B | `Magic`(0xDEADBEEF,检测 EEPROM 是否已初始化;109~111 为对齐填充) |
|
|
||||||
| 116~117 | 2B | `Ver` OTA 固件版本号(确认后落盘,断电保持) |
|
|
||||||
| 120~121 | 2B | 秤参数:去皮/空载重量 `zero`(0.1KG 单位,仅称重模式) |
|
|
||||||
| 122~123 | 2B | 秤参数:单次投喂量 `onceWt`(0.1KG 单位,仅称重模式) |
|
|
||||||
| 124 | 1B | 秤参数初始化标志 |
|
|
||||||
| 130 | 1B | 上次成功联网方式:0=4G,1=WiFi固定热点,2=WiFi配网热点(仅变化时写) |
|
|
||||||
| 132~157 | 26B | 蓝牙名称配置缓存(命中则开机跳过蓝牙 AT 设置流程) |
|
|
||||||
| 160 | 1B | 日志等级(0~5:TRACE/DEBUG/INFO/WARN/ERROR/FATAL) |
|
|
||||||
| 164~171 | 8B | HLW8032 累计脉冲数(uint64,每攒 0.1kWh + 断电前写入) |
|
|
||||||
| 172 | 1B | 产品模式:0=四路继电器(无秤),1=称重投料(带秤),空白时用 main.h 宏默认值(蓝牙 SCALE0/SCALE1 切换) |
|
|
||||||
| 173 | 1B | GPS 定位开关:0=关,1=开,空白时用 main.h 宏默认值(蓝牙 GPS0/GPS1 切换,重启生效) |
|
|
||||||
| 175~194 | 20B | OTA 平台任务 id(下载开始时写入,重启确认补发 result.post 成功后清 0xFF) |
|
|
||||||
| 250 | 1B | 探测字节(上电写 0xA5 回读,验证 I2C 通信) |
|
|
||||||
|
|
||||||
> 注意:改 `MqttInfo_Str` 结构体会改变上述偏移,秤参数(120+)和联网方式(130)在
|
|
||||||
> `feed_scale.c` / `network.c` 里是独立宏定义,需同步避让。
|
|
||||||
|
|
||||||
## 平台数据字段
|
|
||||||
|
|
||||||
| 字段 | 含义 | 说明 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `ver` | 固件版本号 | 每次状态上报携带,平台可据此识别设备是否发生回退 |
|
|
||||||
| `weight` | 剩余料重(净重) | 与平台物模型一致 |
|
|
||||||
| `zero` | 空载重量(去皮) | 与平台物模型一致 |
|
|
||||||
| `onceWt` | 单次投喂量 | 每次投喂目标重量;**无秤模式不上报、下发设置被拒绝** |
|
|
||||||
| `sw1` / `feeding` | 第一路继电器状态 | 1=投喂中,0=停止;无秤模式下两字段**同时上报**,平台两个开关同步刷新 |
|
|
||||||
| `sw2~sw4` | 备用继电器状态 | 0/1 |
|
|
||||||
| `pow` | 电量状态 | 1=正常,0=低电量 |
|
|
||||||
| `energy` | 累计消耗电量 | HLW8032 计量,单位 kWh,两位小数(>10W 每 30s,否则首次+开关切换) |
|
|
||||||
| `power` | 实时功率 | HLW8032 计量,单位 W,一位小数,与 energy 同一条报文上报(策略同 energy) |
|
|
||||||
| `iccid` | SIM 卡号 | 仅 4G 通道,MQTT 连上后随 pow 上报一次,用于物联网卡套餐查询缴费 |
|
|
||||||
| `fwver` | 代码版本字符串 | 格式 `FIRMWARE_VERSION_STR`+`:`+EEPROM 版本序号(如 "1.5.0:15"),字符串类型,每次重启联网后上报一次(与状态大报文中的整数 `ver` OTA 版本号区分开) |
|
|
||||||
| `GPS` | 定位坐标 | 仅 4G 通道(Air780EG),GCJ-02 高德坐标系,格式 `"经度,纬度"`,每 5 分钟上报 |
|
|
||||||
|
|
||||||
> 注:`zero`(剩余料重)与 `onceWt` 为称重模式专用字段——`PRODUCT_WITH_FEED_SCALE=0` 时状态上报不携带这两个字段,平台下发设置会收到 `no scale on this device` 拒绝应答。
|
|
||||||
|
|
||||||
下行报文由 `m_app/proto.c` 按键名定位解析(`"sw2"` 不会误中 `"sw21"`,其他字段里的 true/false 不会造成误判)。
|
|
||||||
|
|
||||||
## 产品模式(带秤/无秤,运行时可切)
|
|
||||||
|
|
||||||
`PRODUCT_WITH_FEED_SCALE` 从纯编译宏改为**运行时可切换**:模式存 EEPROM(172),空白时用 main.h 宏做默认值。蓝牙命令:`SCALE` 查询,`SCALE0`=四路继电器(无秤),`SCALE1`=称重投料(带秤),**切换后自动重启生效**(初始化路径不同,必须重启)。影响范围:状态上报字段(zero/onceWt)、sw1/feeding 指令语义、继电器 1 断电记忆策略、秤轮询开关。
|
|
||||||
|
|
||||||
## 串口调试命令
|
|
||||||
|
|
||||||
通过 USART1(蓝牙串口)发送:
|
|
||||||
|
|
||||||
```text
|
|
||||||
RESET - 系统复位
|
|
||||||
INFO - 打印 MQTT 配置
|
|
||||||
STATUS - 打印继电器状态
|
|
||||||
R1ON - 打开继电器 1
|
|
||||||
R1OFF - 关闭继电器 1
|
|
||||||
R2ON/R2OFF ~ R4ON/R4OFF - 控制继电器 2~4
|
|
||||||
ALLON - 打开所有继电器
|
|
||||||
ALLOFF - 关闭所有继电器
|
|
||||||
OTA - 复位进入 BootLoader 检查升级
|
|
||||||
RECOVERY - WiFi 模块 AT+RESTORE 恢复出厂(忘掉已存热点)+ 整机复位,用于测试 SmartConfig
|
|
||||||
BLESET - 强制重新设置蓝牙名称/GATT UUID(清 EEPROM 缓存后重跑配置流程)
|
|
||||||
LOG - 查询当前日志等级
|
|
||||||
LOGTRACE/LOGDEBUG/LOGINFO/LOGWARN/LOGERROR/LOGFATAL
|
|
||||||
- 设置日志等级(写入 EEPROM,断电不丢)
|
|
||||||
HELP - 显示支持命令
|
|
||||||
```
|
|
||||||
|
|
||||||
SmartConfig 测试流程:关掉固定热点 → 发 `RECOVERY` → 设备复位走完 4G 重试进入 WiFi → 固定热点(60s) 失败后自动进 SmartConfig(60s 倒计时)→ 用 ESP-Touch APP 广播密码;配网成功后下次开机按记忆优先 `AT+CIPSTATUS` 秒连已存热点(无需再配)。
|
|
||||||
|
|
||||||
## 蓝牙模块配置(HLK-B40)
|
|
||||||
|
|
||||||
- **接线**:模块 UART0_TXD/RXD 接 USART1(PA10/PA9),PC5(功能键)接 **PB0**(开漏输出)
|
|
||||||
- **上电自动配置**(`ble_at.c`):拉低 PC5 1s 进 AT 模式 → 设 `AT+NAME=<BLE_NAME>` → 设 GATT UUID(默认 `FFE0` 服务 + `FFE1` 收发共用特征,JDY-31/HM-10 同款,通用 BLE 串口 APP 可连)→ `AT+REBOOT` 生效
|
|
||||||
- **发射功率可调**:`main.h` 的 `BLE_TX_POWER` 宏(AT+RFPOWER,1~18 级,出厂默认 8)——多台设备同场所时调小(如 1~3)缩短搜索距离避免认错;缓存同时记录功率值,改宏后下次开机自动重设一次
|
|
||||||
- **零窗口设计**:配置成功后将名称缓存进 EEPROM(偏移 132),之后每次开机命中缓存就**完全跳过 AT 流程**——蓝牙 APP 不再断联,开机最早的重启原因/重启记录日志都能实时看到;只有代码里改了 `BLE_NAME`(main.h)才会自动重设一次
|
|
||||||
- **手动重设**:蓝牙命令 `BLESET`(清缓存强制重跑)
|
|
||||||
- **日志等级**:`LOG` 查询,`LOGTRACE~LOGFATAL` 六档切换,写 EEPROM(偏移 160)断电保持。档位即"输出阈值"——只打印该级及以上:
|
|
||||||
|
|
||||||
| 命令 | 级别 | 输出内容 | 适用场景 |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `LOGTRACE` | 0 最细 | 全部(当前≈DEBUG) | 底层逐字节排查 |
|
|
||||||
| `LOGDEBUG` | 1 调试(默认) | AT 指令交互、连接细节、正常运行信息 | 现场排查 |
|
|
||||||
| `LOGINFO` | 2 信息 | 联网成功、继电器动作、重启记录、警告/错误 | 日常观察 |
|
|
||||||
| `LOGWARN` | 3 警告 | 仅警告/错误(AT 超时、无 SIM、掉线复位) | 稳定运行免打扰 |
|
|
||||||
| `LOGERROR` | 4 错误 | 仅错误(踢下线、心跳超时、CRC 失败) | 只看故障 |
|
|
||||||
| `LOGFATAL` | 5 致命 | 全部静音 | 极端安静 |
|
|
||||||
|
|
||||||
- USART1 接收中断带 ORE 自愈(`HAL_UART_ErrorCallback` 自动恢复接收,蓝牙模块重启乱码不会把命令通道打死)
|
|
||||||
|
|
||||||
## 代码分层(APP)
|
|
||||||
|
|
||||||
`main.c` 只保留初始化和调度器启动,逻辑全部下沉到任务与模块:
|
|
||||||
|
|
||||||
```c
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
SCB->VTOR = FLASH_BASE | Application_1_Addr;
|
|
||||||
Board_Init(); // 基础硬件:HAL/时钟/GPIO/DMA/UART/看门狗(SYSINIT/system.c)
|
|
||||||
app_init(); // 业务模块初始化(SYSINIT/app_loop.c)
|
|
||||||
app_rtos_start(); // 创建 net/app/sys 任务 + 启动调度器,不再返回
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 目录结构
|
|
||||||
|
|
||||||
```text
|
|
||||||
STM32F103_App1/
|
|
||||||
├── Src/
|
|
||||||
│ ├── main.c # 仅 VTOR + Board_Init/app_init/app_rtos_start
|
|
||||||
│ └── stm32f1xx_hal_timebase_tim.c # TIM2 作 HAL 时基(SysTick 归 FreeRTOS)
|
|
||||||
├── Inc/
|
|
||||||
│ ├── main.h # 产品型号宏 PRODUCT_WITH_FEED_SCALE、MQTT 默认配置
|
|
||||||
│ └── FreeRTOSConfig.h # RTOS 配置(堆10KB/钩子/中断阈值,含内存预算注释)
|
|
||||||
├── Middlewares/
|
|
||||||
│ └── FreeRTOS/Source/ # FreeRTOS V10.0.1 内核(tasks/queue/list + ARM_CM3 port + heap_4)
|
|
||||||
├── m_app/
|
|
||||||
│ ├── proto.c/h # 平台 JSON 报文取值工具(按键名定位)
|
|
||||||
│ ├── ota_app.h # Flash 分区定义(BootLoader 32K + APP1 224K)
|
|
||||||
│ └── boot_share.h # BL↔APP 共享 RAM 结构(复位原因/运行时长/原因码)
|
|
||||||
├── HardWare/
|
|
||||||
│ ├── USER_CMD/ # 蓝牙串口命令处理
|
|
||||||
│ ├── FEED_SCALE/ # RS485 喂料秤与定量投喂状态机
|
|
||||||
│ ├── RELAY/ # 继电器控制、反馈检测、平台指令执行、断电记忆恢复
|
|
||||||
│ ├── NETWORK/ # 网络层:network.c 通道切换/统一分发 + net_wifi.c WiFi 后端 + net_wifi_ota.c WiFi OTA
|
|
||||||
│ ├── WIFI/ # esp8266.c ESP-01S AT驱动/透传/SmartConfig + mqtt_client.c 软件MQTT
|
|
||||||
│ ├── CAR/ # Air780E/EG AT 驱动、波特率自适应、CSQ 信号检测、GNSS 定位、OTA 下载
|
|
||||||
│ ├── UART/ # USART1/2/3+UART4/5 初始化、运行中切波特率、中断/DMA 接收、ORE 自愈
|
|
||||||
│ ├── HLW8032/ # 电能计量(UART5 帧解析、PF 脉冲累计、kWh 换算、EEPROM 持久化)
|
|
||||||
│ ├── 24C02/ # EEPROM 读写(页写入,带跨任务互斥锁)
|
|
||||||
│ ├── IIC/ # 软件 I2C(带总线恢复:SDA 被拉死时补 9 时钟释放)
|
|
||||||
│ ├── ADC/ # 电池电压检测(0~100% 限幅、掉电去抖判定)
|
|
||||||
│ ├── LED/ # 指示灯(LED2 兼任 TPL5010 喂狗)
|
|
||||||
│ ├── LOG/ # 分级日志(USART1 输出,带互斥锁;log_cn.c 中文格式串)
|
|
||||||
│ ├── SYSINIT/ # Board_Init/IWDG、app_init、RTOS 任务、reset_log 重启记录
|
|
||||||
│ └── W25Q64/ # 外部 Flash 驱动与 OTA 双槽信息区(带跨任务互斥锁)
|
|
||||||
└── MDK-ARM/ # Keil 工程文件
|
|
||||||
|
|
||||||
STM32F103_BootLoader/
|
|
||||||
├── Src/main.c # 硬件初始化 + IWDG + 入口
|
|
||||||
├── m_app/ota_boot.c # OTA 状态机:备份/烧录/回退 + 复位原因透传 + 主电源门禁 + 无APP重试
|
|
||||||
├── m_app/boot_share.h # 与 APP 共享的 RAM 结构定义(同一份拷贝)
|
|
||||||
├── HardWare/W25Q64/ # 外部 Flash 驱动(与 APP 共用设计)
|
|
||||||
├── HardWare/LOG/ # 分级日志(与 APP 相同)
|
|
||||||
└── MDK-ARM/ # Keil 工程文件
|
|
||||||
```
|
|
||||||
|
|
||||||
## 编译烧录
|
|
||||||
|
|
||||||
**Keil GUI**:
|
|
||||||
|
|
||||||
1. 打开 `MDK-ARM/STM32F103rb_App1.uvprojx`(BootLoader 工程同理)
|
|
||||||
2. 按产品形态设置 `Inc/main.h` 中的 `PRODUCT_WITH_FEED_SCALE`
|
|
||||||
3. 编译下载(先烧 BootLoader 到 `0x08000000`,APP1 起始地址 `0x08008000`)
|
|
||||||
4. 首次启动会自动把默认 MQTT 配置写入 AT24C02
|
|
||||||
|
|
||||||
**命令行**(CI/脚本可用):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 编译(Keil 安装目录的 UV4,-r 全量 -b 增量,-o 输出日志)
|
|
||||||
/c/Keil_v5/UV4/UV4.exe -r -j0 STM32F103rb_App1.uvprojx -o build.txt
|
|
||||||
|
|
||||||
# 烧录(J-Link 命令脚本,工程 MDK-ARM 目录下有现成的 jlink_flash.jlink)
|
|
||||||
JLink.exe -CommanderScript jlink_flash.jlink
|
|
||||||
```
|
|
||||||
|
|
||||||
## 版本
|
|
||||||
|
|
||||||
- Version: 1.5.0
|
|
||||||
- Author: Helei
|
|
||||||
|
|
@ -1,407 +0,0 @@
|
||||||
/* USER CODE BEGIN Header */
|
|
||||||
/**
|
|
||||||
******************************************************************************
|
|
||||||
* File Name : stm32f1xx_hal_msp.c
|
|
||||||
* Description : This file provides code for the MSP Initialization
|
|
||||||
* and de-Initialization codes.
|
|
||||||
******************************************************************************
|
|
||||||
* @attention
|
|
||||||
*
|
|
||||||
* <h2><center>© Copyright (c) 2019 STMicroelectronics.
|
|
||||||
* All rights reserved.</center></h2>
|
|
||||||
*
|
|
||||||
* This software component is licensed by ST under BSD 3-Clause license,
|
|
||||||
* the "License"; You may not use this file except in compliance with the
|
|
||||||
* License. You may obtain a copy of the License at:
|
|
||||||
* opensource.org/licenses/BSD-3-Clause
|
|
||||||
*
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
|
||||||
/* USER CODE END Header */
|
|
||||||
|
|
||||||
/* Includes ------------------------------------------------------------------*/
|
|
||||||
#include "main.h"
|
|
||||||
/* USER CODE BEGIN Includes */
|
|
||||||
|
|
||||||
/* USER CODE END Includes */
|
|
||||||
extern DMA_HandleTypeDef hdma_usart3_rx;
|
|
||||||
extern DMA_HandleTypeDef hdma_uart4_rx;
|
|
||||||
extern DMA_HandleTypeDef hdma_usart2_rx;
|
|
||||||
|
|
||||||
/* Private typedef -----------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN TD */
|
|
||||||
|
|
||||||
/* USER CODE END TD */
|
|
||||||
|
|
||||||
/* Private define ------------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN Define */
|
|
||||||
|
|
||||||
/* USER CODE END Define */
|
|
||||||
|
|
||||||
/* Private macro -------------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN Macro */
|
|
||||||
|
|
||||||
/* USER CODE END Macro */
|
|
||||||
|
|
||||||
/* Private variables ---------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PV */
|
|
||||||
|
|
||||||
/* USER CODE END PV */
|
|
||||||
|
|
||||||
/* Private function prototypes -----------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PFP */
|
|
||||||
|
|
||||||
/* USER CODE END PFP */
|
|
||||||
|
|
||||||
/* External functions --------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN ExternalFunctions */
|
|
||||||
|
|
||||||
/* USER CODE END ExternalFunctions */
|
|
||||||
|
|
||||||
/* USER CODE BEGIN 0 */
|
|
||||||
|
|
||||||
/* USER CODE END 0 */
|
|
||||||
/**
|
|
||||||
* Initializes the Global MSP.
|
|
||||||
*/
|
|
||||||
void HAL_MspInit(void)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN MspInit 0 */
|
|
||||||
|
|
||||||
/* USER CODE END MspInit 0 */
|
|
||||||
|
|
||||||
__HAL_RCC_AFIO_CLK_ENABLE();
|
|
||||||
__HAL_RCC_PWR_CLK_ENABLE();
|
|
||||||
|
|
||||||
/* System interrupt init*/
|
|
||||||
|
|
||||||
/** NOJTAG: JTAG-DP Disabled and SW-DP Enabled
|
|
||||||
*/
|
|
||||||
__HAL_AFIO_REMAP_SWJ_NOJTAG();
|
|
||||||
|
|
||||||
/* USER CODE BEGIN MspInit 1 */
|
|
||||||
|
|
||||||
/* USER CODE END MspInit 1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief UART MSP Initialization
|
|
||||||
* This function configures the hardware resources used in this example
|
|
||||||
* @param huart: UART handle pointer
|
|
||||||
* @retval None
|
|
||||||
*/
|
|
||||||
void HAL_UART_MspInit(UART_HandleTypeDef* huart)
|
|
||||||
{
|
|
||||||
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
|
||||||
if(huart->Instance==USART1)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN USART1_MspInit 0 */
|
|
||||||
|
|
||||||
/* USER CODE END USART1_MspInit 0 */
|
|
||||||
/* Peripheral clock enable */
|
|
||||||
__HAL_RCC_USART1_CLK_ENABLE();
|
|
||||||
|
|
||||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
|
||||||
/**USART1 GPIO Configuration
|
|
||||||
PA9 ------> USART1_TX
|
|
||||||
PA10 ------> USART1_RX
|
|
||||||
*/
|
|
||||||
GPIO_InitStruct.Pin = GPIO_PIN_9;
|
|
||||||
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
|
||||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
|
|
||||||
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
GPIO_InitStruct.Pin = GPIO_PIN_10;
|
|
||||||
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
|
|
||||||
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
|
||||||
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
/* USART1 interrupt Init */
|
|
||||||
HAL_NVIC_SetPriority(USART1_IRQn, 2, 0);
|
|
||||||
HAL_NVIC_EnableIRQ(USART1_IRQn);
|
|
||||||
|
|
||||||
/* USER CODE BEGIN USART1_MspInit 1 */
|
|
||||||
|
|
||||||
/* USER CODE END USART1_MspInit 1 */
|
|
||||||
}
|
|
||||||
else if(huart->Instance==USART2)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN USART2_MspInit 0 */
|
|
||||||
|
|
||||||
/* USER CODE END USART2_MspInit 0 */
|
|
||||||
/* Peripheral clock enable */
|
|
||||||
__HAL_RCC_USART2_CLK_ENABLE();
|
|
||||||
__HAL_RCC_DMA1_CLK_ENABLE();
|
|
||||||
|
|
||||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
|
||||||
/**USART2 GPIO Configuration
|
|
||||||
PA2 ------> USART2_TX
|
|
||||||
PA3 ------> USART2_RX
|
|
||||||
*/
|
|
||||||
GPIO_InitStruct.Pin = GPIO_PIN_2;
|
|
||||||
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
|
||||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
|
|
||||||
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
GPIO_InitStruct.Pin = GPIO_PIN_3;
|
|
||||||
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
|
|
||||||
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
|
||||||
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
/* USART2_RX DMA Init (DMA1_Channel6) */
|
|
||||||
hdma_usart2_rx.Instance = DMA1_Channel6;
|
|
||||||
hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
|
||||||
hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
|
|
||||||
hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
|
|
||||||
hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
|
|
||||||
hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
|
|
||||||
hdma_usart2_rx.Init.Mode = DMA_NORMAL;
|
|
||||||
hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW;
|
|
||||||
if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
|
|
||||||
{
|
|
||||||
Error_Handler();
|
|
||||||
}
|
|
||||||
__HAL_LINKDMA(huart, hdmarx, hdma_usart2_rx);
|
|
||||||
|
|
||||||
HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 3, 0);
|
|
||||||
HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
|
|
||||||
|
|
||||||
/* USART2 interrupt Init */
|
|
||||||
HAL_NVIC_SetPriority(USART2_IRQn, 3, 0);
|
|
||||||
HAL_NVIC_EnableIRQ(USART2_IRQn);
|
|
||||||
/* USER CODE BEGIN USART2_MspInit 1 */
|
|
||||||
|
|
||||||
/* USER CODE END USART2_MspInit 1 */
|
|
||||||
}
|
|
||||||
else if(huart->Instance==USART3)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN USART3_MspInit 0 */
|
|
||||||
|
|
||||||
/* USER CODE END USART3_MspInit 0 */
|
|
||||||
/* Peripheral clock enable */
|
|
||||||
__HAL_RCC_USART3_CLK_ENABLE();
|
|
||||||
|
|
||||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
|
||||||
/**USART3 GPIO Configuration
|
|
||||||
PB10 ------> USART3_TX
|
|
||||||
PB11 ------> USART3_RX
|
|
||||||
*/
|
|
||||||
GPIO_InitStruct.Pin = GPIO_PIN_10;
|
|
||||||
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
|
||||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
|
|
||||||
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
GPIO_InitStruct.Pin = GPIO_PIN_11;
|
|
||||||
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
|
|
||||||
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
|
||||||
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
/* USART3 DMA Init */
|
|
||||||
/* USART3_RX Init */
|
|
||||||
hdma_usart3_rx.Instance = DMA1_Channel3;
|
|
||||||
hdma_usart3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
|
||||||
hdma_usart3_rx.Init.PeriphInc = DMA_PINC_DISABLE;
|
|
||||||
hdma_usart3_rx.Init.MemInc = DMA_MINC_ENABLE;
|
|
||||||
hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
|
|
||||||
hdma_usart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
|
|
||||||
hdma_usart3_rx.Init.Mode = DMA_NORMAL;
|
|
||||||
hdma_usart3_rx.Init.Priority = DMA_PRIORITY_LOW;
|
|
||||||
if (HAL_DMA_Init(&hdma_usart3_rx) != HAL_OK)
|
|
||||||
{
|
|
||||||
Error_Handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
__HAL_LINKDMA(huart,hdmarx,hdma_usart3_rx);
|
|
||||||
|
|
||||||
/* USART3 interrupt Init */
|
|
||||||
HAL_NVIC_SetPriority(USART3_IRQn, 1, 0);
|
|
||||||
HAL_NVIC_EnableIRQ(USART3_IRQn);
|
|
||||||
/* USER CODE BEGIN USART3_MspInit 1 */
|
|
||||||
|
|
||||||
/* USER CODE END USART3_MspInit 1 */
|
|
||||||
}
|
|
||||||
else if(huart->Instance==UART4)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN UART4_MspInit 0 */
|
|
||||||
|
|
||||||
/* USER CODE END UART4_MspInit 0 */
|
|
||||||
/* Peripheral clock enable */
|
|
||||||
__HAL_RCC_UART4_CLK_ENABLE();
|
|
||||||
|
|
||||||
__HAL_RCC_GPIOC_CLK_ENABLE();
|
|
||||||
/**UART4 GPIO Configuration
|
|
||||||
PC10 ------> UART4_TX (接 ESP-01S RXD)
|
|
||||||
PC11 ------> UART4_RX (接 ESP-01S TXD)
|
|
||||||
*/
|
|
||||||
GPIO_InitStruct.Pin = GPIO_PIN_10;
|
|
||||||
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
|
||||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
|
|
||||||
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
GPIO_InitStruct.Pin = GPIO_PIN_11;
|
|
||||||
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
|
|
||||||
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
|
||||||
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
/* UART4 DMA Init (DMA2_Channel3) */
|
|
||||||
hdma_uart4_rx.Instance = DMA2_Channel3;
|
|
||||||
hdma_uart4_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
|
||||||
hdma_uart4_rx.Init.PeriphInc = DMA_PINC_DISABLE;
|
|
||||||
hdma_uart4_rx.Init.MemInc = DMA_MINC_ENABLE;
|
|
||||||
hdma_uart4_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
|
|
||||||
hdma_uart4_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
|
|
||||||
hdma_uart4_rx.Init.Mode = DMA_NORMAL;
|
|
||||||
hdma_uart4_rx.Init.Priority = DMA_PRIORITY_LOW;
|
|
||||||
if (HAL_DMA_Init(&hdma_uart4_rx) != HAL_OK)
|
|
||||||
{
|
|
||||||
Error_Handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
__HAL_LINKDMA(huart,hdmarx,hdma_uart4_rx);
|
|
||||||
|
|
||||||
/* UART4 interrupt Init */
|
|
||||||
HAL_NVIC_SetPriority(UART4_IRQn, 1, 0);
|
|
||||||
HAL_NVIC_EnableIRQ(UART4_IRQn);
|
|
||||||
/* USER CODE BEGIN UART4_MspInit 1 */
|
|
||||||
|
|
||||||
/* USER CODE END UART4_MspInit 1 */
|
|
||||||
}
|
|
||||||
else if(huart->Instance==UART5)
|
|
||||||
{
|
|
||||||
/* Peripheral clock enable */
|
|
||||||
__HAL_RCC_UART5_CLK_ENABLE();
|
|
||||||
|
|
||||||
__HAL_RCC_GPIOD_CLK_ENABLE();
|
|
||||||
/**UART5 GPIO Configuration
|
|
||||||
PD2 ------> UART5_RX (接 HLW8032 TXD,光耦隔离;PC12=TX 芯片不用不配置)
|
|
||||||
*/
|
|
||||||
GPIO_InitStruct.Pin = GPIO_PIN_2;
|
|
||||||
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
|
|
||||||
GPIO_InitStruct.Pull = GPIO_PULLUP;
|
|
||||||
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
|
|
||||||
|
|
||||||
/* UART5 interrupt Init */
|
|
||||||
HAL_NVIC_SetPriority(UART5_IRQn, 3, 0);
|
|
||||||
HAL_NVIC_EnableIRQ(UART5_IRQn);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief UART MSP De-Initialization
|
|
||||||
* This function freeze the hardware resources used in this example
|
|
||||||
* @param huart: UART handle pointer
|
|
||||||
* @retval None
|
|
||||||
*/
|
|
||||||
void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)
|
|
||||||
{
|
|
||||||
if(huart->Instance==USART1)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN USART1_MspDeInit 0 */
|
|
||||||
|
|
||||||
/* USER CODE END USART1_MspDeInit 0 */
|
|
||||||
/* Peripheral clock disable */
|
|
||||||
__HAL_RCC_USART1_CLK_DISABLE();
|
|
||||||
|
|
||||||
/**USART1 GPIO Configuration
|
|
||||||
PA9 ------> USART1_TX
|
|
||||||
PA10 ------> USART1_RX
|
|
||||||
*/
|
|
||||||
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);
|
|
||||||
|
|
||||||
/* USER CODE BEGIN USART1_MspDeInit 1 */
|
|
||||||
|
|
||||||
/* USER CODE END USART1_MspDeInit 1 */
|
|
||||||
}
|
|
||||||
else if(huart->Instance==USART2)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN USART2_MspDeInit 0 */
|
|
||||||
|
|
||||||
/* USER CODE END USART2_MspDeInit 0 */
|
|
||||||
/* Peripheral clock disable */
|
|
||||||
__HAL_RCC_USART2_CLK_DISABLE();
|
|
||||||
|
|
||||||
/**USART2 GPIO Configuration
|
|
||||||
PA2 ------> USART2_TX
|
|
||||||
PA3 ------> USART2_RX
|
|
||||||
*/
|
|
||||||
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_2|GPIO_PIN_3);
|
|
||||||
|
|
||||||
/* USART2 interrupt DeInit */
|
|
||||||
HAL_NVIC_DisableIRQ(USART2_IRQn);
|
|
||||||
/* USER CODE BEGIN USART2_MspDeInit 1 */
|
|
||||||
|
|
||||||
/* USER CODE END USART2_MspDeInit 1 */
|
|
||||||
}
|
|
||||||
else if(huart->Instance==USART3)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN USART3_MspDeInit 0 */
|
|
||||||
|
|
||||||
/* USER CODE END USART3_MspDeInit 0 */
|
|
||||||
/* Peripheral clock disable */
|
|
||||||
__HAL_RCC_USART3_CLK_DISABLE();
|
|
||||||
|
|
||||||
/**USART3 GPIO Configuration
|
|
||||||
PB10 ------> USART3_TX
|
|
||||||
PB11 ------> USART3_RX
|
|
||||||
*/
|
|
||||||
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10|GPIO_PIN_11);
|
|
||||||
|
|
||||||
/* USART3 DMA DeInit */
|
|
||||||
HAL_DMA_DeInit(huart->hdmarx);
|
|
||||||
|
|
||||||
/* USART3 interrupt DeInit */
|
|
||||||
HAL_NVIC_DisableIRQ(USART3_IRQn);
|
|
||||||
/* USER CODE BEGIN USART3_MspDeInit 1 */
|
|
||||||
|
|
||||||
/* USER CODE END USART3_MspDeInit 1 */
|
|
||||||
}
|
|
||||||
else if(huart->Instance==UART4)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN UART4_MspDeInit 0 */
|
|
||||||
|
|
||||||
/* USER CODE END UART4_MspDeInit 0 */
|
|
||||||
/* Peripheral clock disable */
|
|
||||||
__HAL_RCC_UART4_CLK_DISABLE();
|
|
||||||
|
|
||||||
/**UART4 GPIO Configuration
|
|
||||||
PC10 ------> UART4_TX
|
|
||||||
PC11 ------> UART4_RX
|
|
||||||
*/
|
|
||||||
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_10|GPIO_PIN_11);
|
|
||||||
|
|
||||||
/* UART4 DMA DeInit */
|
|
||||||
HAL_DMA_DeInit(huart->hdmarx);
|
|
||||||
|
|
||||||
/* UART4 interrupt DeInit */
|
|
||||||
HAL_NVIC_DisableIRQ(UART4_IRQn);
|
|
||||||
/* USER CODE BEGIN UART4_MspDeInit 1 */
|
|
||||||
|
|
||||||
/* USER CODE END UART4_MspDeInit 1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* USER CODE BEGIN 1 */
|
|
||||||
|
|
||||||
/* ADC MSP Init: 使能ADC1时钟, 配置PA0为模拟输入 */
|
|
||||||
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
|
|
||||||
{
|
|
||||||
if (hadc->Instance == ADC1) {
|
|
||||||
__HAL_RCC_ADC1_CLK_ENABLE();
|
|
||||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
|
||||||
|
|
||||||
GPIO_InitTypeDef g = {0};
|
|
||||||
g.Pin = GPIO_PIN_0;
|
|
||||||
g.Mode = GPIO_MODE_ANALOG;
|
|
||||||
HAL_GPIO_Init(GPIOA, &g);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* USER CODE END 1 */
|
|
||||||
|
|
||||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
|
||||||
|
|
||||||
/* HAL ADC injected conversion callback (required by HAL, not used) */
|
|
||||||
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc) { (void)hadc; }
|
|
||||||
|
|
||||||
|
|
@ -1,357 +0,0 @@
|
||||||
/* USER CODE BEGIN Header */
|
|
||||||
/**
|
|
||||||
******************************************************************************
|
|
||||||
* @file stm32f1xx_it.c
|
|
||||||
* @brief Interrupt Service Routines.
|
|
||||||
******************************************************************************
|
|
||||||
* @attention
|
|
||||||
*
|
|
||||||
* <h2><center>© Copyright (c) 2019 STMicroelectronics.
|
|
||||||
* All rights reserved.</center></h2>
|
|
||||||
*
|
|
||||||
* This software component is licensed by ST under BSD 3-Clause license,
|
|
||||||
* the "License"; You may not use this file except in compliance with the
|
|
||||||
* License. You may obtain a copy of the License at:
|
|
||||||
* opensource.org/licenses/BSD-3-Clause
|
|
||||||
*
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
|
||||||
/* USER CODE END Header */
|
|
||||||
|
|
||||||
/* Includes ------------------------------------------------------------------*/
|
|
||||||
#include "main.h"
|
|
||||||
#include "stm32f1xx_it.h"
|
|
||||||
/* Private includes ----------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN Includes */
|
|
||||||
#include "string.h"
|
|
||||||
/* USER CODE END Includes */
|
|
||||||
|
|
||||||
/* Private typedef -----------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN TD */
|
|
||||||
|
|
||||||
/* USER CODE END TD */
|
|
||||||
|
|
||||||
/* Private define ------------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PD */
|
|
||||||
|
|
||||||
/* USER CODE END PD */
|
|
||||||
|
|
||||||
/* Private macro -------------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PM */
|
|
||||||
|
|
||||||
/* USER CODE END PM */
|
|
||||||
|
|
||||||
/* Private variables ---------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PV */
|
|
||||||
|
|
||||||
/* USER CODE END PV */
|
|
||||||
|
|
||||||
/* Private function prototypes -----------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PFP */
|
|
||||||
|
|
||||||
/* USER CODE END PFP */
|
|
||||||
|
|
||||||
/* Private user code ---------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN 0 */
|
|
||||||
|
|
||||||
/* USER CODE END 0 */
|
|
||||||
|
|
||||||
/* External variables --------------------------------------------------------*/
|
|
||||||
extern DMA_HandleTypeDef hdma_usart3_rx;
|
|
||||||
extern DMA_HandleTypeDef hdma_uart4_rx;
|
|
||||||
extern UART_HandleTypeDef huart3;
|
|
||||||
extern UART_HandleTypeDef huart4;
|
|
||||||
extern UART_HandleTypeDef huart2;
|
|
||||||
extern UART_HandleTypeDef huart1;
|
|
||||||
extern UART_HandleTypeDef huart5;
|
|
||||||
extern DMA_HandleTypeDef hdma_usart2_rx;
|
|
||||||
extern uint8_t uart2_dma_buf[64];
|
|
||||||
extern uint8_t uart2_frame_buf[64];
|
|
||||||
extern volatile uint16_t uart2_frame_len;
|
|
||||||
extern volatile uint8_t uart2_frame_ready;
|
|
||||||
/* USER CODE BEGIN EV */
|
|
||||||
#include "network.h"
|
|
||||||
/* USER CODE END EV */
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
/* Cortex-M3 Processor Interruption and Exception Handlers */
|
|
||||||
/******************************************************************************/
|
|
||||||
/**
|
|
||||||
* @brief This function handles Non maskable interrupt.
|
|
||||||
*/
|
|
||||||
void NMI_Handler(void)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END NonMaskableInt_IRQn 0 */
|
|
||||||
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END NonMaskableInt_IRQn 1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles Hard fault interrupt.
|
|
||||||
*/
|
|
||||||
void HardFault_Handler(void)
|
|
||||||
{
|
|
||||||
/* 硬件错误兜底:用轮询方式往 USART1 打一条消息(中断已不可靠),
|
|
||||||
* 之后停在这里由 IWDG 复位。串口看到 "HARD FAULT" 即说明发生过硬错误 */
|
|
||||||
{
|
|
||||||
static const char msg[] = "\r\n!!! HARD FAULT, wait IWDG reset !!!\r\n";
|
|
||||||
for (unsigned i = 0; i < sizeof(msg) - 1; i++) {
|
|
||||||
while ((USART1->SR & USART_SR_TXE) == 0) { }
|
|
||||||
USART1->DR = msg[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
|
|
||||||
/* USER CODE END W1_HardFault_IRQn 0 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles Memory management fault.
|
|
||||||
*/
|
|
||||||
void MemManage_Handler(void)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN MemoryManagement_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END MemoryManagement_IRQn 0 */
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
|
|
||||||
/* USER CODE END W1_MemoryManagement_IRQn 0 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles Prefetch fault, memory access fault.
|
|
||||||
*/
|
|
||||||
void BusFault_Handler(void)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN BusFault_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END BusFault_IRQn 0 */
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN W1_BusFault_IRQn 0 */
|
|
||||||
/* USER CODE END W1_BusFault_IRQn 0 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles Undefined instruction or illegal state.
|
|
||||||
*/
|
|
||||||
void UsageFault_Handler(void)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN UsageFault_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END UsageFault_IRQn 0 */
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN W1_UsageFault_IRQn 0 */
|
|
||||||
/* USER CODE END W1_UsageFault_IRQn 0 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles Debug monitor.
|
|
||||||
*/
|
|
||||||
void DebugMon_Handler(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SVC_Handler / PendSV_Handler / SysTick_Handler 已由 FreeRTOS port.c 提供
|
|
||||||
* (FreeRTOSConfig.h 里 vPortSVCHandler/xPortPendSVHandler/xPortSysTickHandler
|
|
||||||
* 宏映射到 CMSIS 标准名),此处不再定义,避免重复。
|
|
||||||
* HAL 时基改用 TIM2(SysTick 归 FreeRTOS 当 RTOS tick)。 */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles TIM2 global interrupt (HAL timebase).
|
|
||||||
*/
|
|
||||||
extern TIM_HandleTypeDef htim2;
|
|
||||||
void TIM2_IRQHandler(void)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN TIM2_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END TIM2_IRQn 0 */
|
|
||||||
HAL_TIM_IRQHandler(&htim2);
|
|
||||||
/* USER CODE BEGIN TIM2_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END TIM2_IRQn 1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
/* STM32F1xx Peripheral Interrupt Handlers */
|
|
||||||
/* Add here the Interrupt Handlers for the used peripherals. */
|
|
||||||
/* For the available peripheral interrupt handler names, */
|
|
||||||
/* please refer to the startup file (startup_stm32f1xx.s). */
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles USART1 global interrupt.
|
|
||||||
*/
|
|
||||||
void USART1_IRQHandler(void)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN USART1_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END USART1_IRQn 0 */
|
|
||||||
HAL_UART_IRQHandler(&huart1);
|
|
||||||
/* USER CODE BEGIN USART1_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END USART1_IRQn 1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles DMA1 channel3 global interrupt.
|
|
||||||
*/
|
|
||||||
void DMA1_Channel6_IRQHandler(void)
|
|
||||||
{
|
|
||||||
HAL_DMA_IRQHandler(&hdma_usart2_rx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DMA1_Channel3_IRQHandler(void)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN DMA1_Channel3_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END DMA1_Channel3_IRQn 0 */
|
|
||||||
HAL_DMA_IRQHandler(&hdma_usart3_rx);
|
|
||||||
/* USER CODE BEGIN DMA1_Channel3_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END DMA1_Channel3_IRQn 1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles DMA2 channel3 global interrupt (UART4_RX / WiFi).
|
|
||||||
*/
|
|
||||||
void DMA2_Channel3_IRQHandler(void)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN DMA2_Channel3_IRQn 0 */
|
|
||||||
|
|
||||||
/* USER CODE END DMA2_Channel3_IRQn 0 */
|
|
||||||
HAL_DMA_IRQHandler(&hdma_uart4_rx);
|
|
||||||
/* USER CODE BEGIN DMA2_Channel3_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END DMA2_Channel3_IRQn 1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles UART4 global interrupt (WiFi ESP-01S, DMA+IDLE).
|
|
||||||
*/
|
|
||||||
void UART4_IRQHandler(void)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN UART4_IRQn 0 */
|
|
||||||
uint32_t temp;
|
|
||||||
if ((__HAL_UART_GET_FLAG(&huart4, UART_FLAG_IDLE) != RESET))
|
|
||||||
{
|
|
||||||
/* 清除状态寄存器和数据寄存器 */
|
|
||||||
__HAL_UART_CLEAR_IDLEFLAG(&huart4);
|
|
||||||
|
|
||||||
/* 失效DMA接收 */
|
|
||||||
HAL_UART_DMAStop(&huart4);
|
|
||||||
|
|
||||||
/* 获取接收长度:总大小-剩余大小 */
|
|
||||||
temp = huart4.hdmarx->Instance->CNDTR;
|
|
||||||
Rx_Len = Rx_Max - temp;
|
|
||||||
|
|
||||||
/* 追加到 U2_CopyBuff 供 AT 指令/MQTT 报文解析;
|
|
||||||
* 与 USART3 复用同一缓冲区,仅 WiFi 后端激活时才追加,防止两路数据交织 */
|
|
||||||
if (Rx_Len > 0 && NET_GetActiveBackend() == NET_BACKEND_WIFI &&
|
|
||||||
(U2_CopyIndex + Rx_Len) < (U2_COPY_SIZE - 1))
|
|
||||||
{
|
|
||||||
memcpy(U2_CopyBuff + U2_CopyIndex, Rx_Buf, Rx_Len);
|
|
||||||
U2_CopyIndex += Rx_Len;
|
|
||||||
U2_CopyBuff[U2_CopyIndex] = '\0';
|
|
||||||
U2_CopyFlag = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 使能接收DMA传输 */
|
|
||||||
HAL_UART_Receive_DMA(&huart4, Rx_Buf, Rx_Max);
|
|
||||||
}
|
|
||||||
/* USER CODE END UART4_IRQn 0 */
|
|
||||||
HAL_UART_IRQHandler(&huart4);
|
|
||||||
/* USER CODE BEGIN UART4_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END UART4_IRQn 1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles USART2 global interrupt.
|
|
||||||
*/
|
|
||||||
void USART2_IRQHandler(void)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN USART2_IRQn 0 */
|
|
||||||
/* 485 秤帧定界:线路静默产生 IDLE,DMA 缓冲区里即一帧完整数据 */
|
|
||||||
if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) != RESET)
|
|
||||||
{
|
|
||||||
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
|
|
||||||
HAL_UART_DMAStop(&huart2);
|
|
||||||
uint16_t len = (uint16_t)(sizeof(uart2_dma_buf) - huart2.hdmarx->Instance->CNDTR);
|
|
||||||
if (len > 0 && !uart2_frame_ready) {
|
|
||||||
memcpy(uart2_frame_buf, uart2_dma_buf, len);
|
|
||||||
uart2_frame_len = len;
|
|
||||||
uart2_frame_ready = 1;
|
|
||||||
}
|
|
||||||
HAL_UART_Receive_DMA(&huart2, uart2_dma_buf, sizeof(uart2_dma_buf));
|
|
||||||
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
|
|
||||||
}
|
|
||||||
/* USER CODE END USART2_IRQn 0 */
|
|
||||||
HAL_UART_IRQHandler(&huart2);
|
|
||||||
/* USER CODE BEGIN USART2_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END USART2_IRQn 1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles USART3 global interrupt.
|
|
||||||
*/
|
|
||||||
void USART3_IRQHandler(void)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN USART3_IRQn 0 */
|
|
||||||
uint32_t temp;
|
|
||||||
if ((__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE) != RESET))
|
|
||||||
{
|
|
||||||
/* 清除状态寄存器和数据寄存器 */
|
|
||||||
__HAL_UART_CLEAR_IDLEFLAG(&huart3);
|
|
||||||
|
|
||||||
/* 失效DMA接收 */
|
|
||||||
HAL_UART_DMAStop(&huart3);
|
|
||||||
|
|
||||||
/* 获取接收长度:总大小-剩余大小 */
|
|
||||||
temp = huart3.hdmarx->Instance->CNDTR;
|
|
||||||
Rx_Len = Rx_Max - temp;
|
|
||||||
|
|
||||||
/* 追加到 U2_CopyBuff 供 AT 指令/MQTT 消息解析;
|
|
||||||
* 与 UART4 复用同一缓冲区,仅 4G 后端激活时才追加,防止两路数据交织 */
|
|
||||||
if (Rx_Len > 0 && NET_GetActiveBackend() == NET_BACKEND_4G &&
|
|
||||||
(U2_CopyIndex + Rx_Len) < (U2_COPY_SIZE - 1))
|
|
||||||
{
|
|
||||||
memcpy(U2_CopyBuff + U2_CopyIndex, Rx_Buf, Rx_Len);
|
|
||||||
U2_CopyIndex += Rx_Len;
|
|
||||||
U2_CopyBuff[U2_CopyIndex] = '\0';
|
|
||||||
U2_CopyFlag = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 使能接收DMA传输 */
|
|
||||||
HAL_UART_Receive_DMA(&huart3, Rx_Buf, Rx_Max);
|
|
||||||
}
|
|
||||||
/* USER CODE END USART3_IRQn 0 */
|
|
||||||
HAL_UART_IRQHandler(&huart3);
|
|
||||||
/* USER CODE BEGIN USART3_IRQn 1 */
|
|
||||||
|
|
||||||
/* USER CODE END USART3_IRQn 1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This function handles UART5 global interrupt (HLW8032).
|
|
||||||
*/
|
|
||||||
void UART5_IRQHandler(void)
|
|
||||||
{
|
|
||||||
HAL_UART_IRQHandler(&huart5);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* USER CODE BEGIN 1 */
|
|
||||||
|
|
||||||
/* USER CODE END 1 */
|
|
||||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
#ifndef BOOT_SHARE_H
|
|
||||||
#define BOOT_SHARE_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/*==== BL ? APP 共享内存(热复位保持)====
|
|
||||||
* 位于 SRAM 顶部,不属于任何段,启动代码不会清零;
|
|
||||||
* 软件复位/看门狗复位后内容保持,断电后丢失。
|
|
||||||
* 注意:若 APP 的 RAM 用量增长超过 0x2000BF00,需调整该地址 */
|
|
||||||
#define BOOT_SHARE_ADDR 0x2000BF00u
|
|
||||||
#define BOOT_SHARE_MAGIC 0x52534852u /* 'RSHR' */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t magic; /* BOOT_SHARE_MAGIC */
|
|
||||||
uint32_t csr_flags; /* BL 开机时写入的 RCC->CSR(复位原因标志) */
|
|
||||||
uint32_t uptime_sec; /* APP 每秒更新的本轮运行时长(热复位不丢) */
|
|
||||||
uint32_t pending_reason; /* APP 主动复位前写入的细分原因码(RSN_x) */
|
|
||||||
uint32_t checksum; /* 前 4 项累加和 */
|
|
||||||
} boot_share_t;
|
|
||||||
|
|
||||||
#define BOOT_SHARE ((volatile boot_share_t *)BOOT_SHARE_ADDR)
|
|
||||||
|
|
||||||
/*==== 重启原因码 ====*/
|
|
||||||
#define RSN_UNKNOWN 0 /* 未知 */
|
|
||||||
#define RSN_POWER_ON 1 /* 上电/意外掉电 */
|
|
||||||
#define RSN_PIN_RESET 2 /* 按键复位 */
|
|
||||||
#define RSN_WATCHDOG 3 /* 卡死看门狗溢出 */
|
|
||||||
#define RSN_KICKED 4 /* 平台/服务器踢下线 */
|
|
||||||
#define RSN_PING_TIMEOUT 5 /* 心跳超时(服务器无响应/假在线) */
|
|
||||||
#define RSN_NO_SERVICE 6 /* 4G 无网络服务 */
|
|
||||||
#define RSN_NO_SIM 7 /* 无 SIM 卡(预留) */
|
|
||||||
#define RSN_OTA_UPDATE 8 /* OTA 升级完成重启 */
|
|
||||||
#define RSN_OTA_HEALTH 9 /* OTA 健康检查超时 */
|
|
||||||
#define RSN_4G_FAIL 10 /* 4G 初始化失败 */
|
|
||||||
#define RSN_CMD_RESET 11 /* 串口 RESET 命令 */
|
|
||||||
#define RSN_CMD_OTA 12 /* 串口 OTA 命令 */
|
|
||||||
#define RSN_CMD_RECOVERY 13 /* 串口 RECOVERY 命令 */
|
|
||||||
#define RSN_POWER_LOSS 14 /* 外部 12V 断电(法拉电容续航中记录) */
|
|
||||||
#define RSN_4G_MODULE 15 /* 4G 模组异常自重启(boot.rom) */
|
|
||||||
#define RSN_SOFT_GENERIC 16 /* 软件复位(未标记细分原因,如 JLink 复位) */
|
|
||||||
#define RSN_OTA_FAIL 17 /* OTA 下载/前置检查失败,中止后复位 */
|
|
||||||
|
|
||||||
static inline uint32_t boot_share_sum(const volatile boot_share_t *s)
|
|
||||||
{
|
|
||||||
return s->magic + s->csr_flags + s->uptime_sec + s->pending_reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int boot_share_valid(void)
|
|
||||||
{
|
|
||||||
return BOOT_SHARE->magic == BOOT_SHARE_MAGIC &&
|
|
||||||
BOOT_SHARE->checksum == boot_share_sum(BOOT_SHARE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void boot_share_store(void)
|
|
||||||
{
|
|
||||||
BOOT_SHARE->checksum = boot_share_sum(BOOT_SHARE);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* BOOT_SHARE_H */
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue