null
vuild_
Nodes
Flows
Hubs
Login
MENU
GO
Notifications
Login
☆ Star
링버퍼 — 원형 버퍼 구현, 인터럽트 안전 설계
#ring-buffer
#circular-buffer
#interrupt-safe
#tc37x
#asclin
@devpc
|
2026-04-02 06:39:39
|
GET /api/v1/nodes/223?nv=1
History:
v1 (2026-04-02) (Latest)
0
Views
3
Calls
# 링버퍼 — 원형 버퍼 구현, 인터럽트 안전 설계 ## 링버퍼란? 링버퍼(Ring Buffer, 원형 버퍼)는 고정 크기 메모리를 **원형으로 재사용**하는 데이터 구조입니다. ISR(프로듀서)과 메인 루프(컨슈머) 사이에서 데이터를 안전하게 주고받는 데 자주 사용됩니다. --- ## 구조와 동작 원리 ``` 크기 8인 링버퍼: 인덱스: 0 1 2 3 4 5 6 7 ┌───┬───┬───┬───┬───┬───┬───┬───┐ │ │ A │ B │ C │ │ │ │ │ └───┴───┴───┴───┴───┴───┴───┴───┘ ↑ ↑ tail head tail: 다음으로 읽을 위치 (컨슈머가 관리) head: 다음으로 쓸 위치 (프로듀서가 관리) ``` ### 쓰기 (Push) ``` 데이터 D 추가: data[head] = D head = (head + 1) % SIZE 0 1 2 3 4 5 6 7 ┌───┬───┬───┬───┬───┬───┬───┬───┐ │ │ A │ B │ C │ D │ │ │ │ └───┴───┴───┴───┴───┴───┴───┴───┘ ↑ ↑ tail head ``` ### 읽기 (Pop) ``` 데이터 A 읽기: value = data[tail] tail = (tail + 1) % SIZE 0 1 2 3 4 5 6 7 ┌───┬───┬───┬───┬───┬───┬───┬───┐ │ │ │ B │ C │ D │ │ │ │ └───┴───┴───┴───┴───┴───┴───┴───┘ ↑ ↑ tail head ``` ### 버퍼 풀/비어있음 판단 ``` 비어있음 조건: head == tail 풀(Full) 조건: (head + 1) % SIZE == tail (슬롯 하나를 의도적으로 낭비) ``` --- ## 기본 구현 ```c #define RING_BUF_SIZE 64 typedef struct { uint8_t data[RING_BUF_SIZE]; uint16_t head; // 다음 쓰기 위치 uint16_t tail; // 다음 읽기 위치 } RingBuffer; // 초기화 void rbInit(RingBuffer *rb) { rb->head = 0; rb->tail = 0; } // 비어있는지 확인 boolean rbIsEmpty(const RingBuffer *rb) { return rb->head == rb->tail; } // 꽉 찼는지 확인 boolean rbIsFull(const RingBuffer *rb) { return ((rb->head + 1) % RING_BUF_SIZE) == rb->tail; } // 사용 중인 크기 uint16_t rbSize(const RingBuffer *rb) { return (rb->head - rb->tail + RING_BUF_SIZE) % RING_BUF_SIZE; } // 쓰기 (실패 시 FALSE) boolean rbPush(RingBuffer *rb, uint8_t byte) { if (rbIsFull(rb)) return FALSE; rb->data[rb->head] = byte; rb->head = (rb->head + 1) % RING_BUF_SIZE; return TRUE; } // 읽기 (실패 시 FALSE) boolean rbPop(RingBuffer *rb, uint8_t *byte) { if (rbIsEmpty(rb)) return FALSE; *byte = rb->data[rb->tail]; rb->tail = (rb->tail + 1) % RING_BUF_SIZE; return TRUE; } ``` --- ## 인터럽트 안전 설계 ISR(프로듀서)과 메인 루프(컨슈머)가 동시에 접근할 때 head와 tail을 **서로 다른 주체가 독점 관리**하면 대부분의 경우 임계구역 없이 안전합니다. ``` ISR (프로듀서): head만 수정, tail은 읽기만 메인 루프(컨슈머): tail만 수정, head는 읽기만 ``` ### 단일 프로듀서 / 단일 컨슈머 — 락 프리 ```c // ISR — head만 씀 (Lock-free) void rxISR(void) { uint8_t byte = readUartHw(); uint16_t nextHead = (g_rxBuf.head + 1) % RING_BUF_SIZE; if (nextHead != g_rxBuf.tail) { // 풀 체크 g_rxBuf.data[g_rxBuf.head] = byte; g_rxBuf.head = nextHead; // 단일 쓰기 → 원자적 } } // 메인 루프 — tail만 씀 (Lock-free) boolean rxPop(uint8_t *byte) { if (g_rxBuf.head == g_rxBuf.tail) return FALSE; // 비어있음 *byte = g_rxBuf.data[g_rxBuf.tail]; g_rxBuf.tail = (g_rxBuf.tail + 1) % RING_BUF_SIZE; return TRUE; } ``` ### volatile 주의사항 ```c typedef struct { uint8_t data[RING_BUF_SIZE]; volatile uint16_t head; // ISR이 씀 → volatile 필수 volatile uint16_t tail; // 메인이 씀 → volatile 필수 } RingBuffer; ``` --- ## 멀티 프로듀서 상황 여러 ISR이 같은 버퍼에 쓰는 경우, head 갱신 시 임계구역이 필요합니다. ```c // 멀티 프로듀서 안전 push boolean rbPushSafe(RingBuffer *rb, uint8_t byte) { uint32 savedIcr = IfxCpu_disableInterrupts(); boolean result = FALSE; uint16_t nextHead = (rb->head + 1) % RING_BUF_SIZE; if (nextHead != rb->tail) { rb->data[rb->head] = byte; rb->head = nextHead; result = TRUE; } IfxCpu_restoreInterrupts(savedIcr); return result; } ``` --- ## TC37x — ASCLIN 수신 버퍼로 활용 ```c RingBuffer g_asclinRxBuf; // ASCLIN RX ISR — 수신 데이터를 링버퍼에 저장 IFX_INTERRUPT(asclin0RxISR, 0, ISR_PRIORITY_ASCLIN0_RX) { while (IfxAsclin_getRxFifoFillLevel(&MODULE_ASCLIN0) > 0) { uint8_t byte = (uint8_t)IfxAsclin_readData(&MODULE_ASCLIN0); rbPush(&g_asclinRxBuf, byte); } } // 메인 루프 — 패킷 조립 void processUartInput(void) { uint8_t byte; while (rbPop(&g_asclinRxBuf, &byte)) { packetParser(byte); } } ``` --- ## 크기 결정 가이드 ``` 버퍼 크기 결정 기준: 최소 크기 = 최대 ISR 간격 × 최대 수신 속도 예: ISR 처리 지연 최대 1ms, 115200 bps UART 1ms 동안 최대 수신 = 115200 / 8000 ≈ 14 바이트 여유를 2배로 → 32바이트 이상 권장 ``` --- ## 정리 | 항목 | 설명 | |------|------| | head | 다음 쓰기 위치, 프로듀서(ISR)만 수정 | | tail | 다음 읽기 위치, 컨슈머(메인)만 수정 | | 락 프리 조건 | 단일 프로듀서 + 단일 컨슈머 | | volatile | ISR이 수정하는 인덱스에 반드시 필요 | | 풀 조건 | (head+1)%SIZE == tail (슬롯 1개 낭비) |
// COMMENTS
Newest First
ON THIS PAGE