Go Down

Topic: Problem with generation of TTL Pulses with defined length using timers (Read 1 time) previous topic - next topic

MarinaK

I'm new to microcontroller programming and to arduino, so please excuse me if I'm asking obvious things.

I'm trying to generate a TLL pulse with a predefined length after the ADC has detected the defined threshold. So far succeeded to detect the threshold (I used this post as an example http://forum.arduino.cc/index.php?topic=205096.0).
For my application it is important to minimize the jitter between the detection of the threshold and the generation of the pulse. So I can't use arduionos functions.

My idea was to use a second timer to define the length of the pulse. In the datasheet I've found that I can restart the timer after a number of counts defined in the TC_RC register, which should raise a flag in the TC_SRx in the CPCS byte.
So I definde the value of RC to correspond to a 75 ms long pulse but the CPCS bit appears to never be set. The code compiles and I don't get any errors.
I've inserted the whole code here:
Code: [Select]
void setup()
{
 Serial.begin (115200) ; // was for debugging
 adc_setup () ;         // setup ADC
 
 pmc_enable_periph_clk (TC_INTERFACE_ID + 0*3+0) ;  // clock the TC0 channel 0 (timer for the adc)
 pmc_enable_periph_clk (TC_INTERFACE_ID + 0*3+1) ;  // clock the TC0 channel 1 (timer for the pulse length)


 TcChannel * t = &(TC0->TC_CHANNEL)[0] ;    // pointer to TC0 registers for its channel 0
 t->TC_CCR = TC_CCR_CLKDIS |TC_CCR_CLKEN;  // disable internal clocking while setup regs
 t->TC_IDR = 0xFFFFFFFF ;     // disable interrupts
 t->TC_SR ;                   // read int status reg to clear pending
 t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 |   // use TCLK1 (prescale by 2, = 42MHz)
             TC_CMR_WAVE |                  // waveform mode
             TC_CMR_WAVSEL_UP_RC |          // count-up PWM using RC as threshold
             TC_CMR_EEVT_XC0 |     // Set external events from XC0 (this setup TIOB as output)
             TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
             TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR ;
 
 t->TC_RC =  875 ;     // counter resets on RC, so sets period in terms of 42MHz clockTC
 t->TC_RA =  440 ;     // roughly square wave
 t->TC_CMR = (t->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET ;  // set clear and set from RA and RC compares
 
 t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG ;  // re-enable local clocking and switch to hardware trigger source.

 TcChannel * t1 = &(TC0->TC_CHANNEL)[1] ;    // pointer to TC0 registers for its channel 1
 t1->TC_CCR = TC_CCR_CLKDIS |TC_CCR_CLKEN;  // disable internal clocking while setup regs
 t1->TC_IER |= 0x10 ;     // enable RC interrupt
 //t1->TC_SR ;                   // read int status reg to clear pending
 t1->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 |   // use TCLK1 (prescale by 2, = 42MHz)
             TC_CMR_WAVE |                  // waveform mode
             TC_CMR_WAVSEL_UP_RC |          // count-up PWM using RC as threshold
             TC_CMR_EEVT_XC0 |     // Set external events from XC0 (this setup TIOB as output)
             TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
             TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR ;
 
 t1->TC_RC =  300000000 ;     // counter resets on RC, so sets period in terms of 13,88 Hz clock (for laser trigger)
 t1->TC_RA =  440 ;     // roughly square wave
 t1->TC_CMR = (t1->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET ;  // set clear and set from RA and RC compares
 
 t1->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG ;  // re-enable local clocking and switch to hardware trigger source.
 
 setup_pio_TIOA0 () ;  // drive Arduino pin 2 at 48kHz to bring clock out
 pio_out_setup();
}

void setup_pio_TIOA0 ()  // Configure Ard pin 2 as output from TC0 channel A (copy of trigger event)
{
 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 pio_out_setup()//set pin 23 as output
{
   pmc_enable_periph_clk(ID_PIOA);
   PIOA->PIO_MDDR = PIO_MDDR_P14;
   PIOA->PIO_CODR = PIO_CODR_P14;
   PIOA->PIO_OWER = PIO_OWER_P14;
   PIOA->PIO_OER = PIO_OER_P14;
   PIOA->PIO_PER = PIO_PER_P14;
}

void adc_setup ()
{
 NVIC_EnableIRQ (ADC_IRQn) ;   // enable ADC interrupt vector
 ADC->ADC_IDR = 0xFFFFFFFF ;   // disable interrupts
 ADC->ADC_IER = 0x80 ;         // enable AD7 End-Of-Conv interrupt (Arduino pin A0)
 ADC->ADC_CHDR = 0xFFFF ;      // disable all channels
 ADC->ADC_CHER = 0x80 ;        // enable just A0
 ADC->ADC_CGR = 0x15555555 ;   // All gains set to x1
 ADC->ADC_COR = 0x00000000 ;   // All offsets off
 
 ADC->ADC_MR = (ADC->ADC_MR & 0xFFFFFFF0) | (1 << 1) | ADC_MR_TRGEN ;  // 1 = trig source TIO from TC0
 ADC->ADC_EMR = 0x201; // generate an event when the data is higher than the threashold, raise flag after one event, compare all channels
 ADC->ADC_CWR = ADC_CWR_HIGHTHRES(2000); //set threshold to 1.5 (12 Bit resolution)
}

void ADC_Handler (void)
{
 if (ADC->ADC_ISR & ADC_ISR_COMPE)   // ensure there was COMPE flag raised
 {
  PIOA->PIO_SODR=PIO_SODR_P14;
  //Serial.println(TC0->TC_CHANNEL[1].TC_SR,BIN);
 
  while(TC0->TC_CHANNEL[1].TC_SR&0x10==0)
  {//v= TC0->TC_CHANNEL[1].TC_CV;
 
  }
   PIOA->PIO_CODR= PIO_CODR_P14;
 }  
}

void loop()
{
}


on the scope I see the following result (see the attached image)
the yellow signal is intup of the ADC channel and the blue is the TTL pulse. it is 70 ns wide and I want 75ms.

I'll appreciate if anybody has an idea how to get the timer going or any other suggestions how to generate such a pulse.

Thanks alot for your help!

Grumpy_Mike

Quote
For my application it is important to minimize the jitter between the detection of the threshold and the generation of the pulse. So I can't use arduionos functions.

In which case do not use an arduino but a bit of simple hardware.
Feed your signal into a voltage comparator like an LM339 with a pot setting the threshold.
Then use the output to trigger a monostable like a 74LS123 or one made with an NE555.

dlloyd

Re: 37.7.11 TC Channel Mode Register: Waveform Mode (datasheet)
In your setup code, try setting CPCSTOP bit to 1 so that the counter clock will stop when it reaches RC.

MarinaK

Thanks for the quick answers!
I've finally got the result I wanted. However it's a little workaround since the CPCS flag was never raised in the TC_RS register in my case.
Here the pieces of code I've modified:
Code: [Select]
t1->TC_CMR|= TC_CMR_CPCSTOP;       //counter clock is stopped when counter reaches RC
  t1->TC_RC =  3000000 ;     // counter resets on RC
  t1->TC_RA =  1500000 ;     // roughly square wave


and
Code: [Select]

void ADC_Handler (void)
{
  long v;
  if (ADC->ADC_ISR & ADC_ISR_COMPE)   // ensure there was COMPE flag raised
  {
   PIOA->PIO_SODR=PIO_SODR_P14;      // set leven of PIN 23 to HIGH
   ADC->ADC_CHDR = 0xFFFF;           // switsch off all ADC CHANNELs
   TC0->TC_CHANNEL[1].TC_CCR=TC_CCR_SWTRG;  // reset timer t1
   v=TC0->TC_CHANNEL[1].TC_CV;       // read out timer value
   while(v<3000000)                  // wait ~70 ms according to timer settings
   {v=TC0->TC_CHANNEL[1].TC_CV;}
   PIOA->PIO_CODR= PIO_CODR_P14;  // set leven of PIN 23 to LOW
   ADC->ADC_CHER = 0x80 ;        // enable just A0
  }


The jitter is currently in the oder of 1us... I'll try out if it's tollerable in my setup . Otherwise I'll look into the comparator option... thanks for the suggestion :)

Go Up