Page 1 of 1

STM32 DAC for resolver excitation

Posted: Thu May 06, 2021 3:40 pm
by johu
I found a sudden urge to experiment with the DAC. I have here an STM32F107VCT6.
Just sort of "blogging" about it here.

Starting out simple with software triggering. First had to upgrade libopencm3 because the forked version didn't have DAC functions.
setup:

Code: Select all

   rcc_periph_clock_enable(RCC_DAC); //CAN
   dac_enable(DAC1, DAC_CHANNEL1);
   dac_set_trigger_source(DAC1, DAC_CR_TSEL1_SW);
   gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO5);
In cyclic 10 ms task:

Code: Select all

   seq1Ctr = (seq1Ctr + 1) & 0x3;

   dac_load_data_buffer_single(DAC1, seq1Ctr << 10, DAC_ALIGN_RIGHT12, DAC_CHANNEL1);
   dac_software_trigger(DAC1, DAC_CHANNEL1);
seq1Ctr is a 0-3 counter.

result:

Re: STM32 DAC

Posted: Thu May 06, 2021 4:37 pm
by johu
Ok, that took slightly longer. Generating a 4.4 kHz sawtooth from DMA:
IMG_20210506_182355.jpg
We need more clocks:

Code: Select all

   rcc_periph_clock_enable(RCC_TIM5); //DAC timing
   rcc_periph_clock_enable(RCC_DMA2);  //DAC
   rcc_periph_clock_enable(RCC_DAC);
Now comes a lot of init. First the DAC

Code: Select all

   dac_enable(DAC1, DAC_CHANNEL1);
   dac_set_trigger_source(DAC1, DAC_CR_TSEL1_T5);
   dac_trigger_enable(DAC1, DAC_CHANNEL1);
   dac_dma_enable(DAC1, DAC_CHANNEL1);
   gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO5);
We set it to be triggered by Timer 5 and fed by DMA (DMA2, channel 3)

then the timer:

Code: Select all

   timer_set_prescaler(TIM5, 0);
   timer_set_period(TIM5, 63); //Wrap at 1.125 MHz
   timer_direction_up(TIM5);
   timer_enable_update_event(TIM5);
   timer_set_master_mode(TIM5, TIM_CR2_MMS_UPDATE);
   timer_generate_event(TIM5, TIM_EGR_UG);
   timer_enable_counter(TIM5);
So it wraps at 1.125 MHz thus loading a new value to the DAC at that rate.
Now we initialize our sawtooth data

Code: Select all

static uint8_t dacdata[256];

   for (int i = 0; i < sizeof(dacdata); i++)
      dacdata[i] = i;
Aha, so 256 values. 1.125 MHz / 256 is 4.394 kHz. Sounds familiar? Yes that is half the frequency at which our PWM interrupt fires. So after two interrupts we have run through a period. Just like the low-pass filtered exciter pin would do. See, where this is going?

Alright, finally some boring DMA setup:

Code: Select all

   dma_channel_reset(DMA2, DMA_CHANNEL3);
   dma_set_read_from_memory(DMA2, DMA_CHANNEL3);
   dma_set_memory_address(DMA2, DMA_CHANNEL3, (uint32_t)dacdata);
   dma_set_peripheral_address(DMA2, DMA_CHANNEL3, (uint32_t)&DAC_DHR8R1(DAC1));
   dma_set_peripheral_size(DMA2, DMA_CHANNEL3, DMA_CCR_PSIZE_8BIT);
   dma_set_memory_size(DMA2, DMA_CHANNEL3, DMA_CCR_MSIZE_8BIT);
   dma_enable_circular_mode(DMA2, DMA_CHANNEL3);
   dma_set_number_of_data(DMA2, DMA_CHANNEL3, sizeof(dacdata));
   dma_enable_memory_increment_mode(DMA2, DMA_CHANNEL3);
   dma_enable_channel(DMA2, DMA_CHANNEL3);
And we're rolling. All thats needed now to replicate the makeshift resolver excitation of the poorer equipped RBT6 is to replace the saw tooth by sine and to synchronize the phase with the PWM interrupt. Like using one shot timer mode or so.

Re: STM32 DAC

Posted: Thu May 06, 2021 4:48 pm
by johu
We wanted sine, right? We have sine:
IMG_20210506_184208.jpg

Code: Select all

   for (int i = 0; i < sizeof(dacdata); i++)
      dacdata[i] = SineCore::Sine(i * 256) / 270 + 128;
We pull one full period from our SineCore module, divide it by 270 (not 256, because maxing out the DAC creates distortion) and add 128 because it's signed.

Whats all that for? viewtopic.php?p=27113#p27113
So I think we're looking into a new "encmode" called "ResolverDAC". Thats for later.

Re: STM32 DAC

Posted: Thu May 06, 2021 5:03 pm
by Jack Bauer
That's brilliant. Nice work Johannes:)

Re: STM32 DAC for resolver excitation

Posted: Fri May 07, 2021 8:27 am
by mjc506
Looking good :-)