X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=bertos%2Fcpu%2Fcortex-m3%2Fdrv%2Fdac_sam3.c;h=f4090468a1be751eceeab4c231b3fe84629bec06;hb=69889bc87d372d18d6222037a098c9c02561439b;hp=9a9ec02509e4d56efcefa16793b0c00397c91917;hpb=a4e0e9b539899bf7cb0a0bae2bab53b7cfb5e39d;p=bertos.git diff --git a/bertos/cpu/cortex-m3/drv/dac_sam3.c b/bertos/cpu/cortex-m3/drv/dac_sam3.c index 9a9ec025..f4090468 100644 --- a/bertos/cpu/cortex-m3/drv/dac_sam3.c +++ b/bertos/cpu/cortex-m3/drv/dac_sam3.c @@ -48,8 +48,13 @@ #include #include +#include #include +#include + +#include + #include #include @@ -57,11 +62,16 @@ struct DacHardware { uint16_t channels; + uint32_t rate; bool end; }; struct DacHardware dac_hw; + +/* We use event to signal the end of conversion */ +static Event buff_emtpy; + #if CONFIG_DAC_TIMER == DACC_TRGSEL_TIO_CH0 /* Select Timer counter TIO Channel 0 */ #define DAC_TC_ID TC0_ID #define DAC_TC_CCR TC0_CCR0 @@ -90,42 +100,58 @@ struct DacHardware dac_hw; #error unimplemented pwm triger select. #endif -INLINE void tc_setup(uint32_t freq) + +INLINE void tc_init(void) { pmc_periphEnable(DAC_TC_ID); - /* Disable TC clock */ - DAC_TC_CCR = TC_CCR_CLKDIS; - /* Disable interrupts */ - DAC_TC_IDR = 0xFFFFFFFF; - /* Clear status register */ - volatile uint32_t dummy = DAC_TC_SR; + /* Disable TC clock */ + DAC_TC_CCR = BV(TC_CCR_CLKDIS); + /* Disable interrupts */ + DAC_TC_IDR = 0xFFFFFFFF; + /* Clear status register */ + volatile uint32_t dummy = DAC_TC_SR; (void)dummy; /* * Setup the timer counter: - * - select clock TCLK1 + * - select clock TCLK1 (MCK/2) * - enable wave form mode * - RA compare effect SET * - RC compare effect CLEAR * - UP mode with automatic trigger on RC Compare */ - DAC_TC_CMR = BV(TC_TIMER_CLOCK1) | BV(TC_CMR_WAVE) | TC_CMR_ACPA_SET | TC_CMR_ACPC_CLEAR | BV(TC_CMR_CPCTRG); + DAC_TC_CMR = TC_TIMER_CLOCK1 | BV(TC_CMR_WAVE) | TC_CMR_ACPA_SET | TC_CMR_ACPC_CLEAR | BV(TC_CMR_CPCTRG); + - /* Compute the sample frequency */ - uint32_t rc = (CPU_FREQ / 8) / (freq * 1000); - DAC_TC_RC = rc; - DAC_TC_RA = 50 * rc / 100; + /* Setup the pio: TODO: fix for more generic */ + PIOB_PDR = BV(25); + PIO_PERIPH_SEL(PIOB_BASE, BV(25), PIO_PERIPH_B); +} + +INLINE void tc_setup(uint32_t freq, size_t n_sample) +{ + /* + * Compute the sample frequency + * the RC counter will update every MCK/2 (see above) + * so to convert one sample at the user freq we generate + * the trigger every TC_CLK / (numer_of_sample * user_freq) + * where TC_CLK = MCK / 2. + */ + uint32_t rc = DIV_ROUND((CPU_FREQ / 2), n_sample * freq); + DAC_TC_RC = rc; + /* generate the square wave with duty = 50% */ + DAC_TC_RA = DIV_ROUND(50 * rc, 100); } INLINE void tc_start(void) { - DAC_TC_CCR = BV(TC_CCR_CLKEN)| BV(TC_CCR_SWTRG); + DAC_TC_CCR = BV(TC_CCR_CLKEN)| BV(TC_CCR_SWTRG); } INLINE void tc_stop(void) { - DAC_TC_CCR = BV(TC_CCR_CLKDIS); + DAC_TC_CCR = BV(TC_CCR_CLKDIS); } static int sam3x_dac_write(struct Dac *dac, unsigned channel, uint16_t sample) @@ -147,53 +173,137 @@ static void sam3x_dac_setCh(struct Dac *dac, uint32_t mask) /* we have only the ch0 and ch1 */ ASSERT(mask < BV(3)); dac->hw->channels = mask; + + if (mask & BV(DACC_CH0)) + DACC_MR |= (DACC_CH0 << DACC_USER_SEL_SHIFT) & DACC_USER_SEL_MASK; + + if (mask & BV(DACC_CH1)) + DACC_MR |= (DACC_CH1 << DACC_USER_SEL_SHIFT) & DACC_USER_SEL_MASK; + + DACC_CHER |= mask; + } static void sam3x_dac_setSampleRate(struct Dac *dac, uint32_t rate) { - (void)dac; - /* Eneble hw trigger */ DACC_MR |= BV(DACC_TRGEN) | (CONFIG_DAC_TIMER << DACC_TRGSEL_SHIFT); - kprintf("%08lx\n", DACC_MR); - tc_setup(rate); + dac->hw->rate = rate; } static void sam3x_dac_conversion(struct Dac *dac, void *buf, size_t len) { - if (dac->hw->channels & BV(DACC_CH0)) - DACC_MR |= (DACC_CH0 << DACC_USER_SEL_SHIFT) & DACC_USER_SEL_MASK; - - if (dac->hw->channels & BV(DACC_CH1)) - DACC_MR |= (DACC_CH1 << DACC_USER_SEL_SHIFT) & DACC_USER_SEL_MASK; - - DACC_CHER |= dac->hw->channels; - - /* Start syncro timer for the dac */ + /* setup timer and start it */ + tc_setup(dac->hw->rate, len); tc_start(); /* Setup dma and start it */ - DACC_TPR = (uint32_t)buf ; + DACC_TPR = (uint32_t)buf; DACC_TCR = len; DACC_PTCR |= BV(DACC_PTCR_TXTEN); } +static uint16_t *sample_buff; +static size_t next_idx = 0; +static size_t chunk_size = 0; +static size_t remaing_size = 0; + +static DECLARE_ISR(irq_dac) +{ + if (DACC_ISR & BV(DACC_ENDTX)) + { + if (remaing_size > 0) + { + DACC_TNPR = (uint32_t)&sample_buff[next_idx]; + DACC_TNCR = chunk_size; + + remaing_size -= chunk_size; + next_idx += chunk_size; + } + else + /* Clear the pending irq when the dma ends the conversion */ + DACC_TCR = 1; + } + event_do(&buff_emtpy); +} + + static bool sam3x_dac_isFinished(struct Dac *dac) { - return 0; + return dac->hw->end; } -static void sam3x_dac_start(struct Dac *dac, void *buf, size_t len, size_t slicelen) +static void sam3x_dac_start(struct Dac *dac, void *_buf, size_t len, size_t slice_len) { + ASSERT(dac); + ASSERT(len >= slice_len); + + /* Reset the previous status. */ + dac->hw->end = false; + + sample_buff = (uint16_t *)_buf; + next_idx = 0; + chunk_size = slice_len; + remaing_size = len; + + + /* Program the dma with the first and second chunk of samples and update counter */ + dac->ctx.callback(dac, &sample_buff[0], chunk_size); + DACC_TPR = (uint32_t)&sample_buff[0]; + DACC_TCR = chunk_size; + remaing_size -= chunk_size; + next_idx += chunk_size; + + if (chunk_size <= remaing_size) + { + dac->ctx.callback(dac, &sample_buff[next_idx], chunk_size); + + DACC_TNPR = (uint32_t)&sample_buff[next_idx]; + DACC_TNCR = chunk_size; + + remaing_size -= chunk_size; + next_idx += chunk_size; + + } + + DACC_PTCR |= BV(DACC_PTCR_TXTEN); + DACC_IER = BV(DACC_ENDTX); + + /* Set up timer and trig the conversions */ + tc_setup(dac->hw->rate, len); + tc_start(); + + while (1) + { + event_wait(&buff_emtpy); + if (remaing_size <= 0) + { + DAC_TC_CCR = BV(TC_CCR_CLKDIS); + dac->hw->end = true; + next_idx = 0; + chunk_size = 0; + remaing_size = 0; + break; + } + + dac->ctx.callback(dac, &sample_buff[next_idx], chunk_size); + } } static void sam3x_dac_stop(struct Dac *dac) { + dac->hw->end = false; + /* Disable the irq, timer and channel */ + DACC_IDR = BV(DACC_ENDTX); + DACC_PTCR |= BV(DACC_PTCR_TXTDIS); + DAC_TC_CCR = BV(TC_CCR_CLKDIS); } void dac_init(struct Dac *dac) { + /* Initialize the dataready event */ + event_initGeneric(&buff_emtpy); /* Fill the virtual table */ dac->ctx.write = sam3x_dac_write; @@ -203,7 +313,7 @@ void dac_init(struct Dac *dac) dac->ctx.isFinished = sam3x_dac_isFinished; dac->ctx.start = sam3x_dac_start; dac->ctx.stop = sam3x_dac_stop; - dac->ctx._type = DAC_SAM3X; + DB(dac->ctx._type = DAC_SAM3X;) dac->hw = &dac_hw; /* Clock DAC peripheral */ @@ -216,4 +326,9 @@ void dac_init(struct Dac *dac) /* Configure the dac */ DACC_MR |= (CONFIG_DAC_REFRESH << DACC_REFRESH_SHIFT) & DACC_REFRESH_MASK; DACC_MR |= (CONFIG_DAC_STARTUP << DACC_STARTUP_SHIFT) & DACC_STARTUP_MASK; + + DACC_IDR = 0xFFFFFFFF; + sysirq_setHandler(INT_DACC, irq_dac); + + tc_init(); }