ATTiny841: timer problem

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

I'd like to create a 5-second timer based on Timer1.  In the code below, I'm running off a 16 MHz external crystal, divided down by 1024.  The counter counts up to 16,000,000/1024 = 15,625 and then toggles PA4 to generate a 2Hz square wave (for measurement with a scope to verify accuracy).  My problem is, things work fine single-stepping under emulation, but while free-running (under emulation or not) TCNT1 usually doesn't change.  Sometimes it will count to 51 or 67 and then hang there; seconds never increments.  Any ideas what I'm doing wrong?  The program does other things, hence all the variables and UART initialization.  I've cut it down to the relevant part here.  Thanks for your help.

 

Karl

 

int main(void)
{
    unsigned int i = 1, font, fontSize, timer = 0;
    unsigned char j, seconds = 0;
    CCP = 0xD8; // Enable configuration change to change clock prescaler.
    CLKPR = 0;  // Set clock prescaler to 1 from 8 (default).
    USART_Init();
    TCCR1B = 0x05;  // Start timer with clock set to clock/1024
    DDRA = 0b00010010;

    while (1)
    {
        TCCR1B = 0; // Stop timer.
        timer = TCNT1;
        if (timer >= 15625)
        {
            PORTA ^= 0x10;  // Toggle A4
            seconds++;
            TCNT1 = 0;      // Reset timer.
            if (seconds < 5)
                TCCR1B = 0x05;  // Restart timer.
            else if (seconds == 5)
            {
                // If the power hasn't been turned off after 5 seconds, toggle the font back.

//                EEPROM_WRITE((int)&efont, font);
            }
        }
        else
            TCCR1B = 0x05;  // Restart timer.
    }
}

 

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

I guess you will need to tell more about how you are determining that TCNT1 is not changing.  Define "emulation".  Are you getting the output square wave, or not?

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

Upon reviewing your code, it is very strange.  You are constantly stopping your timer in the main loop.  Now, the timer prescaler [probably] resets, so you never get the 1024 main clock ticks to increment TCNT1.  That scenario may well explain your symptoms.

 

Now, a basic polling sequence of the timer may be a useful first exercise.  But in any real app creating a seconds or whatever tick, one never fusses with TCNT, nor stop the timer.  The most straightforward way is to use CTC mode with OCR1A as TOP, watch the OCF1A flag in TIFR1, clear the flag (by writing 1 to it), and then take your one second action.

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

Given my comments above, if the only purpose of the app is to create e.g. a 5-second repeating pulse, then divide your system clock so that five seconds is well within reach of one cycle of the 16-bit timer.  Your app then becomes about 10 lines of code or less.

 

Below, you will need to address making the selected pin an output, and making the correct setting in TOCC1.  From the CodeVision Wizard:

#include <io.h>

// Declare your global variables here

