Browse Source

code refactoring

old-version
gudae01 1 month ago
parent
commit
2dcaa0c406
  1. BIN
      DefaultBuild/app_cmd_parser.obj
  2. BIN
      DefaultBuild/app_owi_service.obj
  3. BIN
      DefaultBuild/app_result.obj
  4. BIN
      DefaultBuild/app_scheduler.obj
  5. BIN
      DefaultBuild/gatectrl.obj
  6. BIN
      DefaultBuild/multical.abs
  7. 4
      DefaultBuild/multical.clnk
  8. 700
      DefaultBuild/multical.map
  9. 2721
      DefaultBuild/multical.mot
  10. BIN
      DefaultBuild/owi.obj
  11. BIN
      DefaultBuild/r_main.obj
  12. BIN
      DefaultBuild/uart.obj
  13. 38
      QualityReport(multical,DefaultBuild).txt
  14. 74
      app_cmd_parser.c
  15. 8
      app_cmd_parser.h
  16. 49
      app_owi_service.c
  17. 20
      app_owi_service.h
  18. 12
      app_result.c
  19. 7
      app_result.h
  20. 44
      app_scheduler.c
  21. 11
      app_scheduler.h
  22. 28
      app_types.h
  23. 245
      multical.guseo.mtud
  24. 930
      multical.mtpj
  25. 15
      multical.rcpe
  26. 372
      owi.c
  27. 33
      owi.h
  28. 510
      r_main.c
  29. 20
      uart.c

BIN
DefaultBuild/app_cmd_parser.obj

Binary file not shown.

BIN
DefaultBuild/app_owi_service.obj

Binary file not shown.

BIN
DefaultBuild/app_result.obj

Binary file not shown.

BIN
DefaultBuild/app_scheduler.obj

Binary file not shown.

BIN
DefaultBuild/gatectrl.obj

Binary file not shown.

BIN
DefaultBuild/multical.abs

Binary file not shown.

4
DefaultBuild/multical.clnk

@ -14,6 +14,10 @@
-Input=DefaultBuild\delay.obj -Input=DefaultBuild\delay.obj
-Input=DefaultBuild\dipSwitch.obj -Input=DefaultBuild\dipSwitch.obj
-Input=DefaultBuild\gatectrl.obj -Input=DefaultBuild\gatectrl.obj
-Input=DefaultBuild\app_result.obj
-Input=DefaultBuild\app_cmd_parser.obj
-Input=DefaultBuild\app_scheduler.obj
-Input=DefaultBuild\app_owi_service.obj
-SECURITY_ID=00000000000000000000 -SECURITY_ID=00000000000000000000
-DEVICE=C:\Program Files (x86)\Renesas Electronics\CS+\CC\Device\RL78\Devicefile\DR5F10PPJ.DVF -DEVICE=C:\Program Files (x86)\Renesas Electronics\CS+\CC\Device\RL78\Devicefile\DR5F10PPJ.DVF
-DEBug -DEBug

700
DefaultBuild/multical.map

File diff suppressed because it is too large

2721
DefaultBuild/multical.mot

File diff suppressed because it is too large

BIN
DefaultBuild/owi.obj

Binary file not shown.

BIN
DefaultBuild/r_main.obj

Binary file not shown.

BIN
DefaultBuild/uart.obj

Binary file not shown.

38
QualityReport(multical,DefaultBuild).txt

@ -1,13 +1,13 @@
QualityReport QualityReport
2026년 2월 19일 목요일 오후 2:59:04 2026년 3월 10일 화요일 오전 5:47:16
------ Start build(multical, DefaultBuild) ------ ------ Start build(multical, DefaultBuild) ------
------ Build ended(Error:0, Warning:0)(multical, DefaultBuild) ------ ------ Build ended(Error:0, Warning:0)(multical, DefaultBuild) ------
--- SHA1 hash value of output files --- --- SHA1 hash value of output files ---
C:\Users\MSI\Desktop\amosense\multicabration_4251_fw\new_fw\DefaultBuild\multical.abs: 735b6f2af28a6160c5338fc97eefe8833ca8808e C:\Users\guseo\Desktop\new_fw\DefaultBuild\multical.abs: 92e3ea10b4980f36a64be50e2742704b9d4d675b
C:\Users\MSI\Desktop\amosense\multicabration_4251_fw\new_fw\DefaultBuild\multical.mot: 70d861b12cebf3d2940f2245d70e63b96dcb7b1d C:\Users\guseo\Desktop\new_fw\DefaultBuild\multical.mot: 63552288c18df90e0de3ff438441e543570b1204
--- System Information --- --- System Information ---
@ -18,7 +18,7 @@ C:\Users\MSI\Desktop\amosense\multicabration_4251_fw\new_fw\DefaultBuild\multica
*.NET Framework Version *.NET Framework Version
Microsoft .NET Framework 4 [.NET 4.8 or later] (533509) Microsoft .NET Framework 4 [.NET 4.8 or later] (533509)
*WebView2 Version *WebView2 Version
144.0.3719.115 145.0.3800.97
--- Application Information --- --- Application Information ---
*Product Name *Product Name
@ -29,21 +29,19 @@ C:\Users\MSI\Desktop\amosense\multicabration_4251_fw\new_fw\DefaultBuild\multica
V9.13.00.05 [12 Nov 2024] V9.13.00.05 [12 Nov 2024]
*Assembly Version *Assembly Version
3.12.10.1 3.12.10.1
*Sales Area
Japan
*Product License *Product License
*Execution Place *Execution Place
C:\Program Files (x86)\Renesas Electronics\CS+\CC C:\Program Files (x86)\Renesas Electronics\CS+\CC
*Memory Usage *Memory Usage
*Private Working Set *Private Working Set
404 MB 418 MB
*Number of GDI Objects *Number of GDI Objects
2585 2736
*Number of USER Objects *Number of USER Objects
1389 1718
*Opened Files *Opened Files
25 editors, 25 files, 208 KB 20 editors, 20 files, 121 KB
--- Build Tool Plug-in Information --- --- Build Tool Plug-in Information ---
RH850 Build tool CC-RH Plug-in RH850 Build tool CC-RH Plug-in
@ -148,20 +146,6 @@ Editor plug-in DLL
1.1.0.0 1.1.0.0
*DLL File Name *DLL File Name
SEditor.dll SEditor.dll
Smart Configurator for RH850 Communication Plug-in
*Version
V1.02.10.03 [14 Nov 2024]
*Assembly Version
1.0.0.0
*DLL File Name
SmcCodePartRH850.dll
Smart Configurator for RL78 Communication Plug-in
*Version
V1.00.11.01 [25 Nov 2024]
*Assembly Version
1.0.0.0
*DLL File Name
SmcCodePartRL78.dll
Stack Usage Tracer Stack Usage Tracer
*Version *Version
V1.05.00.02 [30 Jul 2014] V1.05.00.02 [30 Jul 2014]
@ -193,7 +177,7 @@ Device Information Common Interface
DeviceInformation.dll DeviceInformation.dll
--- Main Project Information --- --- Main Project Information ---
C:\Users\MSI\Desktop\amosense\multicabration_4251_fw\new_fw\multical.mtpj C:\Users\guseo\Desktop\new_fw\multical.mtpj
Microcontroller Information Microcontroller Information
*R5F10PPJ *R5F10PPJ
*File Name: Version *File Name: Version
@ -234,10 +218,6 @@ Other Tool Information
*Version *Version
V4.13.00.03 [24 May 2023] V4.13.00.03 [24 May 2023]
*None
*None
*Code Generator *Code Generator
Other Information Other Information
*RL78/F14 Code Library *RL78/F14 Code Library

74
app_cmd_parser.c

@ -0,0 +1,74 @@
#include "app_cmd_parser.h"
#include <string.h>
#include <ctype.h>
static int parse_x_v_addr(const char *line, uint8_t *addr)
{
if (!line || !addr) return 0;
if (!(line[0] == 'x' || line[0] == 'X')) return 0;
if (!isdigit((unsigned char)line[1]) || !isdigit((unsigned char)line[2])) return 0;
if (!(line[3] == 'v' || line[3] == 'V')) return 0;
if (line[4] != '\0') return 0;
*addr = (uint8_t)((line[1] - '0') * 10 + (line[2] - '0'));
return 1;
}
static int find_payload_pos(const char *line)
{
const char *p = strchr(line, ':');
if (!p) return -1;
return (int)(p - line + 1);
}
int app_cmd_parse_line(app_cmd_src_t src, const char *line, app_job_t *job)
{
uint8_t addr = 0;
int payload_pos;
if (!line || !job) return 0;
memset(job, 0, sizeof(*job));
job->src = src;
if (line[0] != 'x' && line[0] != 'X') {
job->type = APP_JOB_LOCAL_EXEC;
strncpy(job->line, line, sizeof(job->line) - 1);
job->line[sizeof(job->line) - 1] = '\0';
return 1;
}
if (parse_x_v_addr(line, &addr)) {
job->type = APP_JOB_SCAN_ADDR;
job->addr = addr;
strncpy(job->line, line, sizeof(job->line) - 1);
job->line[sizeof(job->line) - 1] = '\0';
return 1;
}
payload_pos = find_payload_pos(line);
if (payload_pos >= 0) {
const char *payload = &line[payload_pos];
if ((payload[0] == 'o' || payload[0] == 'O') &&
(payload[1] == 'w' || payload[1] == 'W')) {
job->type = APP_JOB_PROTO_OW;
strncpy(job->line, line, sizeof(job->line) - 1);
job->line[sizeof(job->line) - 1] = '\0';
return 1;
}
if ((payload[0] == 'o' || payload[0] == 'O') &&
(payload[1] == 'r' || payload[1] == 'R')) {
job->type = APP_JOB_PROTO_OR;
strncpy(job->line, line, sizeof(job->line) - 1);
job->line[sizeof(job->line) - 1] = '\0';
return 1;
}
}
job->type = APP_JOB_FORWARD_LINE;
strncpy(job->line, line, sizeof(job->line) - 1);
job->line[sizeof(job->line) - 1] = '\0';
return 1;
}

