dac_sam3.c
Go to the documentation of this file.
00001 00038 #include "dac_sam3.h" 00039 00040 #include "cfg/cfg_dac.h" 00041 00042 #include <cfg/macros.h> 00043 #include <cfg/compiler.h> 00044 00045 // Define log settings for cfg/log.h. 00046 #define LOG_LEVEL DAC_LOG_LEVEL 00047 #define LOG_FORMAT DAC_LOG_FORMAT 00048 #include <cfg/log.h> 00049 00050 #include <drv/dac.h> 00051 #include <cpu/irq.h> 00052 #include <drv/irq_cm3.h> 00053 00054 #include <cpu/types.h> 00055 00056 #include <mware/event.h> 00057 00058 #include <io/cm3.h> 00059 00060 #include <string.h> 00061 00062 struct DacHardware 00063 { 00064 uint16_t channels; 00065 uint32_t rate; 00066 bool end; 00067 }; 00068 00069 struct DacHardware dac_hw; 00070 00071 00072 /* We use event to signal the end of conversion */ 00073 static Event buff_emtpy; 00074 00075 #if CONFIG_DAC_TIMER == DACC_TRGSEL_TIO_CH0 /* Select Timer counter TIO Channel 0 */ 00076 #define DAC_TC_ID TC0_ID 00077 #define DAC_TC_CCR TC0_CCR0 00078 #define DAC_TC_IDR TC0_IDR0 00079 #define DAC_TC_CMR TC0_CMR0 00080 #define DAC_TC_SR TC0_SR0 00081 #define DAC_TC_RA TC0_RA0 00082 #define DAC_TC_RC TC0_RC0 00083 #elif CONFIG_DAC_TIMER == DACC_TRGSEL_TIO_CH1 /* Select Timer counter TIO Channel 1 */ 00084 #define DAC_TC_ID TC1_ID 00085 #define DAC_TC_CCR TC0_CCR1 00086 #define DAC_TC_IDR TC0_IDR1 00087 #define DAC_TC_CMR TC0_CMR1 00088 #define DAC_TC_SR TC0_SR1 00089 #define DAC_TC_RA TC0_RA1 00090 #define DAC_TC_RC TC0_RC1 00091 #elif CONFIG_DAC_TIMER == DACC_TRGSEL_TIO_CH2 /* Select Timer counter TIO Channel 2 */ 00092 #define DAC_TC_ID TC2_ID 00093 #define DAC_TC_CCR TC0_CCR2 00094 #define DAC_TC_IDR TC0_IDR2 00095 #define DAC_TC_CMR TC0_CMR2 00096 #define DAC_TC_SR TC0_SR2 00097 #define DAC_TC_RA TC0_RA2 00098 #define DAC_TC_RC TC0_RC2 00099 #elif CONFIG_DAC_TIMER == DACC_TRGSEL_PWM0 || CONFIG_DAC_TIMER == DACC_TRGSEL_PWM1 00100 #error unimplemented pwm triger select. 00101 #endif 00102 00103 00104 INLINE void tc_init(void) 00105 { 00106 pmc_periphEnable(DAC_TC_ID); 00107 00108 /* Disable TC clock */ 00109 DAC_TC_CCR = BV(TC_CCR_CLKDIS); 00110 /* Disable interrupts */ 00111 DAC_TC_IDR = 0xFFFFFFFF; 00112 /* Clear status register */ 00113 volatile uint32_t dummy = DAC_TC_SR; 00114 (void)dummy; 00115 00116 /* 00117 * Setup the timer counter: 00118 * - select clock TCLK1 (MCK/2) 00119 * - enable wave form mode 00120 * - RA compare effect SET 00121 * - RC compare effect CLEAR 00122 * - UP mode with automatic trigger on RC Compare 00123 */ 00124 DAC_TC_CMR = TC_TIMER_CLOCK1 | BV(TC_CMR_WAVE) | TC_CMR_ACPA_SET | TC_CMR_ACPC_CLEAR | BV(TC_CMR_CPCTRG); 00125 00126 00127 /* Setup the pio: TODO: fix for more generic */ 00128 PIOB_PDR = BV(25); 00129 PIO_PERIPH_SEL(PIOB_BASE, BV(25), PIO_PERIPH_B); 00130 } 00131 00132 INLINE void tc_setup(uint32_t freq, size_t n_sample) 00133 { 00134 /* 00135 * Compute the sample frequency 00136 * the RC counter will update every MCK/2 (see above) 00137 * so to convert one sample at the user freq we generate 00138 * the trigger every TC_CLK / (numer_of_sample * user_freq) 00139 * where TC_CLK = MCK / 2. 00140 */ 00141 uint32_t rc = DIV_ROUND((CPU_FREQ / 2), n_sample * freq); 00142 DAC_TC_RC = rc; 00143 /* generate the square wave with duty = 50% */ 00144 DAC_TC_RA = DIV_ROUND(50 * rc, 100); 00145 } 00146 00147 INLINE void tc_start(void) 00148 { 00149 DAC_TC_CCR = BV(TC_CCR_CLKEN)| BV(TC_CCR_SWTRG); 00150 } 00151 00152 INLINE void tc_stop(void) 00153 { 00154 DAC_TC_CCR = BV(TC_CCR_CLKDIS); 00155 } 00156 00157 static int sam3x_dac_write(struct Dac *dac, unsigned channel, uint16_t sample) 00158 { 00159 (void)dac; 00160 00161 ASSERT(channel <= DAC_MAXCH); 00162 00163 DACC_MR |= (channel << DACC_USER_SEL_SHIFT) & DACC_USER_SEL_MASK; 00164 DACC_CHER |= BV(channel); 00165 00166 DACC_CDR = sample ; 00167 00168 return 0; 00169 } 00170 00171 static void sam3x_dac_setCh(struct Dac *dac, uint32_t mask) 00172 { 00173 /* we have only the ch0 and ch1 */ 00174 ASSERT(mask < BV(3)); 00175 dac->hw->channels = mask; 00176 00177 if (mask & BV(DACC_CH0)) 00178 DACC_MR |= (DACC_CH0 << DACC_USER_SEL_SHIFT) & DACC_USER_SEL_MASK; 00179 00180 if (mask & BV(DACC_CH1)) 00181 DACC_MR |= (DACC_CH1 << DACC_USER_SEL_SHIFT) & DACC_USER_SEL_MASK; 00182 00183 DACC_CHER |= mask; 00184 00185 } 00186 00187 static void sam3x_dac_setSampleRate(struct Dac *dac, uint32_t rate) 00188 { 00189 /* Eneble hw trigger */ 00190 DACC_MR |= BV(DACC_TRGEN) | (CONFIG_DAC_TIMER << DACC_TRGSEL_SHIFT); 00191 dac->hw->rate = rate; 00192 } 00193 00194 static void sam3x_dac_conversion(struct Dac *dac, void *buf, size_t len) 00195 { 00196 /* setup timer and start it */ 00197 tc_setup(dac->hw->rate, len); 00198 tc_start(); 00199 00200 /* Setup dma and start it */ 00201 DACC_TPR = (uint32_t)buf; 00202 DACC_TCR = len; 00203 DACC_PTCR |= BV(DACC_PTCR_TXTEN); 00204 } 00205 00206 static uint16_t *sample_buff; 00207 static size_t next_idx = 0; 00208 static size_t chunk_size = 0; 00209 static size_t remaing_size = 0; 00210 00211 static DECLARE_ISR(irq_dac) 00212 { 00213 if (DACC_ISR & BV(DACC_ENDTX)) 00214 { 00215 if (remaing_size > 0) 00216 { 00217 DACC_TNPR = (uint32_t)&sample_buff[next_idx]; 00218 DACC_TNCR = chunk_size; 00219 00220 remaing_size -= chunk_size; 00221 next_idx += chunk_size; 00222 } 00223 else 00224 /* Clear the pending irq when the dma ends the conversion */ 00225 DACC_TCR = 1; 00226 } 00227 event_do(&buff_emtpy); 00228 } 00229 00230 00231 static bool sam3x_dac_isFinished(struct Dac *dac) 00232 { 00233 return dac->hw->end; 00234 } 00235 00236 static void sam3x_dac_start(struct Dac *dac, void *_buf, size_t len, size_t slice_len) 00237 { 00238 ASSERT(dac); 00239 ASSERT(len >= slice_len); 00240 00241 /* Reset the previous status. */ 00242 dac->hw->end = false; 00243 00244 sample_buff = (uint16_t *)_buf; 00245 next_idx = 0; 00246 chunk_size = slice_len; 00247 remaing_size = len; 00248 00249 00250 /* Program the dma with the first and second chunk of samples and update counter */ 00251 dac->ctx.callback(dac, &sample_buff[0], chunk_size); 00252 DACC_TPR = (uint32_t)&sample_buff[0]; 00253 DACC_TCR = chunk_size; 00254 remaing_size -= chunk_size; 00255 next_idx += chunk_size; 00256 00257 if (chunk_size <= remaing_size) 00258 { 00259 dac->ctx.callback(dac, &sample_buff[next_idx], chunk_size); 00260 00261 DACC_TNPR = (uint32_t)&sample_buff[next_idx]; 00262 DACC_TNCR = chunk_size; 00263 00264 remaing_size -= chunk_size; 00265 next_idx += chunk_size; 00266 00267 } 00268 00269 DACC_PTCR |= BV(DACC_PTCR_TXTEN); 00270 DACC_IER = BV(DACC_ENDTX); 00271 00272 /* Set up timer and trig the conversions */ 00273 tc_setup(dac->hw->rate, len); 00274 tc_start(); 00275 00276 while (1) 00277 { 00278 event_wait(&buff_emtpy); 00279 if (dac->hw->end) 00280 break; 00281 00282 remaing_size -= chunk_size; 00283 next_idx += chunk_size; 00284 00285 if (remaing_size <= 0) 00286 { 00287 remaing_size = len; 00288 next_idx = 0; 00289 } 00290 00291 dac->ctx.callback(dac, &sample_buff[next_idx], chunk_size); 00292 } 00293 } 00294 00295 static void sam3x_dac_stop(struct Dac *dac) 00296 { 00297 dac->hw->end = false; 00298 next_idx = 0; 00299 remaing_size = 0; 00300 chunk_size = 0; 00301 00302 /* Disable the irq, timer and channel */ 00303 DACC_IDR = BV(DACC_ENDTX); 00304 DACC_PTCR |= BV(DACC_PTCR_TXTDIS); 00305 DAC_TC_CCR = BV(TC_CCR_CLKDIS); 00306 00307 event_do(&buff_emtpy); 00308 } 00309 00310 00311 void dac_init(struct Dac *dac) 00312 { 00313 /* Initialize the dataready event */ 00314 event_initGeneric(&buff_emtpy); 00315 00316 /* Fill the virtual table */ 00317 dac->ctx.write = sam3x_dac_write; 00318 dac->ctx.setCh = sam3x_dac_setCh; 00319 dac->ctx.setSampleRate = sam3x_dac_setSampleRate; 00320 dac->ctx.conversion = sam3x_dac_conversion; 00321 dac->ctx.isFinished = sam3x_dac_isFinished; 00322 dac->ctx.start = sam3x_dac_start; 00323 dac->ctx.stop = sam3x_dac_stop; 00324 DB(dac->ctx._type = DAC_SAM3X;) 00325 dac->hw = &dac_hw; 00326 00327 /* Clock DAC peripheral */ 00328 pmc_periphEnable(DACC_ID); 00329 00330 /* Reset hw */ 00331 DACC_CR |= BV(DACC_SWRST); 00332 DACC_MR = 0; 00333 00334 /* Configure the dac */ 00335 DACC_MR |= (CONFIG_DAC_REFRESH << DACC_REFRESH_SHIFT) & DACC_REFRESH_MASK; 00336 DACC_MR |= (CONFIG_DAC_STARTUP << DACC_STARTUP_SHIFT) & DACC_STARTUP_MASK; 00337 00338 DACC_IDR = 0xFFFFFFFF; 00339 sysirq_setHandler(INT_DACC, irq_dac); 00340 00341 tc_init(); 00342 }
![(please configure the [header_logo] section in trac.ini)](/chrome/site/bertos_logo.png)