void main(void)
{
// Declare your local variables here

// Clock Prescaler division factor: 8
#pragma optsize-
CCP=0xd8;
CLKPR=(0<<CLKPS3) | (0<<CLKPS2) | (1<<CLKPS1) | (1<<CLKPS0);
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

// Input/Output Ports initialization
// Port A initialization
// Pull-up initialization
PUEA=(0<<PUEA7) | (0<<PUEA6) | (0<<PUEA5) | (0<<PUEA4) | (0<<PUEA3) | (0<<PUEA2) | (0<<PUEA1) | (0<<PUEA0);
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In 
DDRA=(0<<DDA7) | (0<<DDA6) | (0<<DDA5) | (0<<DDA4) | (0<<DDA3) | (0<<DDA2) | (0<<DDA1) | (0<<DDA0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T 
PORTA=(0<<PORTA7) | (0<<PORTA6) | (0<<PORTA5) | (0<<PORTA4) | (0<<PORTA3) | (0<<PORTA2) | (0<<PORTA1) | (0<<PORTA0);

// Port B initialization
// Pull-up initialization
PUEB=(0<<PUEB3) | (0<<PUEB2) | (0<<PUEB1) | (0<<PUEB0);
// Function: Bit3=In Bit2=In Bit1=In Bit0=In 
DDRB=(0<<DDB3) | (0<<DDB2) | (0<<DDB1) | (0<<DDB0);
// State: Bit3=T Bit2=T Bit1=T Bit0=T 
PORTB=(0<<PORTB3) | (0<<PORTB2) | (0<<PORTB1) | (0<<PORTB0);

// Port A bit 5 High Drive enabled: Off
// Port A bit 7 High Drive enabled: Off
PHDE=(0<<PHDEA0) | (0<<PHDEA1);

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=0xFF
// OC0A output: Disconnected
// OC0B output: Disconnected
TCCR0A=(0<<COM0A1) | (0<<COM0A0) | (0<<COM0B1) | (0<<COM0B0) | (0<<WGM01) | (0<<WGM00);
TCCR0B=(0<<WGM02) | (0<<CS02) | (0<<CS01) | (0<<CS00);
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;

// Ensure that the Timer/Counter 0 is disabled to preserve power
PRR|= 1<<PRTIM0;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 7.813 kHz
// Mode: CTC top=OCR1A
// OC1A output: Toggle on compare match
// OC1B output: Disconnected
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer Period: 4.9999 s
// Output Pulse(s):
// OC1A Period: 9.9999 s Width: 4.9999 s
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=(0<<COM1A1) | (1<<COM1A0) | (0<<COM1B1) | (0<<COM1B0) | (0<<WGM11) | (0<<WGM10);
TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (1<<WGM12) | (1<<CS12) | (0<<CS11) | (0<<CS10);
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x98;
OCR1AL=0x95;
OCR1BH=0x00;
OCR1BL=0x00;

// Ensure that the Timer/Counter 1 is disabled to preserve power
PRR|= 1<<PRTIM1;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer2 Stopped
// Mode: Normal top=0xFFFF
// OC2A output: Disconnected
// OC2B output: Disconnected
// Noise Canceler: Off
// Input Capture on Falling Edge
TCCR2A=(0<<COM2A1) | (0<<COM2A0) | (0<<COM2B1) | (0<<COM2B0) | (0<<WGM21) | (0<<WGM20);
TCCR2B=(0<<ICNC2) | (0<<ICES2) | (0<<WGM23) | (0<<WGM22) | (0<<CS22) | (0<<CS21) | (0<<CS20);
TCNT2H=0x00;
TCNT2L=0x00;
ICR2H=0x00;
ICR2L=0x00;
OCR2AH=0x00;
OCR2AL=0x00;
OCR2BH=0x00;
OCR2BL=0x00;

// Ensure that the Timer/Counter 2 is disabled to preserve power
PRR|= 1<<PRTIM2;

// Timer/Counter Compare Outputs signals routed to TOCCn pins: 
// Nothing -> TOCC0
// Nothing -> TOCC1
// Nothing -> TOCC2
// Nothing -> TOCC3
// Nothing -> TOCC4
// Nothing -> TOCC5
// Nothing -> TOCC6
// Nothing -> TOCC7
TOCPMSA0=(0<<TOCC3S1) | (0<<TOCC3S0) | (0<<TOCC2S1) | (0<<TOCC2S0) | (0<<TOCC1S1) | (0<<TOCC1S0) | (0<<TOCC0S1) | (0<<TOCC0S0);
TOCPMSA1=(0<<TOCC7S1) | (0<<TOCC7S0) | (0<<TOCC6S1) | (0<<TOCC6S0) | (0<<TOCC5S1) | (0<<TOCC5S0) | (0<<TOCC4S1) | (0<<TOCC4S0);
TOCPMCOE=(0<<TOCC7OE) | (0<<TOCC6OE) | (0<<TOCC5OE) | (0<<TOCC4OE) | (0<<TOCC3OE) | (0<<TOCC2OE) | (0<<TOCC1OE) | (0<<TOCC0OE);

// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=(0<<OCIE0B) | (0<<OCIE0A) | (0<<TOIE0);

// Timer/Counter 1 Interrupt(s) initialization
TIMSK1=(0<<ICIE1) | (0<<OCIE1B) | (0<<OCIE1A) | (0<<TOIE1);

// Timer/Counter 2 Interrupt(s) initialization
TIMSK2=(0<<ICIE2) | (0<<OCIE2B) | (0<<OCIE2A) | (0<<TOIE2);

// External Interrupt(s) initialization
// INT0: Off
// Interrupt on any change on pins PCINT0-7: Off
// Interrupt on any change on pins PCINT8-11: Off
MCUCR=(0<<ISC01) | (0<<ISC00);
GIMSK=(0<<INT0) | (0<<PCIE1) | (0<<PCIE0);

// USART0 initialization
// USART0 disabled
UCSR0B=(0<<RXCIE0) | (0<<TXCIE0) | (0<<UDRIE0) | (0<<RXEN0) | (0<<TXEN0) | (0<<UCSZ02) | (0<<RXB80) | (0<<TXB80);

// Ensure that the USART0 is disabled to preserve power
PRR|= 1<<PRUSART0;

// USART1 initialization
// USART1 disabled
UCSR1B=(0<<RXCIE1) | (0<<TXCIE1) | (0<<UDRIE1) | (0<<RXEN1) | (0<<TXEN1) | (0<<UCSZ12) | (0<<RXB81) | (0<<TXB81);

// Ensure that the USART1 is disabled to preserve power
PRR|= 1<<PRUSART1;

// Analog Comparator 0 initialization
// Analog Comparator 0: Off
// The Analog Comparator 0's positive input is
// connected to the AIN00 pin
// The Analog Comparator 0's negative input is
// connected to the AIN01 pin
ACSR0A=(1<<ACD0) | (0<<ACPMUX2) | (0<<ACO0) | (0<<ACI0) | (0<<ACIE0) | (0<<ACIC0) | (0<<ACIS01) | (0<<ACIS00);
// Hysterezis level: 0 mV
ACSR0B=(0<<HSEL0) | (0<<HLEV0) | (0<<ACOE0) | (0<<ACNMUX1) | (0<<ACNMUX0) | (0<<ACPMUX1) | (0<<ACPMUX0);
// Digital input buffer on AIN00: On
// Digital input buffer on AIN01: On
DIDR0=(0<<ADC1D) | (0<<ADC2D);

// Analog Comparator 1 initialization
// Analog Comparator 1: Off
// The Analog Comparator 1's positive input is
// connected to the AIN10 pin
ACSR1A=(1<<ACD1) | (0<<ACBG1) | (0<<ACO1) | (0<<ACI1) | (0<<ACIE1) | (0<<ACIC1) | (0<<ACIS11) | (0<<ACIS10);
// Hysterezis level: 0 mV
// The Analog Comparator 1's negative input is
// connected to the AIN11 pin
// Analog Comparator 1's output: Off
ACSR1B=(0<<HSEL1) | (0<<HLEV1) | (0<<ACOE1) | (0<<ACME1);
// Digital input buffer on AIN10: On
// Digital input buffer on AIN11: On
DIDR0|=(0<<ADC3D) | (0<<ADC4D);

// ADC initialization
// ADC disabled
ADCSRA=(0<<ADEN) | (0<<ADSC) | (0<<ADATE) | (0<<ADIF) | (0<<ADIE) | (0<<ADPS2) | (0<<ADPS1) | (0<<ADPS0);

// Ensure that the ADC is disabled to preserve power
PRR|= 1<<PRADC;

// SPI initialization
// SPI disabled
SPCR=(0<<SPIE) | (0<<SPE) | (0<<DORD) | (0<<MSTR) | (0<<CPOL) | (0<<CPHA) | (0<<SPR1) | (0<<SPR0);

// Ensure that the SPI is disabled to preserve power
PRR|= 1<<PRSPI;

// TWI Slave initialization
// TWI Slave disabled
TWSCRA=(0<<TWSHE) | (0<<TWDIE) | (0<<TWASIE) | (0<<TWEN) | (0<<TWSIE) | (0<<TWPME) | (0<<TWSME);

// Ensure that the TWI is disabled to preserve power
PRR|= 1<<PRTWI;

while (1)
      {
      // Place your code here

      }
}

The 10-line version:

 

#include <io.h>
void main(void)
{
// Clock Prescaler division factor: 8
#pragma optsize-
CCP=0xd8;
CLKPR=(0<<CLKPS3) | (0<<CLKPS2) | (1<<CLKPS1) | (1<<CLKPS0);
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

// Pick your output pin, and also set TOCC1
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In 
DDRA=(0<<DDA7) | (0<<DDA6) | (0<<DDA5) | (0<<DDA4) | (0<<DDA3) | (0<<DDA2) | (0<<DDA1) | (0<<DDA0);
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 7.813 kHz
// Mode: CTC top=OCR1A
// OC1A output: Toggle on compare match
// OC1B output: Disconnected
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer Period: 4.9999 s
// Output Pulse(s):
// OC1A Period: 9.9999 s Width: 4.9999 s
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=(0<<COM1A1) | (1<<COM1A0) | (0<<COM1B1) | (0<<COM1B0) | (0<<WGM11) | (0<<WGM10);
TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (1<<WGM12) | (1<<CS12) | (0<<CS11) | (0<<CS10);
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x98;
OCR1AL=0x95;
OCR1BH=0x00;
OCR1BL=0x00;

// Ensure that the Timer/Counter 1 is disabled to preserve power
PRR|= 1<<PRTIM1;


// Timer/Counter Compare Outputs signals routed to TOCCn pins: 
// Nothing -> TOCC0
// Nothing -> TOCC1 <<<<== set this
// Nothing -> TOCC2
// Nothing -> TOCC3
// Nothing -> TOCC4
// Nothing -> TOCC5
// Nothing -> TOCC6
// Nothing -> TOCC7
TOCPMSA0=(0<<TOCC3S1) | (0<<TOCC3S0) | (0<<TOCC2S1) | (0<<TOCC2S0) | (0<<TOCC1S1) | (0<<TOCC1S0) | (0<<TOCC0S1) | (0<<TOCC0S0);
TOCPMSA1=(0<<TOCC7S1) | (0<<TOCC7S0) | (0<<TOCC6S1) | (0<<TOCC6S0) | (0<<TOCC5S1) | (0<<TOCC5S0) | (0<<TOCC4S1) | (0<<TOCC4S0);
TOCPMCOE=(0<<TOCC7OE) | (0<<TOCC6OE) | (0<<TOCC5OE) | (0<<TOCC4OE) | (0<<TOCC3OE) | (0<<TOCC2OE) | (0<<TOCC1OE) | (0<<TOCC0OE);

while (1)
      {
      // Place your code here

      }
}

 

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

Yup, I was stopping it way too much.  Here's the essence of what I ended up with.  Works fine.  May be off by a tick or two since I'm not using the compare feature, but it's plenty accurate enough for my app (.02%, where 2% would've been fine) .

 

        timer = TCNT1;
        if (timer >= 15625)
        {
            seconds++;
            TCNT1 = 0;      // Reset timer.
            if (seconds == 5)
            {
                TCCR1B = 0; // Stop timer.

                // If the power hasn't been turned off after 5 seconds, toggle the font back.

                EEPROM_WRITE((int)&efont, font);
            }
        }

 

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

Still, no real app will do it this way.

 

There are a number of ways to address polling that count continually, such as TCNT1 -= 15625;  But none of us would fuss with TCNT at all, after CTC mode was added to AVR8 timers for the new millennium.

 

A trivial point:  As you start counting from zero, then isn't 15625 one too many?

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

> As you start counting from zero, then isn't 15625 one too many?

I don't see why. If there were 1 count in a second I'd test TCNT1 for 1. Since there are 15625 counts in a second, that's what I test for.

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

Suit yourself -- fence-post problem.  In CTC mode when we want three ticks, we set the compare register to two as the timer counts 0,1,2,0,1,2,0,...  In your situation zero is also counted.  So I think you are getting an extra count.  But as I repeatedly said, no real app would do anything like that approach.

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.