@ -654,7 +654,7 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint
if ( v_addr > 31 ) { OUT_PRINT ( src , " Err:addr_range \r \n " ) ; return ; }
if ( v_addr > 31 ) { OUT_PRINT ( src , " Err:addr_range \r \n " ) ; return ; }
/* 보드1 (addr=0) + PC에서 x00v => 00~31 스캔 */
/* 보드0 (addr=0) + PC에서 x00v => 00~31 스캔 */
if ( g_fixed_addr = = 0 & & src = = CMD_SRC_PC & & v_addr = = 0 ) {
if ( g_fixed_addr = = 0 & & src = = CMD_SRC_PC & & v_addr = = 0 ) {
/* 자기 자신(00) 응답 */
/* 자기 자신(00) 응답 */
@ -674,19 +674,97 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint
return ;
return ;
}
}
{
/* =========================
uint8_t off_addr = 0 ;
* 1.5 ) xNNo 처 리 ( OFF ) ? 수 정 : 보 드 0 이 다 른 주 소 면 RS485 중 계
if ( parse_x_o_cmd ( line , idx , & off_addr ) ) {
* = = = = = = = = = = = = = = = = = = = = = = = = = */
{
uint8_t off_addr = 0 ;
if ( parse_x_o_cmd ( line , idx , & off_addr ) ) {
if ( off_addr > 31 ) { OUT_PRINT ( src , " Err:addr_range \r \n " ) ; return ; }
/* 1) 내 주소면 로컬 OFF */
if ( off_addr = = g_fixed_addr ) {
Cal_Init ( ) ; // “전체 OFF(기본 상태)”
OUT_PRINT ( src , " <ACK>OFF \r \n " ) ;
send_end_response ( ) ; // GUI가 멀티라인이면 <end> 편함
return ;
}
/* 2) 보드0(addr=0) + PC에서 들어온 x01o~ 는 RS485로 브릿지 */
if ( g_fixed_addr = = 0 & & src = = CMD_SRC_PC ) {
g_rs485_bridge_active = 1 ;
g_rs485_bridge_done = 0 ;
RS485_Bridge_ResetFifo ( ) ;
rs485_rx_done = 0 ;
rs485_rx_index = 0 ;
rs485_rx_length = 0 ;
R_UART0_Receive ( ( uint8_t * ) & rs485_rx_buffer [ 0 ] , 1 ) ;
/* ---- RS485로 명령 송신 ---- */
{
static char txbuf [ UART_RX_BUF_SIZE + 4 ] ;
int n = idx ; // line 길이
if ( n > ( int ) sizeof ( txbuf ) - 3 ) n = ( int ) sizeof ( txbuf ) - 3 ;
memcpy ( txbuf , line , ( size_t ) n ) ;
txbuf [ n + + ] = ' \r ' ;
txbuf [ n + + ] = ' \n ' ;
txbuf [ n ] = ' \0 ' ;
/* UART0 TX busy면 잠깐 대기 후 송신 */
{
unsigned long g = 0 ;
while ( g_uart0_tx_count ! = 0U ) {
if ( g + + > 3000000UL ) break ;
}
}
RS485_PRINT ( txbuf ) ;
}
/* ---- 응답 드레인: 데이터 1번이라도 오고 3ms idle이면 종료 ---- */
{
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 ;
while ( total_us > = 50U ) {
RS485_Bridge_DrainToPC ( ) ;
delay_us ( 50 ) ;
total_us - = 50U ;
if ( g_rs485_bridge_done ) break ; // <end>면 종료
if ( g_rs485_bridge_seq ! = last_seq ) {
last_seq = g_rs485_bridge_seq ;
got_any = 1 ;
idle_us = 0 ;
} else if ( got_any ) {
idle_us + = 50U ;
if ( idle_us > = BRIDGE_IDLE_DONE_US ) break ; // 3ms idle
}
}
RS485_Bridge_DrainToPC ( ) ;
g_rs485_bridge_active = 0 ;
if ( off_addr = = g_fixed_addr ) {
if ( total_us < 50U ) {
Cal_Init ( ) ; // 여기 한 줄로 “전체 OFF(기본 상태)”로
OUT_PRINT ( CMD_SRC_PC , " Err:rs485_timeout \r \n " ) ;
OUT_PRINT ( src , " <ACK>OFF \r \n " ) ;
sprintf ( msg , " Err:rs485_timeout fef=%lu ovf=%lu pef=%lu \r \n " ,
send_end_response ( ) ; // GUI가 멀티라인이면 <end>가 편함
g_uart0_err_fef , g_uart0_err_ovf , g_uart0_err_pef ) ;
OUT_PRINT ( CMD_SRC_PC , msg ) ;
}
}
}
return ; // 슬레이브는 mismatch면 그냥 무시
}
}
return ;
}
}
}
/* RS485 중계용 원본 저장 */
/* RS485 중계용 원본 저장 */
orig_len = idx ;
orig_len = idx ;
@ -706,12 +784,10 @@ 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 total_us = BRIDGE_TOTAL_WAIT_US ;
uint32_t idle_us = 0 ;
uint32_t idle_us = 0 ;
uint16_t last_seq = g_rs485_bridge_seq ;
uint16_t last_seq = g_rs485_bridge_seq ;
uint8_t got_any = 0 ;
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 ) ;
@ -725,85 +801,84 @@ 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 중계 */
/* 보드0(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 total_us ;
uint32_t idle_us ;
uint32_t idle_us ;
uint16_t last_seq ;
uint16_t last_seq ;
uint8_t got_any ;
uint8_t got_any ;
g_rs485_bridge_active = 1 ;
g_rs485_bridge_active = 1 ;
g_rs485_bridge_done = 0 ;
g_rs485_bridge_done = 0 ;
RS485_Bridge_ResetFifo ( ) ;
RS485_Bridge_ResetFifo ( ) ;
rs485_rx_done = 0 ;
rs485_rx_done = 0 ;
rs485_rx_index = 0 ;
rs485_rx_index = 0 ;
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로 명령 송신 ---- */
/* ---- 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 ;
if ( n > ( int ) sizeof ( txbuf ) - 3 ) n = ( int ) sizeof ( txbuf ) - 3 ;
if ( n > ( int ) sizeof ( txbuf ) - 3 ) n = ( int ) sizeof ( txbuf ) - 3 ;
memcpy ( txbuf , orig_line , ( size_t ) n ) ;
memcpy ( txbuf , orig_line , ( size_t ) n ) ;
txbuf [ n + + ] = ' \r ' ;
txbuf [ n + + ] = ' \r ' ;
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이면 종료 ---- */
/* ---- 응답 드레인 ---- */
total_us = BRIDGE_TOTAL_WAIT_US ;
total_us = BRIDGE_TOTAL_WAIT_US ;
idle_us = 0 ;
idle_us = 0 ;
last_seq = g_rs485_bridge_seq ;
last_seq = g_rs485_bridge_seq ;
got_any = 0 ;
got_any = 0 ;
while ( total_us > = 50U ) {
while ( total_us > = 50U ) {
RS485_Bridge_DrainToPC ( ) ;
RS485_Bridge_DrainToPC ( ) ;
delay_us ( 50 ) ;
delay_us ( 50 ) ;
total_us - = 50U ;
total_us - = 50U ;
if ( g_rs485_bridge_done ) break ;
if ( g_rs485_bridge_done ) break ; // 1) <end>면 종료
if ( g_rs485_bridge_seq ! = last_seq ) {
if ( g_rs485_bridge_seq ! = last_seq ) {
last_seq = g_rs485_bridge_seq ;
last_seq = g_rs485_bridge_seq ;
got_any = 1 ;
got_any = 1 ;
idle_us = 0 ;
idle_us = 0 ; // 2) 데이터 들어오면 idle 리셋
} else if ( got_any ) {
} else if ( got_any ) {
idle_us + = 50U ;
idle_us + = 50U ; // 3) 한 번이라도 받은 뒤
if ( idle_us > = BRIDGE_IDLE_DONE_US ) break ;
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 ;
if ( total_us < 50U ) {
if ( total_us < 50U ) {
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 " ,
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 ) ;
g_uart0_err_fef , g_uart0_err_ovf , g_uart0_err_pef ) ;
OUT_PRINT ( CMD_SRC_PC , msg ) ;
OUT_PRINT ( CMD_SRC_PC , msg ) ;
}
}
}
}
return ; /* 슬레이브는 addr mismatch 무시 */
return ; /* 슬레이브는 addr mismatch 무시 */
}
}
/* addr == g_fixed_addr: 로컬 처리(채널 선택 등) */
/* addr == g_fixed_addr: 로컬 처리(채널 선택 등) */
if ( mode = = ' C ' ) {
if ( mode = = ' C ' ) {
@ -906,6 +981,7 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint
process_cmd_by_prefix ( s_prefix_mode , proto , id , cmd , byte_len ) ;
process_cmd_by_prefix ( s_prefix_mode , proto , id , cmd , byte_len ) ;
}
}
/* =========================
/* =========================
* Main loop handler
* Main loop handler
* = = = = = = = = = = = = = = = = = = = = = = = = = */
* = = = = = = = = = = = = = = = = = = = = = = = = = */