shuichan_new/STM32F103_App1/HardWare/RELAY/ext_relay.c

292 lines
9.0 KiB
C
Raw Normal View History

/* ext_relay.c - 485 <20><>չ<EFBFBD>̵<EFBFBD><CCB5><EFBFBD><EFBFBD><EFBFBD>(4 ·<><C2B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)<29><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֧<EFBFBD><D6A7>
*
* Ӳ<EFBFBD><EFBFBD>: <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>سӹ<EFBFBD><EFBFBD><EFBFBD> USART2 RS485 <EFBFBD><EFBFBD><EFBFBD><EFBFBD>(9600-8N1, DMA+IDLE <EFBFBD><EFBFBD>֡)
* Э<EFBFBD><EFBFBD>: Modbus RTU, <EFBFBD>ӻ<EFBFBD><EFBFBD><EFBFBD>ַ 0x10, <EFBFBD><EFBFBD><EFBFBD><EFBFBD> Docs/RS485<EFBFBD><EFBFBD>չ<EFBFBD>̵<EFBFBD><EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD>.md
* FC05 д<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȧ(<EFBFBD><EFBFBD>Ȧ0~3=<EFBFBD>̵<EFBFBD><EFBFBD><EFBFBD>1~4, 0xFF00<EFBFBD><EFBFBD><EFBFBD><EFBFBD>/0x0000<EFBFBD>Ͽ<EFBFBD>), <EFBFBD>ӻ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
* FC03 <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ּĴ<EFBFBD><EFBFBD><EFBFBD> 0x0000 <EFBFBD><EFBFBD> 4 <EFBFBD><EFBFBD>: <EFBFBD><EFBFBD><EFBFBD><EFBFBD>״̬/<EFBFBD><EFBFBD><EFBFBD><EFBFBD>״̬/<EFBFBD><EFBFBD><EFBFBD>ϱ<EFBFBD>־/<EFBFBD>̼<EFBFBD><EFBFBD>
* ʱ<EFBFBD><EFBFBD>: <EFBFBD><EFBFBD><EFBFBD>߾<EFBFBD>Ĭ >=50ms <EFBFBD>ŷ<EFBFBD>֡(<EFBFBD><EFBFBD><EFBFBD>Ӳ<EFBFBD>ѯ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> rs485_bus ʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>);
* Ӧ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ 300ms <EFBFBD><EFBFBD><EFBFBD><EFBFBD> 1 <EFBFBD><EFBFBD>; <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ÿ 2s <EFBFBD><EFBFBD>ѯһ<EFBFBD><EFBFBD>״̬<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߼<EFBFBD><EFBFBD><EFBFBD>
*/
#include "ext_relay.h"
#include "relay.h"
#include "uart.h"
#include "log.h"
#include "network.h"
#include "FreeRTOS.h"
#include "task.h"
#include <string.h>
#include <stdio.h>
#define EXT_RESP_TIMEOUT_MS 300u /* Ӧ<><D3A6><EFBFBD><EFBFBD>ʱ */
#define EXT_FB_SETTLE_MS 150u /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ժ<EFBFBD><D4BA>ȴ<EFBFBD><C8B4><EFBFBD>/<2F><><EFBFBD><EFBFBD><EFBFBD>ȶ<EFBFBD><C8B6>ٶ<EFBFBD><D9B6><EFBFBD><EFBFBD><EFBFBD> */
#define EXT_POLL_INTERVAL_MS 2000u /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѯ<EFBFBD><D1AF><EFBFBD><EFBFBD> */
#define EXT_OFFLINE_MISS 3u /* <20><><EFBFBD><EFBFBD>ʧ<EFBFBD>ܴ<EFBFBD><DCB4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
/* Modbus CRC16(<28><><EFBFBD><EFBFBD>ͬ<EFBFBD><CDAC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ) */
static uint16_t ext_crc16(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++) {
crc = (crc & 0x0001) ? ((crc >> 1) ^ 0xA001) : (crc >> 1);
}
}
return crc;
}
/* <20><><EFBFBD>߷<EFBFBD><DFB7><EFBFBD>: <20><> 50ms <20><>Ĭ + <20><><EFBFBD><EFBFBD><EFBFBD>ջ<EFBFBD><D5BB><EFBFBD>(<28><><EFBFBD>¾<EFBFBD>֡) + <20><><EFBFBD><EFBFBD> + ˢ<><CBA2>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD> */
static void ext_tx(const uint8_t *frame, uint16_t len)
{
uint32_t dt = HAL_GetTick() - rs485_bus_last_tick();
if (dt < 50u) vTaskDelay(50u - dt);
USART2_FlushRxBuf();
HAL_UART_Transmit(&huart2, (uint8_t *)frame, len, 100);
rs485_bus_touch();
}
static void ext_send_fc05(uint8_t ch, uint8_t on)
{
uint8_t f[8];
f[0] = EXT_RELAY_ADDR;
f[1] = 0x05;
f[2] = 0x00;
f[3] = (uint8_t)(ch - 1); /* <20><>Ȧ<EFBFBD><C8A6>ַ 0~3 */
f[4] = on ? 0xFF : 0x00;
f[5] = 0x00;
uint16_t c = ext_crc16(f, 6);
f[6] = (uint8_t)(c & 0xFF);
f[7] = (uint8_t)(c >> 8);
ext_tx(f, 8);
}
static void ext_send_fc03(void)
{
uint8_t f[8];
f[0] = EXT_RELAY_ADDR;
f[1] = 0x03;
f[2] = 0x00; f[3] = 0x00; /* <20><>ʼ<EFBFBD>Ĵ<EFBFBD><C4B4><EFBFBD> 0x0000 */
f[4] = 0x00; f[5] = 0x04; /* <20><> 4 <20><> */
uint16_t c = ext_crc16(f, 6);
f[6] = (uint8_t)(c & 0xFF);
f[7] = (uint8_t)(c >> 8);
ext_tx(f, 8);
}
/* <20><>չ<EFBFBD><D5B9>״̬(<28><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB> FC03 Ӧ<><D3A6>) */
static uint8_t s_online = 0;
static uint8_t s_state = 0; /* REG0 <20><><EFBFBD><EFBFBD>״̬ bit0~3 */
static uint8_t s_fb = 0; /* REG1 <20><><EFBFBD><EFBFBD>״̬ */
static uint8_t s_fault = 0; /* REG2 <20><><EFBFBD>ϱ<EFBFBD>־ */
static uint16_t s_ver = 0; /* REG3 <20>ӻ<EFBFBD><D3BB>汾 */
static uint8_t s_miss = 0; /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
static uint32_t s_last_poll = 0;
/* <20><><EFBFBD><EFBFBD>״̬<D7B4><CCAC> */
static uint8_t cmd_pending = 0; /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
static uint8_t cmd_stage = 0; /* 0<><30> 1<><31>FC05<30><35><EFBFBD><EFBFBD> 2<><32><EFBFBD><EFBFBD><EFBFBD>ȶ<EFBFBD><C8B6><EFBFBD>ʱ 3<><33>FC03Ӧ<33><D3A6> */
static uint8_t cmd_ch = 0, cmd_on = 0, cmd_retry = 0;
static uint8_t cmd_is_poll = 0; /* <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѯ(<28><>Ӧ<EFBFBD><D3A6>ƽ̨) */
static uint32_t cmd_tick = 0;
static char cmd_id[20] = {0};
/* ֡<>¼<EFBFBD>(on_frame <20><>λ, process <20><><EFBFBD><EFBFBD>; ͬ<><CDAC> app_task <20><><EFBFBD>߳<EFBFBD>) */
static uint8_t rx_echo_ok = 0;
static uint8_t rx_state_ok = 0;
static uint8_t rx_exc = 0;
uint8_t ext_relay_online(void) { return s_online; }
uint8_t ext_relay_state_mask(void) { return s_state; }
uint8_t ext_relay_fault_mask(void) { return s_fault; }
static void ext_note_success(void)
{
s_miss = 0;
if (!s_online) {
s_online = 1;
log_info("> EXT: <20><>չ<EFBFBD>̵<EFBFBD><CCB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (<28>ӻ<EFBFBD><D3BB>汾 V%u.%02X)", s_ver >> 8, s_ver & 0xFF);
relayStatueUpdata(); /* <20><><EFBFBD>ߺ<EFBFBD><DFBA><EFBFBD>һ<EFBFBD><D2BB>ȫ״̬, ƽ̨ˢ<CCA8><CBA2> sw5~sw8 */
}
}
static void ext_note_fail(void)
{
if (s_miss < 255) s_miss++;
if (s_online && s_miss >= EXT_OFFLINE_MISS) {
s_online = 0;
log_warn("> EXT: <20><>չ<EFBFBD>̵<EFBFBD><CCB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>(<28><><EFBFBD><EFBFBD> %u <20><><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6>)", EXT_OFFLINE_MISS);
}
}
static void ext_respond(int ok, const char *msg)
{
if (cmd_id[0]) {
NET_publish_response(cmd_id, ok, msg);
cmd_id[0] = 0;
}
}
void ext_relay_cmd(uint8_t ch, uint8_t on, const char *id)
{
if (ch < 1 || ch > EXT_RELAY_CH) return;
if (!s_online) {
log_warn("> EXT: <20><>չ<EFBFBD><D5B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20>ܾ<EFBFBD> SW%d <20><><EFBFBD><EFBFBD>", ch + 4);
if (id) NET_publish_response(id, 0, "ext relay board offline");
return;
}
if (cmd_pending || cmd_stage != 0) {
log_warn("> EXT: <20><>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>δ<EFBFBD><CEB4><EFBFBD><EFBFBD>, <20>ܾ<EFBFBD> SW%d <20><><EFBFBD><EFBFBD>", ch + 4);
if (id) NET_publish_response(id, 0, "ext relay busy");
return;
}
cmd_ch = ch;
cmd_on = on ? 1 : 0;
if (id) {
strncpy(cmd_id, id, sizeof(cmd_id) - 1);
cmd_id[sizeof(cmd_id) - 1] = 0;
} else {
cmd_id[0] = 0;
}
cmd_pending = 1;
log_info("> EXT: SW%d %s (<28>·<EFBFBD><C2B7><EFBFBD>չ<EFBFBD><D5B9>)", ch + 4, on ? "<EFBFBD><EFBFBD>" : "<EFBFBD><EFBFBD>");
}
/* 485 ֡<><D6A1><EFBFBD><EFBFBD>: feed_scale <20><>֡<EFBFBD><D6A1><EFBFBD><EFBFBD>վ<EFBFBD><D5BE> 0x10 <20>ַ<EFBFBD><D6B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
void ext_relay_on_frame(const uint8_t *b, uint16_t len)
{
if (len < 5) return;
uint16_t c = ext_crc16(b, (uint16_t)(len - 2));
uint16_t r = (uint16_t)(((uint16_t)b[len - 1] << 8) | b[len - 2]);
if (c != r) {
log_warn("> EXT: ֡ CRC <20><><EFBFBD><EFBFBD> (len=%u)", (unsigned)len);
return;
}
if (b[1] & 0x80) { /* Modbus <20>쳣Ӧ<ECB3A3><D3A6> */
log_warn("> EXT: <20>쳣Ӧ<ECB3A3><D3A6> FC=%02X <20><>=%02X", b[1] & 0x7F, b[2]);
rx_exc = 1;
return;
}
if (b[1] == 0x05 && len == 8) {
/* <20><><EFBFBD><EFBFBD>У<EFBFBD><D0A3>: <20><>Ȧ<EFBFBD><C8A6>ַ<EFBFBD><D6B7>ͨ<EFBFBD><CDA8>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB> */
if (b[2] == 0x00 && b[3] == (uint8_t)(cmd_ch - 1) &&
((b[4] == 0xFF && cmd_on) || (b[4] == 0x00 && !cmd_on)) && b[5] == 0x00) {
rx_echo_ok = 1;
} else {
log_warn("> EXT: FC05 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>󲻷<EFBFBD>");
}
return;
}
if (b[1] == 0x03 && len == 13 && b[2] == 0x08) {
/* 4 <20><><EFBFBD>Ĵ<EFBFBD><C4B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: <20><><EFBFBD><EFBFBD>/<2F><><EFBFBD><EFBFBD>/<2F><><EFBFBD><EFBFBD>ֻ<EFBFBD>õ<EFBFBD><C3B5>ֽ<EFBFBD> */
s_state = b[4] & 0x0F;
s_fb = b[6] & 0x0F;
s_fault = b[8] & 0x0F;
s_ver = (uint16_t)(((uint16_t)b[9] << 8) | b[10]);
rx_state_ok = 1;
return;
}
log_warn("> EXT: δʶ<CEB4><CAB6>֡ FC=%02X len=%u", b[1], (unsigned)len);
}
void ext_relay_process(void)
{
uint32_t now = HAL_GetTick();
if (now < 3000u) return; /* <20>ϵ<EFBFBD><CFB5>ȶ<EFBFBD><C8B6><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD>״β<D7B4>ѯ<EFBFBD><D1AF><EFBFBD><EFBFBD> */
/* ȡ<><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>(<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ) */
if (cmd_pending && cmd_stage == 0) {
cmd_pending = 0;
cmd_is_poll = 0;
cmd_retry = 0;
cmd_stage = 1;
ext_send_fc05(cmd_ch, cmd_on);
cmd_tick = now;
return;
}
if (cmd_stage == 1) { /* <20><> FC05 <20><><EFBFBD><EFBFBD> */
if (rx_echo_ok) {
rx_echo_ok = 0;
ext_note_success();
cmd_stage = 2;
cmd_tick = now;
} else if (rx_exc || (now - cmd_tick) > EXT_RESP_TIMEOUT_MS) {
rx_exc = 0;
if (cmd_retry < 1) {
cmd_retry++;
ext_send_fc05(cmd_ch, cmd_on);
cmd_tick = now;
} else {
log_warn("> EXT: SW%d <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6>", cmd_ch + 4);
ext_respond(0, "ext relay no response");
ext_note_fail();
cmd_stage = 0;
}
}
return;
}
if (cmd_stage == 2) { /* <20><><EFBFBD><EFBFBD>/<2F><><EFBFBD><EFBFBD><EFBFBD>ȶ<EFBFBD><C8B6><EFBFBD>ʱ */
if ((now - cmd_tick) >= EXT_FB_SETTLE_MS) {
cmd_retry = 0;
cmd_stage = 3;
ext_send_fc03();
cmd_tick = now;
}
return;
}
if (cmd_stage == 3) { /* <20><> FC03 ״̬Ӧ<CCAC><D3A6>(<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>β<EFBFBD><CEB2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѯ<EFBFBD><D1AF><EFBFBD><EFBFBD>) */
if (rx_state_ok) {
rx_state_ok = 0;
ext_note_success();
if (cmd_is_poll) {
cmd_is_poll = 0;
if (s_fault) {
log_warn("> EXT: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><ECB3A3>־=%02X (<28><><EFBFBD><EFBFBD>=%02X <20><><EFBFBD><EFBFBD>=%02X)", s_fault, s_state, s_fb);
}
} else {
uint8_t bit = (uint8_t)(1u << (cmd_ch - 1));
int ok = (((s_state & bit) != 0) == cmd_on) && ((s_fault & bit) == 0);
if (ok) {
log_info("> EXT: SW%d %s<>ɹ<EFBFBD> (<28><><EFBFBD><EFBFBD>=%02X)", cmd_ch + 4, cmd_on ? "<EFBFBD><EFBFBD>" : "<EFBFBD><EFBFBD>", s_fb);
ext_respond(1, "OK");
} else {
log_warn("> EXT: SW%d %sδ<73><CEB4>Ч (<28><><EFBFBD><EFBFBD>=%02X <20><><EFBFBD><EFBFBD>=%02X <20><><EFBFBD><EFBFBD>=%02X)",
cmd_ch + 4, cmd_on ? "<EFBFBD><EFBFBD>" : "<EFBFBD><EFBFBD>", s_state, s_fb, s_fault);
ext_respond(0, "ext relay failed / load unpowered");
}
relayStatueUpdata(); /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɺ<EFBFBD><C9BA>ϱ<EFBFBD>ȫ״̬ */
}
cmd_stage = 0;
} else if (rx_exc || (now - cmd_tick) > EXT_RESP_TIMEOUT_MS) {
rx_exc = 0;
if (cmd_retry < 1) {
cmd_retry++;
ext_send_fc03();
cmd_tick = now;
} else {
if (!cmd_is_poll) {
log_warn("> EXT: SW%d ״̬ȷ<CCAC><C8B7><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6>", cmd_ch + 4);
ext_respond(0, "ext relay no response");
}
cmd_is_poll = 0;
ext_note_fail();
cmd_stage = 0;
}
}
return;
}
/* <20><><EFBFBD><EFBFBD>: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѯ(<28><><EFBFBD><EFBFBD><EFBFBD>߼<EFBFBD><DFBC><EFBFBD>) */
if ((now - s_last_poll) >= EXT_POLL_INTERVAL_MS) {
s_last_poll = now;
cmd_is_poll = 1;
cmd_retry = 0;
cmd_stage = 3;
ext_send_fc03();
cmd_tick = now;
}
}