I am currently working on Arduino DUE in order to generate a wave and acquire sinals from ADC (both with DMA and/or Interrupt) , first I used DAC pin but it was not suited for my work, so I changed and I am now using a "common" pin where a PWM wave with periodcally different duty cycle is generated.
Both methods work and do what they have to do, but ADC code, although it is the same, works only in the DAC case. In PWM case ADC_handler is never called (everything I write in ADC_handler is never computed) and the code is not stuck in a point (the loop works).
It seems like either ADC is not enabled or timer that command ADC interrupt has a problem. Actually, in the DAC case, ADC works only if DAC is working too: if DAC Init functions are commented, ADC doesn't work too.
I really can not cope with this absurdity, if someone has an idea, I would be very grateful!
#define BaudRate 460800
#define sinsize (64) // Sample number (a power of 2 is better)
#define PERIOD_VALUE (4200) // For a 312.5 Hz sinwave 4200 + 64 samples
#define NbCh (1) // Only channel 0 ---> Number of channels = 1
#define DUTY_BUFFER_LENGTH (sinsize * NbCh) // Half words
uint16_t volt[2];
uint8_t flag = 0;
uint16_t Duty_Buffer[DUTY_BUFFER_LENGTH];
#define UpdatePeriod_Msk (0b1111)
#define UpdatePeriod (UpdatePeriod_Msk & 0b0000) //Defines the time between each duty cycle update of the synchronous channels
//This time equals to (UpdatePeriod + 1) periods of the Reference channel 0
uint16_t Sin_Duty[sinsize];
void setup () {
//Generate lookup table
for (int i = 0; i < sinsize; i++) {
Sin_Duty[i] = 1 + (2047 * (sin( i * 2 * PI / sinsize ) + 1));
}
for (uint32_t i = 0; i < sinsize; i++) {
Duty_Buffer[i * NbCh + 0] = Sin_Duty[i];
adc_setup();
tc_adc_setup();
PWM_setup();
}
Serial.begin(BaudRate);
while(!Serial); // Wait for connection
}
void loop(){
uint8_t checksum;
if(flag == 1){ //Protocol via Serial
checksum = (uint8_t)(volt[0] >> 8) + (uint8_t)(volt[0]) + (uint8_t)(volt[1] >> 8) + (uint8_t)(volt[1]);
Serial.write(0x02); // Start of text
Serial.write((uint8_t) (volt[0] >> 8));
Serial.write((uint8_t) volt[0]);
Serial.write((uint8_t) (volt[1] >> 8));
Serial.write((uint8_t) volt[1]);
Serial.write(checksum);
flag = 0 ;
}
}
void PWM_setup(){
PMC->PMC_PCER1 |= PMC_PCER1_PID36; // PWM controller power ON
PMC->PMC_PCER0 |= PMC_PCER0_PID13; // PIOC power ON
// PWML0 on PC2, peripheral type B: Pin34
PIOC->PIO_PDR |= PIO_PDR_P2;
PIOC->PIO_ABSR |= PIO_PC2B_PWML0;
// Set synchro channels list : Channel 0
PWM->PWM_DIS = PWM_DIS_CHID0;
PWM->PWM_SCM = PWM_SCM_SYNC0 // Add SYNCx accordingly, at least SYNC0
| PWM_SCM_UPDM_MODE2; //Automatic write of duty-cycle update registers by the PDC DMA
// Set duty cycle update period
PWM->PWM_SCUP = PWM_SCUP_UPR(UpdatePeriod);
// Set the PWM Reference channel 0 i.e. : Clock/Frequency/Alignment
PWM->PWM_CLK = PWM_CLK_PREA(0b0000) | PWM_CLK_DIVA(1); // Set the PWM clock rate for 84 MHz/1
PWM->PWM_CH_NUM[0].PWM_CMR = PWM_CMR_CPRE_CLKA; // The period is left aligned, clock source as CLKA on channel 0
PWM->PWM_CH_NUM[0].PWM_CPRD = PERIOD_VALUE; // Set the PWM frequency (84MHz/1)/PERIOD_VALUE Hz
/**** Final frequency = MCK/DIVA/PRES/CPRD/(UPR + 1)/sinsize ****/
// Set Interrupt events
PWM->PWM_IER2 = PWM_IER2_WRDY; //Write Ready for Synchronous Channels Update Interrupt Enable
//synchro with ENDTX End of TX Buffer Interrupt Enable
// Fill duty cycle buffer for channels 0, x, y ...
// Duty_Buffer is a buffer of Half Words(H_W) composed of N lines which structure model for each duty cycle update is :
// [ H_W: First synchro channel 0 duty cycle **Mandatory** ]/[ H_W: Second synchro channel duty cycle ] ... and so on
PWM->PWM_ENA = PWM_ENA_CHID0; // Enable PWM for all channels, channel 0 Enable is sufficient
PWM->PWM_TPR = (uint32_t)Duty_Buffer; // FIRST DMA buffer
PWM->PWM_TCR = DUTY_BUFFER_LENGTH; // Number of Half words
//PWM->PWM_TNPR = (uint32_t)Duty_Buffer; // Next DMA buffer
//PWM->PWM_TNCR = DUTY_BUFFER_LENGTH;
PWM->PWM_PTCR = PWM_PTCR_TXTEN; // Enable PDC Transmit channel request
NVIC_EnableIRQ(PWM_IRQn);
NVIC_SetPriority(PWM_IRQn, 0x01);
}
void PWM_Handler() { // move PDC DMA pointers to next buffer
PWM->PWM_ISR2; // Clear status register
PWM->PWM_TNPR = (uint32_t)Duty_Buffer;
PWM->PWM_TNCR = DUTY_BUFFER_LENGTH;
}
}
/******* Timer Counter 0 Channel 1 to generate PWM pulses thru TIOA1 for ADC ********/
void tc_adc_setup() {
PMC->PMC_PCER0 |= PMC_PCER0_PID28; // TC1 power ON : Timer Counter 0 channel 1
TC0->TC_CHANNEL[1].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 TIOA1 on RA compare match
| TC_CMR_ACPC_SET; // Set TIOA1 on RC compare match
TC0->TC_CHANNEL[1].TC_RC = 1000; //<******************** Frequency = (Mck/8)/TC_RC Hz = 4000 Hz -> Adc can be sampled less frequently because a range of 5 values verify the if caluse in adchandler
TC0->TC_CHANNEL[1].TC_RA = 20; //<******************** Any Duty cycle in between 1 and TC_RC
TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_CLKEN; // TC1 enable
}
ADC code which is the same for both cases, I apologize but the limit of words forced me to split the code in different posts
/************* Configure adc_setup function *******************/
void adc_setup() {
PMC->PMC_PCER1 |= PMC_PCER1_PID37; // ADC power ON
ADC->ADC_CR = ADC_CR_SWRST; // Reset ADC
ADC->ADC_MR |= ADC_MR_TRGEN_EN // Hardware trigger select
| ADC_MR_TRGSEL_ADC_TRIG2 // Trigger by TIOA1
| ADC_MR_PRESCAL(1);
//ADC->ADC_ACR = ADC_ACR_IBCTL(0b01); // For frequencies > 500 KHz
ADC->ADC_IDR = ~ADC_IDR_EOC7;
ADC->ADC_IER = ADC_IER_EOC7; // End Of Conversion interrupt enable for channel 7
NVIC_EnableIRQ(ADC_IRQn); // Enable ADC interrupt
NVIC_SetPriority(ADC_IRQn, 0x00);
ADC->ADC_CHER = ADC_CHER_CH5 | ADC_CHER_CH6 | ADC_CHER_CH7; // Enable Channels 7 = A0 and 6 = A1, 5 A2; Trigger frequency is multiplied by 3
// The sampling frequency for 1 channel times the number of channels !!
adc_start(ADC);
}
/********* Call back function for ADC Int **************/
void ADC_Handler () {
while(!(((ADC->ADC_ISR & ADC_ISR_EOC7) && (ADC->ADC_ISR & ADC_ISR_EOC6) && (ADC->ADC_ISR & ADC_ISR_EOC5))));
if((*(ADC -> ADC_CDR+7) > 5) && (*(ADC -> ADC_CDR+7) < 2650)){ // range from 0.041 to 2.28V - > peak when ADC measures xxx
volt[0] = *(ADC -> ADC_CDR+6); //max value after filter: 3.3V ok
volt[1] = *(ADC -> ADC_CDR+5);
flag = 1;
}
}
/******* Timer Counter 0 Channel 1 to generate PWM pulses thru TIOA1 for ADC ********/
void tc_adc_setup() {
PMC->PMC_PCER0 |= PMC_PCER0_PID28; // TC1 power ON : Timer Counter 0 channel 1
TC0->TC_CHANNEL[1].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 TIOA1 on RA compare match
| TC_CMR_ACPC_SET; // Set TIOA1 on RC compare match
TC0->TC_CHANNEL[1].TC_RC = 1000; //<******************** Frequency = (Mck/8)/TC_RC Hz = 4000 Hz -> Adc can be sampled less frequently because a range of 5 values verify the if caluse in adchandler
TC0->TC_CHANNEL[1].TC_RA = 20; //<******************** Any Duty cycle in between 1 and TC_RC
TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_CLKEN; // TC1 enable
}