8
app_cmd_parser.h

@ -0,0 +1,8 @@
#ifndef APP_CMD_PARSER_H
#define APP_CMD_PARSER_H
#include "app_types.h"
int app_cmd_parse_line(app_cmd_src_t src, const char *line, app_job_t *job);
#endif

49
app_owi_service.c

@ -0,0 +1,49 @@
#include "app_owi_service.h"
#include "owi.h"
#include <string.h>
static app_owi_result_t app_owi_from_raw(const owi_io_result_t *io)
{
app_owi_result_t r;
uint16_t i;
memset(&r, 0, sizeof(r));
if (!io) {
r.ok = 0;
return r;
}
r.ok = io->ok;
r.timeout = io->timeout;
r.read_len = io->read_len;
r.timeout_byte_index = io->timeout_byte_index;
r.timeout_bit_index = io->timeout_bit_index;
for (i = 0; i < io->read_len && i < OWI_IO_MAX_BYTES; i++) {
r.data[i] = io->data[i];
}
return r;
}
app_owi_result_t app_owi_read_basic(uint8_t id, int length)
{
owi_io_result_t io;
OWI_ReadBytesRaw(length, id, &io);
return app_owi_from_raw(&io);
}
app_owi_result_t app_owi_write_basic(uint8_t id, const uint8_t *tx_data, uint8_t tx_len)
{
owi_io_result_t io;
OWI_CommandModeRaw(tx_data, tx_len, id, &io);
return app_owi_from_raw(&io);
}
app_owi_result_t app_owi_write_t_basic(uint8_t id, const uint8_t *tx_data, uint8_t tx_len)
{
owi_io_result_t io;
OWI_T_CommandModeRaw(tx_data, tx_len, id, &io);
return app_owi_from_raw(&io);
}

20
app_owi_service.h

@ -0,0 +1,20 @@
#ifndef APP_OWI_SERVICE_H
#define APP_OWI_SERVICE_H
#include "r_cg_macrodriver.h"
#include "owi.h"
typedef struct {
uint8_t ok;
uint8_t timeout;
uint16_t read_len;
uint8_t data[OWI_IO_MAX_BYTES];
uint16_t timeout_byte_index;
uint8_t timeout_bit_index;
} app_owi_result_t;
app_owi_result_t app_owi_read_basic(uint8_t id, int length);
app_owi_result_t app_owi_write_basic(uint8_t id, const uint8_t *tx_data, uint8_t tx_len);
app_owi_result_t app_owi_write_t_basic(uint8_t id, const uint8_t *tx_data, uint8_t tx_len);
#endif

12
app_result.c

@ -0,0 +1,12 @@
#include "app_result.h"
#include "uart.h"
void app_result_print_ok(const char *msg)
{
uart1_send_string(msg);
}
void app_result_print_err(const char *msg)
{
uart1_send_string(msg);
}

7
app_result.h

@ -0,0 +1,7 @@
#ifndef APP_RESULT_H
#define APP_RESULT_H
void app_result_print_ok(const char *msg);
void app_result_print_err(const char *msg);
#endif

44
app_scheduler.c

@ -0,0 +1,44 @@
#include "app_scheduler.h"
#include <string.h>
#define APP_JOB_QUEUE_SIZE 8
static app_job_t g_queue[APP_JOB_QUEUE_SIZE];
static uint8_t g_head;
static uint8_t g_tail;
static uint8_t g_count;
void app_scheduler_init(void)
{
g_head = 0;
g_tail = 0;
g_count = 0;
memset(g_queue, 0, sizeof(g_queue));
}
int app_scheduler_push(const app_job_t *job)
{
if (!job) return 0;
if (g_count >= APP_JOB_QUEUE_SIZE) return 0;
g_queue[g_tail] = *job;
g_tail = (uint8_t)((g_tail + 1U) % APP_JOB_QUEUE_SIZE);
g_count++;
return 1;
}
int app_scheduler_pop(app_job_t *job)
{
if (!job) return 0;
if (g_count == 0) return 0;
*job = g_queue[g_head];
g_head = (uint8_t)((g_head + 1U) % APP_JOB_QUEUE_SIZE);
g_count--;
return 1;
}
int app_scheduler_is_empty(void)
{
return (g_count == 0U);
}

11
app_scheduler.h

@ -0,0 +1,11 @@
#ifndef APP_SCHEDULER_H
#define APP_SCHEDULER_H
#include "app_types.h"
void app_scheduler_init(void);
int app_scheduler_push(const app_job_t *job);
int app_scheduler_pop(app_job_t *job);
int app_scheduler_is_empty(void);
#endif

28
app_types.h

@ -0,0 +1,28 @@
#ifndef APP_TYPES_H
#define APP_TYPES_H
#include "r_cg_macrodriver.h"
typedef enum {
APP_CMD_SRC_PC = 0,
APP_CMD_SRC_RS485 = 1
} app_cmd_src_t;
typedef enum {
APP_JOB_NONE = 0,
APP_JOB_SCAN_ADDR,
APP_JOB_PROTO_OW,
APP_JOB_PROTO_OR,
APP_JOB_FORWARD_LINE,
APP_JOB_LOCAL_EXEC
} app_job_type_t;
typedef struct {
app_job_type_t type;
app_cmd_src_t src;
uint8_t addr;
uint8_t channel;
char line[320];
} app_job_t;
#endif

245
multical.guseo.mtud

File diff suppressed because one or more lines are too long

930
multical.mtpj

File diff suppressed because it is too large

15
multical.rcpe

@ -51,6 +51,15 @@
<Path>gatectrl.h</Path> <Path>gatectrl.h</Path>
<Path>gatectrl.c</Path> <Path>gatectrl.c</Path>
</Category> </Category>
<Path>app_types.h</Path>
<Path>app_result.c</Path>
<Path>app_result.h</Path>
<Path>app_cmd_parser.c</Path>
<Path>app_cmd_parser.h</Path>
<Path>app_scheduler.c</Path>
<Path>app_scheduler.h</Path>
<Path>app_owi_service.c</Path>
<Path>app_owi_service.h</Path>
</Category> </Category>
</Files> </Files>
<DebugOptions> <DebugOptions>
@ -79,6 +88,10 @@
<Path>DefaultBuild\delay.obj</Path> <Path>DefaultBuild\delay.obj</Path>
<Path>DefaultBuild\dipSwitch.obj</Path> <Path>DefaultBuild\dipSwitch.obj</Path>
<Path>DefaultBuild\gatectrl.obj</Path> <Path>DefaultBuild\gatectrl.obj</Path>
<Path>DefaultBuild\app_result.obj</Path>
<Path>DefaultBuild\app_cmd_parser.obj</Path>
<Path>DefaultBuild\app_scheduler.obj</Path>
<Path>DefaultBuild\app_owi_service.obj</Path>
</LinkOrder> </LinkOrder>
<CommonOptions> <CommonOptions>
<IncludePathForC>..\..\Documents\카카오톡 받은 파일\IDH1.1\IDH1.1</IncludePathForC> <IncludePathForC>..\..\Documents\카카오톡 받은 파일\IDH1.1\IDH1.1</IncludePathForC>
@ -379,7 +392,7 @@
&lt;/RTC1HZ&gt; &lt;/RTC1HZ&gt;
&lt;RXD0 Name="RXD0" Text="enable" /&gt; &lt;RXD0 Name="RXD0" Text="enable" /&gt;
&lt;ProjectName Name="PrjName" Text="multical" /&gt; &lt;ProjectName Name="PrjName" Text="multical" /&gt;
&lt;ProjectPath Name="PrjPath" Text="C:\Users\temp\Desktop\new_fw" /&gt; &lt;ProjectPath Name="PrjPath" Text="C:\Users\guseo\Desktop\new_fw" /&gt;
&lt;ProjectKind Name="PrjKind" Text="Project78K0R" /&gt; &lt;ProjectKind Name="PrjKind" Text="Project78K0R" /&gt;
&lt;DeviceName Name="DeviceName" Fixed="" Text="RL78F14" /&gt; &lt;DeviceName Name="DeviceName" Fixed="" Text="RL78F14" /&gt;
&lt;MCUName Name="MCUName" Text="RL78F14_100pin" /&gt; &lt;MCUName Name="MCUName" Text="RL78F14_100pin" /&gt;

372
owi.c

