|
|
@ -1,6 +1,20 @@ |
|
|
|
|
|
|
|
|
/***********************************************************************************************************************
|
|
|
/***********************************************************************************************************************
|
|
|
* DISCLAIMER ... (생략: 원본 그대로) |
|
|
* DISCLAIMER |
|
|
|
|
|
* This software is supplied by Renesas Electronics Corporation and is only intended for use with Renesas products. |
|
|
|
|
|
* No other uses are authorized. This software is owned by Renesas Electronics Corporation and is protected under all |
|
|
|
|
|
* applicable laws, including copyright laws. |
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING THIS SOFTWARE, WHETHER EXPRESS, IMPLIED |
|
|
|
|
|
* OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
|
|
|
|
* NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY |
|
|
|
|
|
* LAW, NEITHER RENESAS ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE FOR ANY DIRECT, |
|
|
|
|
|
* INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR |
|
|
|
|
|
* ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. |
|
|
|
|
|
* Renesas reserves the right, without notice, to make changes to this software and to discontinue the availability |
|
|
|
|
|
* of this software. By using this software, you agree to the additional terms and conditions found by accessing the |
|
|
|
|
|
* following link: |
|
|
|
|
|
* http://www.renesas.com/disclaimer
|
|
|
|
|
|
* |
|
|
|
|
|
* Copyright (C) 2012, 2021 Renesas Electronics Corporation. All rights reserved. |
|
|
***********************************************************************************************************************/ |
|
|
***********************************************************************************************************************/ |
|
|
|
|
|
|
|
|
/***********************************************************************************************************************
|
|
|
/***********************************************************************************************************************
|
|
|
@ -12,6 +26,10 @@ |
|
|
* Creation Date: 2026-01-29 |
|
|
* Creation Date: 2026-01-29 |
|
|
***********************************************************************************************************************/ |
|
|
***********************************************************************************************************************/ |
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************************************
|
|
|
|
|
|
Includes |
|
|
|
|
|
***********************************************************************************************************************/ |
|
|
|
|
|
|
|
|
#include "r_cg_macrodriver.h" |
|
|
#include "r_cg_macrodriver.h" |
|
|
#include "r_cg_cgc.h" |
|
|
#include "r_cg_cgc.h" |
|
|
#include "r_cg_port.h" |
|
|
#include "r_cg_port.h" |
|
|
@ -28,13 +46,16 @@ |
|
|
#include <r_cg_port.h> |
|
|
#include <r_cg_port.h> |
|
|
/* End user code. Do not edit comment generated here */ |
|
|
/* End user code. Do not edit comment generated here */ |
|
|
#include "r_cg_userdefine.h" |
|
|
#include "r_cg_userdefine.h" |
|
|
|
|
|
/* Start user code for adding. Do not edit comment generated here */ |
|
|
/* =========================
|
|
|
/* =========================
|
|
|
* Config |
|
|
* Config |
|
|
* ========================= */ |
|
|
* ========================= */ |
|
|
#define CMD_MAX 529 |
|
|
#define CMD_MAX 529 |
|
|
#define UART_RX_BUF_SIZE 256 /* 네 프로젝트 값에 맞춰 유지 */ |
|
|
#define UART_RX_BUF_SIZE 256 |
|
|
#define RS485_BRIDGE_FIFO_SZ 256 |
|
|
#define RS485_BRIDGE_FIFO_SZ 2048 |
|
|
|
|
|
|
|
|
|
|
|
#define BRIDGE_IDLE_DONE_US 5000U // 마지막 바이트 이후 3ms 조용하면 종료로 판단
|
|
|
|
|
|
#define BRIDGE_TOTAL_WAIT_US 600000U |
|
|
|
|
|
|
|
|
/* =========================
|
|
|
/* =========================
|
|
|
* UART RX Buffers |
|
|
* UART RX Buffers |
|
|
@ -49,10 +70,18 @@ volatile uint8_t rs485_rx_index = 0; |
|
|
volatile uint8_t rs485_rx_buffer[UART_RX_BUF_SIZE] = {0}; |
|
|
volatile uint8_t rs485_rx_buffer[UART_RX_BUF_SIZE] = {0}; |
|
|
volatile uint16_t rs485_rx_length = 0; |
|
|
volatile uint16_t rs485_rx_length = 0; |
|
|
|
|
|
|
|
|
|
|
|
volatile uint32_t g_uart0_err_fef = 0; |
|
|
|
|
|
volatile uint32_t g_uart0_err_ovf = 0; |
|
|
|
|
|
volatile uint32_t g_uart0_err_pef = 0; |
|
|
|
|
|
|
|
|
/* RS485 bridge flags */ |
|
|
/* RS485 bridge flags */ |
|
|
volatile uint8_t g_rs485_bridge_active = 0; |
|
|
volatile uint8_t g_rs485_bridge_active = 0; |
|
|
volatile uint8_t g_rs485_bridge_done = 0; |
|
|
volatile uint8_t g_rs485_bridge_done = 0; |
|
|
|
|
|
|
|
|
|
|
|
volatile uint16_t g_rs485_bridge_seq = 0; |
|
|
|
|
|
|
|
|
|
|
|
static volatile uint8_t s_end_st = 0; |
|
|
|
|
|
static const char s_end_pat[] = "<end>"; |
|
|
/* (0~31) */ |
|
|
/* (0~31) */ |
|
|
uint8_t g_fixed_addr = 0; |
|
|
uint8_t g_fixed_addr = 0; |
|
|
|
|
|
|
|
|
@ -62,25 +91,24 @@ uint8_t g_fixed_addr = 0; |
|
|
extern volatile uint16_t g_uart1_tx_count; /* UART1 TX 진행 카운트 */ |
|
|
extern volatile uint16_t g_uart1_tx_count; /* UART1 TX 진행 카운트 */ |
|
|
extern volatile uint16_t g_uart0_tx_count; /* UART0 TX 진행 카운트 */ |
|
|
extern volatile uint16_t g_uart0_tx_count; /* UART0 TX 진행 카운트 */ |
|
|
|
|
|
|
|
|
|
|
|
extern volatile uint8_t g_uart0_tx_done; |
|
|
|
|
|
|
|
|
/* =========================
|
|
|
/* =========================
|
|
|
* RS485 Bridge FIFO |
|
|
* RS485 Bridge FIFO |
|
|
* ========================= */ |
|
|
* ========================= */ |
|
|
static volatile uint8_t s_rb_fifo[RS485_BRIDGE_FIFO_SZ]; |
|
|
static volatile uint8_t s_rb_fifo[RS485_BRIDGE_FIFO_SZ]; |
|
|
static volatile uint8_t s_rb_head = 0; |
|
|
static volatile uint16_t s_rb_head = 0; |
|
|
static volatile uint8_t s_rb_tail = 0; |
|
|
static volatile uint16_t s_rb_tail = 0; |
|
|
|
|
|
|
|
|
void RS485_Bridge_Push(uint8_t b) |
|
|
void RS485_Bridge_Push(uint8_t b) |
|
|
{ |
|
|
{ |
|
|
uint8_t next = (uint8_t)(s_rb_head + 1); |
|
|
uint16_t next = (uint16_t)(s_rb_head + 1); |
|
|
if (next >= RS485_BRIDGE_FIFO_SZ) next = 0; |
|
|
if (next >= RS485_BRIDGE_FIFO_SZ) next = 0; |
|
|
|
|
|
|
|
|
/* FIFO full -> drop oldest (tail++) */ |
|
|
|
|
|
if (next == s_rb_tail) { |
|
|
if (next == s_rb_tail) { |
|
|
uint8_t t = (uint8_t)(s_rb_tail + 1); |
|
|
uint16_t t = (uint16_t)(s_rb_tail + 1); |
|
|
if (t >= RS485_BRIDGE_FIFO_SZ) t = 0; |
|
|
if (t >= RS485_BRIDGE_FIFO_SZ) t = 0; |
|
|
s_rb_tail = t; |
|
|
s_rb_tail = t; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
s_rb_fifo[s_rb_head] = b; |
|
|
s_rb_fifo[s_rb_head] = b; |
|
|
s_rb_head = next; |
|
|
s_rb_head = next; |
|
|
} |
|
|
} |
|
|
@ -89,14 +117,56 @@ static void RS485_Bridge_ResetFifo(void) |
|
|
{ |
|
|
{ |
|
|
s_rb_head = 0; |
|
|
s_rb_head = 0; |
|
|
s_rb_tail = 0; |
|
|
s_rb_tail = 0; |
|
|
|
|
|
s_end_st = 0; |
|
|
|
|
|
g_rs485_bridge_done = 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int RS485_Bridge_ReadLine(char *out, int out_sz, uint32_t timeout_us) |
|
|
|
|
|
{ |
|
|
|
|
|
int n = 0; |
|
|
|
|
|
|
|
|
|
|
|
while (timeout_us--) { |
|
|
|
|
|
// FIFO에 데이터가 있으면 1바이트씩 꺼내서 line 구성
|
|
|
|
|
|
while (s_rb_tail != s_rb_head) { |
|
|
|
|
|
char c = (char)s_rb_fifo[s_rb_tail]; |
|
|
|
|
|
s_rb_tail++; |
|
|
|
|
|
if (s_rb_tail >= RS485_BRIDGE_FIFO_SZ) s_rb_tail = 0; |
|
|
|
|
|
|
|
|
|
|
|
if (c == '\r') continue; |
|
|
|
|
|
|
|
|
|
|
|
if (n < out_sz - 1) out[n++] = c; |
|
|
|
|
|
|
|
|
|
|
|
if (c == '\n') { |
|
|
|
|
|
out[n] = '\0'; |
|
|
|
|
|
return 1; // line 완성
|
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
delay_us(1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (n > 0) { out[n] = '\0'; } |
|
|
|
|
|
return 0; // timeout
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int Is_V_Response_For(char *line, uint8_t addr) |
|
|
|
|
|
{ |
|
|
|
|
|
uint8_t a; |
|
|
|
|
|
|
|
|
|
|
|
// line 예: "V02\n" 또는 "V02\r\n" (CR은 위에서 제거됨)
|
|
|
|
|
|
if (line[0] != 'V' && line[0] != 'v') return 0; |
|
|
|
|
|
if (!isdigit((unsigned char)line[1]) || !isdigit((unsigned char)line[2])) return 0; |
|
|
|
|
|
|
|
|
|
|
|
a = (uint8_t)((line[1]-'0')*10 + (line[2]-'0')); |
|
|
|
|
|
return (a == addr); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* =========================
|
|
|
/* =========================
|
|
|
* ? PC(UART1) Safe TX (중요) |
|
|
* ? PC(UART1) Safe TX (중요) |
|
|
* - 로컬 버퍼 금지 |
|
|
* - 로컬 버퍼 금지 |
|
|
* - 항상 전역 버퍼로 복사 후 R_UART1_Send |
|
|
* - 항상 전역 버퍼로 복사 후 R_UART1_Send |
|
|
* ========================= */ |
|
|
* ========================= */ |
|
|
static uint8_t g_uart1_txbuf[128]; |
|
|
static uint8_t g_uart1_txbuf[256]; |
|
|
|
|
|
|
|
|
static void UART1_WaitTxIdle(void) |
|
|
static void UART1_WaitTxIdle(void) |
|
|
{ |
|
|
{ |
|
|
@ -157,7 +227,7 @@ static void RS485_Bridge_DrainToPC(void) |
|
|
if (g_uart1_tx_count != 0U) return; |
|
|
if (g_uart1_tx_count != 0U) return; |
|
|
|
|
|
|
|
|
/* FIFO -> 임시 버퍼에 최대 64바이트까지 모아서 한 번에 전송 */ |
|
|
/* FIFO -> 임시 버퍼에 최대 64바이트까지 모아서 한 번에 전송 */ |
|
|
while (s_rb_tail != s_rb_head && n < 64U) { |
|
|
while (s_rb_tail != s_rb_head && n < 256U) { |
|
|
g_uart1_txbuf[n++] = s_rb_fifo[s_rb_tail]; |
|
|
g_uart1_txbuf[n++] = s_rb_fifo[s_rb_tail]; |
|
|
s_rb_tail++; |
|
|
s_rb_tail++; |
|
|
if (s_rb_tail >= RS485_BRIDGE_FIFO_SZ) s_rb_tail = 0; |
|
|
if (s_rb_tail >= RS485_BRIDGE_FIFO_SZ) s_rb_tail = 0; |
|
|
@ -245,6 +315,34 @@ static int parse_x_prefix(const char *s, int len, |
|
|
return -1; |
|
|
return -1; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int bridge_wait_until_end(uint32_t total_us) |
|
|
|
|
|
{ |
|
|
|
|
|
const char *pat = "<end>"; |
|
|
|
|
|
uint8_t st = 0; |
|
|
|
|
|
|
|
|
|
|
|
while (total_us--) { |
|
|
|
|
|
// FIFO -> PC 전송
|
|
|
|
|
|
while (s_rb_tail != s_rb_head) { |
|
|
|
|
|
char c = (char)s_rb_fifo[s_rb_tail]; |
|
|
|
|
|
s_rb_tail = (s_rb_tail + 1) % RS485_BRIDGE_FIFO_SZ; |
|
|
|
|
|
|
|
|
|
|
|
// PC로 바로 출력(버퍼링은 기존처럼 모아서 보내도 OK)
|
|
|
|
|
|
// 여기서는 단순화를 위해 1바이트라 가정하지 말고 기존 256모아서 보내는 함수 유지 권장
|
|
|
|
|
|
|
|
|
|
|
|
// 패턴 매칭
|
|
|
|
|
|
if (c == pat[st]) { |
|
|
|
|
|
st++; |
|
|
|
|
|
if (pat[st] == 0) return 1; // found
|
|
|
|
|
|
} else { |
|
|
|
|
|
st = (c == pat[0]) ? 1 : 0; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
delay_us(1); |
|
|
|
|
|
} |
|
|
|
|
|
return 0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* =========================
|
|
|
/* =========================
|
|
|
* Print routing |
|
|
* Print routing |
|
|
* ========================= */ |
|
|
* ========================= */ |
|
|
@ -307,18 +405,58 @@ static void send_v_response(CmdSource src, uint8_t addr) |
|
|
resp[5] = '\0'; |
|
|
resp[5] = '\0'; |
|
|
|
|
|
|
|
|
if (src == CMD_SRC_RS485) { |
|
|
if (src == CMD_SRC_RS485) { |
|
|
delay_us(300); // 200~500us 권장 (환경 따라 조정)
|
|
|
delay_us(600); // 200~500us 권장 (환경 따라 조정)
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
OUT_PRINT(src, resp); |
|
|
OUT_PRINT(src, resp); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void UART0_WaitTxDone_Flag(uint32_t guard_us) |
|
|
|
|
|
{ |
|
|
|
|
|
while (!g_uart0_tx_done && guard_us--) { |
|
|
|
|
|
delay_us(1); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void RS485_FlushJunk(uint16_t loops, uint16_t step_us) |
|
|
|
|
|
{ |
|
|
|
|
|
// 스캔 시작 전에, 들어와 있던 CR/LF/노이즈를 PC로 빼고 FIFO도 비움
|
|
|
|
|
|
while (loops--) { |
|
|
|
|
|
RS485_Bridge_DrainToPC(); |
|
|
|
|
|
delay_us(step_us); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void PC_PrintLine_CRLF(const char *line) |
|
|
|
|
|
{ |
|
|
|
|
|
char tmp[40]; |
|
|
|
|
|
size_t n = strlen(line); |
|
|
|
|
|
size_t i; |
|
|
|
|
|
|
|
|
|
|
|
// line이 '\n'로 끝나면, "\r\n"로 보정
|
|
|
|
|
|
if (n > 0 && line[n-1] == '\n') { |
|
|
|
|
|
size_t m = 0; |
|
|
|
|
|
for (i=0; i<n && m < sizeof(tmp)-3; i++) { |
|
|
|
|
|
if (line[i] == '\r') continue; |
|
|
|
|
|
if (line[i] == '\n') { tmp[m++] = '\r'; tmp[m++] = '\n'; } |
|
|
|
|
|
else tmp[m++] = line[i]; |
|
|
|
|
|
} |
|
|
|
|
|
tmp[m] = 0; |
|
|
|
|
|
UART1_SendString_Safe(tmp); |
|
|
|
|
|
} else { |
|
|
|
|
|
UART1_SendString_Safe(line); |
|
|
|
|
|
UART1_SendString_Safe("\r\n"); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/* =========================
|
|
|
/* =========================
|
|
|
* RS485 scan: xNNv\r\n |
|
|
* RS485 scan: xNNv\r\n |
|
|
* ========================= */ |
|
|
* ========================= */ |
|
|
static void scan_one_addr_rs485(uint8_t addr) |
|
|
static void scan_one_addr_rs485(uint8_t addr) |
|
|
{ |
|
|
{ |
|
|
char cmdline[8]; |
|
|
char cmdline[8]; |
|
|
|
|
|
uint8_t try; |
|
|
|
|
|
uint8_t i; |
|
|
|
|
|
|
|
|
if (addr == 0) return; |
|
|
if (addr == 0) return; |
|
|
|
|
|
|
|
|
@ -330,6 +468,8 @@ static void scan_one_addr_rs485(uint8_t addr) |
|
|
cmdline[5] = '\n'; |
|
|
cmdline[5] = '\n'; |
|
|
cmdline[6] = '\0'; |
|
|
cmdline[6] = '\0'; |
|
|
|
|
|
|
|
|
|
|
|
for (try = 0; try < 3; try++) |
|
|
|
|
|
{ |
|
|
// --- bridge 준비 ---
|
|
|
// --- bridge 준비 ---
|
|
|
g_rs485_bridge_active = 1; |
|
|
g_rs485_bridge_active = 1; |
|
|
g_rs485_bridge_done = 0; |
|
|
g_rs485_bridge_done = 0; |
|
|
@ -341,35 +481,48 @@ static void scan_one_addr_rs485(uint8_t addr) |
|
|
rs485_rx_length = 0; |
|
|
rs485_rx_length = 0; |
|
|
R_UART0_Receive((uint8_t*)&rs485_rx_buffer[0], 1); |
|
|
R_UART0_Receive((uint8_t*)&rs485_rx_buffer[0], 1); |
|
|
|
|
|
|
|
|
// ? (A) 이전 송신이 남아있으면 정리
|
|
|
RS485_FlushJunk(40, 10); // 400us 정도
|
|
|
UART0_WaitTxDone_Us(5000); |
|
|
|
|
|
|
|
|
|
|
|
// ? (B) 송신
|
|
|
// ? (B) 송신
|
|
|
|
|
|
g_uart0_tx_done = 0; |
|
|
RS485_PRINT(cmdline); |
|
|
RS485_PRINT(cmdline); |
|
|
|
|
|
|
|
|
// ? (C) “진짜 송신 끝 + RX 전환”까지 대기
|
|
|
// ? (C) TX 끝 + rs485_set_tx(0)까지 보장 (sendend callback이 set)
|
|
|
UART0_WaitTxDone_Us(5000); |
|
|
UART0_WaitTxDone_Flag(3000); // 3ms 안에 충분
|
|
|
|
|
|
delay_us(30); |
|
|
|
|
|
|
|
|
// ? (D) 턴어라운드 여유 (보드/트랜시버 따라 200~500us 권장)
|
|
|
// ? (D) 턴어라운드 (460800이면 100~300us면 충분)
|
|
|
delay_us(300); |
|
|
delay_us(120); |
|
|
|
|
|
|
|
|
// ? (E) 응답 대기 (예: 20ms)
|
|
|
|
|
|
{ |
|
|
{ |
|
|
uint32_t wait_us = 80000; |
|
|
uint32_t total_wait_us = 4000; // 필요시 20~30ms까지 올려도 됨
|
|
|
while (!g_rs485_bridge_done && wait_us >= 50) { |
|
|
char linebuf[32]; |
|
|
RS485_Bridge_DrainToPC(); |
|
|
|
|
|
delay_us(50); |
|
|
while (total_wait_us > 0) { |
|
|
wait_us -= 50; |
|
|
int got = RS485_Bridge_ReadLine(linebuf, sizeof(linebuf), 2000); // 2ms chunk
|
|
|
|
|
|
if (got) { |
|
|
|
|
|
// 에코(x02v\n)나 잡음 라인은 무시하고 계속 본다
|
|
|
|
|
|
if (Is_V_Response_For(linebuf, addr)) { |
|
|
|
|
|
PC_PrintLine_CRLF(linebuf); |
|
|
|
|
|
g_rs485_bridge_active = 0; |
|
|
|
|
|
return; // 성공
|
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
// 2ms 동안 라인 못 만들었으면 남은 시간 줄이고 계속
|
|
|
|
|
|
} |
|
|
|
|
|
if (total_wait_us > 2000) total_wait_us -= 2000; |
|
|
|
|
|
else total_wait_us = 0; |
|
|
} |
|
|
} |
|
|
RS485_Bridge_DrainToPC(); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
g_rs485_bridge_active = 0; |
|
|
g_rs485_bridge_active = 0; |
|
|
|
|
|
|
|
|
if (!g_rs485_bridge_done) { |
|
|
delay_us(300); |
|
|
send_n_response(addr); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 3번 다 실패하면 Nxx
|
|
|
|
|
|
send_n_response(addr); |
|
|
|
|
|
|
|
|
// 다음을 위해 RX 재arm
|
|
|
// 다음을 위해 RX 재arm
|
|
|
rs485_rx_done = 0; |
|
|
rs485_rx_done = 0; |
|
|
rs485_rx_index = 0; |
|
|
rs485_rx_index = 0; |
|
|
@ -474,6 +627,8 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint |
|
|
char orig_line[UART_RX_BUF_SIZE]; |
|
|
char orig_line[UART_RX_BUF_SIZE]; |
|
|
int orig_len = 0; |
|
|
int orig_len = 0; |
|
|
|
|
|
|
|
|
|
|
|
char msg[64]; |
|
|
|
|
|
|
|
|
s_prefix_mode = PREFIX_NONE; |
|
|
s_prefix_mode = PREFIX_NONE; |
|
|
|
|
|
|
|
|
idx = build_line_from_rx(rx_buf, (int)rx_len, line, (int)sizeof(line)); |
|
|
idx = build_line_from_rx(rx_buf, (int)rx_len, line, (int)sizeof(line)); |
|
|
@ -529,6 +684,13 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint |
|
|
uint8_t anaout_on = 0; |
|
|
uint8_t anaout_on = 0; |
|
|
uint8_t check_on = 1; |
|
|
uint8_t check_on = 1; |
|
|
|
|
|
|
|
|
|
|
|
uint32_t total_us = BRIDGE_TOTAL_WAIT_US; |
|
|
|
|
|
uint32_t idle_us = 0; |
|
|
|
|
|
uint16_t last_seq = g_rs485_bridge_seq; |
|
|
|
|
|
uint8_t got_any = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int r = parse_x_prefix(line, idx, &addr, &ch, &mode, &hash_on, &anaout_on, &check_on, &payload_pos); |
|
|
int r = parse_x_prefix(line, idx, &addr, &ch, &mode, &hash_on, &anaout_on, &check_on, &payload_pos); |
|
|
|
|
|
|
|
|
if (r == -1) { |
|
|
if (r == -1) { |
|
|
@ -541,10 +703,15 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint |
|
|
if (ch < 1 || ch > 20) { OUT_PRINT(src, "Err:ch_range\r\n"); return; } |
|
|
if (ch < 1 || ch > 20) { OUT_PRINT(src, "Err:ch_range\r\n"); return; } |
|
|
|
|
|
|
|
|
/* addr mismatch */ |
|
|
/* addr mismatch */ |
|
|
if (addr != g_fixed_addr) { |
|
|
if (addr != g_fixed_addr) |
|
|
|
|
|
{ |
|
|
/* 보드1(addr=0) + PC에서만 RS485 중계 */ |
|
|
/* 보드1(addr=0) + PC에서만 RS485 중계 */ |
|
|
if (g_fixed_addr == 0 && src == CMD_SRC_PC) { |
|
|
if (g_fixed_addr == 0 && src == CMD_SRC_PC) |
|
|
|
|
|
{ |
|
|
|
|
|
uint32_t total_us; |
|
|
|
|
|
uint32_t idle_us; |
|
|
|
|
|
uint16_t last_seq; |
|
|
|
|
|
uint8_t got_any; |
|
|
|
|
|
|
|
|
g_rs485_bridge_active = 1; |
|
|
g_rs485_bridge_active = 1; |
|
|
g_rs485_bridge_done = 0; |
|
|
g_rs485_bridge_done = 0; |
|
|
@ -555,6 +722,7 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint |
|
|
rs485_rx_length = 0; |
|
|
rs485_rx_length = 0; |
|
|
R_UART0_Receive((uint8_t*)&rs485_rx_buffer[0], 1); |
|
|
R_UART0_Receive((uint8_t*)&rs485_rx_buffer[0], 1); |
|
|
|
|
|
|
|
|
|
|
|
/* ---- RS485로 명령 송신 ---- */ |
|
|
{ |
|
|
{ |
|
|
static char txbuf[UART_RX_BUF_SIZE + 4]; |
|
|
static char txbuf[UART_RX_BUF_SIZE + 4]; |
|
|
int n = orig_len; |
|
|
int n = orig_len; |
|
|
@ -566,44 +734,52 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint |
|
|
txbuf[n++] = '\n'; |
|
|
txbuf[n++] = '\n'; |
|
|
txbuf[n] = '\0'; |
|
|
txbuf[n] = '\0'; |
|
|
|
|
|
|
|
|
/* (권장) UART0 TX busy면 잠깐 대기 후 송신 */ |
|
|
/* UART0 TX busy면 잠깐 대기 후 송신 */ |
|
|
{ |
|
|
{ |
|
|
unsigned long g = 0; |
|
|
unsigned long g = 0; |
|
|
while (g_uart0_tx_count != 0U) { |
|
|
while (g_uart0_tx_count != 0U) { |
|
|
if (g++ > 3000000UL) break; /* 무한 대기 방지 */ |
|
|
if (g++ > 3000000UL) break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
RS485_PRINT(txbuf); |
|
|
RS485_PRINT(txbuf); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
{ |
|
|
/* ---- 응답 드레인: “수신이 한 번이라도 있었고”, 이후 3ms idle이면 종료 ---- */ |
|
|
/* 시간 기반 대기(us)로 변경: OWT/I2C 동작 포함해서 여유 있게 */ |
|
|
total_us = BRIDGE_TOTAL_WAIT_US; |
|
|
uint32_t wait_us = 80000; /* 80ms (필요시 50~120ms로 조정) */ |
|
|
idle_us = 0; |
|
|
|
|
|
last_seq = g_rs485_bridge_seq; |
|
|
|
|
|
got_any = 0; |
|
|
|
|
|
|
|
|
if (strstr(orig_line, ":OR") || strstr(orig_line, ":or")) { |
|
|
|
|
|
wait_us = 3000000; // 300ms
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
while (!g_rs485_bridge_done && wait_us >= 50U) { |
|
|
while (total_us >= 50U) { |
|
|
RS485_Bridge_DrainToPC(); |
|
|
RS485_Bridge_DrainToPC(); |
|
|
delay_us(50); |
|
|
delay_us(50); |
|
|
wait_us -= 50U; |
|
|
total_us -= 50U; |
|
|
|
|
|
|
|
|
|
|
|
if (g_rs485_bridge_done) break; // 1) <end>면 종료
|
|
|
|
|
|
|
|
|
|
|
|
if (g_rs485_bridge_seq != last_seq) { |
|
|
|
|
|
last_seq = g_rs485_bridge_seq; |
|
|
|
|
|
got_any = 1; |
|
|
|
|
|
idle_us = 0; // 2) 데이터 들어오면 idle 리셋
|
|
|
|
|
|
} else if (got_any) { |
|
|
|
|
|
idle_us += 50U; // 3) 한 번이라도 받은 뒤
|
|
|
|
|
|
if (idle_us >= BRIDGE_IDLE_DONE_US) break; // 3ms 무응답이면 종료
|
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
RS485_Bridge_DrainToPC(); |
|
|
RS485_Bridge_DrainToPC(); |
|
|
g_rs485_bridge_active = 0; |
|
|
g_rs485_bridge_active = 0; |
|
|
|
|
|
|
|
|
/* 다음을 위해 RX 재arm */ |
|
|
if (total_us < 50U) { |
|
|
rs485_rx_done = 0; |
|
|
|
|
|
rs485_rx_index = 0; |
|
|
|
|
|
rs485_rx_length = 0; |
|
|
|
|
|
R_UART0_Receive((uint8_t*)&rs485_rx_buffer[0], 1); |
|
|
|
|
|
|
|
|
|
|
|
if (!g_rs485_bridge_done) { |
|
|
|
|
|
OUT_PRINT(CMD_SRC_PC, "Err:rs485_timeout\r\n"); |
|
|
OUT_PRINT(CMD_SRC_PC, "Err:rs485_timeout\r\n"); |
|
|
|
|
|
sprintf(msg, "Err:rs485_timeout fef=%lu ovf=%lu pef=%lu\r\n", |
|
|
|
|
|
g_uart0_err_fef, g_uart0_err_ovf, g_uart0_err_pef); |
|
|
|
|
|
OUT_PRINT(CMD_SRC_PC, msg); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return; /* 슬레이브는 addr mismatch 무시 */ |
|
|
return; /* 슬레이브는 addr mismatch 무시 */ |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -787,6 +963,7 @@ void main(void) |
|
|
|
|
|
|
|
|
void R_MAIN_UserInit(void) |
|
|
void R_MAIN_UserInit(void) |
|
|
{ |
|
|
{ |
|
|
|
|
|
/* Start user code. Do not edit comment generated here */ |
|
|
EI(); |
|
|
EI(); |
|
|
R_PORT_Create(); |
|
|
R_PORT_Create(); |
|
|
|
|
|
|
|
|
@ -794,5 +971,7 @@ void R_MAIN_UserInit(void) |
|
|
|
|
|
|
|
|
DipSwitch_Init(); |
|
|
DipSwitch_Init(); |
|
|
g_fixed_addr = DipSwitch_ReadAddr_0to31(); |
|
|
g_fixed_addr = DipSwitch_ReadAddr_0to31(); |
|
|
|
|
|
/* End user code. Do not edit comment generated here */ |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* End user code. Do not edit comment generated here */ |