Go Down

Topic: Same code different behaviour (Read 260 times) previous topic - next topic

Tyarel

Hi everyone,

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!

Thank you!
Luigi


Tyarel

DAC case code (buf[] is the lookup table)
Code: [Select]

#define BaudRate 460800

uint16_t volt[2];
uint8_t flag = 0;

volatile uint8_t bufn, Oldbufn, bufn_dac;
const uint16_t bufsize = 150;           
const uint8_t bufnumber = 1;             
const uint8_t _bufnumber = bufnumber - 1;

volatile uint16_t buf[bufnumber][bufsize] =...;
void setup()
{
 
  Serial.begin(BaudRate);
  while(!Serial);  // Wait for connection
 
  pinMode(LED_BUILTIN, OUTPUT);  // For ADC debugging
  pinMode(12, OUTPUT);           // For DACC debugging
 
 
  adc_setup();
  dac_setup();
  tc_adc_setup();
  tc_dac_setup();
}

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 ;
  }
/*************  Configure adc_setup function  *******************/
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                    // Output on DAC0 and DAC1
                  | DACC_MR_WORD_HALF                 
                  | DACC_MR_REFRESH (1)
                  | DACC_MR_STARTUP_8
                  | DACC_MR_MAXS;

  DACC->DACC_ACR = DACC_ACR_IBCTLCH0(0b11) //0b10
                   | DACC_ACR_IBCTLCH1(0b11) // 0b10
                   | DACC_ACR_IBCTLDACCORE(0b01);

  DACC->DACC_IDR = ~DACC_IDR_ENDTX;
  DACC->DACC_IER = DACC_IER_ENDTX;                    // TXBUFE works too !!!
  NVIC_SetPriority(DACC_IRQn, 0xFF);
  NVIC_EnableIRQ(DACC_IRQn);
  DACC->DACC_CHER = DACC_CHER_CH0;    // enable channels 0 = DAC0

  /*************   configure PDC/DMA  for DAC *******************/
  DACC->DACC_TPR  = (uint32_t)buf[0];                 // DMA buffer
  DACC->DACC_TCR  = bufsize;
  //DACC->DACC_TNPR = (uint32_t)buf[1];                 // next DMA buffer
  //DACC->DACC_TNCR = bufsize;
  bufn_dac = 1;
  DACC->DACC_PTCR = DACC_PTCR_TXTEN;                  // Enable PDC Transmit channel request

}

/*********  Call back function for DAC PDC/DMA **************/
void DACC_Handler() {   // DACC_ISR_HANDLER()         // move Sinus/PDC/DMA pointers to next buffer

  //if ( DACC->DACC_ISR & DACC_ISR_ENDTX) {           // Useless because the only one

  bufn_dac = (bufn_dac + 1) & _bufnumber;
  DACC->DACC_TNPR = (uint32_t)buf[bufn_dac];
  DACC->DACC_TNCR = bufsize;
 
}

/*************  Configure adc_setup function  *******************/
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                    // Output on DAC0 and DAC1
                  | DACC_MR_WORD_HALF                 
                  | DACC_MR_REFRESH (1)
                  | DACC_MR_STARTUP_8
                  | DACC_MR_MAXS;

  DACC->DACC_ACR = DACC_ACR_IBCTLCH0(0b11) //0b10
                   | DACC_ACR_IBCTLCH1(0b11) // 0b10
                   | DACC_ACR_IBCTLDACCORE(0b01);

  DACC->DACC_IDR = ~DACC_IDR_ENDTX;
  DACC->DACC_IER = DACC_IER_ENDTX;                    // TXBUFE works too !!!
  NVIC_SetPriority(DACC_IRQn, 0xFF);
  NVIC_EnableIRQ(DACC_IRQn);
  DACC->DACC_CHER = DACC_CHER_CH0;    // enable channels 0 = DAC0

  /*************   configure PDC/DMA  for DAC *******************/
  DACC->DACC_TPR  = (uint32_t)buf[0];                 // DMA buffer
  DACC->DACC_TCR  = bufsize;
  //DACC->DACC_TNPR = (uint32_t)buf[1];                 // next DMA buffer
  //DACC->DACC_TNCR = bufsize;
  bufn_dac = 1;
  DACC->DACC_PTCR = DACC_PTCR_TXTEN;                  // Enable PDC Transmit channel request

}

/*********  Call back function for DAC PDC/DMA **************/
void DACC_Handler() {   // DACC_ISR_HANDLER()         // move Sinus/PDC/DMA pointers to next buffer

  //if ( DACC->DACC_ISR & DACC_ISR_ENDTX) {           // Useless because the only one

  bufn_dac = (bufn_dac + 1) & _bufnumber;
  DACC->DACC_TNPR = (uint32_t)buf[bufn_dac];
  DACC->DACC_TNCR = bufsize;
 
}

Tyarel

PWM case code:

Code: [Select]
#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
}

Tyarel

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

Code: [Select]
/*************  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
}

Go Up