Atmega32 setting timer1

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

Hallo,
I have a strange situation here with my atmega32.
I need to set timer1 to output clocks on both pins OC1A and OC1B. I need OC1A to be toggled in normal mode, while OC1B should be toggled by CTC mode.
On OC1B I need no interrupt. It just generates clock signal and makes no change on the program.
The two clock signals must have different frequencies so I have to use OCR1A and OCR1B registers.

This is how I set the timer:

TCCR1A=0b00010000;
TCCR1B=0b00001001;

OCR1AH=var;
OCR1AL=var;

//the fastest freq needed on OC1B
OCR1BH=0x00;
OCR1BL=0x00;

TIMSK=0b00010000;

Actually I get both clocks but the rest of my program breaks down. With my debugger I can see some variables taking wrong values so I thought an interrupt was needed for comparing to OCR1B. I placed the ISR but no change.
I found out that the clock on OC1B varies by changing the values in OCR1A.

How can I make TCNT1 compare to OCR1B?

Thank you.

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

Quote:

OCR1AH=var;
OCR1AL=var;

This certainly makes no sense.

Neither does the two clocks as you described them. If one is toggled by CTC, then that needs to be the longer period. But you are setting OCR1B to 0.

Is there an ISR for the interrupt?

I guess post the smallest complete program that demonstrates the situation, tell us how fast your AVR is running, tell us the frequencies desired.

If you need clk/2 output then you probably need a separate timer for that. If you use Mega324 instead you could use CLKO pin to output clk frequency.

Lee

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

Quote:
I need OC1A to be toggled in normal mode, while OC1B should be toggled by CTC mode. .. The two clock signals must have different frequencies

You can't do that with one timer. Normal mode and CTC mode are mutually exclusive. You can create two outputs with the same frequency but different duty cycles. There is no way to create two different frequencies simultaneously with one timer.

Regards,
Steve A.

The Board helps those that help themselves.

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

Hi,
I use Atmega32 on 16Mhz.

The frequency I want by the CTC mode(the 'B' part of timer1) is 8Mhz (16 will be better but I can't make the MCU clock that fast :) ).

The normal mode (the 'A' part of timer1) generates pulses on every 5us.

I have an interrupt for the normal mode (pin OC1A) of operation and it is working fine.

As for the CTC (OC1B pin) mode I prefer if there is no interrupt routine. As I said earlier the CTC mode generates a clock signal nothing else.

The code I posted here is working on my board. You need an oscilloscope to see how it behaves. I will give you some explanations on what the code does:

I use Timer1, OC1A, to generate pulses and count them. When I reach 64 pulses I slow the timer down for a while and then I generate new pulses. I have some more pulses on the other pins of PORTD. The comments in this code are not in english :)

//Gubi se sinhronizaciq poradi izbarzvane na slave ustrojstvoto. Poradi tova postavqme edin delay(7) v ISR za vanshni prekasvaniq, 
//koito da zabavq svale ustroistvoto. Raboti do momenta v koito podam danni ot komputara. Sled tova slave zapo4va da zakasnqva.

#include "avr/io.h"
#include "avr/interrupt.h"
#include "avr/eeprom.h"

#define SLOW_CLK_L 0x40
#define SLOW_CLK_H 0x06

#define CLOCK_H 0x00
#define CLOCK_L 0x01//zadavam clock kam hc595

//RAZMERI NA SAOB6TENIETO KOETO SAHRANQVAM V MCU
#define ROW 0x0f//saob6tenie ot 15 reda
#define COL 0x15//21 bukvi na red

#define TAB_X 64//dalginata na tabloto po X
#define TAB_Y 4//viso4inata na tabloto po Y

unsigned char display_1[TAB_Y][TAB_X];//tova sa dvata masiva koito sadargat obraza na ekrana
unsigned char display_2[TAB_Y][TAB_X];//raznerat e 7x65

unsigned char disp=0x01;//kogato disp=1, zna4i display_1 e popalnen

unsigned char load_done=0x00;//promenliva za sledene za zavar6vane na izpisvaneto na display_x

//nastroika na timerA za 1ms - vreme za stati4no sastoqnie na obhovdaneto po y
void timer_1500us(void)
{	

	OCR1AH=SLOW_CLK_H;//zadanie za 1.5ms
	OCR1AL=SLOW_CLK_L;

	TCNT1H=0x00;
	TCNT1L=0x00;
}

