shuichan_old/V1.4/STM32F103_App1/HardWare/SYSINIT/app_loop.c.bak

254 lines
8.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* app_loop.c - FreeRTOS 任务调度封装
*
* 任务划分(裸机主循环轮询项拆分而来):
* net_task(优先级3, 栈3KB) - 网络状态机 NET_process4G/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 "../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("* Version: 1.4.0 (RTOS) *");
log_info("* * * * * * * By Helei * * * *");
/* EEPROM 中的 MQTT/版本配置 */
iicInit();
eepromReadInfo();
log_info("> Server: %s:%d", MqttInfoStr.ServerIP, MqttInfoStr.ServerPort);
log_info("> Client: %s", MqttInfoStr.ClientID);
log_info("> User : %s", MqttInfoStr.Username);
log_info("> Firmware Ver: %u", MqttInfoStr.Ver);
/* W25Q64 外部 Flash用于 OTA 固件暂存 */
W25Q64_Init();
/* 继电器 GPIO控制输出 + 反馈输入) */
relayInit();
/* RS485 喂料秤控制模块 */
feedScaleInit();
/* LEDLED2 心跳同时兼任外部看门狗 TPL5010 喂狗) */
ledInit();
/* ADC 电量检测 */
adcInit();
update_batter();
/* 网络模块初始化:按 EEPROM 记忆的上次成功方式优先4G 或 WiFi */
NET_init();
/* 启动各路串口接收 */
USART2_StartRx(); /* RS485 喂料秤 */
USART3_StartRx(); /* 4G MQTT 消息DMA+IDLE */
USART1_StartRx(); /* 蓝牙调试命令 */
}
/*==== 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 uint32_t ota_confirm_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();
log_info("> Main: first status report after MQTT ready");
ota_confirm_tick = HAL_GetTick();
}
/* 首次发布成功后延迟 3s 再 OTA 确认 */
if (!ota_confirmed && g_net_mqtt_ready && g_net_first_publish_ok &&
(HAL_GetTick() - ota_confirm_tick) > 3000) {
ota_confirmed = 1;
NET_ota_confirm();
log_info("> OTA confirmed (MQTT ready + first publish ok + 3s delay)");
}
/* MQTT 就绪后 1 分钟仍未首次 MPUB主动复位 */
if (!ota_confirmed && mqtt_ready_recorded &&
(HAL_GetTick() - mqtt_ready_tick) > OTA_HEALTH_TIMEOUT_MS) {
log_warn("> OTA health timeout: MQTT ready but no MPUB in 1min, reset");
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);
}
}
/*==== sys_task系统辅助LED/电量/蓝牙命令/运行时长)====*/
static void sys_task(void *arg)
{
(void)arg;
uint32_t batt_tick = HAL_GetTick();
uint32_t up_last = HAL_GetTick();
uint32_t up_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();
/* 电量 1s 节流更新 */
if (HAL_GetTick() - batt_tick >= 1000) {
batt_tick = HAL_GetTick();
update_batter();
}
/* 运行时长心跳:每 30s 串口输出一次(天/时/分),
* 64 位累计HAL_GetTick 约 49 天回绕也不受影响 */
{
uint32_t now = HAL_GetTick();
up_ms += (uint32_t)(now - up_last); /* 32位差值天然容忍回绕 */
up_last = now;
if (now - up_print >= 30000) {
up_print = now;
uint32_t mins = (uint32_t)(up_ms / 60000);
log_info("> Uptime: %uday %02uh:%02um\r\n",
(unsigned)(mins / 1440),
(unsigned)((mins % 1440) / 60),
(unsigned)(mins % 60));
}
}
vTaskDelay(20);
}
}
/*==== FreeRTOS 钩子 ====*/
/* idle 钩子:喂独立看门狗。有任务死循环占 CPU 时 idle 饿死 → IWDG 复位 */
void vApplicationIdleHook(void)
{
IWDG_Feed();
}
void vApplicationMallocFailedHook(void)
{
log_error("> RTOS: malloc failed (heap exhausted)");
taskDISABLE_INTERRUPTS();
for (;;) { }
}
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{
(void)xTask;
log_error("> RTOS: stack overflow in task %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); /* 栈 3KBAT/OTA 有大的 sprintf 缓冲 */
xTaskCreate(app_task, "app", 512, NULL, 2, NULL); /* 栈 2KB */
xTaskCreate(sys_task, "sys", 256, NULL, 1, NULL); /* 栈 1KB */
vTaskStartScheduler();
/* 不会走到这里(堆不足创建失败才会) */
log_error("> RTOS: scheduler failed to start");
for (;;) { }
}