Attiny85 Timer glitches

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

Hello freaks,

I'm using my tiny to create different sounds. I have a recorded sample that says "Number 9" 9 times using Timer 1's 64 MHZ PLL fast pwm out of OCR1x and ~(OCR1x) to drive a speaker. using Timer 0's clear timer on compare feature it increments the pointer corresponding to the number9 array on a COMPx_vect interrupt. I did my best to encapsulate it in a function so that when I call say "playRecorded()" it configures Timer0 and Timer1 exactly to what it needs. I tried to take care set and clear bits on registers such as TIMSK and GTCCR where bits are shared between Timer0 and Timer1. I also start and stop the timers using functions:
From what I could tell by the data sheet the best way to start and stop Timer1 was by clearing its prescaler bits:
#define Timer1_reset() TCNT1 = 0x00; GTCCR |= (1 << PSR1)
#define Timer1_start() TCCR1 |= Timer1_prescale
#define Timer1_stop() TCCR1 &= 0xf0
And Timer0 you could stop the Timer by holding TSM with the prescaler reset
#define Timer0_start() GTCCR &= ~(1 << TSM)
#define Timer0_stop() GTCCR |= (1 << TSM); GTCCR |= (1 << PSR0)
#define Timer0_reset() TCNT0 = 0x00

ok so thats the recorded sound thats interrupt driven, textbook style approach.

So for my next effect I wanted Timer 1 to produce a CTC square wave style sound that pulsates to a Sinewave wave table that I have stored. Unfortunately the CTC doesn't have an invert feature like the fast pwm - it only produces a square wave out of OCR1x but not the inverse out of ~(OCR1x). So I set Timer 1 to go off on a overflow interrupt with OCR1C the special compare register. Now that I'm typing it out I guess I could've used any OCR1A or OCR1B to create a COMPx_vect and not connect it to OC1A/OC1B hmm. Anyways, on the Timer1's Overflow interrupt it simply ^ PORT pins respective to OC1A and OC1B. And Timer0 increments a sine wave table that varies OCR1C.

Thats the jist of it. All the sounds work, but the problem is,
this works:
while(1) {
playRecorded(); //first sound
playPulsating0(); //second sound I talked about
}
this doesn't:
while(1) {
playPulsating0();
playRecorded();
}
I'm pretty sure it has something to do with timer 1 and setting it to fast pwm. I'm going to copy my source and all the other chip tune sounds work in any order ( I also have a sine wave and triangle wave), but in order for the recorded sound to work, it has to be first.

I've tried resetting all the Timer1 and Timer0 registers to 0 before I initialize them but that doesn't work.

Right now being able to completely reset the Timers before I used them would be nice and I think that might work because I don't know what's going on.

#include 
#include 
#include 
#include 
#include 
#define RECORDED_SIZE NUMBER9_SIZE
#define RECORDED_ARRAY number9_6000hz
#define RECORDED_SAMPLING_RATE 6000
#define SPKR_PLUS PB3
#define SPKR_MINUS PB4
#define SPKR_STROBE PB2
#define IDLE 0
#define openSpeakerDrain() DDRB |= (1 << SPKR_STROBE); PORTB |= (1 << SPKR_STROBE) 
#define closeSpeakerDrain() DDRB &= ~(1 << SPKR_STROBE); PORTB &= ~(1 << SPKR_STROBE)
//Global Variables--------------------------------------------------------------------------
uint8_t i;
//Function Pointers--------------------------------------------------------------------------
void (*Timer0_compareMatchAActionListener)(void);
void (*Timer1_overflowActionListener)(void);  //this is a pointer to a FUNCTION