//nastojka na timerA za nai-visoka skorost - vreme na shift clocka kam 595
void timer_f(void)
{
	OCR1AH=CLOCK_H;//zadanie za 1.5ms
	OCR1AL=CLOCK_L;

	TCNT1H=0x00;
	TCNT1L=0x00;
}  


void init(void)
{
	//izhodni portove
	DDRB=0xff;
	PORTB=0x00;

	//nastoivam OC1B kato izhod. Na nego 6te generiram clock kam ostanalite platki
	DDRD =0xff;
	PORTD=0x00;

	DDRA=0xff;
	PORTA=0x00;

	//DDRC=0xff;
	//PORTC=0x00;

	//nastoika na timer1b kato sistemen clock
	TCCR1A=0b00010000;
	TCCR1B=0b00001001;

	OCR1BH=0x00;
	OCR1BL=0x00;

	TIMSK=0b00010000;

	sei();
}


//funkciq za obhogdane na portovete na kontrolera za da izpisvame informaciqta
void disp_row(void)
{
	static unsigned char pin=128;//poso4va koi e pina na kontrolera ot dadeniq port
	/*
	Ozna4eniq za portovete: portb = 0x00
							porta = 0x01
							portc = 0x02
							portd = 0x03
	Tova e i posledovatelnostta na obhogdane na protovete B -> A -> C -> D -> B -> A -> C ->D -> ....
	*/
	static unsigned char port=0x00;

	unsigned char temp=0x00;
	//proverqvam do koi pin sam dostignal
	switch(port)
	{
		//PORTB
		case 0x00:
			if(pin>=0x02)
			{
				//1. Premahvam predhodnoto sastoqnie na portb i nuliram o
				//nulirane na portovete
				temp = PORTA & 0x00;
				PORTA = temp | 0x00;
				temp = PORTB & 0x00;
				PORTB = temp | 0x00;
				/*temp = PORTC & 0b00000011;
				PORTC= temp | 0b00000011;
				temp = PORTD & 0b00111111; 
				PORTD = temp | 0b00111111;*/

				//setvane na pin ot porta
				//2. Zapisvam novata stoinost
				temp = PORTB & (~pin);
				PORTB = temp | pin;

				pin=pin/2;
			}
			else
			{
				//1. Premahvam predhodnoto sastoqnie na portb
				//nulirane na portovete
				temp = PORTA & 0x00;
				PORTA = temp | 0x00;
				temp = PORTB & 0x00;
				PORTB = temp | 0x00;
				/*temp = PORTC & 0b00000011;
				PORTC= temp | 0b00000011;
				temp = PORTD & 0b00111111; 
				PORTD = temp | 0b00111111;*/

				//setvane na pin ot porta
				//2.Zapisvam novta stojnost
				temp = PORTB & (~pin);
				PORTB = temp | pin;

				pin=128;
				port=0x01;
			}
			break;

		//PORTA
		case 0x01:
			if(pin>=0x02)
			{
				//1. Premahvam predhodnoto sastoqnie na porta
				//nulirane na portovete
				temp = PORTA & 0x00;
				PORTA = temp | 0x00;
				temp = PORTB & 0x00;
				PORTB = temp | 0x00;
				/*temp = PORTC & 0b00000011;
				PORTC= temp | 0b00000011;
				temp = PORTD & 0b00111111; 
				PORTD = temp | 0b00111111;*/

				//setvane na pin ot porta
				//2. Zapisvam novata stoinost
				temp = PORTA & (~pin);
				PORTA = temp | pin;

				pin=pin/2;
			}
			else
			{
				//1. Premahvam predhodnoto sastoqnie na porta
				//nulirane na portovete
				temp = PORTA & 0x00;
				PORTA = temp | 0x00;
				temp = PORTB & 0x00;
				PORTB = temp | 0x00;
				/*temp = PORTC & 0b00000011;
				PORTC= temp | 0b00000011;
				temp = PORTD & 0b00111111; 
				PORTD = temp | 0b00111111;*/

				//setvane na pin ot porta
				//2. Zapisvam novata stoinost
				temp = PORTA & (~pin);
				PORTA = temp | pin;

				//tova e koda za upravlenie na sledva6tite podtove c i d.
				pin=128;
				port=0x02;
			}
			break;
	
		//PORTC
		case 0x02:
			if(pin>=0x08)
			{
				//nulirane na portovete
				temp = PORTA & 0x00;
				PORTA = temp | 0x00;
				temp = PORTB & 0x00;
				PORTB = temp | 0x00;
				/*temp = PORTC & 0b00000011;
				PORTC= temp | 0b00000011;
				temp = PORTD & 0b00111111; 
				PORTD = temp | 0b00111111;*/

				//setvane na pin ot porta
				/*temp = PORTC & (~pin);
				PORTC = temp | pin;*/

				pin=pin/2;
			}
			else
			{
				//nulirane na portovete
				temp = PORTA & 0x00;
				PORTA = temp | 0x00;
				temp = PORTB & 0x00;
				PORTB = temp | 0x00;
				/*temp = PORTC & 0b00000011;
				PORTC= temp | 0b00000011;
				temp = PORTD & 0b00111111; 
				PORTD = temp | 0b00111111;*/

				//setvane na pin ot porta
				/*temp = PORTC & (~pin);
				PORTC = temp | pin;*/

				pin=128;
				port=0x03;
			}
			break;

		//PORTD
		case 0x03:
			if(pin>=128)
			{
				//nulirane na portovete
				temp = PORTA & 0x00;
				PORTA = temp | 0x00;
				temp = PORTB & 0x00;
				PORTB = temp | 0x00;
				/*temp = PORTC & 0b00000011;
				PORTC= temp | 0b00000011;
				temp = PORTD & 0b00111111; 
				PORTD = temp | 0b00111111;*/

				//setvane na pin ot porta
				/*temp = PORTD & (~pin);
				PORTD = temp | pin;*/

				pin=pin/2;
			}
			else
			{
				//nulirane na portovete
				temp = PORTA & 0x00;
				PORTA = temp | 0x00;
				temp = PORTB & 0x00;
				PORTB = temp | 0x00;
				/*temp = PORTC & 0b00000011;
				PORTC= temp | 0b00000011;
				temp = PORTD & 0b00111111; 
				PORTD = temp | 0b00111111;*/

				//setvane na pin ot porta
				/*temp = PORTD & (~pin);
				PORTD = temp | pin;*/

				pin=128;
				port=0x00;
			}
			break;
	}	
}

