Problem with PWM on Attiny261

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

Hello some clever person!

I am trying to get the pwm working on an Attiny261. I know there's a lot of posts in here about pwm (I have gone through a lot of them but I still can't get it to work). The Attiny261 is pretty new compared to the Avr's used in the examples I have read so some of the registers seems to have different parameters/names.

Well I still guess it is something simple I have overlooked, hope someone can spot the error. I have stripped down the code so it only should include the pwm-related things.

; PWM test 
.nolist 
.include "tn261def.inc"
.list 
.def tmp1 = r16  ; tmp1 register 
.def tmp2 = r3
.def tri = r5 ; Timer interruptregister 

;***** Initialization 
.cseg  ; codesegment
.org 0
rjmp RESET

reti ;	 INT0addr  	  ;  = 0x0001	 External Interrupt 0        	                
reti ;	 PCIaddr	  	  ;  = 0x0002	 Pin Change Interrupt         	                
rjmp T1intCM ;	OC1Aaddr  	  ;  = 0x0003	 Timer/Counter1 Compare Match 1A	           	                
reti ;	 OC1Baddr  	  ;  = 0x0004	 Timer/Counter1 Compare Match 1B       	                
reti ;	 OVF1addr  	  ;  = 0x0005	 Timer/Counter1 Overflow	           	                
reti  ;	 OVF0addr  	  ;  = 0x0006	 Timer/Counter0 Overflow
reti ;	 USI_STARTaddr ; = 0x0007	; USI Start
reti ;	 USI_OVFaddr	  ; = 0x0008	 USI Overflow                  
reti ;	 ERDYaddr    	; = 0x0009	 EEPROM Ready                   
reti ;	 ACIaddr	    	; = 0x000a	 Analog Comparator                       
reti ; 	 ADCCaddr       ; = 0x000b	 ADC Conversion Complete
reti ;	 WDTaddr			; = 0x000c	 	Watchdog Time-Out
reti ;	 INT1addr 		 ;	= 0x000d	 External Interrupt 1                        
reti ;	 OC0Aaddr		;	= 0x000e	 Timer/Counter0 Compare Match A                       
reti ;     OC0Baddr 		;	= 0x000f	 Timer/Counter0 Compare Match B                        
reti ;	 ICP0addr 		 ;	= 0x0010	 ADC Conversion Complete                       
reti ;	 OC1Daddr 		 ;	= 0x0011	 Timer/Counter1 Compare Match D
reti ;	 FAULT_PROTaddr 	; = 0x0012  Timer/Counter1 Fault Protection
    
.equ CPU_FREQUENCY = 16000000
                            
	
T1intCM:
	
	push tmp1
	in tri, SREG
	ldi tmp1, 0x32
	out OCR1A, tmp1
	pop tmp1
	out SREG, tri
	reti
	

