Gigadevice GD32VF103 DMA and the DAC - Page 1
Forum section: electronics / general

author: ale500
author_rank: Frequent Contributor
timestamp: November 18, 2021, 07:12:33 pm
content: After wasting several hours trying to get this to work: The DAC0 gets its DATA from memory using DMA triggered by a timer.The DAC gets triggered by a timer (5) and then initiates a DMA transfer (DMA1, CH2), after 1024 transfers an interrupt occurs and the buffer where the data resides changes, so far so good, this works but...The symptom ? 0 V on PIN A4.Before you ask, if I trigger the DAC per software and write the data manually, I get a signal at PIN A4 (see code below). It just does not output anything when the data comes from the DMA. The timer works, triggers DMA, interrupts work, the DAC0_R12DH gets written, but nothing gets throughAny ideas ?Notes:- I'm using segger's embedded studio for RISCV 5.66 and segger's jlink too. And Gigadevice's gd32vf103_xx.c/h. I could use platformio, I do not see that something will change.- Something I observed is that the DAC0_DO register remains at 0 regardless of what the DAC0_R12DH gets written with. Something contrary to the manual approach where the DAC0_DO register mirrors what I write manually.The code:Code:void dac_init( void ){dma_parameter_struct dma_init_struct;timer_parameter_struct timer5_init_struct;// PINrcu_periph_clock_enable(RCU_GPIOA);rcu_periph_clock_enable(RCU_DAC);rcu_periph_clock_enable(RCU_DMA1);rcu_periph_clock_enable(RCU_AF);gpio_init(GPIOA, GPIO_MODE_AIN, 0U, \GPIO_PIN_4 );// timerrcu_periph_clock_enable(RCU_TIMER5);timer_deinit(TIMER5);timer_struct_para_init(&timer5_init_struct);timer5_init_struct.prescaler = 0U;timer5_init_struct.alignedmode = TIMER_COUNTER_EDGE;timer5_init_struct.counterdirection = TIMER_COUNTER_UP;timer5_init_struct.period = 65535U;timer5_init_struct.clockdivision = TIMER_CKDIV_DIV1;timer5_init_struct.repetitioncounter = 0U;timer_init(TIMER5, &timer5_init_struct);timer_update_event_enable(TIMER5);timer_dma_enable(TIMER5, TIMER_DMA_UPD);// DMAdma_deinit(DMA1, DMA_CH2);dma_struct_para_init(&dma_init_struct);dma_init_struct.periph_addr = &DAC0_R12DH;dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_32BIT;dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;dma_init_struct.memory_addr = &dac_buf_0;dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;dma_init_struct.number = 1024U;dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;dma_init_struct.priority = DMA_PRIORITY_MEDIUM;dma_init(DMA1, DMA_CH2, &dma_init_struct);// interrupt on the full channeldma_interrupt_enable(DMA1, DMA_CH2, DMA_INT_FTF);eclic_irq_enable(DMA1_Channel2_IRQn, 1, 1);// DACdac_deinit();dac_trigger_source_config(DAC0, DAC_TRIGGER_T5_TRGO);dac_trigger_enable(DAC0);dac_wave_mode_config(DAC0, DAC_WAVE_DISABLE);dac_dma_enable(DAC0);dac_output_buffer_enable(DAC0);dac_enable(DAC0);dma_channel_enable(DMA1, DMA_CH2);timer_enable(TIMER5);}// the interrupt to switch buffersvoid DMA1_Channel2_IRQHandler(void){// Clear the interrupt flag to avoid retriggering.dma_interrupt_flag_clear(DMA1, DMA_CH2, DMA_INT_FLAG_G);dma_channel_disable(DMA1, DMA_CH2);if (dac_buff_current == 0U){dma_memory_address_config(DMA1, DMA_CH2, &dac_buf_1);GPIO_BC(GPIOC) = GPIO_PIN_13;}else{dma_memory_address_config(DMA1, DMA_CH2, &dac_buf_0);GPIO_BOP(GPIOC) = GPIO_PIN_13;}dma_transfer_number_config(DMA1, DMA_CH2, 1024U);dac_buff_current = dac_buff_current ^ 1;dma_channel_enable(DMA1, DMA_CH2);}And the manual version:Code:rcu_periph_clock_enable(RCU_GPIOA);gpio_init(GPIOA, GPIO_MODE_AIN, 0U, \GPIO_PIN_4 );rcu_periph_clock_enable(RCU_DAC);dac_trigger_source_config(DAC0, DAC_TRIGGER_SOFTWARE);dac_trigger_enable(DAC0);dac_wave_mode_config(DAC0, DAC_WAVE_DISABLE);dac_output_buffer_enable(DAC0);dac_enable(DAC0);for (;;){dac_data_set(DAC0, DAC_ALIGN_12B_R, value);dac_software_trigger_enable(DAC0);delay_us(50)value = (value + 1) & 4095;}

author: bson
author_rank: Unknown
timestamp: November 18, 2021, 09:22:02 pm
content: R12DH sounds like a 12-bit dual conversion holding register. Note "dual" - it's used when driving both DAC channels using a single DMA stream. You probably want to use the appropriate single-conversion data register. If you haven't set up both DAC channels, with one slaving off the other (I forget the details, but the F4xx has common registers for this) I'm not surprised if a DMA transfer simply stalls.

author: wek
author_rank: Frequent Contributor
timestamp: November 18, 2021, 09:47:23 pm
content: I don't use the GD32, but IMO you should select Update as TRGO in TIMERx_CTL1.MMC (I don't know what's the incantation for that in the "library" you are using); and you should not enable DMA from TIM5 (which presumably sets TIMERx_DMAINTEN.UPDEN) as DMA should be triggered from DAC itself (where you did enable it, too).If this won't work, read out and check/post content of relevant DAC, DMA and TIMER5 registers.JW

author: bson
author_rank: Unknown
timestamp: November 18, 2021, 10:12:33 pm
content: Also, the peripheral width should be 16-bit unless you in fact intend to write to a 32-bit dual holding register. Otherwise if you feed 16 bit memory into 32 bit peripheral regs the DMA will collect two 16 bit samples into a 32 bit peripheral write.I also think you need to enable master mode on the timer by setting MMS = 0b010 in CR2. But again, this is based on STM32F4xx (no idea if the RISC-V GD32 is still the same). STM32F4xx timers won't output TRGO to the DAC (or anything else) unless in master mode.

author: ale500
author_rank: Frequent Contributor
timestamp: November 20, 2021, 08:59:25 am
content: Thanks for the comments, and tips, sadly mostly do not apply.DAC0_R12DH is the register for the DAC0, the common register is named DACC_R12DH, DH stands for Data Hold (register), according to the manual.Reading 16 bits and writing them to a 32 bit port is possible and pads the upper 16 bits with zeroes (DACx_R12DH have to be read/write using 32 bit accesses), as described page 133 of the user manual.Now to the tricky part regarding the timer and DMA. If I do not set DMA request update (UPDEN Update DMA request enable), nothing happens. There are no transfers.If I disable the DMA feature in the DAC the same. Maybe there is a conflict between the two, it is not clear to me. Maybe I should use the timer5 and the DMA to try to copy a block and see if that works, then.

author: wek
author_rank: Frequent Contributor
timestamp: November 20, 2021, 09:18:56 am
content: I said:- select Update as TRGO in TIMERx_CTL1.MMC- don't enable DMA from TIM5- if this won't work, read out and check/post content of relevant DAC, DMA and TIMER5 registersJW

author: ale500
author_rank: Frequent Contributor
timestamp: November 20, 2021, 03:34:20 pm
content: Quote from: wek on November 20, 2021, 09:18:56 amI know what you said, but all 3 possible modes make (some) pulses on TRGO, that's why I did not see that this made a difference (according to the manual). But it does, then it works.There is another issue, it has to do with the debugging, when I download the code to the chip it triggers the DMA once and then the interrupts does not get triggered and that's itThanks

author: wek
author_rank: Frequent Contributor
timestamp: November 20, 2021, 05:16:49 pm
content: Quote from: ale500 on November 20, 2021, 03:34:20 pmRead out and check/post TIM/DAC and mainly DMA registers content, including flags.This writeupis for STM32 but maybe helps. Sorry, I have no easy solutions to offer, just blood, toil, tears and sweat.JW