//funkciq za generirane na st_cp clock(store clock)
void store_data(void)
{
	unsigned char temp=0x00;

	//podavam clock
	temp = PORTD & 0b11111011;
	PORTD = temp | 0b00000100;

	disp_row();

	//maham clock
	temp = PORTD & 0b11111011;
	PORTD = temp | 0b00000000;

}

//funkciq za generiane na sh_cp clock(shift clock) 
void shift_data(unsigned char in)
{
	//tazi promenliva sledi kolko impulsa sa bili podadeni na sh_cp clocka
	unsigned char temp=0x00;

	//maham clocka za izpisvane na saob6tebnieto
	//temp = PORTD & 0b11111011;
	//PORTD = temp | 0b00000000;

	if(in==0)
	{
		//podavam danna 0 na seriiniq vhod
		temp = PORTD & 0b11110111;
		PORTD = temp | 0b00000000;

		//vdigam CLOCK
		temp = PORTD & 0b11011111;
		PORTD = temp | 0b00100000;

		//maham CLOCK
		temp = PORTD & 0b11011111;
		PORTD = temp | 0b00000000;

		//maham dannata ot seriiniq kanal
		temp = PORTD & 0b11110111;
		PORTD = temp | 0b00000000;	
	}
	else
	{
		//podavam danna 1 na seriiniq vhod
		temp = PORTD & 0b11110111;
		PORTD = temp | 0b00001000;

		//vdigam CLOCK
		temp = PORTD & 0b11011111;
		PORTD = temp | 0b00100000;

		//vdigam CLOCK
		temp = PORTD & 0b11011111;
		PORTD = temp | 0b00000000;

		//maham dannata ot seriiniq kanal
		temp = PORTD & 0b11110111;
		PORTD = temp | 0b00000000;
	}
}

