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 through
Any 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:
void dac_init( void )
{
dma_parameter_struct dma_init_struct;
timer_parameter_struct timer5_init_struct;
// PIN
rcu_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 );
// timer
rcu_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);
// DMA
dma_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 channel
dma_interrupt_enable(DMA1, DMA_CH2, DMA_INT_FTF);
eclic_irq_enable(DMA1_Channel2_IRQn, 1, 1);
// DAC
dac_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 buffers
void 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:
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;
}