Fast timer operation

Go To Last Post
23 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Freaks,

I am running an M328 at 8MHz. I need an output of 4 MHz. I want this to be controllable as I am trying to sync multiple operations with this. To achieve this, I am using the timer interrupts. Here is what I have so far:

void timer_init()
  {

   TCCR1B |= (1 << WGM12);
   TIMSK1  |= (1 << OCIE1A);

   sei();

   OCR1A = 1;

   TCCR1B |= (1 << CS10);

  }



ISR(TIMER1_COMPA_vect)
 {

   PORTB ^= (1 <<PORTB5);
//Set flag here and process in main
                     
 }

However, with this PORTB5 shows a frequency of about 102 kHz. I checked the AVR frequency on the CLKO pin by setting the fuse and it does read 8MHz.

Why am I losing almost 78 cycles? Is this because of the XORing in the interrupt?

Is there a faster way to do this so that I can get 4MHz?

Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How can you possible get 4MHz output when your cpu frequency is only 8MHz using interrupts? You need to toggle the bit every clock. The interrupt handling alone takes several clocks, plus the code inside the interrupt itself. Even just:

   PORTB ^= (1 <<PORTB5);

takes 3 clocks. You could do this:

PINB = 1 << PORTB5;

which would take 1 clock, but you would have to have your entire flash filled with this statement for it to work since any looping of any kind would add more clocks in between.

The best you could do is a timer in CTC mode with the COMx bits set to toggle mode, but even then I believe the best you could do is 2MHz.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What does "controllable" mean?

Yes, you can get clk/2 (IIRC) with CTC and a compare match of 0.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, Lee and Steve.

I will try the timer CTC mode with a compare match of 0.

By controllable, I mean I would like to set a flag when the timer has finished counting so that I can use this flag to control other events (set bits on another port, etc.)

Can I do this without the interrupts? In other words, can I check the counter value and set a flag without using too many cycles? I can always go for a higher crystal frequency (e.g. 16MHz) to get the 4MHz after the processing delay.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

May be best to get rid of the chip and hook your clock source up (or CLK0, if the chip must stay) to a toggle (JK, T) latch...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Can I do this without the interrupts? In other words, can I check the counter value and set a flag without using too many cycles?

Do you mean can you check the timer, make a decision based on what value it is and then do something all in 2 clock cycles? No.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can you please give us more (much more) detail as to what you are trying to do?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am trying to create a given timing waveform with the help of an AVR (something similar to say controlling an LCD). To do this, I am trying to use bit banging of the AVR ports. The protocol basically has clock and data. The clock should be 4MHz. The data is sent bit by bit and should be synchronized with the clock.

If I use a 4MHz clock then I have about 250 ns to send my data on the AVR port. That is the reason I was thinking of using the AVR timer so that every 250 ns, I can change my data bit (i.e. change the PORT bit from 1 to 0 or 0 to 1 depending on the data bit to be sent).

Is it better to use the SPI instead of the bit banging?

If I used a 20 MHz crystal, fclk/4 would still give me 5MHz and maybe I don't have to worry about the synchronization of the data going out of the AVR with the AVR clock as the SPI will take care of that?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
If I use a 4MHz clock then I have about 250 ns to send my data on the AVR port.

Again, don't worry how many nanoseconds you have, worry about how many clock cycles you have.
Quote:
Is it better to use the SPI instead of the bit banging?

Now that you've told us what you really want to do, then yes, the USI in two-wire mode would probably work and be able to give you your 4MHz clock with an 8MHz cpu frequency.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok I tried using the SPI to generate a clock at 4MHz and could not get it. I am using only Master configuration and checking the Sck and the MOSI pins. Following is my code:

#include              
#include  
#include 
#define ddr_spi    DDRB     
#define port_spi   PORTB     
#define pin_ss     PORTB2       
#define pin_mosi   PORTB3       
#define pin_miso   PORTB4       
#define pin_sck    PORTB5       

unsigned char Data;

//---------------------------------------------------------------------------
void SPI_MasterInit(void)
{
   ddr_spi   = ((1<<pin_mosi)|(1<<pin_sck))|((1<<pin_ss));    // MOSI, SCK = 1
   PORTB = 0xFF; //This is very important; need to toggle Slave select line from high to low
   SPCR      = (1<<SPE) | (1<<MSTR) | ( 0 << SPI2X) | (1 << SPR1) | (0 << SPR0) ;  // Enable SPI, master
   PORTB &= ~(1 << pin_ss); //toggle slave select line from high to low
   Data = SPSR;
   Data = SPDR;

}


//---------------------------------------------------------------------------
void SPI_MasterTransmit(char Spi_Data)
{
      SPDR =   Spi_Data;               // Load byte to output register
      while(!(SPSR&(1<<SPIF)));     // Wait for byte to be sent
}


//---------------------------------------------------------------------------

int main (void)
{   
   
   SPI_MasterInit();               

   while(1)                         
   {
           
              SPI_MasterTransmit(0x01);
    }

}

I tried different frequencies as follows:

SPI2X	SPR1	SPR0	SPI clock Prescalar
0	1	1	61.8 kHz	128
0	1	0	122.59 kHz	64
0	0	1	463.73 kHz	16
1	0	1	463.73 kHz	8
0	0	0	1.5237MHz	4
1	0	0	463.73 kHz	2

I am using 8MHz as my AVR fosc. A prescalar of 16 should give me 500 kHz but I am getting about 463 kHz. For a prescalar less than 64, I am getting what looks like a race condition? on my scope. Waveforms are attached. Could this be my scope? I will try changing the scope to see if I get the 4MHz SPI clock.

