return to index

RH850 UART0 DMA TX / RX (interrupt + idle timer)

Reference Project

This training material is based on the below reference project:

Agenda


1. System overview

Base board & MCU

Peripherals

back to top


2. DMA access: PEG & register update

Before using DMA to access SRAM, PEG registers must be configured.

PEG register references

Example code

PDMA0.DM00CM = _DMAC_PE1_SETTING | _DMAC_SPID0_SETTING | _DMAC_SUPERVISON_MODE;    
PDMA0.DM01CM = _DMAC_PE1_SETTING | _DMAC_SPID0_SETTING | _DMAC_SUPERVISON_MODE;
PEG.SP.UINT32 = 0x00000001U;                /* SPEN = 1, permit access */
PEG.G0MK.UINT32 = 0xFFFFF000U;              /* 32KB window mask */
PEG.G0BA.UINT32 = ADDR_LOCAL_RAM_CPU1 |     /* Base address of PE Guard protection Area (start of Local RAM) */
                (0x1U<<7U) |                /* Enable Access for SPID 3 */
                (0x1U<<6U) |                /* Enable Access for SPID 2 */
                (0x1U<<5U) |                /* Enable Access for SPID 1 */
                (0x1U<<4U) |                /* Enable Access for SPID 0 */
                (0x1U<<2U) |                /* Write access is enabled */
                (0x1U<<1U) |                /* Read access is enabled */
                (0x1U<<0U);                 /* Settings for access enable conditions are enabled */
/* LOCAL_RAM_CPU1 start address (PEG) */
#define ADDR_LOCAL_RAM_CPU1         (0xFEBF0000)

back to top


3. Memory map (RH850/F1KM-S1)

FLASH LOCAL RAM(CPU1) RETENTION RAM(CPU1) LOCAL RAM(SELF) RETENTION RAM(SELF)
512K 0xFEBF0000 ~ 0xFEBF7FFF 0xFEBF8000 ~ 0xFEBFFFFF 0xFEDF0000 ~ 0xFEDF7FFF 0xFEDF8000 ~ 0xFEDFFFFF
768K 0xFEBE8000 ~ 0xFEBF7FFF 0xFEBF8000 ~ 0xFEBFFFFF 0xFEDE8000 ~ 0xFEDF7FFF 0xFEDF8000 ~ 0xFEDFFFFF
1MB 0xFEBE0000 ~ 0xFEBF7FFF 0xFEBF8000 ~ 0xFEBFFFFF 0xFEDE0000 ~ 0xFEDF7FFF 0xFEDF8000 ~ 0xFEDFFFFF

CPU1 area vs Self area

DMA access area (only allow access CPU1 area)

back to top


4. SRAM allocation (DMA buffer)

Need to allocate SRAM section for DMA buffer.

Define TX / RX DMA buffers in dma_buf section:

#pragma section dma_buf
volatile uint8_t s_uart0_dma_rx_ring[APP_UART0_DMA_RX_RING_SIZE];
volatile uint8_t s_uart0_dma_tx_buf[APP_UART0_DMA_TX_BUF_SIZE];
#pragma section default

DMA buffer allocation result in map file:

UART DMA TX / RX config

Item DMA source address DMA source address count direction DMA dest. address DMA dest. address count direction
TX s_uart0_dma_tx_buf increase APP_UART0_TX_DR fix
RX APP_UART0_RX_DR fix s_uart0_dma_rx_ring increase

back to top


5. DMA address / register map

back to top


6. UART address / register map

TX : RLN3nLUTDR

RX : RLN3nLURDR

back to top


7. Smart Config settings

Target trigger source : UART0

TX configuration

RX configuration

back to top


8. Code flow: TX DMA / RX interrupt

TX DMA (UART0)

Goal: CPU prepares a buffer, DMA moves bytes to UART0 TX data register.

Flow summary:

Notes:

