How to use all 4 pwm channel on attiny84

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

hello, i want to use all 4 pwm channels of a attiny84. how can i do that? is there any example code for doing that?

 

here is my code i working on, but it did't work

#define F_CPU 8000000UL 

#include <avr/io.h>
#include <util/delay.h>

uint16_t readADC(uint8_t channel) {
	uint16_t ergebnis = 0;
	uint8_t i;
	
	
	ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1);
	
	
	ADMUX = channel | (0 << REFS1) | (0 << REFS0);
	
	
	ADCSRA |= (1 << ADSC);
	while (ADCSRA & (1 << ADSC));
	
	
	for (i=0; i<3; i++){
	
		ADCSRA |= (1 << ADSC);
		while (ADCSRA & (1 << ADSC));
		
		ergebnis += ADCW;
	}
	

	ADCSRA &= ~(1 << ADEN);
	
	ergebnis /= 3;
	
	return ergebnis;
}

void pwm_init(){
	

	DDRA |= (1 << PA6) | (1 << PA7) | (1 << PA5);
	DDRB |= (1 << PB2);
	

	TCCR0A |= (0 << COM0A1) | 
			  (0 << COM0A0) |
			  (0 << COM0B1) |
			  (0 << COM0B0) |
			  (0 << WGM02)  | 
			  (0 << WGM01)  | 
			  (1 << WGM00);
	
	 
	TCCR0B |= (0 << WGM02)|
			  (0 << FOC0A)|
			  (0 << FOC0B)|
			  (0 << CS02) | 
			  (0 << CS01) | 
			  (1 << CS00);
	

	TCCR1A |= (0 << COM1A1) | 
			  (0 << COM1A0) |
			  (0 << COM1B1) |
			  (0 << COM1B0) |
			  (1 << WGM11)  |   
			  (1 << WGM10);
	

	TCCR1B |= (0 << WGM13)|
			  (1 << WGM12)|
			  (0 << CS12) | 
			  (0 << CS11) | 
			  (1 << CS10);
}

void pwm_OC0A(int adc){
	TCCR0A &= ~(1 << COM0B1);
	TCCR0A |= (1 << COM0A1);
	
	OCR0A = adc;
}

void pwm_OC0B(int adc){
	TCCR0A &= ~(1 << COM0A1);
	TCCR0A |= (1 << COM0B1);
	
	OCR0B = adc;
}

void pwm_OC1A(int adc){
	TCCR1A &= ~(1 << COM1B1);
	TCCR1A |= (1 << COM1A1);
	
	OCR1A = adc;
}

void pwm_OC1B(int adc){
	TCCR1A &= ~ (1 << COM1A1);
	TCCR1A |= (1 << COM1B1);
	
	OCR1B = adc;
}

int main(void){		
	
	pwm_init();
	
	
	pwm_OC0A(255);
	pwm_OC0B(255);
	pwm_OC1A(255);
	pwm_OC1B(255);
	_delay_ms(2000);		
	
    while(1){
			
	
		pwm_OC0A(readADC(2) / 4);//
		pwm_OC0B(readADC(1) / 4);//
		pwm_OC1A(readADC(0) / 4);//
		pwm_OC1B(readADC(3) / 4);//
	}
}

tank you for your help!

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

SMoTH wrote:
but it did't work
   Does not tell us much!

 

What were expecting to happen, what did happen?

Please post a schematic or a picture of your setup.

 

Jim

 

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

the program reads the adc values of PA0 (ADC0), PA1 (ADC1), PA2 (ADC2) and PA3 (ADC3) and set the pwm outputs according to the adc values. the problem is, that the pwm outputs don't work. the plan is to build a simple fan controller.

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

How about putting some comments in your code so that we don't have to guess at what you are trying to do?

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

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

SMoTH wrote:
the problem is, that the pwm outputs don't work.

You still haven't given us much information.  Not nearly enough to address a possible PWM issue.

 

How are you determining your conclusion?  Are you using a 'scope?

 

What voltage are you seeing, right on the AVR pins, for the four ADC input channels?

 

I'll have some comments on the code later; for now, it doesn't look too bad.  For example, no need (and you might get better results) continually turning the ADC off and on.

 

I'd probably break down the situation.  Take one channel.  Change the PWM duty with e.g. a for() loop periodically; cycle through all the output values.  If that doesn't give good results then adding more channels and using ADC values in the transfer function is not going to help.

 

 

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

Nothing jumps out at me as being wrong, so I would start with some basics.

Replace:

pwm_OC0A(255);
	pwm_OC0B(255);
	pwm_OC1A(255);
	pwm_OC1B(255);
	_delay_ms(2000);	

with:

    pwm_OC0A(80);
	pwm_OC0B(120);
	pwm_OC1A(180);
	pwm_OC1B(240);
	while(1);	

in the setup portion and see if you can get some fixed pwm outputs. (Note pwm output pins need to be set to outputs)

Once that is working, then add one ADC channel read and see if you can get that to vary with the pot.