Also why do I get the error in my frequency? Even with 128 as my prescalar I am getting an error of about 1.12% in the frequency.

I checked transmitting the data and it looked ok at 122.59 kHz.

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your last pic shows pulses starting at varying delays from the pulse that the scope triggers on. This COULD reflect varying program execution times. It could be varying interrupt service times (depending on the type of instruction being executed when the int occurs) but not likely because the variation is much too large.

The first pic seems odd, because you ought to have 8 clocks per SPI transfer. The signal trace is so badly focussed that I really cannot tell what is happening.

I cannot tell how the frequency is being measured in the second one. I would go for a time scale, not frequency, personally. My personal opinion is that oscilloscope frequency measurements are highly over-rated and not worth all the digits shown.

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Take a look at the .lss file
Then you'll find that there is a little overhead for calling SPI_MasterTransmit(), loading SPDR , polling SPSR, and returning to main().

So the SPI clock does pause a while between each byte transfer.

Regards
Sebastian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But I seem to be going from 8MHz to about 1 MHz (even Khz) Is there a faster way of doing this so I can get the spiclk at 4MHz using a divide by 2 prescalar for a clock of 8MHz?

Here is my .lss file:


//--------------------------------------------------------------------------- 
void SPI_MasterTransmit(char Spi_Data) 
{ 
      SPDR =   Spi_Data;               // Load byte to output register 
  c8:	81 e0       	ldi	r24, 0x01	; 1
  ca:	8e bd       	out	0x2e, r24	; 46
      while(!(SPSR&(1<<SPIF)));     // Wait for byte to be sent 
  cc:	0d b4       	in	r0, 0x2d	; 45
  ce:	07 fe       	sbrs	r0, 7
  d0:	fd cf       	rjmp	.-6      	; 0xcc 
  d2:	fb cf       	rjmp	.-10     	; 0xca 

000000d4 <_exit>:
  d4:	f8 94       	cli

000000d6 <__stop_program>:
  d6:	ff cf       	rjmp	.-2      	; 0xd6 <__stop_program>



Should I get rid of the function to make it faster?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
But I seem to be going from 8MHz to about 1 MHz
Are you sure you haven't programmed the CKDIV8 fuse?
Have you enabled compiler optimization?

When I compile your example above with -Os I get very small code - no big overhead

SPCR      = (1<<SPE) | (1<<MSTR) | ( 0 << SPI2X) | (1 << SPR1) | (0 << SPR0) ;

This line sets the SPI clock to F_CPU / 64. So if your AVR is really running at 8MHz, you should see a 125kHz clock.

If you need to transmit the bytes gapless, then try the USART in Master SPI Mode. The UDR0 register is double buffered. So you can write a second byte to UDR0 while the USART transmits the first byte.

Regards
Sebastian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
//---------------------------------------------------------------------------
void SPI_MasterTransmit(char Spi_Data)
{
      SPDR =   Spi_Data;               // Load byte to output register
  c8:   81 e0          ldi   r24, 0x01   ; 1
  ca:   8e bd          out   0x2e, r24   ; 46
      while(!(SPSR&(1<<SPIF)));     // Wait for byte to be sent
  cc:   0d b4          in   r0, 0x2d   ; 45
  ce:   07 fe          sbrs   r0, 7
  d0:   fd cf          rjmp   .-6         ; 0xcc 
  d2:   fb cf          rjmp   .-10        ; 0xca 

000000d4 <_exit>:
  d4:   f8 94          cli

000000d6 <__stop_program>:
  d6:   ff cf          rjmp   .-2         ; 0xd6 <__stop_program> 

This is the max. performance you can get with SPI. Only USART0 in Master SPI Mode could beat that.

Regards
Sebastian

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

This is the max. performance you can get with SPI. Only USART0 in Master SPI Mode could beat that.


Well, in the quest for "fastest" an Xmega could stream nicely.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Using SPI, the clocks will be in bursts of 8 (byte transfer) with a gap while things are reset for the byte. If you need a sustained string equal-period of pulses, then this is not the way to do it.

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That is exactly what I was going to add.

Anyway, I got it to work at 4MHz. For now I think I am ok with the burst of bytes. Just out of curiosity is bit banging the only other way to get around this limitation?

My problem was the scope. Changed scope and I got good waveforms.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

This is the max. performance you can get with SPI. Only USART0 in Master SPI Mode could beat that.

Untrue. For a burst of bytes you can cycle-count and skip the SPIF test. Lessee if we can get it "seamless". With an index register set the load of the next byte is 2 clocks. Loop is 2 clocks. We have a 16 clock budget so I don't think you have to loop unroll; even the end-of-burst test can be done.

Quote:

My problem was the scope. Changed scope and I got good waveforms.

??? I thought npat was the EE starting to dabble in micros?
Quote:

I got it to work at 4MHz.

Byte-by-byte transfer, and no inter-byte gaps?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

For a burst of bytes you can cycle-count and skip the SPIF test.

Wow ... I never ever considered this option.
theusch .. you're a clever bugger!

Cheers Jim

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Byte-by-byte transfer, and no inter-byte gaps?

I wish :). No, I do have the inter-byte gap between bytes.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
Untrue. For a burst of bytes you can cycle-count and skip the SPIF test.
Ahh, directly after I hit the submit button I knew that I had spread bean counter food. :wink:

You're right, of course!

Regards
Sebastian

EDIT: npat_avr, have you looked at the chapter "USART in SPI mode" in the datasheet?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, I did read it briefly after it was mentioned on my post.

I will read in detail to see if this will work for my app.