//prekasvane za sustem clock
//ISR(TIMER1_COMPB_vect)
//{
//	cli();
//	sei();
//}

//za MASTER timer interrupt
ISR(TIMER1_COMPA_vect)
{
	//premnliva za maskirane na nenugnite elemnti na vseki red ot display_x
	static unsigned char l_mask=128;
	//promenlivi za obhogdane na masiva s dannite po redove i po koloni
	static int row=0x00;
	static int col=(TAB_X-1);
	static unsigned char del=0x00;
	static unsigned char f_clock_set=0x00;
	unsigned char temp=0x00;
	unsigned char stat_reg=0x00;

	stat_reg=SREG;
	cli();

	//proverqvam dali sa e setnat gerimat s barziq clock
	if(f_clock_set==0x00)
	{
		timer_f();
		f_clock_set=0x01;
		del=0x00;
	}

	//1. Proverqvam koi e popalneniqt v momenta masiv display_x
	if(disp==1)
	{
		//2. Proverqvam dali sam stignal do posledniq red
		if(row<=(TAB_Y-2))
		{
			//3. Proverqvam dali sam stignal do poslednite bitove ot dadeniq red
			if(l_mask>=2)
			{
				//4. vzimam sadarganieto na reda
				if(col>=1)
				{
					//5. polu4avam sadarganieto na bitovete v kolonata
					temp = display_1[row][col] & l_mask;
					shift_data(temp);
					col--;
				}
				else
				{
					temp = display_1[row][col] & l_mask;
					shift_data(temp);
					col=(TAB_X-1);
					l_mask=l_mask/2;
					
					f_clock_set=0x00;
					timer_1500us();
					store_data();
				}
			}
			else
			{
				//4. vzimam sadarganieto na reda
				if(col>=1)
				{
					//5. polu4avam sadarganieto na bitovete v kolonata
					temp = display_1[row][col] & l_mask;
					shift_data(temp);
					col--;
				}
				else
				{
					temp = display_1[row][col] & l_mask;
					shift_data(temp);
					col=(TAB_X-1);
					l_mask=128;
					row++;
	
					f_clock_set=0x00;
					timer_1500us();
					store_data();
				}	
			}
		}
		else
		{
			//3. Proverqvam dali sam stignal do poslednite bitove ot dadeniq red
			if(l_mask>=2)
			{
				//4. vzimam sadarganieto na reda
				if(col>=1)
				{
					//5. polu4avam sadarganieto na bitovete v kolonata
					temp = display_1[row][col] & l_mask;
					shift_data(temp);
					col--;
				}
				else
				{
					temp = display_1[row][col] & l_mask;
					shift_data(temp);
					col=(TAB_X-1);
					l_mask=l_mask/2;

					f_clock_set=0x00;
					timer_1500us();
					store_data();
				}
			}
			else
			{
				//4. vzimam sadarganieto na reda
				if(col>=1)
				{
					//5. polu4avam sadarganieto na bitovete v kolonata
					temp = display_1[row][col] & l_mask;
					shift_data(temp);
					col--;
				}
				else
				{
					temp = display_1[row][col] & l_mask;
					shift_data(temp);
					col=(TAB_X-1);
					l_mask=128;
					row=0;
					//tuk setvam promenlivata koqto pokazva 4e sam izpisal ekrana
					load_done=0x01;

					f_clock_set=0x00;
					timer_1500us();
					store_data();

					disp=2;
				}	
			}			
		}
	}
	else
	{
		//2. Proverqvam dali sam stignal do posledniq red
		if(row<=(TAB_Y-2))
		{
			//3. Proverqvam dali sam stignal do poslednite bitove ot dadeniq red
			if(l_mask>=2)
			{
				//4. vzimam sadarganieto na reda
				if(col>=1)
				{
					//5. polu4avam sadarganieto na bitovete v kolonata
					temp = display_2[row][col] & l_mask;
					shift_data(temp);
					col--;
				}
				else
				{
					temp = display_2[row][col] & l_mask;
					shift_data(temp);
					col=(TAB_X-1);
					l_mask=l_mask/2;
					
					f_clock_set=0x00;
					timer_1500us();
					store_data();
				}
			}
			else
			{
				//4. vzimam sadarganieto na reda
				if(col>=1)
				{
					//5. polu4avam sadarganieto na bitovete v kolonata
					temp = display_2[row][col] & l_mask;
					shift_data(temp);
					col--;
				}
				else
				{
					temp = display_2[row][col] & l_mask;
					shift_data(temp);
					col=(TAB_X-1);
					l_mask=128;
					row++;
	
					f_clock_set=0x00;
					timer_1500us();
					store_data();
				}	
			}
		}
		else
		{
			//3. Proverqvam dali sam stignal do poslednite bitove ot dadeniq red
			if(l_mask>=2)
			{
				//4. vzimam sadarganieto na reda
				if(col>=1)
				{
					//5. polu4avam sadarganieto na bitovete v kolonata
					temp = display_2[row][col] & l_mask;
					shift_data(temp);
					col--;
				}
				else
				{
					temp = display_2[row][col] & l_mask;
					shift_data(temp);
					col=(TAB_X-1);
					l_mask=l_mask/2;

					f_clock_set=0x00;
					timer_1500us();
					store_data();
				}
			}
			else
			{
				//4. vzimam sadarganieto na reda
				if(col>=1)
				{
					//5. polu4avam sadarganieto na bitovete v kolonata
					temp = display_2[row][col] & l_mask;
					shift_data(temp);
					col--;
				}
				else
				{
					temp = display_2[row][col] & l_mask;
					shift_data(temp);
					col=(TAB_X-1);
					l_mask=128;
					row=0;
					//tuk setvam promenlivata koqto pokazva 4e sam izpisal ekrana
					load_done=0x01;

					f_clock_set=0x00;
					timer_1500us();
					store_data();

					disp=1;
				}	
			}			
		}
	}	
	//nuliram timera
	SREG=stat_reg;
	sei();
	TCNT1H=0x00;
	TCNT1L=0x00;
}