By breaking it down in steps you can verify each section and work out what is wrong.

 

Jim

 

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

on the pwm pins i get allways a 1 signal (5V, i use a scope), on the adc inputs i can measure values between 0V-5V dependigt on the pot. i'll try to test all channels single and do it like you described it

 

which pwm mode should i use?

 

Attachment(s): 

Last Edited: Tue. Dec 26, 2017 - 03:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

SMoTH wrote:
i use a scope
  Excellent tool for that!

By using a starting value of less than 255, you can see if the pwm timer init is working as expected, as well as if the passed value is setting a different pwm output.

 

Now you can test your ADC function to see if it changes as expected by sending one channel to the pwm function........

 

Let us know when you get it working and what you find for the next reader of this post.

 

Jim

 

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

SMoTH wrote:
TCCR0A |= (0 << COM0A1) | (0 << COM0A0) | (0 << COM0B1) | (0 << COM0B0) | (0 << WGM02) | (0 << WGM01) | (1 << WGM00);

Mode 1?  Tell you what:  Let's start with Mode 3, Fast PWM with fixed TOP of 0xff.

 

Let's see:  Timer1 is in Mode 7 -- 10-bit Fast PWM.  Do you really want 8-bit Fast PWM, Mode 5?

 

What is with all the |= all over your code?  If it were me, I'd have a dozen or so lines of timer init code, once, at startup, with straight assignment and no RMW.  Certainly, there is no need to fuss with COM bits every pass.  And forces us to decode any possible action those might be doing.

 

Let's see what CodeVision Wizard has to say...

 

 

 

 

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

With PB2 PA5 PA6 PA7 made outputs, and an arbitrary 8MHz AVR clock and 8MHz timer clock and 0xff initial compare match values:

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 8000.000 kHz
// Mode: Fast PWM top=0xFF
// OC0A output: Non-Inverted PWM
// OC0B output: Non-Inverted PWM
// Timer Period: 0.032 ms
// Output Pulse(s):
// OC0A Period: 0.032 ms Width: 0.032 ms
// OC0B Period: 0.032 ms Width: 0.032 ms
TCCR0A=(1<<COM0A1) | (0<<COM0A0) | (1<<COM0B1) | (0<<COM0B0) | (1<<WGM01) | (1<<WGM00);
TCCR0B=(0<<WGM02) | (0<<CS02) | (0<<CS01) | (1<<CS00);
TCNT0=0x00;
OCR0A=0xFF;
OCR0B=0xFF;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 8000.000 kHz
// Mode: Fast PWM top=0x00FF
// OC1A output: Non-Inverted PWM
// OC1B output: Non-Inverted PWM
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer Period: 0.032 ms
// Output Pulse(s):
// OC1A Period: 0.032 ms Width: 0.032 ms
// OC1B Period: 0.032 ms Width: 0.032 ms
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=(1<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (0<<COM1B0) | (0<<WGM11) | (1<<WGM10);
TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (1<<WGM12) | (0<<CS12) | (0<<CS11) | (1<<CS10);
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0xFF;
OCR1BH=0x00;
OCR1BL=0xFF;

Note that e.g. COM1A1 and COM1A0 determine action for the A channel of Timer1.  But your code has A's and B's intermixed.  May well be your underlying problem.

 

The code I posted will indeed have solid high on the output pins.


while (1)
{
    for uint8_t looper = 0xff; ; looper--)
    {
        OCR0A = looper;
        OCR0B = looper;
        OCR1A = looper; // or OCR1AL
        OCR1B = looper; // or OCR1BL
        _delay_ms (1000);
    }
}

Yes, the above will take four minutes to completely count down and wrap.  But as you have a 'scope, every second you should see the duty cycle lower slightly.  Check both the timer0 and timer1 pin(s).

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

SMoTH wrote:

the program reads the adc values of PA0 (ADC0), PA1 (ADC1), PA2 (ADC2) and PA3 (ADC3) and set the pwm outputs according to the adc values. the problem is, that the pwm outputs don't work. the plan is to build a simple fan controller.

 

 ARE YOU FROGET TO SET PWM PINS AS OUTPUT?

 

Have you tried inverting PWM Mode? Value of OCR reg is matched with TCNT reg while downcount (eg. TCNT0=0xFF down to 0x00) and you should use 

OCR0 = adc_read (0);  // for adc0 

and use inverting mode.

 

IMPORTANT: DONT FORGRT TO SET ALL PWM PINs AS OUTPUT.

Last Edited: Thu. Dec 28, 2017 - 04:54 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

and if you are generating square wave at output pin (voltage controlled oscillator, VCO) whose frquency is changed as adc voltage changes then use 

ICRx=adc_read(0); //adc0
OCRxB=ICRx>>1; // 50% duty cycle independant of frequency  x=1,2...

Hope this will help you. try to put image of your output so that it help other to solve your problem.

Last Edited: Thu. Dec 28, 2017 - 04:47 AM