This training material is based on the below reference project:
TAUJ0_0 : timer interval for 1ms interrupt
UART1 : RLIN31 (TX > P10_12 , RX > P10_11) , for printf and receive from keyboard
TAUJ1_0 : timer interval for UART1 RX idle byte detection
UART0 : RLIN30 (TX > P10_10 , RX > P10_09)
Before using DMA to access SRAM, PEG registers must be configured.
PEG.SP (SPEN = 1)PEG.GnMKPEG.GnBAPDMAnDMyiCMPDMA0.DM00CM / DM01CM (SPID / PE / supervisor)






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)

| 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)

Need to allocate SRAM section for DMA buffer.
dma_buf at 0xFEBF0000
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:

| 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 |



TX : RLN3nLUTDR
RX : RLN3nLURDR

Target trigger source : UART0
INTRLIN30UR0 : RLIN30 transmit interrupt
INTRLIN30UR1 : RLIN30 receive complete interrupt

TX configuration
s_uart0_dma_tx_buf)APP_UART0_TX_DR)
RX configuration
APP_UART0_RX_DR)s_uart0_dma_rx_ring)
Goal: CPU prepares a buffer, DMA moves bytes to UART0 TX data register.
Flow summary:
s_uart0_dma_tx_buf[] with payload.APP_UART0_TX_DR.Notes:
INTRLIN30UR0 trigger.PDMA0.DM00CM must include PE/SPID/supervisor settings before start.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 */
}
}
Goal: Use UART RX interrupt to capture bytes into a ring buffer, then use idle timer to decide a frame is complete.
Flow summary:
APP_UART0_RX_DR and pushes into s_uart0_dma_rx_ring[].g_rcv_data_finish.Notes:
INTRLIN30UR1 interrupt source.dma_buf section to ensure DMA-safe SRAM layout, even though RX is interrupt based.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
If need to check global variable / array in watch windows, enable [Access during the execution] at Debugger Property > Debug Tool Settings tab.