RESET: 
	ldi tmp1, 0b10000001
	out DDRA,tmp1   ; Set PORTA
	ldi	tmp1, RAMEND ;Initiate Stackpointer (for subroutines/interupts)
	out	SPL,tmp1  ;StackPointerLow (this MCU doesn't have an SPH)
	; ----------- PWM setting ----------------

	ldi tmp1, (1<<PLLE) ; enable PLL
	out PLLCSR, tmp1
	rcall DELAY         ; wait >100ms
INI_POLL_PLOCK:
	in tmp1, PLLCSR
	andi tmp1, 0x01
	cpi tmp1, 0x01
	brne INI_POLL_PLOCK  ; poll PLOCK until set
	ldi tmp1, (1<<PCKE)  
	out PLLCSR, tmp1      ; set PCKE in PLLCSR register
	ldi tmp1, (1<<PWM1A) | (1<<COM1A1)
	out TCCR1A, tmp1
	ldi tmp1, (1<<OCIE1A)
	out TIMSK, tmp1
	ldi tmp1, 0x32
	out TCNT1, tmp1
	ldi tmp1, 0x0A
	out OCR1A, tmp1 
	ldi tmp1, 0b00000001
	out DDRB, tmp1  ; set PORTB
	ldi tmp1, (1<<CS10) ;start timer, no prescaler
	out TCCR1B, tmp1
	sei ;global interrupt enable
	
MAIN:

	jmp MAIN


DELAY:
	ser tmp1
	mov tmp2, tmp1
DELAY_LOOP:
	dec  tmp1	
	brne DELAY_LOOP 
	dec  tmp2	
	brne DELAY_LOOP	
	ret

I am using: STK-500V2 programmer, AVRA 1.3.0, MAC OSX, avrfuse.
(Datasheet:
http://www.atmel.com/dyn/resources/prod_documents/doc2588.pdf)

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

Further explanation:

I am measuring at the OC1A (PB0) with a Voltmeter. If I insert the line

cbi PORTA, 7

in main, and

sbi PORTA, 7

in the timer ISR, I get a dimmed ctrl_led, which I guess indicates that I am getting in the ISR periodically..

But it doesn't seem to have any effect on the OC1A pin.

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

Quote:

reti ; INT0addr ; = 0x0001 External Interrupt 0

When you build your vector table and are commenting the crap out of it to ensure it is orderly, why not just insert an .ORG for each vector? It is harder to screw up then.

It is hard to measure PWM with a voltmeter. You may or may not get an average reading, based on the period, the duty cycle, and the voltmeter.

I suppose you could run very slow AVR clock and very long PWM periods (in the seconds) and measure that. At that point, an LED would work (perhaps better).

You might want to check out the worked PWM examples in ASM at www.avrbeginners.net . But that may not apply to the enhanced PWM beastie you are working with.

From the CodeVision Wizard, it appears you either need to select a mode with OCR1C as TOP, or select PWM6. digging... A quick datasheet read indicates OCR1C is involved; search the datasheet for "ocr1c".

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

Maybe consider a "soundcard scope" is you don't have access to a real oscilloscope. See the sticky "Freeware" thread in Off Topic forum for some recommended free software. (be sure to build the resistor based potential divider that ensures you don't apply more than 1.0V to your soundcard's line input though - it's also shown in that thread)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
  ldi tmp1, (1<<PCKE)  
   out PLLCSR, tmp1      ; set PCKE in PLLCSR register 

Doesn't this turn off the PLL that you so diligently turned on?

   ldi tmp1, 0x32 
   out TCNT1, tmp1

Why are you doing this? If you think that it will set the TOP value of the timer, you are wrong.

Regards,
Steve A.

The Board helps those that help themselves.

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

Koshchi wrote:

  ldi tmp1, (1<<PCKE)  
   out PLLCSR, tmp1      ; set PCKE in PLLCSR register 

Doesn't this turn off the PLL that you so diligently turned on?


I don't suppose so(?) I followed this from the datasheet page 93:

12.3.1.2, 12.4
Counter Unit
To change Timer/Counter1 to the asynchronous mode follow the procedure below:

1. Enable PLL.
2. Wait 100 μs for PLL to stabilize.
3. Poll the PLOCK bit until it is set.
4. Set the PCKE bit in the PLLCSR register which enables the asynchronous mode.

Quote:

   ldi tmp1, 0x32 
   out TCNT1, tmp1

Why are you doing this? If you think that it will set the TOP value of the timer, you are wrong.

Ok, thanks for the information! It was something I saw in a thread then I tried it...

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

theusch wrote:

When you build your vector table and are commenting the crap out of it to ensure it is orderly, why not just insert an .ORG for each vector? It is harder to screw up then.

Yes, I know it is good style, have done that now..

Quote:

It is hard to measure PWM with a voltmeter. You may or may not get an average reading, based on the period, the duty cycle, and the voltmeter.

I suppose you could run very slow AVR clock and very long PWM periods (in the seconds) and measure that. At that point, an LED would work (perhaps better).

You might want to check out the worked PWM examples in ASM at www.avrbeginners.net . But that may not apply to the enhanced PWM beastie you are working with.

From the CodeVision Wizard, it appears you either need to select a mode with OCR1C as TOP, or select PWM6. digging... A quick datasheet read indicates OCR1C is involved; search the datasheet for "ocr1c".

Lee

Ok, have set the OCR1C. I have two oscilloscopes. One USB-based that goes only to 2Mhz, and an old, pretty unstable analog one that runs 60Mhz. It actually managed now to get a waveform at the analog oscilloscope (jpg picture). I guess that was an answer to Clawson too!

I still have a problem though. As I understand the OCR1A-value then should control the amplitude? (which it doesn't).

Attachment(s): 

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

Quote:

I still have a problem though. As I understand the OCR1A-value then should control the amplitude? (which it doesn't).

No it varies the duty cycle. What you should be seeing is a full swing Gnd-Vcc square wave at the PWM frequency with the high:low ratio varied by the OCR1A.

Only after you have passed this through an R-C filter (as a minimum) can you hope to see a varying voltage.

See AVR335 which has some pretty pictures showing how the varying duty cycle leads to a varying analogue wave. (in the case of AVR335 it's talking about audio but it could just as easily be a DC motor voltage or the brightness of an LED - except that those things don't generally vary as often as a sound wave which probably uses a different OCR1A value on every cycle of the wave)

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

Quote:
4. Set the PCKE bit in the PLLCSR register which enables the asynchronous mode.
But that line also clears every other bit in that register. To set that one bit only you need:

in tmp1, PLLCSR
ori tmp1, (1<<PCKE)
out PLLCSR, tmp1 

Regards,
Steve A.

The Board helps those that help themselves.

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

Koshchi wrote:
Quote:
4. Set the PCKE bit in the PLLCSR register which enables the asynchronous mode.
But that line also clears every other bit in that register. To set that one bit only you need:

in tmp1, PLLCSR
ori tmp1, (1<<PCKE)
out PLLCSR, tmp1 

Ahh thanks I overlooked that one!

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

clawson wrote:

No it varies the duty cycle. What you should be seeing is a full swing Gnd-Vcc square wave at the PWM frequency with the high:low ratio varied by the OCR1A.

Then I guess the reason for the triangle-like wave I see is that the oscilloscope can't keep up..

Quote:

See AVR335 which has some pretty pictures showing how the varying duty cycle leads to a varying analogue wave. (in the case of AVR335 it's talking about audio but it could just as easily be a DC motor voltage or the brightness of an LED - except that those things don't generally vary as often as a sound wave which probably uses a different OCR1A value on every cycle of the wave)

Thanks! it makes perfectly sense. I will read the note and see if I can get it to work.

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

Finally a working "hello PWM-world" for Attiny261!
I have connected 3 first order filters to the non-inverting OC1A (PB1) with R=10K and C=100n (just something I saw somewhere, haven't calculated the cut-off frequency yet). I have placed a voltmeter at the end of the filter.

'count' controles the duty cycle, that is written to OCR1A in the ISR routine. if count = 0x7F (127 dec) it gives a duty cycle of 50%, which makes the voltmeter measure 2.5V.

Thanks again for your support guys! For someone in the same situation, here is the code



; Hello PWM -  Attiny261
.nolist 
.include "tn261def.inc"
.list 
.def tmp1 = r16  ; tmp1 register 
.def count = r17
.def tmp2 = r3
.def tri = r5 ; Timer interruptregister 

;***** Initialization 
.cseg  ; codesegment
.org 0
rjmp RESET

.org	INT0addr  	  ;  = 0x0001	 External Interrupt 0
	reti           	                
.org	PCIaddr	  	  ;  = 0x0002	 Pin Change Interrupt
	reti           	                
.org	OC1Aaddr  	  ;  = 0x0003	 Timer/Counter1 Compare Match 1A
	rjmp T1intCM           	                
.org	OC1Baddr  	  ;  = 0x0004	 Timer/Counter1 Compare Match 1B
	reti           	                
.org	OVF1addr  	  ;  = 0x0005	 Timer/Counter1 Overflow
	reti           	                
.org	OVF0addr  	  ;  = 0x0006	 Timer/Counter0 Overflow
	reti
.org	USI_STARTaddr ; = 0x0007	; USI Start
	reti
.org	USI_OVFaddr	  ; = 0x0008	 USI Overflow
	reti                         
.org	ERDYaddr    	; = 0x0009	 EEPROM Ready
	reti                         
.org	ACIaddr	    	; = 0x000a	 Analog Comparator
	reti                         
.org	ADCCaddr       ; = 0x000b	 ADC Conversion Complete
	reti
.org	WDTaddr			; = 0x000c	 	Watchdog Time-Out
	reti
.org	INT1addr 		 ;	= 0x000d	 External Interrupt 1
	reti                         
.org	OC0Aaddr		;	= 0x000e	 Timer/Counter0 Compare Match A
	reti                         
.org	OC0Baddr 		;	= 0x000f	 Timer/Counter0 Compare Match B
	reti                         
.org	ICP0addr 		 ;	= 0x0010	 ADC Conversion Complete
	reti                         
.org	OC1Daddr 		 ;	= 0x0011	 Timer/Counter1 Compare Match D
	reti
.org	FAULT_PROTaddr 	; = 0x0012  Timer/Counter1 Fault Protection
	reti
    
.equ CPU_FREQUENCY = 16000000
                            
	
T1intCM:
	
	in tri, SREG
	out OCR1A, count
	sbi PORTA, 7
	out SREG, tri
	reti
	
RESET: 
	ldi tmp1, 0b10000001
	out DDRA,tmp1   ; Set PORTA
	ldi	tmp1, RAMEND ;Initiate Stackpointer (for subroutines/interupts)
	out	SPL,tmp1  ;StackPointerLow (this MCU doesn't have an SPH)
	ldi count, 0x7F ; 50% duty cycle
	; ----------- PWM setting ----------------

	ldi tmp1, (1<<PLLE) ; enable PLL
	out PLLCSR, tmp1
	rcall DELAY         ; wait >100ms
INI_POLL_PLOCK:
	in tmp1, PLLCSR
	andi tmp1, 0x01
	cpi tmp1, 0x01
	brne INI_POLL_PLOCK  ; poll PLOCK until set
	in tmp1, PLLCSR
	ori tmp1, (1<<PCKE) 
	out PLLCSR, tmp1      ; set PCKE in PLLCSR register
	ldi tmp1, (1<<PWM1A) | (1<<COM1A1)
	out TCCR1A, tmp1
	ldi tmp1, (1<<OCIE1A)
	out TIMSK, tmp1
	ldi tmp1, 10
	out OCR1A, tmp1
	ldi tmp1, 0b00000011
	out DDRB, tmp1  ; set PORTB
	ldi tmp1, (1<<CS10) ;start timer, no prescaler
	out TCCR1B, tmp1
	ldi tmp1, 0xFF
	out OCR1C, tmp1
	ldi tmp1, (1<<WGM10) ;Phase & freq correct PWM
	out TCCR1D, tmp1
	sei ;global interrupt enable
	
MAIN:
	cbi PORTA, 7
	jmp MAIN


DELAY:
	ser tmp1
	mov tmp2, tmp1
DELAY_LOOP:
	dec  tmp1	
	brne DELAY_LOOP 
	dec  tmp2	
	brne DELAY_LOOP	
	ret