@ -4,27 +4,37 @@
#include <stdio.h> #include <stdio.h>
#include "uart.h" #include "uart.h"
#ifndef OWI_STRONG_DRIVE_HIGH_US
#define OWI_STRONG_DRIVE_HIGH_US 5u
#endif
/* 내부 상태 */ /* 내부 상태 */
static uint32_t bit_period_us = OWI_BIT_PERIOD_US; static uint32_t bit_period_us = OWI_BIT_PERIOD_US;
static uint8_t g_owi_timeout_latched = 0;
static uint16_t g_owi_last_timeout_byte_index = 0xFFFFu;
static uint8_t g_owi_last_timeout_bit_index = 0xFFu;
static uint16_t g_owi_current_read_byte_index = 0u;
static uint8_t g_owi_current_read_bit_index = 0u;
/* ========================================================= /* =========================================================
* GPIO helpers (P70 / HW Open-Drain) * GPIO helpers (P70 / HW Open-Drain)
* - LOW : + 0 * - LOW : + 0
* - HIGH : (Hi-Z) release (pull-up으로 ) * - HIGH : (Hi-Z) release
* ========================================================= */ * ========================================================= */
void GPIO_Clear(void) void GPIO_Clear(void)
{ {
/* latch low (P70만) */ OWI_PORT_POM |= (uint8_t)OWI_PIN_MASK;
OWI_PORT_P &= (uint8_t)~OWI_PIN_MASK; OWI_PORT_P &= (uint8_t)~OWI_PIN_MASK;
OWI_PORT_PM &= (uint8_t)~OWI_PIN_MASK;
/* output mode (P70만) */
OWI_PORT_PM &= (uint8_t)~OWI_PIN_MASK;
} }
void GPIO_Input(void) void GPIO_Input(void)
{ {
/* input mode (Hi-Z) */ OWI_PORT_POM |= (uint8_t)OWI_PIN_MASK;
OWI_PORT_PM |= (uint8_t)OWI_PIN_MASK; OWI_PORT_P |= (uint8_t)OWI_PIN_MASK;
OWI_PORT_PM |= (uint8_t)OWI_PIN_MASK;
} }
int GPIO_Read(void) int GPIO_Read(void)
@ -32,22 +42,80 @@ int GPIO_Read(void)
return (OWI_PORT_P & (uint8_t)OWI_PIN_MASK) ? 1 : 0; return (OWI_PORT_P & (uint8_t)OWI_PIN_MASK) ? 1 : 0;
} }
static void OWI_Release(void)
{
OWI_PORT_POM |= (uint8_t)OWI_PIN_MASK;
OWI_PORT_P |= (uint8_t)OWI_PIN_MASK;
OWI_PORT_PM |= (uint8_t)OWI_PIN_MASK;
}
static void GPIO_StrongDriveHighKick(uint32_t kick_us)
{
OWI_Release();
if (!GPIO_Read()) {
return;
}
OWI_PORT_POM &= (uint8_t)~OWI_PIN_MASK;
OWI_PORT_P |= (uint8_t)OWI_PIN_MASK;
OWI_PORT_PM &= (uint8_t)~OWI_PIN_MASK;
delay_us(kick_us);
OWI_PORT_POM |= (uint8_t)OWI_PIN_MASK;
OWI_Release();
}
void GPIO_ForceHighKick(void)
{
GPIO_StrongDriveHighKick(OWI_STRONG_DRIVE_HIGH_US);
}
static void OWI_DriveLow(void)
{
OWI_PORT_POM |= (uint8_t)OWI_PIN_MASK;
OWI_PORT_P &= (uint8_t)~OWI_PIN_MASK;
OWI_PORT_PM &= (uint8_t)~OWI_PIN_MASK;
}
/* =========================================================
* Diagnostics
* ========================================================= */
uint8_t OWI_HasTimeout(void)
{
return g_owi_timeout_latched;
}
void OWI_ClearTimeout(void)
{
g_owi_timeout_latched = 0;
g_owi_last_timeout_byte_index = 0xFFFFu;
g_owi_last_timeout_bit_index = 0xFFu;
}
uint16_t OWI_GetLastTimeoutByteIndex(void)
{
return g_owi_last_timeout_byte_index;
}
uint8_t OWI_GetLastTimeoutBitIndex(void)
{
return g_owi_last_timeout_bit_index;
}
/* ========================================================= /* =========================================================
* OWI Init * OWI Init
* ========================================================= */ * ========================================================= */
void OWI_Init(uint32_t bit_time_us) void OWI_Init(uint32_t bit_time_us)
{ {
bit_period_us = bit_time_us; bit_period_us = bit_time_us;
(void)bit_period_us; /* 현재 타이밍은 매크로(TBIT) 사용 */ (void)bit_period_us;
/* HW Open-Drain enable: P70만 */
OWI_PORT_POM |= (uint8_t)OWI_PIN_MASK; OWI_PORT_POM |= (uint8_t)OWI_PIN_MASK;
OWI_PORT_PU &= (uint8_t)~OWI_PIN_MASK;
/* 내부 Pull-up: 필요 시 사용(외부 풀업이면 꺼도 됨) */ OWI_ClearTimeout();
//OWI_PORT_PU |= (uint8_t)OWI_PIN_MASK;
OWI_PORT_PU &= (uint8_t)~OWI_PIN_MASK; // P70만 OFF
/* idle = release */
GPIO_Input(); GPIO_Input();
} }
@ -58,8 +126,9 @@ void OWI_Start(void)
{ {
GPIO_Clear(); GPIO_Clear();
delay_us(TSTART_HOLD); delay_us(TSTART_HOLD);
GPIO_Input(); GPIO_Input();
delay_us(TBIT / 2u); delay_us(TBIT);
} }
void OWI_Stop(void) void OWI_Stop(void)
@ -67,7 +136,21 @@ void OWI_Stop(void)
GPIO_Input(); GPIO_Input();
delay_us(TSTOP_LOW); delay_us(TSTOP_LOW);
delay_us(TIDLE); delay_us(TIDLE);
GPIO_Clear(); }
static void OWI_StopWrite(void)
{
OWI_Release();
delay_us(TSTOP_LOW);
delay_us(TIDLE);
}
static void OWI_StopRead(void)
{
OWI_DriveLow();
delay_us(TSTOP_LOW);
OWI_Release();
delay_us(TIDLE);
} }
void OWI_SecureStop(void) void OWI_SecureStop(void)
@ -80,12 +163,14 @@ void OWI_SecureStop(void)
for (i = 0; i < (int)SECURE_TOGGLE_COUNT; i++) { for (i = 0; i < (int)SECURE_TOGGLE_COUNT; i++) {
GPIO_Input(); GPIO_Input();
delay_us(SECURE_TOGGLE_HIGH); delay_us(SECURE_TOGGLE_HIGH);
GPIO_Clear(); GPIO_Clear();
delay_us(SECURE_TOGGLE_LOW); delay_us(SECURE_TOGGLE_LOW);
} }
GPIO_Input(); GPIO_Input();
delay_us(SECURE_HIGH); delay_us(SECURE_HIGH);
GPIO_Clear(); GPIO_Clear();
delay_us(TSTART_HOLD); delay_us(TSTART_HOLD);
GPIO_Input(); GPIO_Input();
@ -99,11 +184,10 @@ void OWI_WriteBit(int bit)
uint32_t t_low = bit ? (uint32_t)TLOW_1 : (uint32_t)TLOW_0; uint32_t t_low = bit ? (uint32_t)TLOW_1 : (uint32_t)TLOW_0;
uint32_t t_high = (uint32_t)TBIT - t_low; uint32_t t_high = (uint32_t)TBIT - t_low;
/* 기존 동작 순서 유지 */ OWI_Release();
GPIO_Input(); /* Release (High by pull-up) */
delay_us(t_high); delay_us(t_high);
GPIO_Clear(); /* Drive Low */ OWI_DriveLow();
delay_us(t_low); delay_us(t_low);
} }
@ -119,22 +203,26 @@ void OWI_WriteByte(uint8_t data)
uint8_t OWI_ReadBit(void) uint8_t OWI_ReadBit(void)
{ {
uint8_t bit; uint8_t bit;
int timeout = 500; int timeout = (int)(bit_period_us * 2u);
while (!(GPIO_Read()) && timeout-- > 0) { while (!(GPIO_Read()) && timeout-- > 0) {
delay_us(1); delay_us(1u);
} }
if (timeout <= 0) { if (timeout <= 0) {
HOST_PRINT("OWI Timeout\r\n"); if (!g_owi_timeout_latched) {
return 0xFF; g_owi_timeout_latched = 1;
g_owi_last_timeout_byte_index = g_owi_current_read_byte_index;
g_owi_last_timeout_bit_index = g_owi_current_read_bit_index;
}
return 0u;
} }
delay_us(50); delay_us((bit_period_us * 1u) / 2u);
bit = (uint8_t)GPIO_Read(); bit = (uint8_t)GPIO_Read();
delay_us(30); delay_us((bit_period_us * 2u) / 5u);
return bit; return (bit ? 1u : 0u);
} }
uint8_t OWI_ReadByte(void) uint8_t OWI_ReadByte(void)
@ -143,7 +231,11 @@ uint8_t OWI_ReadByte(void)
int i; int i;
for (i = 7; i >= 0; i--) { for (i = 7; i >= 0; i--) {
g_owi_current_read_bit_index = (uint8_t)(7 - i);
data |= (uint8_t)(OWI_ReadBit() << i); data |= (uint8_t)(OWI_ReadBit() << i);
if (OWI_HasTimeout()) {
break;
}
} }
return data; return data;
} }
@ -160,9 +252,17 @@ void OWI_T_ReadBytesAndPrint(int length)
uint8_t va0, va1; uint8_t va0, va1;
if (length > 129) length = 129; if (length > 129) length = 129;
if (length <= 0) return;
OWI_ClearTimeout();
for (i = 0; i < length; i++) { for (i = 0; i < length; i++) {
g_owi_current_read_byte_index = (uint16_t)i;
buf[i] = OWI_ReadByte(); buf[i] = OWI_ReadByte();
if (OWI_HasTimeout()) {
i++;
break;
}
} }
sprintf(uart_buf, "%02X ", buf[0]); sprintf(uart_buf, "%02X ", buf[0]);
@ -171,121 +271,116 @@ void OWI_T_ReadBytesAndPrint(int length)
for (i = 1; i < length; i += 2) { for (i = 1; i < length; i += 2) {
va0 = buf[i]; va0 = buf[i];
va1 = buf[i + 1]; va1 = (uint8_t)((i + 1 < length) ? buf[i + 1] : 0u);
delay(10000); delay_us(200);
sprintf(uart_buf, "%02X%02X ", va0, va1); sprintf(uart_buf, "%02X%02X ", va0, va1);
strcpy(tmp_buf, uart_buf); strcpy(tmp_buf, uart_buf);
HOST_PRINT(tmp_buf); HOST_PRINT(tmp_buf);
delay(10000); delay_us(200);
} }
} }
/* ========================================================= /* =========================================================
* Command / Diagnostic ( ) * Raw helpers
* ========================================================= */ * ========================================================= */
#define OWI_MAX_RETRY 2 static void owi_result_init(owi_io_result_t *r)
#define OWI_RECOVERY_MIN_US 500 {
if (!r) return;
memset(r, 0, sizeof(*r));
r->ok = 1;
r->timeout_byte_index = 0xFFFFu;
r->timeout_bit_index = 0xFFu;
}
void OWI_A_CommandMode(const uint8_t *tx_data, uint8_t tx_len, uint8_t id) static void owi_result_latch_timeout(owi_io_result_t *r)
{ {
uint8_t CMD_LIST[6][4] = { if (!r) return;
{0x50,0x2E,0x00,0x00},
{0x50,0x2E,0x01,0x00},
{0x50,0x2E,0x02,0x00},
{0x50,0x2E,0x16,0x00},
{0x50,0x2E,0x41,0x00},
{0x50,0x2E,0x00,0x00}
};
char line[128];
size_t n = 0;
uint8_t rx[RAM_BYTES];
int i, j, retry, all_ff;
uint8_t read_address = 0x51;
delay_us(7000);
for (j = 0; j < 6; j++) {
OWI_SecureStop();
for (i = 0; i < 4; i++) {
OWI_WriteByte(CMD_LIST[j][i]);
}
OWI_Stop();
delay_us(OWI_RECOVERY_MIN_US); if (OWI_HasTimeout()) {
r->ok = 0;
r->timeout = 1;
r->timeout_byte_index = OWI_GetLastTimeoutByteIndex();
r->timeout_bit_index = OWI_GetLastTimeoutBitIndex();
}
}
for (i = 0; i < RAM_BYTES; i++) rx[i] = 0xFF; void OWI_T_CommandModeRaw(const uint8_t *tx_data, uint8_t tx_len, uint8_t id, owi_io_result_t *r)
{
int i;
for (retry = 0; retry <= OWI_MAX_RETRY; retry++) { owi_result_init(r);
delay_us(OWI_RECOVERY_MIN_US);
OWI_SecureStop(); delay_us(10000);
OWI_WriteByte(read_address); OWI_Init(OWI_BIT_PERIOD_US);
for (i = 0; i < RAM_BYTES; i++) rx[i] = OWI_ReadByte(); OWI_ClearTimeout();
OWI_Stop();
all_ff = 1; OWI_SecureStop();
for (i = 0; i < RAM_BYTES; i++) { OWI_WriteByte((uint8_t)(id << 1));
if (rx[i] != 0xFF) { all_ff = 0; break; }
}
if (!all_ff) break; for (i = 0; i < (int)tx_len; i++) {
OWI_WriteByte(tx_data[i]);
}
if (retry == OWI_MAX_RETRY) { OWI_Stop();
return; owi_result_latch_timeout(r);
} }
}
n += sprintf(&line[n], "%02X%02X", rx[1], rx[2]); void OWI_CommandModeRaw(const uint8_t *tx_data, uint8_t tx_len, uint8_t id, owi_io_result_t *r)
line[n++] = ','; {
} int i;
if (tx_data != NULL && tx_len == 3) { owi_result_init(r);
for (retry = 0; retry <= OWI_MAX_RETRY; retry++) { OWI_ClearTimeout();
OWI_SecureStop(); OWI_SecureStop();
OWI_WriteByte((uint8_t)(id << 1)); OWI_WriteByte((uint8_t)(id << 1));
for (i = 0; i < 3; i++) {
OWI_WriteByte(tx_data[i]);
}
OWI_Stop();
delay_us(OWI_RECOVERY_MIN_US); for (i = 0; i < (int)tx_len; i++) {
OWI_WriteByte(tx_data[i]);
}
for (i = 0; i < RAM_BYTES; i++) rx[i] = 0xFF; OWI_Stop();
owi_result_latch_timeout(r);
}
OWI_SecureStop(); void OWI_ReadBytesRaw(int length, uint8_t id, owi_io_result_t *r)
OWI_WriteByte((uint8_t)((id << 1) | 1u)); {
for (i = 0; i < RAM_BYTES; i++) { int i;
rx[i] = OWI_ReadByte(); int max_len;
}
OWI_Stop();
all_ff = 1; if (!r) return;
for (i = 0; i < RAM_BYTES; i++) {
if (rx[i] != 0xFF) { all_ff = 0; break; }
}
if (!all_ff) break; owi_result_init(r);
if (retry == OWI_MAX_RETRY) { if (length <= 0) {
return; r->ok = 0;
} return;
} }
n += sprintf(&line[n], "%02X%02X", rx[1], rx[2]); max_len = (int)OWI_IO_MAX_BYTES;
} else { if (length > max_len) {
n += sprintf(&line[n], "0000"); length = max_len;
} }
line[n++] = '\r'; r->read_len = (uint16_t)length;
line[n++] = '\n';
line[n] = '\0';
HOST_PRINT(line); OWI_ClearTimeout();
OWI_SecureStop();
OWI_WriteByte((uint8_t)((id << 1) | 1u));
for (i = 0; i < length; i++) {
g_owi_current_read_byte_index = (uint16_t)i;
r->data[i] = OWI_ReadByte();
if (OWI_HasTimeout()) {
owi_result_latch_timeout(r);
break;
}
}
delay(10000); OWI_StopRead();
} }
void OWI_disable(void) void OWI_disable(void)
@ -295,42 +390,26 @@ void OWI_disable(void)
void OWI_T_CommandMode(const uint8_t *tx_data, uint8_t tx_len, uint8_t id) void OWI_T_CommandMode(const uint8_t *tx_data, uint8_t tx_len, uint8_t id)
{ {
int i; owi_io_result_t r;
(void)r;
delay_us(7000); OWI_T_CommandModeRaw(tx_data, tx_len, id, &r);
OWI_Init(OWI_BIT_PERIOD_US);
OWI_SecureStop();
OWI_WriteByte((uint8_t)(id << 1));
for (i = 0; i < (int)tx_len; i++) {
OWI_WriteByte(tx_data[i]);
}
OWI_Stop();
HOST_PRINT("51\r\n"); HOST_PRINT("51\r\n");
} }
void OWI_CommandMode(const uint8_t *tx_data, uint8_t tx_len, uint8_t id) void OWI_CommandMode(const uint8_t *tx_data, uint8_t tx_len, uint8_t id)
{ {
int i; owi_io_result_t r;
(void)r;
OWI_SecureStop(); OWI_CommandModeRaw(tx_data, tx_len, id, &r);
OWI_WriteByte((uint8_t)(id << 1));
for (i = 0; i < (int)tx_len; i++) {
OWI_WriteByte(tx_data[i]);
}
OWI_Stop();
HOST_PRINT("51\r\n"); HOST_PRINT("51\r\n");
} }
void OWI_ReadBytesAndPrint(int length, uint8_t id) void OWI_ReadBytesAndPrint(int length, uint8_t id)
{ {
static uint8_t buf[600]; owi_io_result_t r;
static char out[2*600 + 3]; char out[(2 * OWI_IO_MAX_BYTES) + 40];
int i; int i;
uint16_t p = 0; uint16_t p = 0;
@ -338,26 +417,29 @@ void OWI_ReadBytesAndPrint(int length, uint8_t id)
HOST_PRINT("Err:read_len_nonzero\r\n"); HOST_PRINT("Err:read_len_nonzero\r\n");
return; return;
} }
if (length > 600) length = 600;
OWI_SecureStop(); OWI_ReadBytesRaw(length, id, &r);
OWI_WriteByte((uint8_t)((id << 1) | 1u));
for (i = 0; i < length; i++) { if (r.read_len == 0) {
buf[i] = OWI_ReadByte(); HOST_PRINT("Err:read_len_nonzero\r\n");
return;
} }
/* ✅ 공백 없이 전부 붙이기 */ for (i = 0; i < (int)r.read_len; i++) {
for (i = 0; i < length; i++) { uint8_t b = r.data[i];
uint8_t b = buf[i];
out[p++] = "0123456789ABCDEF"[b >> 4]; out[p++] = "0123456789ABCDEF"[b >> 4];
out[p++] = "0123456789ABCDEF"[b & 0x0F]; out[p++] = "0123456789ABCDEF"[b & 0x0F];
} }
if (r.timeout) {
p += (uint16_t)sprintf(&out[p], " !TO(B%u b%u)",
(unsigned)r.timeout_byte_index,
(unsigned)r.timeout_bit_index);
}
out[p++] = '\r'; out[p++] = '\r';
out[p++] = '\n'; out[p++] = '\n';
out[p] = '\0'; out[p] = '\0';
HOST_PRINT(out); /* ✅ 딱 1번만 출력 */ HOST_PRINT(out);
} }

