/* * (c) danielinux 2020 * GPLv.2 * * See LICENSE for details */ #include #include #include #include #include #include #include "pot.h" #define DAC_BUFSIZ 512 extern const unsigned char raw_au[]; const unsigned int raw_au_len; static volatile int dac_written; static int dac_transfer_size; static int dac_chunk_size; static uint8_t dac_outb[DAC_BUFSIZ]; static void dac_xmit(void) { uint32_t size = DAC_BUFSIZ; if ((dac_transfer_size - dac_written ) < size) size = dac_transfer_size - dac_written; dac_chunk_size = size; /* Start DMA transfer of waveform */ dac_trigger_enable(CHANNEL_1); dac_set_trigger_source(DAC_CR_TSEL1_T2); dac_dma_enable(CHANNEL_1); dma_set_memory_address(DMA1, DMA_STREAM5, (uint32_t) (dac_outb + dac_written)); dma_set_number_of_data(DMA1, DMA_STREAM5, size); dma_enable_transfer_complete_interrupt(DMA1, DMA_STREAM5); dma_channel_select(DMA1, DMA_STREAM5, DMA_SxCR_CHSEL_7); dma_enable_stream(DMA1, DMA_STREAM5); } int dac_write(const void *buf, unsigned int len) { if (dac_written < dac_transfer_size) { if ((len + dac_transfer_size) > DAC_BUFSIZ) return 0; } if (dac_written >= dac_transfer_size) { dac_written = 0; dac_transfer_size = 0; } memcpy(dac_outb + dac_transfer_size, buf, len); dac_transfer_size += len; dac_xmit(); return dac_written; } int dac_space(void) { if (dac_written >= dac_transfer_size) { dac_written = 0; dac_transfer_size = 0; } return DAC_BUFSIZ - dac_transfer_size; } /* IRQ Handler */ void dma1_stream5_isr(void) { if (dma_get_interrupt_flag(DMA1, DMA_STREAM5, DMA_TCIF)) { if (dac_written < dac_transfer_size) dac_written += dac_chunk_size; dma_clear_interrupt_flags(DMA1, DMA_STREAM5, DMA_TCIF); dma_disable_stream(DMA1, DMA_STREAM5); dac_trigger_disable(CHANNEL_1); dac_dma_disable(CHANNEL_1); if (dac_written >= dac_transfer_size) { return; } else { dac_xmit(); } } } /* Initialization functions */ //#define PERIOD (5200) #define PERIOD 8800 static void timer_setup(void) { /* Enable TIM2 clock. */ rcc_periph_clock_enable(RCC_TIM2); timer_reset(TIM2); /* Timer global mode: - No divider, Alignment edge, Direction up */ timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); timer_continuous_mode(TIM2); timer_set_period(TIM2, PERIOD); timer_disable_oc_output(TIM2, TIM_OC2 | TIM_OC3 | TIM_OC4); timer_enable_oc_output(TIM2, TIM_OC1); timer_disable_oc_clear(TIM2, TIM_OC1); timer_disable_oc_preload(TIM2, TIM_OC1); timer_set_oc_slow_mode(TIM2, TIM_OC1); timer_set_oc_mode(TIM2, TIM_OC1, TIM_OCM_TOGGLE); timer_set_oc_value(TIM2, TIM_OC1, 500); timer_disable_preload(TIM2); /* Set the timer trigger output (for the DAC) to the channel 1 output * compare */ timer_set_master_mode(TIM2, TIM_CR2_MMS_COMPARE_OC1REF); timer_enable_counter(TIM2); } static void dac_dma_setup(void) { /* DAC channel 1 uses DMA controller 1 Stream 5 Channel 7. */ /* Enable DMA1 clock and IRQ */ rcc_periph_clock_enable(RCC_DMA1); nvic_set_priority(NVIC_DMA1_STREAM5_IRQ, 1); nvic_enable_irq(NVIC_DMA1_STREAM5_IRQ); dma_stream_reset(DMA1, DMA_STREAM5); dma_set_priority(DMA1, DMA_STREAM5, DMA_SxCR_PL_LOW); dma_set_memory_size(DMA1, DMA_STREAM5, DMA_SxCR_MSIZE_8BIT); dma_set_peripheral_size(DMA1, DMA_STREAM5, DMA_SxCR_PSIZE_8BIT); dma_enable_memory_increment_mode(DMA1, DMA_STREAM5); dma_enable_circular_mode(DMA1, DMA_STREAM5); dma_set_transfer_mode(DMA1, DMA_STREAM5, DMA_SxCR_DIR_MEM_TO_PERIPHERAL); /* The register to target is the DAC1 8-bit right justified data register */ dma_set_peripheral_address(DMA1, DMA_STREAM5, (uint32_t) &DAC_DHR8R1); } static void dac_hw_init(data_channel c) { /* Set DAC GPIO pin to analog mode */ rcc_periph_clock_enable(RCC_GPIOC); gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO4); timer_setup(); /* Set up DAC */ rcc_periph_clock_enable(RCC_DAC); dac_enable(c); } void dac_play(const uint8_t *buf, int len) { int i = 0; int space; while(i < len) { space = dac_space(); if (space > 0) { if (space > (len - i)) space = len - i; dac_write(buf + i, space); i += space; } } } int dac_init(void) { int i; dac_hw_init(CHANNEL_1); dac_dma_setup(); pot_set_master(200); dac_play(raw_au, raw_au_len); return 0; }