DTC chain example for multiple SCI SPI interfaces based on Code Generator Drivers
Background
When we use SCI at high bit rates, servicing the data trasnfer from interrupts may have performance concern. For this consideration, it is recommended to service the data transfers using the DTC. However, if the application makes heavy use of DTC transfer for multiple SCI SPI instances, SCI SPI may experience the overrun error at high bit rates, and this is caused by the arbitration between different transfers in DTC.
Use the DTC chain transfer
If user application has to use multiple SCI SPI interfaces with DTC, one of the approach for preventing the overrun error is to use the DTC chain. When we use a single DTC transfer to chain the TX transfer or RX transfer of each SCI SPI interfaces, DTC will sequentially move the data per SCI SPI interface once TX Buffer Empty Interrupt or RX Buffer Full interrupt is triggered. By doing so, the DTC TX chain transfer or RX chain transfer is not preempted by other DTC transfer, so it ensures that no overrun occurs in the SCI SPI interface with lower interrupt priority.
Configure DTC and SCI SPI Master Code Generator (CG) Drivers in Smart Configurator
In this example, the SPI Clock Synchronous Mode CG driver is used for all 9 SCI SPI masters, and the SCI SPI bit rate of all interfaces is set to the same as 5MHz.
The SPI Clock Synchronous Mode CG driver for SCI SPI master should be configured to use DTC on Data handling setting.
As shown in the picture below, a total of two DTC chains should be created, one for the TX chain and the other for the RX chain. Both must have the same settings in the "Base setting", otherwise the Smart Configurator will throw an error. When you check the "Chain transfer" box on the "DTC0" tab, another tab "DTC1" will be created on the right, and so on. All DTC transfers in a chain are automatically assigned to use the "Activation source" selected in the DTC0 transfer, so select the TX source for the TX chain and the RX source for the RX chain.
The other default settings can be ignored now as they can be dynamically changed in the application code, unlike the Smart Configurator settings.
Example code except the Smart Configurator generated code (smc_gen)
#include "r_smc_entry.h"
#include "r_cg_userdefine.h"
#include "string.h"
extern volatile st_dtc_data_t dtc_transferdata_vector58[9];
extern volatile st_dtc_data_t dtc_transferdata_vector59[9];
uint8_t spi_rx_buff[MAX_SPI_CH][MAX_SPI_BUFF_SIZE];
uint8_t spi_tx_buff[MAX_SPI_CH][MAX_SPI_BUFF_SIZE];
const SPI_DTC_INFO spi_dtc_init_tb[MAX_SPI_CH] = {
{&dtc_transferdata_vector58[0], &dtc_transferdata_vector59[0], (uint32_t)&SCI0.RDR, (uint32_t)spi_rx_buff[0], (uint32_t)spi_tx_buff[0], (uint32_t)&SCI0.TDR, MAX_SPI_BUFF_SIZE},
{&dtc_transferdata_vector58[1], &dtc_transferdata_vector59[1], (uint32_t)&SCI1.RDR, (uint32_t)spi_rx_buff[1], (uint32_t)spi_tx_buff[1], (uint32_t)&SCI1.TDR, MAX_SPI_BUFF_SIZE},
{&dtc_transferdata_vector58[2], &dtc_transferdata_vector59[2], (uint32_t)&SCI2.RDR, (uint32_t)spi_rx_buff[2], (uint32_t)spi_tx_buff[2], (uint32_t)&SCI2.TDR, MAX_SPI_BUFF_SIZE},
{&dtc_transferdata_vector58[3], &dtc_transferdata_vector59[3], (uint32_t)&SCI3.RDR, (uint32_t)spi_rx_buff[3], (uint32_t)spi_tx_buff[3], (uint32_t)&SCI3.TDR, MAX_SPI_BUFF_SIZE},
{&dtc_transferdata_vector58[4], &dtc_transferdata_vector59[4], (uint32_t)&SCI5.RDR, (uint32_t)spi_rx_buff[4], (uint32_t)spi_tx_buff[4], (uint32_t)&SCI5.TDR, MAX_SPI_BUFF_SIZE},
{&dtc_transferdata_vector58[5], &dtc_transferdata_vector59[5], (uint32_t)&SCI6.RDR, (uint32_t)spi_rx_buff[5], (uint32_t)spi_tx_buff[5], (uint32_t)&SCI6.TDR, MAX_SPI_BUFF_SIZE},
{&dtc_transferdata_vector58[6], &dtc_transferdata_vector59[6], (uint32_t)&SCI8.RDR, (uint32_t)spi_rx_buff[6], (uint32_t)spi_tx_buff[6], (uint32_t)&SCI8.TDR, MAX_SPI_BUFF_SIZE},
{&dtc_transferdata_vector58[7], &dtc_transferdata_vector59[7], (uint32_t)&SCI9.RDR, (uint32_t)spi_rx_buff[7], (uint32_t)spi_tx_buff[7], (uint32_t)&SCI9.TDR, MAX_SPI_BUFF_SIZE},
{&dtc_transferdata_vector58[8], &dtc_transferdata_vector59[8], (uint32_t)&SCI12.RDR, (uint32_t)spi_rx_buff[8], (uint32_t)spi_tx_buff[8], (uint32_t)&SCI12.TDR, MAX_SPI_BUFF_SIZE},
};
void config_dtc_spi_init_all(void);
void config_dtc_spi_reinit_all(void);
void config_dtc_chain_spi_send_receive(void);
void main(void);
void config_dtc_spi_init_all(void)
{
volatile st_dtc_data_t* dtc_ptr;
for(uint32_t i=0;i<MAX_SPI_CH; i++){
//SPI master rx DTC setting
dtc_ptr = spi_dtc_init_tb[i].p_dtc_rx;
/* Set DTC0 transfer data */
//please note that the setting is different from smart configurator
dtc_ptr->mra_sar = ((uint32_t)(_00_DTC_SRC_ADDRESS_FIXED | _00_DTC_TRANSFER_SIZE_8BIT | _00_DTC_TRANSFER_MODE_NORMAL)<<24U) |
(spi_dtc_init_tb[i].rx_src_addr & 0x00FFFFFFUL);
dtc_ptr->mrb_dar = ((uint32_t)(_08_DTC_DST_ADDRESS_INCREMENTED | _00_DTC_INTERRUPT_COMPLETED)<<24U) |
(spi_dtc_init_tb[i].rx_dst_addr & 0x00FFFFFFUL);
//make it to chain transfer except the last one
if(i < MAX_SPI_CH-1){
dtc_ptr->mrb_dar |= ((uint32_t)(_80_DTC_CHAIN_TRANSFER_ENABLED) << 24U);
}
//length
dtc_ptr[i].cra_crb = (uint32_t)(spi_dtc_init_tb[i].trans_length) << 16U;
//SPI master tx DTC setting
dtc_ptr = spi_dtc_init_tb[i].p_dtc_tx;
/* Set DTC0 transfer data */
//please note that the setting is different from smart configurator
dtc_ptr->mra_sar = ((uint32_t)( _08_DTC_SRC_ADDRESS_INCREMENTED | _00_DTC_TRANSFER_SIZE_8BIT | _00_DTC_TRANSFER_MODE_NORMAL)<<24U) |
(spi_dtc_init_tb[i].tx_src_addr & 0x00FFFFFFUL);
dtc_ptr->mrb_dar = ((uint32_t)(_00_DTC_DST_ADDRESS_FIXED | _00_DTC_INTERRUPT_COMPLETED)<<24U) |
(spi_dtc_init_tb[i].tx_dst_addr & 0x00FFFFFFUL);
//make it to chain transfer except the last one
if(i < MAX_SPI_CH-1){
dtc_ptr->mrb_dar |= ((uint32_t)(_80_DTC_CHAIN_TRANSFER_ENABLED) << 24U);
}
//length
dtc_ptr->cra_crb = (uint32_t)(spi_dtc_init_tb[i].trans_length) << 16U;
}
}
void config_dtc_spi_reinit_all(void)
{
volatile st_dtc_data_t* dtc_ptr;
for(uint32_t i=0;i<MAX_SPI_CH; i++){
//SPI master rx DTC setting
dtc_ptr = spi_dtc_init_tb[i].p_dtc_rx;
dtc_ptr->mrb_dar &= ~0x00FFFFFFUL;
dtc_ptr->mrb_dar |= (spi_dtc_init_tb[i].rx_dst_addr & 0x00FFFFFFUL);
dtc_ptr->cra_crb = (uint32_t)(spi_dtc_init_tb[i].trans_length) << 16U;
//SPI master tx DTC setting
dtc_ptr = spi_dtc_init_tb[i].p_dtc_tx;
dtc_ptr->mra_sar &= ~0x00FFFFFFUL;
dtc_ptr->mra_sar |= (spi_dtc_init_tb[i].tx_src_addr & 0x00FFFFFFUL);
dtc_ptr->cra_crb = (uint32_t)(spi_dtc_init_tb[i].trans_length) << 16U;
}
}
void config_dtc_chain_spi_send_receive(void)
{
R_Config_SCI1_SPI_Master_Send_Receive(spi_tx_buff[SPI_CH_1], MAX_SPI_BUFF_SIZE, spi_rx_buff[SPI_CH_1], MAX_SPI_BUFF_SIZE);
R_Config_SCI2_SPI_Master_Send_Receive(spi_tx_buff[SPI_CH_2], MAX_SPI_BUFF_SIZE, spi_rx_buff[SPI_CH_2], MAX_SPI_BUFF_SIZE);
R_Config_SCI3_SPI_Master_Send_Receive(spi_tx_buff[SPI_CH_3], MAX_SPI_BUFF_SIZE, spi_rx_buff[SPI_CH_3], MAX_SPI_BUFF_SIZE);
R_Config_SCI5_SPI_Master_Send_Receive(spi_tx_buff[SPI_CH_5], MAX_SPI_BUFF_SIZE, spi_rx_buff[SPI_CH_5], MAX_SPI_BUFF_SIZE);
R_Config_SCI6_SPI_Master_Send_Receive(spi_tx_buff[SPI_CH_6], MAX_SPI_BUFF_SIZE, spi_rx_buff[SPI_CH_6], MAX_SPI_BUFF_SIZE);
R_Config_SCI8_SPI_Master_Send_Receive(spi_tx_buff[SPI_CH_8], MAX_SPI_BUFF_SIZE, spi_rx_buff[SPI_CH_8], MAX_SPI_BUFF_SIZE);
R_Config_SCI9_SPI_Master_Send_Receive(spi_tx_buff[SPI_CH_9], MAX_SPI_BUFF_SIZE, spi_rx_buff[SPI_CH_9], MAX_SPI_BUFF_SIZE);
R_Config_SCI12_SPI_Master_Send_Receive(spi_tx_buff[SPI_CH_12], MAX_SPI_BUFF_SIZE, spi_rx_buff[SPI_CH_12], MAX_SPI_BUFF_SIZE);
//call this chain trigger source at last
R_Config_SCI0_SPI_Master_Send_Receive(spi_tx_buff[SPI_CH_0], MAX_SPI_BUFF_SIZE, spi_rx_buff[SPI_CH_0], MAX_SPI_BUFF_SIZE);
}
void main(void)
{
uint16_t i, j;
for(i=0; i<MAX_SPI_CH; i++ ){
for(j = 0 ; j < MAX_SPI_BUFF_SIZE; j++){
spi_tx_buff[i][j] = j;
}
}
config_dtc_spi_init_all();
while(1){
//For testing, send every 3 seconds
R_BSP_SoftwareDelay(3, BSP_DELAY_SECS);
//re-init the DTC address and length
config_dtc_spi_reinit_all();
//enable DTC
R_Config_SPI_RX_DTC_Start();
R_Config_SPI_TX_DTC_Start();
//enable SCI0 interrupts
R_Config_SCI0_Start();
//assert the first SCI0 chip select pins
PORT0.PODR.BIT.B5 = 0;
/*
* assert your own SCI1, SCI2, SCI3, ... chip select pins here
*/
//assert the last SCI12 chip select pins
PORT0.PODR.BIT.B7 = 0;
//start transfer
config_dtc_chain_spi_send_receive();
}
}
void handle_dtc_sci0_spi_transmit_callback(void)
{
IEN(SCI0,TXI0) = 0U;
IR(SCI0,TXI0) = 0U;
//enable transmitend (TEIE interrupt, TEND flag) interrupt
//It's up to user to use transmitend interrupt (TEIE) handler to release SPI chip select pin (IC1_CS)
SCI0.SCR.BIT.TEIE = 1U;
//enable the last chain of SCI TXI, TEIE interrupt
IR(SCI12,TXI12) = 0U;
IEN(SCI12,TXI12) = 1U;
ICU.GENBL0.BIT.EN16 = 1U;
}
void handle_dtc_sci0_spi_transmitend_callback(void)
{
//disable transmitend (TEIE interrupt, TEND flag) interrupt
SCI0.SCR.BIT.TEIE = 0U;
//disable SCI-SPI transmitter and receiver
R_Config_SCI0_Stop();
}
void handle_dtc_sci12_spi_transmit_callback(void)
{
IEN(SCI12,TXI12) = 0U;
IR(SCI12,TXI12) = 0U;
//enable transmitend (TEIE interrupt, TEND flag) interrupt
//It's up to user to use transmitend interrupt (TEIE) handler to release SPI chip select pin (IC1_CS)
SCI12.SCR.BIT.TEIE = 1U;
}
void handle_dtc_sci12_spi_transmitend_callback(void)
{
//disable transmitend (TEIE interrupt, TEND flag) interrupt
SCI12.SCR.BIT.TEIE = 0U;
//disable SCI-SPI transmitter and receiver
R_Config_SCI12_Stop();
//release the first SCI0 chip select pins
PORT0.PODR.BIT.B5 = 1;
/*
* release your own SCI1, SCI2, SCI3, ... chip select pins here
*/
//release the last SCI12 chip select pins
PORT0.PODR.BIT.B7 = 1;
}