33
owi.h

@ -18,27 +18,35 @@
#define OWI_PIN_MASK (1u << 0) #define OWI_PIN_MASK (1u << 0)
/* ========================================================= /* =========================================================
* Timing ( ) * Timing
* ========================================================= */ * ========================================================= */
#define OWI_BIT_PERIOD_US 100u #define OWI_BIT_PERIOD_US 100u
#define TBIT OWI_BIT_PERIOD_US #define TBIT OWI_BIT_PERIOD_US
#define TLOW_0 (TBIT * 0.75) /* 75us (double calc) */ #define TLOW_0 (TBIT * 0.75)
#define TLOW_1 (TBIT * 0.25) /* 25us */ #define TLOW_1 (TBIT * 0.25)
#define TSTOP_LOW (TBIT * 2) /* 200us */ #define TSTOP_LOW (TBIT * 2)
#define TIDLE (TBIT * 3) /* 300us */ #define TIDLE (TBIT * 3)
#define TSTART_HOLD 50u #define TSTART_HOLD 50u
#define SECURE_HIGH 250u #define SECURE_HIGH 250u
#define SECURE_TOGGLE_COUNT 3u #define SECURE_TOGGLE_COUNT 3u
#define SECURE_TOGGLE_LOW 40u #define SECURE_TOGGLE_LOW 40u
#define SECURE_TOGGLE_HIGH 60u #define SECURE_TOGGLE_HIGH 60u
/* =========================================================
* External dependencies
* ========================================================= */
#ifndef RAM_BYTES #ifndef RAM_BYTES
#define RAM_BYTES 3 #define RAM_BYTES 3
#endif #endif
#define OWI_IO_MAX_BYTES 64u
typedef struct {
uint8_t ok;
uint8_t timeout;
uint16_t read_len;
uint8_t data[OWI_IO_MAX_BYTES];
uint16_t timeout_byte_index;
uint8_t timeout_bit_index;
} owi_io_result_t;
/* ========================================================= /* =========================================================
* API * API
* ========================================================= */ * ========================================================= */
@ -64,7 +72,16 @@ void OWI_T_CommandMode(const uint8_t *tx_data, uint8_t tx_len, uint8_t id);
void OWI_CommandMode (const uint8_t *tx_data, uint8_t tx_len, uint8_t id); void OWI_CommandMode (const uint8_t *tx_data, uint8_t tx_len, uint8_t id);
void OWI_A_CommandMode(const uint8_t *tx_data, uint8_t tx_len, uint8_t id); void OWI_A_CommandMode(const uint8_t *tx_data, uint8_t tx_len, uint8_t id);
void OWI_T_CommandModeRaw(const uint8_t *tx_data, uint8_t tx_len, uint8_t id, owi_io_result_t *r);
void OWI_CommandModeRaw (const uint8_t *tx_data, uint8_t tx_len, uint8_t id, owi_io_result_t *r);
void OWI_ReadBytesRaw (int length, uint8_t id, owi_io_result_t *r);
void OWI_disable(void); void OWI_disable(void);
void GPIO_ForceHighKick(void);
uint8_t OWI_HasTimeout(void);
void OWI_ClearTimeout(void);
uint16_t OWI_GetLastTimeoutByteIndex(void);
uint8_t OWI_GetLastTimeoutBitIndex(void);
#endif /* OWI_H */ #endif /* OWI_H */