void APP_UART0_DMA_TxSend(const uint8_t *data, uint16_t len)
{
    uint16_t copy_len;
 
    // memset(s_uart0_dma_tx_buf, 0xAA, sizeof(s_uart0_dma_tx_buf));


    if ((data == 0) || (len == 0U))
    {
        return;
    }

    while (UART0_TX_IS_BUSY())
    {
        /* wait until TX can accept data */
    }

    R_Config_DMAC00_Suspend();
    R_Config_DMAC00_Stop();
    (void)DRV_DMA_ClearTcAndErr(APP_UART0_DMA_UNIT, APP_UART0_DMA_TX_CH);

    copy_len = app_uart0_dma_txbuf_write(data, len);
    if (copy_len == 0U)
    {
        return;
    }
    // tiny_printf("len2:%u\r\n", len);
    // tiny_printf("copy_len2:%u\r\n", copy_len);

    (void)DRV_DMA_SetChannelEx(APP_UART0_DMA_UNIT,
                              APP_UART0_DMA_TX_CH,
                              APP_DMA_CH0_SRC_ADDR,
                              APP_DMA_CH0_DST_ADDR,
                              copy_len);
                              
    s_uart0_dma_tx_busy = 1U;
    R_Config_DMAC00_Resume();
    R_Config_DMAC00_Start();

    APP_UART0_TX_DR = s_uart0_dma_tx_buf[0];

    // while (! (PDMA0.DCST0 & _DMAC_TRANSFER_COMPLETION_FLAG_CLEAR));
    while (s_uart0_dma_tx_busy != 0U)
    {
        /* optional: add timeout / feed WDT */
    }
}

RX interrupt (UART0)

Goal: Use UART RX interrupt to capture bytes into a ring buffer, then use idle timer to decide a frame is complete.

Flow summary:

Notes:

back to top


9. RX idle detection timer

RH850 UART has no idle interrupt , need to use timer for idle detection (target: 2.5 bytes).

for uart rx idle timer isr :
8N1 , 1 byte = 10 bits
target : 2.5 bytes = 25 bits

target_isr_timing
- baud rate 115200 = (25 / 115200)*10^6 = 217.0us
- baud rate 415000 = (25 / 415000)*10^6 = 60.24us

timer_freq = PCLK(80MHz) / prescaler(1)
CDR0 = (timer_freq * target_isr_timing/10^6) - 1
- baud rate 115200 217.0us = (80MHz * 217us/10^6) - 1 = 80 * 217 - 1 = 0x43CF
- baud rate 415000 60.24us = (80MHz * 60.24us/10^6) - 1 = 80 * 60.24 - 1 = 0x12D3
// #define APP_UART0_BAUD              (415000U)
#define APP_UART0_BAUD              (115200U)

#if (APP_UART0_BAUD == 115200U)
    TAUJ1.CDR0 = 0x43CFU;   /* 217 us = 2.5 bytes @115200 */
#elif (APP_UART0_BAUD == 415000U)
    TAUJ1.CDR0 = 0x12D3U;   /* 60.24 us = 2.5 bytes @415000 */
#else
    #error "Unsupported APP_UART0_BAUD"
#endif

RX TIMER IDLE detection flow

flowchart TD A[APP_UART0_RX_Init] --> B[Reset RX buffer & state] B --> C[UART0 Receive 1 byte enable] C --> D[UART0 Start] D --> E[STATE = RX_INIT] E --> F[Start t3.5 Timer] %% UART RX interrupt F -->|UART RX IRQ| G[APP_UART0_RX_callback_receiveend] G --> H[Store received byte] H --> I[_prvvUARTRxISR] %% State machine I -->|STATE_RX_INIT| J[Enable Timer] I -->|STATE_RX_ERROR| J I -->|STATE_RX_IDLE| K[bufferPos=0] K --> L[Store first byte] L --> M[STATE = RX_RCV] M --> J I -->|STATE_RX_RCV| N{bufferPos < BUF_SIZE?} N -->|Yes| O[Store byte] O --> J N -->|No| P[STATE = RX_ERROR] P --> Q[Reset state] %% Timer expiry (IDLE detected) J -->|Timer expired| R[APP_UART0_RX_TimerIsr] R --> S[_prvvTIMERExpiredISR] S --> T{STATE == RX_RCV?} T -->|Yes| U[UART Stop] U --> V[g_rcv_data_finish = 1] T -->|No| W[Ignore] V --> X[STATE = RX_IDLE] %% Main loop process X --> Y[APP_UART0_RX_Process] Y -->|g_rcv_data_finish| Z[Dump RX buffer] Z --> AA[Clear buffer] AA --> AB[Reset state] AB --> AC[UART Start]

back to top


10. Debug watch window

If need to check global variable / array in watch windows, enable [Access during the execution] at Debugger Property > Debug Tool Settings tab.

back to top


RH850 UART0 DMA TX / RX (interrupt + idle timer)

  1. System overview
  1. DMA access: PEG & register update
  1. SRAM allocation (DMA buffer)
  1. Code flow: TX DMA / RX interrupt