I'm evaluating the Renesas RX65N MCU EV Kit. In this post: A Direct Memory Access example. source: application note R01AN2063EJ0220 DMAC Module Using Firmware Integration Technology |
The Example - DMA and ADC
The Renesas DMA example is something you can use in the real world:
One of the ADC pins is sampled 32 times and the data is put into RAM by the DMA controller, without loading the microcontroller.
When all data is available and transferred, the firmware logs it to the debug console.
Pin Assignment
The analog input A000 is used. It's broken out on the Envision board to the Arduino connector CN10, pin 1.
The voltage at this pin will be sampled by the application. That voltage is made tapping off the wiper of a 10K potentiometer.
For reference, I've connected a voltmeter to the wiper.
The result is that the log shows a number between 0 and 4095, where 0 is 0V and 4095 represents 3.3V.
The potentiometer can be used to swipe between the two values.
Firmware
DMA init
The DMA channel used is channel 1 (DMACA_CH1), and the source is - as expected - ADC input 0 (IR_S12ADC0_S12ADI0).
dmaca_return_t dma_init(void) { dmaca_return_t ret = DMACA_SUCCESS; dmaca_stat_t p_stat_dmaca; /* Open DMA module */ R_DMACA_Init(); ret = R_DMACA_Open(DMACA_CH1); if (DMACA_SUCCESS == ret) { /* Initial DMA */ g_dma_cfg.act_source = IR_S12ADC0_S12ADI0; g_dma_cfg.request_source = DMACA_TRANSFER_REQUEST_PERIPHERAL; g_dma_cfg.transfer_mode = DMACA_TRANSFER_MODE_REPEAT; g_dma_cfg.data_size = DMACA_DATA_SIZE_WORD; g_dma_cfg.src_addr_mode = DMACA_SRC_ADDR_FIXED; g_dma_cfg.src_addr_repeat_area = DMACA_SRC_ADDR_EXT_REP_AREA_NONE; g_dma_cfg.des_addr_mode = DMACA_DES_ADDR_INCR; g_dma_cfg.des_addr_repeat_area = DMACA_DES_ADDR_EXT_REP_AREA_NONE; g_dma_cfg.repeat_block_side = DMACA_REPEAT_BLOCK_DESTINATION; g_dma_cfg.interrupt_sel = DMACA_CLEAR_INTERRUPT_FLAG_BEGINNING_TRANSFER; g_dma_cfg.dtie_request = DMACA_TRANSFER_END_INTERRUPT_ENABLE; g_dma_cfg.esie_request = DMACA_TRANSFER_ESCAPE_END_INTERRUPT_DISABLE; g_dma_cfg.rptie_request = DMACA_REPEAT_SIZE_END_INTERRUPT_DISABLE; g_dma_cfg.sarie_request = DMACA_SRC_ADDR_EXT_REP_AREA_OVER_INTERRUPT_DISABLE; g_dma_cfg.darie_request = DMACA_DES_ADDR_EXT_REP_AREA_OVER_INTERRUPT_DISABLE; g_dma_cfg.p_src_addr = (void *)&S12AD.ADDR0; g_dma_cfg.p_des_addr = (void *)&g_dma_buf; g_dma_cfg.offset_value = 0; g_dma_cfg.transfer_count = 1; g_dma_cfg.block_size = BUF_SIZE; ret = R_DMACA_Control(DMACA_CH1, DMACA_CMD_ALL_ENABLE, &p_stat_dmaca); } return ret; }
ADC init
adc_err_t adc_init(void) { adc_err_t ret = ADC_SUCCESS; adc_cfg_t adc_cfg; adc_ch_cfg_t ch_cfg; adc_cfg.resolution = ADC_RESOLUTION_12_BIT; adc_cfg.alignment = ADC_ALIGN_RIGHT; adc_cfg.add_cnt = ADC_ADD_OFF; adc_cfg.clearing = ADC_CLEAR_AFTER_READ_OFF; adc_cfg.trigger = ADC_TRIG_SYNC_TRG0AN; adc_cfg.trigger_groupb = ADC_TRIG_NONE; adc_cfg.priority = 3; adc_cfg.priority_groupb = 0; adc_cfg.temp_sensor = ADC_TEMP_SENSOR_NOT_AD_CONVERTED; adc_cfg.add_temp_sensor = ADC_TEMP_SENSOR_ADD_OFF; ret = R_ADC_Open(0, ADC_MODE_SS_ONE_CH, &adc_cfg, adc_callback); R_ADC_PinSet_S12AD0(); ch_cfg.chan_mask = ADC_MASK_CH0; // select channel 0 (POT) ch_cfg.priority_groupa = ADC_GRPA_PRIORITY_OFF; // group mode not used ch_cfg.chan_mask_groupb = ADC_MASK_GROUPB_OFF; // group mode not used ch_cfg.chan_mask_groupc = ADC_MASK_GROUPC_OFF; // group mode not used ch_cfg.add_mask = ADC_MASK_ADD_OFF; // no channels using addition ch_cfg.diag_method = ADC_DIAG_OFF; // self-diagnosis off ch_cfg.anex_enable = false; // no external amplifier ch_cfg.sample_hold_mask = ADC_MASK_SAMPLE_HOLD_OFF; // Bypass chnl-dedicated S&H circuits ch_cfg.sample_hold_states = ADC_SST_SH_CNT_DEFAULT; // default sample & hold states if (ADC_SUCCESS == ret) { R_ADC_Control(0, ADC_CMD_ENABLE_CHANS, &ch_cfg); init_mtu0(); } if (ADC_SUCCESS == ret) { R_ADC_Control(0, ADC_CMD_ENABLE_TRIG, FIT_NO_PTR); } return ret; }
The Multi-Function Timer Pulse Unit is used to wait after the ADC channel is enabled (line 34). The code is available in the example and a nice example on how to create a wait without a while(nothing) loop.
An ADC callback function is mandatory in an interrupt driven design. In this case, where the info is transferred via the DMA, the callback doesn't have to do anything.
Hence this code:
void adc_callback(void *pArgs) { nop(); }
DMA start
The DMA is kicked off in this function:
First, channel 0 is disabled (line 6), then configured line 9).
After that it's activated (line 13).
dmaca_return_t dma_start(void) { dmaca_return_t ret = DMACA_SUCCESS; dmaca_stat_t p_stat_dmaca; ret = R_DMACA_Control(DMACA_CH1, DMACA_CMD_DISABLE, &p_stat_dmaca); if (DMACA_SUCCESS == ret) { ret = R_DMACA_Create(DMACA_CH1, &g_dma_cfg); } if (DMACA_SUCCESS == ret) { ret = R_DMACA_Control(DMACA_CH1, DMACA_CMD_ENABLE, &p_stat_dmaca); } return ret; }
Main program
The main program, as usual, initialised buffers and variables, then sets the DMA mechanism in motion.
You'll see how the functions documented above work together.
Each loop, DMA is started, then the RX65 waits for the sampling and transfer to complete.
You can do something else than waiting here. The processor is free because DMA works autonomous.
Once the cycle is finished, the results are logged.
Repeat.
#define BUF_SIZE (32) /* Buffer size */ // ... static uint16_t g_dma_buf[BUF_SIZE]; /* Reception buffer */ dmaca_transfer_data_cfg_t g_dma_cfg; void main(void) { static uint8_t cnt; static uint8_t rcpflag; dmaca_stat_t p_stat_dmaca; /* Initial configurations */ R_Systeminit(); dma_init(); adc_init(); /* Main routine */ while (1) { /* Reset DMA buffer */ for (cnt = 0; cnt < BUF_SIZE; cnt++) { g_dma_buf[cnt] = 0; } /* Start DMA transfer */ dma_start(); /* Wait routine for the completion of DMA transfer (CPU can run other task instead) */ rcpflag = 0; while (rcpflag == 0) { R_DMACA_Control(DMACA_CH1, DMACA_CMD_STATUS_GET, &p_stat_dmaca); if ((true == p_stat_dmaca.dtif_stat) && (0 == p_stat_dmaca.transfer_count)) { rcpflag = 1; } } /* Print out result */ for (cnt = 0; cnt < BUF_SIZE; cnt++) { printf("\n DMA buffer no.%d: %d", cnt, g_dma_buf[cnt]); } printf("\n DMA transmission end \n"); } }
All code displayed here is from the application note R01AN2063EJ0220 DMAC Module Using Firmware Integration Technology's example, without changes. In this case I can publish selected snippets as fair use - comment upon. /*********************************************************************************************************************** * DISCLAIMER * This software is supplied by Renesas Electronics Corporation and is only intended for use with Renesas products. No * other uses are authorized. This software is owned by Renesas Electronics Corporation and is protected under all * applicable laws, including copyright laws. * THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING * THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED. TO THE MAXIMUM * EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES * SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY REASON RELATED TO THIS * SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * Renesas reserves the right, without notice, to make changes to this software and to discontinue the availability of * this software. By using this software, you agree to the additional terms and conditions found by accessing the * following link: * http://www.renesas.com/disclaimer * * Copyright (C) 2019 Renesas Electronics Corporation. All rights reserved. ***********************************************************************************************************************/ |
Top Comments