//--------------------------------------------------------------------------
void delay_ms(unsigned long int x)
{
  for (unsigned long int j=0; j

Any help would be greatly appreciated.
Thanks in advance
-Robby

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

It smells like this for the second time in a week. I'm not sure, but you can have a look there.

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

Thanks snigelen but that didn't work, but I'm glad I found another problem


void main(void) {
   DDRB = (1 << SPKR_PLUS) | (1 << SPKR_MINUS);
   sei();
      playPulsating0(5000);
      playHighPitch1(5000);
      playPulsating0(5000);
      playPulsating1(5000);
      playPulsating2(5000);
   while(1) {
      for (i = 0; i < 3; i++) {
         playRecorded();
         delay_ms(1000);
      }
   }
}

only loops around to play the chip tunes and sounds like it never plays playRecorded. I threw in an ISR(BADISR_vect) at the top but that didn't work. I'm thinking its a power issue thats causing it to reset? maybe from starting the PLL clock at 64mhz?

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

Ok so just for reference I figured it out and its weird. So I resorted to just turning on an LED and inserting PORTB |= (1 << LED); again and again to see where the LED wouldn't turn on, as I said earlier, I figured out that it was reseting. Ok, so I knew it my code never actually got to playRecorded(). the code gets through the for(..) and into play record but never turned on the led past:

void playRecorded() { 
   Timer0_enableCTC(TIMER0_DIV_1); 
   PORTB |= (1 << LED); //LED NEVER TURNS ON!!!!
   #if (RECORDED_SAMPLING_RATE == 6000) 
      OCR0A = OCR0x_6000; 
   #elif (RECORDED_SAMPLING_RATE == 8000) 
      OCR0A = OCR0x_8000; 
   #endif 
   Timer0_compareMatchAActionListener = &playRecordedSound; 
   OCR1B = 0; 
   tableCount = 0; 
   openSpeakerDrain(); 
   Timer1_enableFastPwm();  //timer1 starts automatically 
   Timer0_start(); 
   while(tableCount < RECORDED_SIZE); 
   closeSpeakerDrain(); 
} 

so I looked into "Timer0_enableCTC(xxx)" and started to put LED statements to see where it resets. And after putting "PORTB |= (1 << LED)" after every line, the led turned on each time. huh.. so it had to be a problem with the void return statement or something? idk, I don't know assembler that well, but I do know that the return actually returns to a pointer in the program.. I think. So I inlined it to force the compiler to just insert the code.. I think thats what it does can any body verify my I thinks? and it did turn on the led after calling the function. only to reset at the next line of code where I modify OCR0A. But I got the suspicion the having interrupts going off left and right that it was probably messing up the fragility of things like returning to the function caller, so I cli() once I entered the actual playRecorded() and sei() right at the very last second... and it worked. So there you have it. I answered my own question yipeee.

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

Actually now that I think of it, it looks like a time bomb, see the calling and returning of a function takes time, two more instructions, and by inlining it doesn't have those and can make it just a little bit farther before the interrupt kicks in, and calls a blank pointer to a function and restarts... oh my, is it because it calls a function that is pointed to the beginning of the program? And it was restarting because it was calling a blank function pointer i just figured that out. I did try stopping timer0 just as it exits Timer0_enableCTC() but that didn't work and I'll read the data sheet tomorrow bc maybe my stop and start functions aren't working and I'm tired. so turning off interrupts enterring playRecorded() works and simply making sure it doesn't call a blank funtion worked also:

 
void playRecorded() {
   //cli();
   Timer0_compareMatchAActionListener = &playRecordedSound;
   Timer0_enableCTC(TIMER0_DIV_1);
   Timer0_stop();
   #if (RECORDED_SAMPLING_RATE == 6000)
      OCR0A = OCR0x_6000;
   #elif (RECORDED_SAMPLING_RATE == 8000)
      OCR0A = OCR0x_8000;
   #endif 
   OCR1B = 0;
   tableCount = 0;
   openSpeakerDrain();
   Timer1_enableFastPwm();  //timer1 starts automatically
   Timer0_start();
   //sei();
   while(tableCount < RECORDED_SIZE);
   closeSpeakerDrain();
}