Hi everyone.
I have read the PWM controller chapter in ATSAM3X8E MCU datasheet. After reading the PWM comparison unit of the chapter. I thought of sensing an analog signal through ADC and output the value through DAC of MCU for every 20 microsecond.
Is it possible ?
If it is, How to configure the COMPARISON unit in PWM controller for such an event based execution.
Thanks in advance.
ADC and DAC conversions can be triggered by a PWM pulse generated with a Timer Counter, or triggered by a PWM Event Line 0 or 1.
You will find both example sketches in these threads:
https://forum.arduino.cc/index.php?topic=605842.0
https://forum.arduino.cc/index.php?topic=332795.0
A 20 us period equals a 50 KHz frequency so it should be doable, but use a DMA so that interrupts frequency will be dramatically reduced.
chamarthi:
- I am trying to initialize the DACC module. But the DACC chapter in datasheet is not clear. The difference between the free running mode and triggered mode is not mentioned.
See page 1364 of Sam3x datasheet
An example sketch to trigger the DAC peripheral (DAC0 and DAC1) from a timer counter with a PDC DMA:
/***********************************************************************************/
/* DAC0 and DAC1 output of a sin wave - Frequency of sin = 44.1 KHz / sinsize */
/***********************************************************************************/
const uint32_t sinsize = 256 ; // Size of buffer must be a power of 2
uint32_t sinus[2][sinsize];
volatile uint32_t bufn;
void dac_setup () {
PMC->PMC_PCER1 = PMC_PCER1_PID38; // DACC power ON
DACC->DACC_CR = DACC_CR_SWRST ; // Reset DACC
DACC->DACC_MR = DACC_MR_TRGEN_EN // Hardware trigger select
| DACC_MR_TRGSEL(0b011) // Trigger by TIOA2
| DACC_MR_TAG_EN // enable TAG to set channel in CDR
| DACC_MR_WORD_WORD // write to both channels
| DACC_MR_REFRESH (1)
| DACC_MR_STARTUP_8
| DACC_MR_MAXS;
DACC->DACC_IER |= DACC_IER_TXBUFE; // Interrupt used by PDC DMA
DACC->DACC_ACR = DACC_ACR_IBCTLCH0(0b10)
| DACC_ACR_IBCTLCH1(0b10)
| DACC_ACR_IBCTLDACCORE(0b01);
NVIC_EnableIRQ(DACC_IRQn); // Enable DACC interrupt
DACC->DACC_CHER = DACC_CHER_CH0 // enable channel 0 = DAC0
| DACC_CHER_CH1; // enable channel 1 = DAC1
/************* configure PDC/DMA for DAC *******************/
DACC->DACC_TPR = (uint32_t)sinus[0]; // DMA buffer
DACC->DACC_TCR = sinsize;
DACC->DACC_TNPR = (uint32_t)sinus[1]; // next DMA buffer (circular buffer)
DACC->DACC_TNCR = sinsize;
bufn = 1;
DACC->DACC_PTCR = DACC_PTCR_TXTEN; // Enable PDC Transmit channel request
}
void DACC_Handler() {
uint32_t status = DACC->DACC_ISR; // Read and save DAC status register
if (status & DACC_ISR_TXBUFE) { // move DMA pointer to next buffer
bufn = (bufn + 1) & 1;
DACC->DACC_TNPR = (uint32_t)sinus[bufn];
DACC->DACC_TNCR = sinsize;
}
}
void tc_setup() {
PMC->PMC_PCER0 |= PMC_PCER0_PID29; // TC2 power ON : Timer Counter 0 channel 2 IS TC2
TC0->TC_CHANNEL[2].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK2 // MCK/8, clk on rising edge
| TC_CMR_WAVE // Waveform mode
| TC_CMR_WAVSEL_UP_RC // UP mode with automatic trigger on RC Compare
| TC_CMR_ACPA_CLEAR // Clear TIOA2 on RA compare match
| TC_CMR_ACPC_SET; // Set TIOA2 on RC compare match
TC0->TC_CHANNEL[2].TC_RC = 238; //<********************* Frequency = (Mck/8)/TC_RC = 44.1 MHz
TC0->TC_CHANNEL[2].TC_RA = 40; //<******************** Any Duty cycle in between 1 and TC_RC
TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC2 counter and enable
}
void setup() {
for(int i = 0; i < sinsize; i++)
{
uint32_t chsel = (0<<12) | (1<<28); // LSB on DAC0, MSB DAC1 !!
sinus[0][i] = 2047*sin(i * 2 * PI/sinsize) + 2047; // 0 < sinus [i] < 4096
sinus[1][i] = sinus[0][i] |= sinus[0][i] <<16 | chsel; // two buffers formated
// MSB [31:16]on channel 1
// LSB [15:0] on chanel 0
}
tc_setup();
dac_setup();
}
void loop() {
}
Thanks for the help.
I made my own code file. I want to use the DACC in tag mode for the present code example. What are the modification should I make.
[
void setup()
{
ADC_setup();
DAC_setup();
TC_setup();
}
void loop()
{
// Do nothing.
}
void ADC_setup(void)
{
pmc_set_writeprotect(false); // PMC controller write protection disable.
PMC->PMC_PCER1 |= (PMC_PCER1_PID37); // power on of the ADC module
ADC->ADC_CR = (ADC_CR_SWRST); // Reset of ADC.
ADC->ADC_IDR = (0xFFFFFFFF) ; // disabling the interrupts.
ADC->ADC_IER = (ADC_IER_EOC7); // enabling the EOC interrupt of the channel-6.
ADC->ADC_MR |= (ADC_MR_TRGEN_EN)|
(ADC_MR_TRGSEL_ADC_TRIG1); // ADC Trigger setting TIOA of TIMER/COUNTER channel-0.
ADC->ADC_CHER = (ADC_CHER_CH6)| // Selecting the Channel-6
(ADC_CHER_CH7); // Selecting the channel-7 for the conversion.
ADC->ADC_CR = (ADC_CR_START); // Starting the ADC module
NVIC_EnableIRQ(ADC_IRQn); // Enabling the ADC interrut handler in NVIC.
}
void TC_setup(void)
{
pmc_enable_periph_clk(ID_TC0); // Power on the TC0 peripheral.
TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_CLKDIS; // disabling the internal clock while setting the registers.
TC0->TC_CHANNEL[0].TC_IDR = 0xFFFFFFFF; // disabling the interrupts (Actually there are eight different interrupts available for each channel).
TC0->TC_CHANNEL[0].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 |
TC_CMR_WAVE |
TC_CMR_WAVSEL_UP_RC |
TC_CMR_ACPA_CLEAR |
TC_CMR_ACPC_CLEAR ;
TC0->TC_CHANNEL[0].TC_RC = 830; // The Triggering frequency selected is 50KHz for ADC and DACC modules.
TC0->TC_CHANNEL[0].TC_RA = 300;
TC0->TC_CHANNEL[0].TC_CMR = ((TC0->TC_CHANNEL[0].TC_CMR) & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET ;
TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG ;
// This part of the code is to check the sampling frequency in Arduino DUE pin.
PIOB->PIO_WPMR = 0x50494F00; // disabling the write protect registers for PIOB Peripheral controller.
PIOB->PIO_PDR = PIO_PB25B_TIOA0 ; // disable PIO control.
PIOB->PIO_IDR = PIO_PB25B_TIOA0 ; // disable PIO interrupts.
PIOB->PIO_ABSR |= PIO_PB25B_TIOA0 ; // switch to B peripheral.
}
void DAC_setup(void)
{
PMC->PMC_PCER1 |= (PMC_PCER1_PID38); // Power on for the DACC peripheral.
DACC->DACC_CR = (DACC_CR_SWRST); // Resetting the DAC.
DACC->DACC_MR |= (DACC_MR_TRGEN_EN)|
(DACC_MR_TRGSEL(1))| // Selecting TIOA as the trigger.
(DACC_MR_WORD_HALF)| // Enabling Halg word transfer.
(DACC_MR_USER_SEL_CHANNEL0)|
(DACC_MR_REFRESH(5))|
(DACC_MR_STARTUP_16);
DACC->DACC_IDR = 0xFFFFFFFF ;
DACC->DACC_CHER |= DACC_CHER_CH0;
}
void dac_write (int val)
{
DACC->DACC_CDR = val & 0xFFF ;
}
void ADC_Handler (void)
{
if (ADC->ADC_ISR & ADC_ISR_EOC7) // ensure there was an End-of-Conversion and we read the ISR reg.
{
int V = *(ADC->ADC_CDR+7) ;
int U = *(ADC->ADC_CDR+6) ;
dac_write (0xFFF & (V - U)) ; // copy inverted to DAC output FIFO.
}
}
//]