#include "owi.h" #include "r_cg_wdt.h" #include "delay.h" #include "r_cg_timer.h" #include "r_cg_cgc.h" #include /* 내부 상태 */ #ifndef OWI_USE_TAU1_CAPTURE #define OWI_USE_TAU1_CAPTURE 1u #endif /* Renesas CG: count-clock period is half the selected 0.063 us minimum. Therefore TI15 capture runs at approximately 32 timer ticks per us. */ #define OWI_TAU1_TICKS_PER_US 32UL #define OWI_CAPTURE_QUEUE_WAIT_US (bit_period_us * 4u) #define OWI_CAPTURE_SYNC_BITS 16u /* 내부 상태 */ static uint32_t bit_period_us = OWI_BIT_PERIOD_US; static uint8_t g_owi_timeout_latched = 0; /* 0=none, 1=no pulse, 2=queue overflow, 3=header mismatch. */ static uint8_t g_capture_error = 0U; static uint16_t g_capture_sync = 0U; uint8_t OWI_GetCaptureError(void) { return g_capture_error; } uint16_t OWI_GetCaptureSync(void) { return g_capture_sync; } #if OWI_USE_TAU1_CAPTURE static uint32_t g_owi_capture_threshold_ticks = 0UL; static uint8_t g_owi_capture_pending_bits[OWI_CAPTURE_SYNC_BITS]; static uint8_t g_owi_capture_pending_index = 0U; static uint8_t g_owi_capture_pending_count = 0U; #endif 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; static void OWI_KickWdt(void) { R_WDT_Restart(); NVM_TraceStack(); } /* ========================================================= * GPIO helpers (P70 / HW Open-Drain) * - LOW : 출력 모드 + 0 * - HIGH : 입력(Hi-Z)로 release * ========================================================= */ void GPIO_Clear(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; } void GPIO_Input(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; } int GPIO_Read(void) { 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 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_capture_error = 0U; g_capture_sync = 0U; 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 * ========================================================= */ void OWI_Init(uint32_t bit_time_us) { bit_period_us = bit_time_us; (void)bit_period_us; PMC7 &= (uint8_t)~OWI_PIN_MASK; PIM7 &= (uint8_t)~OWI_PIN_MASK; OWI_PORT_POM |= (uint8_t)OWI_PIN_MASK; OWI_PORT_PU &= (uint8_t)~OWI_PIN_MASK; OWI_ClearTimeout(); GPIO_Input(); OWI_KickWdt(); } /* ========================================================= * Start/Stop/Secure * ========================================================= */ void OWI_Start(void) { GPIO_Clear(); delay_us(TSTART_HOLD); GPIO_Input(); delay_us(TBIT); } void OWI_Stop(void) { GPIO_Input(); delay_us(TSTOP_LOW); delay_us(TIDLE); } static void OWI_StopRead(void) { OWI_DriveLow(); delay_us(TSTOP_LOW); OWI_Release(); delay_us(TIDLE); } static uint8_t OWI_WaitForFallingEdge(uint16_t byte_index, uint8_t bit_index) { int timeout = (int)(bit_period_us * 4u); while (GPIO_Read() && timeout-- > 0) { delay_us(1u); } if (timeout <= 0) { if (!g_owi_timeout_latched) { g_owi_timeout_latched = 1; g_owi_last_timeout_byte_index = byte_index; g_owi_last_timeout_bit_index = bit_index; } return 0u; } return 1u; } static uint8_t OWI_ReadByte_StreamSynced(uint16_t byte_index) { uint8_t data = 0; int b; for (b = 7; b >= 0; b--) { uint8_t bit_index = (uint8_t)(7 - b); uint8_t bit; g_owi_current_read_byte_index = byte_index; g_owi_current_read_bit_index = bit_index; /* 슬롯 시작 후 중앙 샘플 */ delay_us(bit_period_us / 2u); bit = GPIO_Read() ? 1u : 0u; data |= (uint8_t)(bit << b); /* 남은 반 슬롯 대기 */ delay_us(bit_period_us / 2u); } return data; } #if OWI_USE_TAU1_CAPTURE static void OWI_CapturePrepare(void) { /* P70 remains a normal open-drain GPIO at boot. Configure TI15 only immediately before a captured receive transaction. */ /* R_Systeminit configured TAU1; do not reset unrelated channels. */ R_TAU1_Channel5_Stop(); /* Slow RC rising edges on P70 benefit from the TI15 digital noise filter. Give capture precedence over UART interrupts while receiving a frame. */ NFEN2 |= _20_TAU_CH5_NOISE_ON; TMPR115 = 0U; TMPR015 = 0U; GPIO_Input(); } static void OWI_CaptureBegin(void) { /* Arm while the master still holds the final address LOW. Its release is the first edge; the first complete slave LOW follows. */ g_owi_capture_threshold_ticks = 0UL; g_owi_capture_pending_index = 0U; g_owi_capture_pending_count = 0U; R_TAU1_Channel5_CaptureReset(); g_trace_stage = 7U; R_TAU1_Channel5_Start(); } /* Send the read address and arm capture BEFORE releasing its final LOW. Otherwise the GPIO/function-return overhead can lose the first response bit. */ static void OWI_WriteCapturedReadAddress(uint8_t address) { int b; uint32_t t_low = (address & 1U) ? (uint32_t)TLOW_1 : (uint32_t)TLOW_0; for (b = 7; b > 0; b--) OWI_WriteBit((address >> b) & 1U); OWI_Release(); delay_us((uint32_t)TBIT - t_low); OWI_DriveLow(); delay_us(t_low); OWI_CaptureBegin(); OWI_Release(); } static void OWI_CaptureEnd(void) { R_TAU1_Channel5_Stop(); GPIO_Input(); } static uint8_t OWI_CaptureWaitLowWidth(uint16_t byte_index, uint8_t bit_index, uint32_t * const low_ticks) { uint32_t timeout_us = OWI_CAPTURE_QUEUE_WAIT_US; while (timeout_us-- > 0UL) { /* Keep the bounded wait alive even when the input stays idle. */ if ((timeout_us & 63UL) == 0UL) OWI_KickWdt(); if (R_TAU1_Channel5_CaptureOverflowed()) break; if (R_TAU1_Channel5_GetLowWidthTicks(low_ticks)) { return 1U; } if (R_TAU1_Channel5_CaptureOverflowed()) { break; } /* This delay is only a timeout yield; pulse timing is captured by TAU1. */ delay_us(1U); } if (!g_owi_timeout_latched) { g_capture_error = R_TAU1_Channel5_CaptureOverflowed() ? 2U : 1U; g_owi_timeout_latched = 1U; g_owi_last_timeout_byte_index = byte_index; g_owi_last_timeout_bit_index = bit_index; } return 0U; } static uint32_t OWI_CaptureFindThreshold(const uint32_t *widths, uint8_t width_count) { uint32_t sorted[OWI_CAPTURE_SYNC_BITS]; uint32_t max_gap = 0UL; uint32_t threshold = ((uint32_t)bit_period_us * OWI_TAU1_TICKS_PER_US) / 2UL; uint32_t gap; uint32_t temp; uint8_t i; uint8_t j; for (i = 0U; i < width_count; i++) { sorted[i] = widths[i]; } for (i = 0U; i < (uint8_t)(width_count - 1U); i++) { for (j = (uint8_t)(i + 1U); j < width_count; j++) { if (sorted[j] < sorted[i]) { temp = sorted[i]; sorted[i] = sorted[j]; sorted[j] = temp; } } } /* The 0x26 header contains both short and long LOW pulses. The largest gap separates the two measured clusters without assuming timer scale. */ for (i = 0U; i < (uint8_t)(width_count - 1U); i++) { gap = sorted[i + 1U] - sorted[i]; if (gap > max_gap) { max_gap = gap; threshold = sorted[i] + (gap / 2UL); } } return threshold; } static uint8_t OWI_CaptureExpectedHeaderBit(uint8_t header_bit_index) { return (uint8_t)((0x26U >> (7U - header_bit_index)) & 0x01U); } /* Find a complete header in the bounded startup window. The read-address release may add a leading capture (observed SYNC=9343: 1|00100110|1000011). Preserve every bit after the header; never reconstruct a partial header. */ static uint8_t OWI_CaptureFindHeaderEnd(const uint8_t *bits, uint8_t bit_count, uint8_t * const header_end) { uint8_t start; uint8_t i; if (!bits || !header_end || bit_count < 8U || bit_count > OWI_CAPTURE_SYNC_BITS) return 0U; for (start = 0U; start <= (uint8_t)(bit_count - 8U); start++) { for (i = 0U; i < 8U; i++) { if (bits[start + i] != OWI_CaptureExpectedHeaderBit(i)) break; } if (i == 8U) { *header_end = (uint8_t)(start + 8U); return 1U; } } return 0U; } static uint8_t OWI_CaptureGetDecodedBit(uint16_t byte_index, uint8_t bit_index, uint8_t * const decoded_bit) { uint32_t low_ticks; if (decoded_bit == 0) { return 0U; } if (g_owi_capture_pending_index < g_owi_capture_pending_count) { *decoded_bit = g_owi_capture_pending_bits[g_owi_capture_pending_index++]; return 1U; } if (!OWI_CaptureWaitLowWidth(byte_index, bit_index, &low_ticks)) { return 0U; } *decoded_bit = (low_ticks < g_owi_capture_threshold_ticks) ? 1U : 0U; return 1U; } static uint8_t OWI_ReadByte_Captured(uint16_t byte_index) { uint8_t data = 0U; int b; uint32_t sync_widths[OWI_CAPTURE_SYNC_BITS]; if (byte_index == 0U) { uint8_t sync_bits[OWI_CAPTURE_SYNC_BITS]; uint8_t header_end; uint8_t i; for (i = 0U; i < OWI_CAPTURE_SYNC_BITS; i++) { OWI_KickWdt(); g_owi_current_read_byte_index = 0U; g_owi_current_read_bit_index = i; if (!OWI_CaptureWaitLowWidth(0U, i, &sync_widths[i])) { return 0U; } } g_owi_capture_threshold_ticks = OWI_CaptureFindThreshold(sync_widths, OWI_CAPTURE_SYNC_BITS); for (i = 0U; i < OWI_CAPTURE_SYNC_BITS; i++) { sync_bits[i] = (sync_widths[i] < g_owi_capture_threshold_ticks) ? 1U : 0U; } g_capture_sync = 0U; for (i = 0U; i < OWI_CAPTURE_SYNC_BITS; i++) { g_capture_sync = (uint16_t)((g_capture_sync << 1) | sync_bits[i]); } if (!OWI_CaptureFindHeaderEnd(sync_bits, OWI_CAPTURE_SYNC_BITS, &header_end)) { if (!g_owi_timeout_latched) { g_owi_timeout_latched = 1U; g_capture_error = 3U; g_owi_last_timeout_byte_index = 0U; g_owi_last_timeout_bit_index = 0U; } return 0U; } g_owi_capture_pending_index = 0U; g_owi_capture_pending_count = (uint8_t)(OWI_CAPTURE_SYNC_BITS - header_end); for (i = 0U; i < g_owi_capture_pending_count; i++) { g_owi_capture_pending_bits[i] = sync_bits[header_end + i]; } return 0x26U; } for (b = 7; b >= 0; b--) { uint8_t bit_index = (uint8_t)(7 - b); uint8_t bit; g_owi_current_read_byte_index = byte_index; g_owi_current_read_bit_index = bit_index; if (!OWI_CaptureGetDecodedBit(byte_index, bit_index, &bit)) { break; } if (bit) { data |= (uint8_t)(1U << b); } } return data; } #endif void OWI_SecureStop(void) { int i; GPIO_Clear(); delay_us(SECURE_HIGH); for (i = 0; i < (int)SECURE_TOGGLE_COUNT; i++) { GPIO_Input(); delay_us(SECURE_TOGGLE_HIGH); GPIO_Clear(); delay_us(SECURE_TOGGLE_LOW); } GPIO_Input(); delay_us(SECURE_HIGH); GPIO_Clear(); delay_us(TSTART_HOLD); GPIO_Input(); } /* ========================================================= * Bit/Byte IO * ========================================================= */ void OWI_WriteBit(int bit) { uint32_t t_low = bit ? (uint32_t)TLOW_1 : (uint32_t)TLOW_0; uint32_t t_high = (uint32_t)TBIT - t_low; /* HIGH 구간 */ OWI_Release(); delay_us(t_high); /* LOW 구간 */ OWI_DriveLow(); delay_us(t_low); /* 다음 슬롯 시작 전 반드시 release */ OWI_Release(); } void OWI_WriteByte(uint8_t data) { int i; for (i = 7; i >= 0; i--) { OWI_WriteBit((data >> i) & 0x01u); } GPIO_Input(); } uint8_t OWI_ReadBit(void) { int timeout; int low_width = 0; /* 1) 라인이 HIGH가 될 때까지 정리 */ timeout = (int)(bit_period_us * 2u); while (!GPIO_Read() && timeout-- > 0) { delay_us(1u); } if (timeout <= 0) { if (!g_owi_timeout_latched) { 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; } /* 2) 다음 LOW 시작 대기 */ timeout = (int)(bit_period_us * 2u); while (GPIO_Read() && timeout-- > 0) { delay_us(1u); } if (timeout <= 0) { if (!g_owi_timeout_latched) { 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; } /* 3) LOW가 유지되는 시간을 직접 측정 */ while (!GPIO_Read() && low_width < (int)(bit_period_us * 2u)) { delay_us(1u); low_width++; } /* 4) LOW 폭 기준으로 판정 - 짧으면 1 - 길면 0 threshold ~= TBIT/2 */ return (low_width < (int)(bit_period_us / 2u)) ? 1u : 0u; } uint8_t OWI_ReadByte(void) { uint8_t data = 0; int i; for (i = 7; i >= 0; i--) { g_owi_current_read_bit_index = (uint8_t)(7 - i); data |= (uint8_t)(OWI_ReadBit() << i); if (OWI_HasTimeout()) { break; } } return data; } /* ========================================================= * Raw helpers * ========================================================= */ static void owi_result_init(owi_io_result_t *r) { if (!r) return; memset(r, 0, sizeof(*r)); r->ok = 1; r->timeout_byte_index = 0xFFFFu; r->timeout_bit_index = 0xFFu; } static void owi_result_latch_timeout(owi_io_result_t *r) { if (!r) return; if (OWI_HasTimeout()) { r->ok = 0; r->timeout = 1; r->timeout_byte_index = OWI_GetLastTimeoutByteIndex(); r->timeout_bit_index = OWI_GetLastTimeoutBitIndex(); } } void OWI_T_CommandModeRaw(const uint8_t *tx_data, uint8_t tx_len, uint8_t id, owi_io_result_t *r) { int i; owi_result_init(r); //delay_us(200); OWI_Init(OWI_BIT_PERIOD_US); OWI_ClearTimeout(); OWI_KickWdt(); OWI_SecureStop(); OWI_KickWdt(); OWI_WriteByte((uint8_t)(id << 1)); OWI_KickWdt(); for (i = 0; i < (int)tx_len; i++) { OWI_KickWdt(); OWI_WriteByte(tx_data[i]); OWI_KickWdt(); } OWI_Stop(); OWI_KickWdt(); owi_result_latch_timeout(r); } void OWI_CommandModeRaw(const uint8_t *tx_data, uint8_t tx_len, uint8_t id, owi_io_result_t *r) { int i; owi_result_init(r); OWI_Init(OWI_BIT_PERIOD_US); OWI_ClearTimeout(); OWI_KickWdt(); OWI_SecureStop(); OWI_KickWdt(); OWI_WriteByte((uint8_t)(id << 1)); OWI_KickWdt(); for (i = 0; i < (int)tx_len; i++) { OWI_KickWdt(); OWI_WriteByte(tx_data[i]); OWI_KickWdt(); } OWI_Stop(); OWI_KickWdt(); owi_result_latch_timeout(r); } void OWI_ReadBytesRaw(int length, uint8_t id, owi_io_result_t *r) { int i; int max_len; uint8_t is_long_read; if (!r) return; owi_result_init(r); OWI_Init(OWI_BIT_PERIOD_US); if (length <= 0) { r->ok = 0; return; } max_len = (int)OWI_IO_MAX_BYTES; if (length > max_len) { length = max_len; } r->read_len = 0U; OWI_ClearTimeout(); is_long_read = (length >= 119) ? 1u : 0u; OWI_KickWdt(); #if OWI_USE_TAU1_CAPTURE if (length == 127) OWI_CapturePrepare(); #endif OWI_SecureStop(); #if OWI_USE_TAU1_CAPTURE if (length == 127) OWI_WriteCapturedReadAddress((uint8_t)((id << 1) | 1u)); else #endif OWI_WriteByte((uint8_t)((id << 1) | 1u)); /* 기존 잘 되던 값 유지 */ #if OWI_USE_TAU1_CAPTURE if (length == 127) { for (i = 0; i < length; i++) { uint8_t value; OWI_KickWdt(); g_trace_stage = 8U; g_trace_byte = (uint16_t)i; value = OWI_ReadByte_Captured((uint16_t)i); if (R_TAU1_Channel5_CaptureOverflowed() && !OWI_HasTimeout()) { g_owi_timeout_latched = 1U; g_owi_last_timeout_byte_index = (uint16_t)i; g_owi_last_timeout_bit_index = 0xFFU; g_capture_error = 2U; } if (OWI_HasTimeout()) { owi_result_latch_timeout(r); break; } r->data[r->read_len++] = value; } g_trace_stage = 9U; OWI_CaptureEnd(); OWI_StopRead(); OWI_KickWdt(); return; } #endif delay_us(100u); OWI_KickWdt(); /* 첫 falling edge를 한 번 잡는다 */ if (!OWI_WaitForFallingEdge(0u, 0u)) { owi_result_latch_timeout(r); OWI_StopRead(); OWI_KickWdt(); return; } for (i = 0; i < length; i++) { OWI_KickWdt(); /* long read만 주기적 재동기화 */ if (is_long_read && i == (length - 50)) { OWI_KickWdt(); if (!OWI_WaitForFallingEdge((uint16_t)i, 0u)) { owi_result_latch_timeout(r); break; } } r->data[i] = OWI_ReadByte_StreamSynced((uint16_t)i); if (OWI_HasTimeout()) { owi_result_latch_timeout(r); break; } r->read_len++; } OWI_StopRead(); OWI_KickWdt(); }