510
r_main.c

@ -44,6 +44,10 @@ Includes
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <r_cg_port.h> #include <r_cg_port.h>
#include "app_types.h"
#include "app_cmd_parser.h"
#include "app_scheduler.h"
#include "app_owi_service.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 */ /* Start user code for adding. Do not edit comment generated here */
@ -54,14 +58,14 @@ Includes
#define UART_RX_BUF_SIZE 1024 #define UART_RX_BUF_SIZE 1024
#define RS485_BRIDGE_FIFO_SZ 2048 #define RS485_BRIDGE_FIFO_SZ 2048
#define BRIDGE_IDLE_DONE_US 5000U // 마지막 바이트 이후 3ms 조용하면 종료로 판단 #define BRIDGE_IDLE_DONE_US 5000U // ? 3ms ? ?
#define BRIDGE_TOTAL_WAIT_US 600000U #define BRIDGE_TOTAL_WAIT_US 1290000U
/* ========================= /* =========================
* UART RX Buffers * UART RX Buffers
* ========================= */ * ========================= */
volatile uint8_t uart_rx_done = 0; volatile uint8_t uart_rx_done = 0;
volatile uint16_t uart_rx_index = 0; // uint8_t -> uint16_t volatile uint16_t uart_rx_index = 0; // uint8_t -> uint16_t
volatile uint8_t uart_rx_buffer[UART_RX_BUF_SIZE] = {0}; volatile uint8_t uart_rx_buffer[UART_RX_BUF_SIZE] = {0};
volatile uint16_t uart_rx_length = 0; volatile uint16_t uart_rx_length = 0;
@ -88,8 +92,8 @@ uint8_t g_fixed_addr = 0;
/* ========================= /* =========================
* Driver globals (Renesas) * Driver globals (Renesas)
* ========================= */ * ========================= */
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; extern volatile uint8_t g_uart0_tx_done;
@ -126,7 +130,7 @@ static int RS485_Bridge_ReadLine(char *out, int out_sz, uint32_t timeout_us)
int n = 0; int n = 0;
while (timeout_us--) { while (timeout_us--) {
// FIFO에 데이터가 있으면 1바이트씩 꺼내서 line 구성 // FIFO
while (s_rb_tail != s_rb_head) { while (s_rb_tail != s_rb_head) {
char c = (char)s_rb_fifo[s_rb_tail]; char c = (char)s_rb_fifo[s_rb_tail];
s_rb_tail++; s_rb_tail++;
@ -138,7 +142,7 @@ static int RS485_Bridge_ReadLine(char *out, int out_sz, uint32_t timeout_us)
if (c == '\n') { if (c == '\n') {
out[n] = '\0'; out[n] = '\0';
return 1; // line 완성 return 1; // line
} }
} }
delay_us(1); delay_us(1);
@ -152,7 +156,6 @@ static int Is_V_Response_For(char *line, uint8_t addr)
{ {
uint8_t a; uint8_t a;
// line 예: "V02\n" 또는 "V02\r\n" (CR은 위에서 제거됨)
if (line[0] != 'V' && line[0] != 'v') return 0; if (line[0] != 'V' && line[0] != 'v') return 0;
if (!isdigit((unsigned char)line[1]) || !isdigit((unsigned char)line[2])) return 0; if (!isdigit((unsigned char)line[1]) || !isdigit((unsigned char)line[2])) return 0;
@ -162,9 +165,8 @@ static int Is_V_Response_For(char *line, uint8_t addr)
/* ========================= /* =========================
* ? PC(UART1) Safe TX () * PC(UART1) Safe TX
* - * R_UART1_Send
* - R_UART1_Send
* ========================= */ * ========================= */
static uint8_t g_uart1_txbuf[1024]; static uint8_t g_uart1_txbuf[1024];
@ -172,7 +174,7 @@ static void UART1_WaitTxIdle(void)
{ {
unsigned long guard = 0; unsigned long guard = 0;
while (g_uart1_tx_count != 0U) { while (g_uart1_tx_count != 0U) {
if (guard++ > 3000000UL) break; /* 무한루프 방지 */ if (guard++ > 3000000UL) break; /* ? */
} }
} }
@ -200,7 +202,7 @@ static void UART1_SendString_Safe(const char *s)
if (s == 0) return; if (s == 0) return;
UART1_WaitTxIdle(); // ? 보내기 대기 UART1_WaitTxIdle(); // ?
len = (uint16_t)strlen(s); len = (uint16_t)strlen(s);
if (len >= (uint16_t)sizeof(g_uart1_txbuf)) if (len >= (uint16_t)sizeof(g_uart1_txbuf))
@ -211,22 +213,21 @@ static void UART1_SendString_Safe(const char *s)
R_UART1_Send(g_uart1_txbuf, len); R_UART1_Send(g_uart1_txbuf, len);
UART1_WaitTxIdle(); // ? ★ 추가: 보내고 난 뒤에도 대기 (버퍼 덮어쓰기 방지) UART1_WaitTxIdle(); // ? ?: ? ( ? )
} }
/* ========================= /* =========================
* RS485 Bridge Drain -> PC * RS485 Bridge Drain -> PC
* - uart1_send_string(out) 1 * - uart1_send_string(out)
* - UART1_SendBytes_Safe "안전하게" * - UART1_SendBytes_Safe
* ========================= */ * ========================= */
static void RS485_Bridge_DrainToPC(void) static void RS485_Bridge_DrainToPC(void)
{ {
uint16_t n = 0; uint16_t n = 0;
/* UART1 TX중이면 다음 루프에서 */ /* UART1 TX */
if (g_uart1_tx_count != 0U) return; if (g_uart1_tx_count != 0U) return;
/* FIFO -> 임시 버퍼에 최대 64바이트까지 모아서 한 번에 전송 */
while (s_rb_tail != s_rb_head && n < 1024U) { while (s_rb_tail != s_rb_head && n < 1024U) {
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++;
@ -262,6 +263,15 @@ typedef enum {
static PrefixMode s_prefix_mode = PREFIX_NONE; static PrefixMode s_prefix_mode = PREFIX_NONE;
typedef struct {
uint8_t active;
uint8_t scan_started;
uint8_t current_scan_addr;
app_job_t job;
} app_runtime_job_t;
static app_runtime_job_t g_app_runtime_job;
/* ========================= /* =========================
* parse x-prefix: xNNc_001011:... * parse x-prefix: xNNc_001011:...
* ========================= */ * ========================= */
@ -321,15 +331,11 @@ static int bridge_wait_until_end(uint32_t total_us)
uint8_t st = 0; uint8_t st = 0;
while (total_us--) { while (total_us--) {
// FIFO -> PC 전송 // FIFO -> PC
while (s_rb_tail != s_rb_head) { while (s_rb_tail != s_rb_head) {
char c = (char)s_rb_fifo[s_rb_tail]; char c = (char)s_rb_fifo[s_rb_tail];
s_rb_tail = (s_rb_tail + 1) % RS485_BRIDGE_FIFO_SZ; s_rb_tail = (s_rb_tail + 1) % RS485_BRIDGE_FIFO_SZ;
// PC로 바로 출력(버퍼링은 기존처럼 모아서 보내도 OK)
// 여기서는 단순화를 위해 1바이트라 가정하지 말고 기존 256모아서 보내는 함수 유지 권장
// 패턴 매칭
if (c == pat[st]) { if (c == pat[st]) {
st++; st++;
if (pat[st] == 0) return 1; // found if (pat[st] == 0) return 1; // found
@ -351,21 +357,20 @@ typedef enum {
CMD_SRC_RS485 = 1 CMD_SRC_RS485 = 1
} CmdSource; } CmdSource;
/* ? PC 출력은 무조건 Safe로 */
static void OUT_PRINT(CmdSource src, const char *s) static void OUT_PRINT(CmdSource src, const char *s)
{ {
if (src == CMD_SRC_PC) { if (src == CMD_SRC_PC) {
UART1_SendString_Safe(s); UART1_SendString_Safe(s);
} else { } else {
/* RS485_PRINT는 네 프로젝트 기존 함수(보통 UART0 TX) */ /* RS485_PRINT ? ?( UART0 TX) */
RS485_PRINT(s); RS485_PRINT(s);
} }
} }
static void send_end_response(void) static void send_end_response(CmdSource src)
{ {
OUT_PRINT(CMD_SRC_PC, "<end>\r\n"); OUT_PRINT(src, "<end>\r\n");
} }
/* ========================= /* =========================
* xNNv command * xNNv command
@ -404,7 +409,7 @@ static int parse_x_o_cmd(const char *s, int len, uint8_t *addr)
} }
/* ? 여기서도 OUT_PRINT Safe를 타므로 깨짐 없음 */ /* OUT_PRINT Safe */
static void send_v_response(CmdSource src, uint8_t addr) static void send_v_response(CmdSource src, uint8_t addr)
{ {
char resp[8]; char resp[8];
@ -417,7 +422,7 @@ 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(600); // 200~500us 권장 (환경 따라 조정) delay_us(1200); // 200~500us
} }
OUT_PRINT(src, resp); OUT_PRINT(src, resp);
@ -432,7 +437,6 @@ static void UART0_WaitTxDone_Flag(uint32_t guard_us)
static void RS485_FlushJunk(uint16_t loops, uint16_t step_us) static void RS485_FlushJunk(uint16_t loops, uint16_t step_us)
{ {
// 스캔 시작 전에, 들어와 있던 CR/LF/노이즈를 PC로 빼고 FIFO도 비움
while (loops--) { while (loops--) {
RS485_Bridge_DrainToPC(); RS485_Bridge_DrainToPC();
delay_us(step_us); delay_us(step_us);
@ -445,7 +449,7 @@ static void PC_PrintLine_CRLF(const char *line)
size_t n = strlen(line); size_t n = strlen(line);
size_t i; size_t i;
// line '\n' 끝나면, "\r\n" 보정 // line '\n' , "\r\n"
if (n > 0 && line[n-1] == '\n') { if (n > 0 && line[n-1] == '\n') {
size_t m = 0; size_t m = 0;
for (i=0; i<n && m < sizeof(tmp)-3; i++) { for (i=0; i<n && m < sizeof(tmp)-3; i++) {
@ -464,9 +468,6 @@ static void PC_PrintLine_CRLF(const char *line)
/* ========================= /* =========================
* RS485 scan: xNNv\r\n * RS485 scan: xNNv\r\n
* ========================= */ * ========================= */
/* =========================
* RS485 scan: xNNv\r\n (pattern-based, no line needed)
* ========================= */
static void scan_one_addr_rs485(uint8_t addr) static void scan_one_addr_rs485(uint8_t addr)
{ {
char cmdline[8]; char cmdline[8];
@ -482,65 +483,52 @@ static void scan_one_addr_rs485(uint8_t addr)
cmdline[5] = '\n'; cmdline[5] = '\n';
cmdline[6] = '\0'; cmdline[6] = '\0';
/* ? try 횟수 줄이기: 속도 핵심 */ RS485_FlushJunk(2, 100);
for (try = 0; try < 3; try++)
{ for (try = 0; try < 2; try++) {
if (g_rs485_need_recover) {
rs485_recover();
}
{
/* --- bridge 준비 --- */
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();
/* RX arm (브릿지 모드는 항상 buffer[0]로 1바이트 받는 구조여야 함) */
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);
/* ---- 송신 ---- */
g_uart0_tx_done = 0; g_uart0_tx_done = 0;
RS485_PRINT(cmdline); RS485_PRINT(cmdline);
/* TX 완료 대기 (너무 길게 잡지 말기) */ UART0_WaitTxDone_Flag(5000);
UART0_WaitTxDone_Flag(2500); // 2.5ms 가드 rs485_set_tx(0);
rs485_set_tx(0); // 혹시 몰라 강제 RX delay_us(250);
delay_us(80); // turnaround (환경 따라 50~150us)
/* ---- 응답 대기 (짧게) ---- */
{ {
uint32_t total_wait_us = 2500; // ? 2.5ms면 대부분 충분 uint32_t total_wait_us = 8000;
char linebuf[32]; char linebuf[32];
while (total_wait_us > 0) { while (total_wait_us > 0) {
/* 0.5ms씩 끊어 보기 */ int got = RS485_Bridge_ReadLine(linebuf, sizeof(linebuf), 1000);
int got = RS485_Bridge_ReadLine(linebuf, sizeof(linebuf), 500); if (got && Is_V_Response_For(linebuf, addr)) {
if (got) { PC_PrintLine_CRLF(linebuf);
if (Is_V_Response_For(linebuf, addr)) { g_rs485_bridge_active = 0;
PC_PrintLine_CRLF(linebuf); return;
g_rs485_bridge_active = 0;
return;
}
/* 다른 라인(쓰레기/에코)면 계속 */
} }
total_wait_us -= 500;
if (total_wait_us >= 1000) total_wait_us -= 1000;
else total_wait_us = 0;
} }
} }
g_rs485_bridge_active = 0; g_rs485_bridge_active = 0;
}
// 실패 원인이 에러/꼬임이면 복구 후 재시도
if (g_rs485_need_recover) {
rs485_recover();
}
delay_us(500); delay_us(500);
} }
/* 실패 시 Nxx */
send_n_response(addr); send_n_response(addr);
/* 다음을 위해 RX 재arm */
rs485_rx_done = 0; rs485_rx_done = 0;
rs485_rx_index = 0; rs485_rx_index = 0;
rs485_rx_length = 0; rs485_rx_length = 0;
@ -548,7 +536,6 @@ static void scan_one_addr_rs485(uint8_t addr)
} }
/* ========================= /* =========================
* Build line from rx buffer * Build line from rx buffer
* ========================= */ * ========================= */
@ -599,7 +586,199 @@ static void cmd_unknown(const unsigned char *d, unsigned int len)
delay(100000); delay(100000);
} }
/* 여기는 네 프로젝트 기존 함수들 호출 */ static void print_owi_write_result(CmdSource src, const app_owi_result_t *r)
{
(void)r;
OUT_PRINT(src, "51\r\n");
}
static void print_owi_read_result(CmdSource src, const app_owi_result_t *r)
{
char out[(2 * OWI_IO_MAX_BYTES) + 40];
uint16_t p = 0;
uint16_t i;
if (!r) {
OUT_PRINT(src, "Err:owi_null\r\n");
return;
}
if (r->read_len == 0) {
OUT_PRINT(src, "Err:read_len_nonzero\r\n");
return;
}
for (i = 0; i < r->read_len && i < OWI_IO_MAX_BYTES; i++) {
uint8_t b = r->data[i];
out[p++] = "0123456789ABCDEF"[b >> 4];
out[p++] = "0123456789ABCDEF"[b & 0x0F];
}
if (r->timeout) {
p += (uint16_t)sprintf(&out[p], " !TO(B%u b%u)",
(unsigned)r->timeout_byte_index,
(unsigned)r->timeout_bit_index);
}
out[p++] = '\r';
out[p++] = '\n';
out[p] = '\0';
OUT_PRINT(src, out);
}
static int execute_owi_service_from_line(CmdSource src, const char *line)
{
uint8_t addr = 0, ch = 0;
char mode = 0;
uint8_t hash_on = 0, anaout_on = 0, check_on = 1;
int payload_pos = 0;
int len;
ProtocolType proto;
uint8_t id = 0;
unsigned int byte_len = 0;
int pos;
uint8_t cmd[CMD_MAX];
unsigned int k;
app_owi_result_t r;
if (!line) return 0;
len = (int)strlen(line);
/* x-prefix 파싱 */
if (parse_x_prefix(line, len,
&addr, &ch, &mode,
&hash_on, &anaout_on, &check_on,
&payload_pos) != 1) {
return 0;
}
/* 자기 주소가 아니면 기존 경로로 넘김 */
if (addr != g_fixed_addr) {
return 0;
}
if (ch < 1 || ch > 20) {
OUT_PRINT(src, "Err:ch_range\r\n");
return 1;
}
/* prefix 동작 유지: 기존 process_one_line_now()와 동일하게 선행 */
if (mode == 'C') {
s_prefix_mode = PREFIX_CAL;
Cal_Init();
Gate_SetByNum(ch, hash_on, anaout_on, check_on);
GateCtrl_SelectChannel(ch);
} else {
s_prefix_mode = PREFIX_EOL;
Eol_Init();
Gate_SetByNum(ch, hash_on, anaout_on, check_on);
GateCtrl_SelectChannel(ch);
}
if (payload_pos >= len) {
if (mode == 'E') {
OUT_PRINT(src, "<ACK>XE51\r\n");
return 1;
} else {
OUT_PRINT(src, "Err:CAL_need_payload\r\n");
return 1;
}
}
pos = payload_pos;
{
char h0, h1;
h0 = (char)toupper((unsigned char)line[pos]);
h1 = (char)toupper((unsigned char)line[pos + 1]);
proto = detect_protocol(h0, h1);
if (proto != PROTOCOL_OWIW && proto != PROTOCOL_OWIR) {
return 0; /* I2C 등은 기존 경로 fallback */
}
}
pos += 2;
if ((line[pos] == 't' || line[pos] == 'T') && proto == PROTOCOL_OWIW) {
proto = PROTOCOL_OWIT;
pos++;
}
if (line[pos] == '_' || line[pos] == ':') {
pos++;
}
if (pos + 1 >= len) {
OUT_PRINT(src, "Err:id_short\r\n");
return 1;
}
id = hex2byte(line[pos], line[pos + 1]);
pos += 2;
if (pos + 2 >= len ||
!(line[pos] >= '0' && line[pos] <= '9') ||
!(line[pos+1] >= '0' && line[pos+1] <= '9') ||
!(line[pos+2] >= '0' && line[pos+2] <= '9')) {
OUT_PRINT(src, "Err:len_dec\r\n");
return 1;
}
byte_len = (unsigned int)(100 * (line[pos] - '0') +
10 * (line[pos+1] - '0') +
(line[pos+2] - '0'));
pos += 3;
if (byte_len > CMD_MAX) {
OUT_PRINT(src, "Err:len_range\r\n");
return 1;
}
if (proto == PROTOCOL_OWIT || proto == PROTOCOL_OWIW) {
if (byte_len == 0) {
OUT_PRINT(src, "Err:payload0\r\n");
return 1;
}
if ((int)(pos + (int)byte_len * 2) > len) {
OUT_PRINT(src, "Err:len_mismatch\r\n");
return 1;
}
for (k = 0; k < byte_len; k++) {
cmd[k] = hex2byte(line[pos + (int)(2 * k)],
line[pos + (int)(2 * k + 1)]);
}
if (proto == PROTOCOL_OWIT) {
r = app_owi_write_t_basic(id, cmd, (uint8_t)byte_len);
} else {
r = app_owi_write_basic(id, cmd, (uint8_t)byte_len);
}
print_owi_write_result(src, &r);
return 1;
}
if (proto == PROTOCOL_OWIR) {
if (byte_len == 0) {
OUT_PRINT(src, "Err:read_len_nonzero\r\n");
return 1;
}
r = app_owi_read_basic(id, (int)byte_len);
print_owi_read_result(src, &r);
return 1;
}
return 0;
}
static void process_cmd(ProtocolType protocol, uint8_t id, static void process_cmd(ProtocolType protocol, uint8_t id,
const unsigned char *data, unsigned int len) const unsigned char *data, unsigned int len)
{ {
@ -625,7 +804,7 @@ static void process_cmd_by_prefix(PrefixMode pm,
/* ========================= /* =========================
* Main line processor * Main line processor
* ========================= */ * ========================= */
static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint16_t rx_len) static void process_one_line_now(CmdSource src, const char *input_line)
{ {
char line[UART_RX_BUF_SIZE]; char line[UART_RX_BUF_SIZE];
@ -650,35 +829,39 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint
s_prefix_mode = PREFIX_NONE; s_prefix_mode = PREFIX_NONE;
idx = build_line_from_rx(rx_buf, (int)rx_len, line, (int)sizeof(line)); if (!input_line) return;
strncpy(line, input_line, sizeof(line) - 1);
line[sizeof(line) - 1] = '\0';
idx = (int)strlen(line);
if (idx <= 0) return; if (idx <= 0) return;
if (src == CMD_SRC_RS485) { if (src == CMD_SRC_RS485) {
if (!(line[0] == 'x' || line[0] == 'X')) return; // V/N/Err 응답 등 무시 if (!(line[0] == 'x' || line[0] == 'X')) return; // V/N/Err
} }
/* ========================= /* =========================
* 1) xNNv (x-prefix보다 ) * 1) xNNv (x-prefix )
* ========================= */ * ========================= */
is_v = parse_x_v_cmd(line, idx, &v_addr); is_v = parse_x_v_cmd(line, idx, &v_addr);
if (is_v) { if (is_v) {
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; }
/* 보드0(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) 응답 */
send_v_response(CMD_SRC_PC, 0); send_v_response(CMD_SRC_PC, 0);
/* 01~31 순차 조회 */ /* 01~31 */
for (a = 1; a <= 31; a++) { for (a = 1; a <= 31; a++) {
scan_one_addr_rs485(a); scan_one_addr_rs485(a);
} }
return; return;
} }
/* 그 외: 내 주소와 일치할 때만 응답 */
if (v_addr == g_fixed_addr) { if (v_addr == g_fixed_addr) {
send_v_response(src, g_fixed_addr); send_v_response(src, g_fixed_addr);
} }
@ -686,7 +869,7 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint
} }
/* ========================= /* =========================
* 1.5) xNNo (OFF) ? : 0 RS485 * 1.5) xNNo(OFF)
* ========================= */ * ========================= */
{ {
uint8_t off_addr = 0; uint8_t off_addr = 0;
@ -694,15 +877,15 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint
if (off_addr > 31) { OUT_PRINT(src, "Err:addr_range\r\n"); return; } if (off_addr > 31) { OUT_PRINT(src, "Err:addr_range\r\n"); return; }
/* 1) 내 주소면 로컬 OFF */ /* 1) OFF */
if (off_addr == g_fixed_addr) { if (off_addr == g_fixed_addr) {
Cal_Init(); // “전체 OFF(기본 상태)” Cal_Init();
OUT_PRINT(src, "<ACK>OFF\r\n"); OUT_PRINT(src, "<ACK>OFF\r\n");
send_end_response(); // GUI가 멀티라인이면 <end> 편함 send_end_response(src); //? <end>
return; return;
} }
/* 2) 보드0(addr=0) + PC에서 들어온 x01o~ 는 RS485로 브릿지 */ /* 2) 0(addr=0) + PC x01o RS485 */
if (g_fixed_addr == 0 && src == CMD_SRC_PC) { if (g_fixed_addr == 0 && src == CMD_SRC_PC) {
g_rs485_bridge_active = 1; g_rs485_bridge_active = 1;
@ -714,10 +897,10 @@ 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로 명령 송신 ---- */ /* ---- RS485 ---- */
{ {
static char txbuf[UART_RX_BUF_SIZE + 4]; static char txbuf[UART_RX_BUF_SIZE + 4];
int n = idx; // line 길이 int n = idx; // line
if (n > (int)sizeof(txbuf) - 3) n = (int)sizeof(txbuf) - 3; if (n > (int)sizeof(txbuf) - 3) n = (int)sizeof(txbuf) - 3;
@ -726,7 +909,7 @@ 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) {
@ -737,7 +920,7 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint
RS485_PRINT(txbuf); RS485_PRINT(txbuf);
} }
/* ---- 응답 드레인: 데이터 1번이라도 오고 3ms idle이면 종료 ---- */
{ {
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;
@ -749,7 +932,7 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint
delay_us(50); delay_us(50);
total_us -= 50U; total_us -= 50U;
if (g_rs485_bridge_done) break; // <end> 종료 if (g_rs485_bridge_done) break; // <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;
@ -764,9 +947,9 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint
RS485_Bridge_DrainToPC(); RS485_Bridge_DrainToPC();
g_rs485_bridge_active = 0; g_rs485_bridge_active = 0;
if (total_us < 50U) { if (total_us < 300U) {
rs485_recover(); rs485_recover();
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);
@ -774,17 +957,17 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint
} }
} }
return; // 슬레이브는 mismatch 그냥 무시 return; // mismatch
} }
} }
/* RS485 중계용 원본 저장 */ /* RS485 */
orig_len = idx; orig_len = idx;
memcpy(orig_line, line, (size_t)orig_len); memcpy(orig_line, line, (size_t)orig_len);
orig_line[orig_len] = '\0'; orig_line[orig_len] = '\0';
/* ========================= /* =========================
* 2) x-prefix * 2) x-prefix
* ========================= */ * ========================= */
{ {
uint8_t addr = 0; uint8_t addr = 0;
@ -815,7 +998,7 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint
/* addr mismatch */ /* addr mismatch */
if (addr != g_fixed_addr) if (addr != g_fixed_addr)
{ {
/* 보드0(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;
@ -832,7 +1015,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로 명령 송신 ---- */ /* ---- 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;
@ -844,7 +1027,7 @@ 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) {
@ -855,7 +1038,7 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint
RS485_PRINT(txbuf); RS485_PRINT(txbuf);
} }
/* ---- 응답 드레인 ---- */
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;
@ -881,8 +1064,8 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint
RS485_Bridge_DrainToPC(); RS485_Bridge_DrainToPC();
g_rs485_bridge_active = 0; g_rs485_bridge_active = 0;
if (total_us < 50U) { if (total_us < 300U) {
rs485_recover(); rs485_recover();
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);
@ -890,10 +1073,10 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint
} }
} }
return; /* 슬레이브는 addr mismatch 무시 */ return; /* addr mismatch */
} }
/* addr == g_fixed_addr: 로컬 처리(채널 선택 등) */ /* addr == g_fixed_addr */
if (mode == 'C') { if (mode == 'C') {
s_prefix_mode = PREFIX_CAL; s_prefix_mode = PREFIX_CAL;
Cal_Init(); Cal_Init();
@ -906,7 +1089,7 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint
GateCtrl_SelectChannel(ch); GateCtrl_SelectChannel(ch);
} }
/* prefix만 온 경우 / payload 처리 */ /* prefix */
{ {
int rem = idx - payload_pos; int rem = idx - payload_pos;
@ -933,9 +1116,7 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint
} }
} }
/* =========================
* 3)
* ========================= */
if (idx < 7) { OUT_PRINT(src, "Err:short\r\n"); return; } if (idx < 7) { OUT_PRINT(src, "Err:short\r\n"); return; }
{ {
@ -995,6 +1176,111 @@ static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint
} }
static void process_one_line(CmdSource src, const volatile uint8_t *rx_buf, uint16_t rx_len)
{
char line[UART_RX_BUF_SIZE];
int idx;
app_job_t job;
app_cmd_src_t app_src;
idx = build_line_from_rx(rx_buf, (int)rx_len, line, (int)sizeof(line));
if (idx <= 0) return;
if (src == CMD_SRC_RS485) {
if (!(line[0] == 'x' || line[0] == 'X')) return;
}
app_src = (src == CMD_SRC_PC) ? APP_CMD_SRC_PC : APP_CMD_SRC_RS485;
if (!app_cmd_parse_line(app_src, line, &job)) {
OUT_PRINT(src, "Err:parse\r\n");
return;
}
if (!app_scheduler_push(&job)) {
OUT_PRINT(src, "Err:queue_full\r\n");
return;
}
}
static void app_runtime_reset(void)
{
memset(&g_app_runtime_job, 0, sizeof(g_app_runtime_job));
}
static void app_runtime_try_start(void)
{
if (g_app_runtime_job.active) return;
if (app_scheduler_pop(&g_app_runtime_job.job)) {
g_app_runtime_job.active = 1;
g_app_runtime_job.scan_started = 0;
g_app_runtime_job.current_scan_addr = 1;
}
}
static void app_job_tick(void)
{
CmdSource src;
uint8_t scan_addr;
app_runtime_try_start();
if (!g_app_runtime_job.active) return;
src = (g_app_runtime_job.job.src == APP_CMD_SRC_PC) ? CMD_SRC_PC : CMD_SRC_RS485;
switch (g_app_runtime_job.job.type) {
case APP_JOB_SCAN_ADDR:
scan_addr = g_app_runtime_job.job.addr;
if (scan_addr > 31) {
OUT_PRINT(src, "Err:addr_range\r\n");
app_runtime_reset();
return;
}
/* 기존 x00v on master(0) from PC 만 분할 실행 */
if (g_fixed_addr == 0 && src == CMD_SRC_PC && scan_addr == 0) {
if (!g_app_runtime_job.scan_started) {
send_v_response(CMD_SRC_PC, 0);
g_app_runtime_job.scan_started = 1;
g_app_runtime_job.current_scan_addr = 1;
return;
}
if (g_app_runtime_job.current_scan_addr <= 31) {
scan_one_addr_rs485(g_app_runtime_job.current_scan_addr);
g_app_runtime_job.current_scan_addr++;
return;
}
app_runtime_reset();
return;
}
/* 나머지는 기존 실행 경로 그대로 */
process_one_line_now(src, g_app_runtime_job.job.line);
app_runtime_reset();
return;
case APP_JOB_PROTO_OW:
case APP_JOB_PROTO_OR:
if (!execute_owi_service_from_line(src, g_app_runtime_job.job.line)) {
/* service로 못 처리하면 기존 경로로 fallback */
process_one_line_now(src, g_app_runtime_job.job.line);
}
app_runtime_reset();
return;
case APP_JOB_FORWARD_LINE:
case APP_JOB_LOCAL_EXEC:
default:
process_one_line_now(src, g_app_runtime_job.job.line);
app_runtime_reset();
return;
}
}
/* ========================= /* =========================
* Main loop handler * Main loop handler
* ========================= */ * ========================= */
@ -1004,22 +1290,22 @@ void handle_uart_command_line(void)
{ {
if (g_rs485_need_recover) { if (g_rs485_need_recover) {
/* 1) 브릿지 강제 종료 */
g_rs485_bridge_active = 0; g_rs485_bridge_active = 0;
g_rs485_bridge_done = 0; g_rs485_bridge_done = 0;
RS485_Bridge_ResetFifo(); // 같은 파일 내 static이라 호출 가능 RS485_Bridge_ResetFifo(); // static? ?
/* 2) RS485 RX 상태 정리 */ /* RS485 RX */
rs485_rx_done = 0; rs485_rx_done = 0;
rs485_rx_index = 0; rs485_rx_index = 0;
rs485_rx_length = 0; rs485_rx_length = 0;
/* 3) UART0 stop/start + RX arm */ /* UART0 stop/start + RX arm */
rs485_recover(); rs485_recover();
g_rs485_need_recover = 0; g_rs485_need_recover = 0;
} }
/* 브릿지 중이면 RS485->PC 계속 드레인 */
if (g_rs485_bridge_active) { if (g_rs485_bridge_active) {
RS485_Bridge_DrainToPC(); RS485_Bridge_DrainToPC();
} }
@ -1053,6 +1339,7 @@ void handle_uart_command_line(void)
{ {
} }
app_job_tick();
} }
} }
@ -1061,7 +1348,7 @@ void handle_uart_command_line(void)
* ========================= */ * ========================= */
void R_MAIN_UserInit(void); void R_MAIN_UserInit(void);
void main(void) void main(void)
{ {
char b[64]; char b[64];
@ -1075,9 +1362,12 @@ void main(void)
R_UART0_Start(); R_UART0_Start();
R_UART1_Start(); R_UART1_Start();
app_scheduler_init();
app_runtime_reset();
sprintf(b, "BOOT addr=%u\r\n", g_fixed_addr); sprintf(b, "BOOT addr=%u\r\n", g_fixed_addr);
/* ? BOOT 출력도 안전 출력 사용 */ /* ? BOOT μ */
UART1_SendString_Safe(b); UART1_SendString_Safe(b);
RS485_PRINT(b); RS485_PRINT(b);

20
uart.c

@ -25,7 +25,7 @@ volatile uint8_t g_rs485_need_recover = 0;
void rs485_recover(void) void rs485_recover(void)
{ {
DI(); // ISR 경쟁 줄이기 (권장) DI(); // ISR 경쟁 줄이기
rs485_set_tx(0); // RX 모드 고정 rs485_set_tx(0); // RX 모드 고정
// UART0 재시작 // UART0 재시작
@ -49,14 +49,14 @@ void rs485_recover(void)
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);
g_rs485_need_recover = 0; // recover 완료 처리 (여기서 내려도 OK) g_rs485_need_recover = 0;
EI(); EI();
} }
/* UART1 송신용 내부 버퍼(스택 포인터 안전) */ /* UART1 송신용 내부 버퍼(스택 포인터 안전) */
static uint16_t s_uart1_txbuf[1024]; // 너 출력 단위가 "72 " / "7272 " 정도라 64면 충분 static uint8_t s_uart1_txbuf[1024];
// (더 길게 보내면 키워도 됨)
static void UART1_WaitTxIdle(void) static void UART1_WaitTxIdle(void)
{ {
@ -91,10 +91,10 @@ void RS485_Send(const uint8_t* data, uint16_t len)
{ {
if (!data || len == 0) return; if (!data || len == 0) return;
/* 송신 시작을 명확히 */ UART0_WaitTxDone_Us(5000U);
g_uart0_tx_done = 0; g_uart0_tx_done = 0;
rs485_set_tx(1); rs485_set_tx(1);
delay_us(30U);
R_UART0_Send((uint8_t*)data, len); R_UART0_Send((uint8_t*)data, len);
} }
@ -152,21 +152,21 @@ void uart1_send_string(const char *str)
if (str == 0) return; if (str == 0) return;
while (str[len] != '\0') len++; while (str[len] != '\0') len++;
/* 이전 전송 끝날 때까지 대기 */ /* 이전 전송 끝날 때까지 대기 */
UART1_WaitTxIdle(); UART1_WaitTxIdle();
g_uart1_tx_done = 0; g_uart1_tx_done = 0;
/* 스택 포인터/버퍼 수명 문제 방지: 내부 버퍼로 복사 */ /* 스택 포인터/버퍼 수명 문제 방지: 내부 버퍼로 복사 */
if (len >= (uint16_t)sizeof(s_uart1_txbuf)) if (len >= (uint16_t)sizeof(s_uart1_txbuf))
len = (uint16_t)(sizeof(s_uart1_txbuf) - 1); len = (uint16_t)(sizeof(s_uart1_txbuf) - 1);
memcpy(s_uart1_txbuf, str, len); memcpy(s_uart1_txbuf, str, len);
/* 비동기 송신 시작 */ /* 비동기 송신 시작 */
R_UART1_Send(s_uart1_txbuf, len); R_UART1_Send(s_uart1_txbuf, len);
/* ❌ 여기서 또 기다리지 마(속도 저하). 다음 호출이 알아서 대기함 */ /* 여기서 또 기다리지 말고(속도 저하). 다음 호출이 알아서 대기함 */
} }
/** /**
* : uart_send_hex * : uart_send_hex

Loading…
Cancel
Save