void main(void)
{
	init();
	timer_f();	

	while(1)
	{}
}

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

Quote:

There is no way to create two different frequencies simultaneously with one timer.

I beg to differ. ;)
https://www.avrfreaks.net/index.p...

But to use that method the frequencies have to be "comfortably slow" so that interrupts can be handled for both compare matches. Say, up to a few kHz. Not workable for clk/n output for small n.

Lee

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

Quote:

The frequency I want by the CTC mode(the 'B' part of timer1) is 8Mhz (16 will be better but I can't make the MCU clock that fast Smile ).

The normal mode (the 'A' part of timer1) generates pulses on every 5us.


1) Use a Mega324, running at 16MHz.
2) Output 16MHz on CLKO.
3) Output variable frequency using a timer.

or
A) Use one timer of Mega32 in CTC mode with compare match of 0 to output clk/2.
B) Use a second timer for the variable frequency.

In my opinion you have much too much code in the ISR. I suppose it will work if you calculate the worst-case time for the ISR and the desired frequency is slow enough. Certainly not 5us. I'd speculate that your code is 100us or more, giving a max frequency around 10kHz.

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

So you suggest I should give up my approach. Fine I will use another timer.

I will go back to a question I never mentioned clearly:
Can I make the OCx pins toggle without using an interrupt routine? What can happen if I simply do not set the OCIEx pin in the TIMSK?

And another question: how can I make timer1 compare with the content from OCR1B registers? Is it simply setting COMs from TCCR1A?

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

Obviously these were stupid questions. After using the RTFM approach :) I found out that the CTC mode works only with OCR1A register.
As for the interrupts I think that wave generation should work fine without interrupts.

Thank you again.

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

Quote:
Can I make the OCx pins toggle without using an interrupt routine?

Yes, by setting the COMxx bits. But you will get the same frequency on both pins. If this is in normal mode (or CTC mode with OCRB set to lower than OCRA), then they will differ only in phase. You can use PWM mode to get the do be the same phase, but differ in duty cycle.

Quote:
And another question: how can I make timer1 compare with the content from OCR1B registers? Is it simply setting COMs from TCCR1A?

OCR1B is always comparing, you need to do nothing. However to do anything with that compare you either need to set the COMxx bits (to toggle the output), set the interrupt enable (for which you need to provide an ISR), or poll (and then reset) the interrupt flag manually.

Regards,
Steve A.

The Board helps those that help themselves.