Timers and counters

Z MAM wiki

Přejít na: navigace, hledání

In the previous task, the problem of regulation of speed of rotation of the fan was solved by software implementation of PWM. However, it is not acceptable solution. The processor is fully load just by waiting between voltage swings of the PWM, it is very complicated to set up timing properly, and it is moreover affected by possible interrupts from hardware components of the MCU. Much better and preferrable solution is utilization of timer / counter block, which directly supports PWM generation. The ATmega88 has two 8-bit and one 16-bit counter / timer unit, fitted with frequency prescaler and value comparators. Overall 6 PWM channels are tight to the counter / timer unit. Even there are some slight differences, most of properties are same. We will use 8-bit channel 0.

[editovat] Basic properties

The fundamental part of each timer/counter unit is Timer/Counter register TCNTn (see lecture 7, or datasheet). It is clocked by either processor's internal clock, which frequency can be divided in the prescaler 1, 8, 32, 64, 128, 256 or 1024 times, or from external low frequency watch crystal. Prescaler, as well as counting (waveform generation) modes and compare modes are controlled by Timer/Counter Control register TCCRn. The immediate value in the TCCNn register is compared against zero, maximum value (255 in the case of eight-bit timer/counter), or the top value, stored in the Output Compare Register OCRn. When the value in the TCNTn register reaches the value in the OCRn register, the logic value at the OCn pin is toggled, and an interrupt may be also triggered, if enabled.

Besides the TCNTn register (and TCCRn compare register) there are two other registers, which must be configured:

  1. Data Direction Register DDRn. Yes, this is the same register, you already know from the tasks, targeted to manipulate with ports - matrix keyboard, e.g. Timer/Counter is an alternate function of Port D, pins PD5 and PD6. The pin PD5 (Timer B), or PD6 (Timer A) must be configurated as output, or no output value will be asserted by the Timer/Counter unit at the output pin.
  2. Asynchronous Status Register (ASSR). This register control, whether the Timer/Counter uses an internal, or external clock source, and if it operates in synchronous or asynchronous mode. In our case, we use internal clock source, so default values can be kept, so this register may not be set.

The Timer/Counter must be set using the WGMnx (Waveform Generation mode) and COMnx (Compare Output mode) bits of the TCCRn register. One of the four modes can be set (see details in the lecture 7, or in the datasheet).

Normal Mode, (WGM02:0 = 0) It is the simplest mode. The timer counts always up, when it reaches 0xFF, it sets the Timer/Counter overflow flag (TOV0), which may trigger an interrupt, and toggles output pin. Since the counter in this mode always counts up to maximum value (255 in the case of eight-bit counter), this mode is very limited in terms of adjustable frequency. It is possible to set up the frequency only by a prescaler division ratio. Actually, there is a trick, how to set up other frequencies too - after overflow, when an interrupt is triggered, write some non-zero value into the Timer Counter Register (TCNT). However, because it is not generally possible before the timer is updated, this mode cannot ensure constant counting time.

Clear Timer on Compare Match (CTC) Mode, (WGM02:0 = 2) This mode extent the Normal Mode in the intent that we can control adjustable frequency much better. This is achieved by setting up the TOP value using the Output Compare Register (OCR). When the counter reaches the value, set in the OCR, it toggles output, and start to count from zero again.

Fast PWM Mode, (WGM02:0 = 3 or 7) The counter counts up. The TOP value is 0xFF, if WGM2:0 = 3, or the value of the register OCR0A if WGM2:0 = 7. This allows relatively wide control of a signal period. The duty cycle is controlled by comparator unit. Output signal is toggled when the counter is equal to the value of Output Compare Register OCR, and when the counter reaches the TOP value.

Phase Correct PWM Mode, (WGM02:0 = 1 or 5) This mode is similar to the Fast PWM mode. The difference is in the way, how the counter counts. It counts up, but when it reaches the TOP value, it starts to count down. The counting frequency is the half of that of the Fast PWM mode. However, it allows to generate a signal symmetrical to the TOP value of the counter. In this case, the center of the pulses is constant, even the duty cycle varies, so the phase is the same. It is important for motor control, especially the three-phase ones.

[editovat] The program

For the regulation of speed of fan rotation we will use the Fast PWM. Comparator of the OC0A output will be used for control of the period of the counter. Therefore we use the OC0B output of the Timer 0. The TOP value of the counter may be 0xFF, when WGM2:0 = 3, or OCR0A when WGM2:0 = 7. The output OC0B is alternate function of the PD5 pin (11). If we would set the TOP value to 0xFF, we would use 255 levels of speed regulations. On the other hand, with 1 MHz processor clock, the PWM frequency would be 1MHz / 256 = 3.9 kHz, which is audible frequency. From this reason, we will use WGM2:0 = 7 setting. Than, we can set the TOP value in the OCR0A register. The appropriate value is 1 MHz / 25 kHz = 40. In this case we have just 40 levels of regulation, however, this is enough for our purposes. The only problem may arise, if we require zero value on the output. According to the datasheet, "If the OCR0A is set equal to BOTTOM, the output will be a narrow spike for each MAX+1 timer clock cycle". To prevent this, we may switch the pin function to the standard output port with zero value. To control speed of rotation by the keyboard with keys 0...9 we may set nine steps. The value of OCR0A can be set as multiple of 9, e.g. 36, or 45.The value of the pressed key we will multiply by 4 for simplicity. This value will be written into the OC0B register. However, in this simple example, we do not measure speed of rotation, and it is not linear with respect to average voltage. So, the speed of rotation will not be in the 0...100% range with 10% steps.

; 
; example fan program for ATmega168 with Timer controlled PWM&Interrupt&Sleep
;
; Rotation speed  is controlled by keyboard, 
; keys 0-9 represent the speed between 0 and 100 %
;
; Fan starts to rotate with the first valid key press
;
; To do: *** Catch only the key press, not key release
;
; To do: *** Set higher value of PWM (=higher fan rpm) for 
; a few first periods to start the fan reliably
;
;
; P4 CPU fan Ucc pin is supposed to be connected 
; to +15 V, GND pin is supposed to be connected 
; to NMOS Drain,, Source on GND, Gate on 
; pin 11 (OC0B, PD5). Fan runs when pin 11 is high.


.INCLUDE "m168def.inc"

.EQU PWM_PULSES = 36	; value for 27.8 kHz PWM
;.EQU PWM_PULSES = 45	; value for 22.2 kHz PWM


.ORG 0x0000 
	jmp Main ; Reset Handler

.ORG 0x0008 
	jmp isr1 ; PCINT1 Handler



; Main program start

Main: 

; Stack pointer init:
	ldi r16,high(RAMEND); Main program start
	out SPH,r16 ; Set Stack Pointer to top of RAM
	ldi r16,low(RAMEND)
	out SPL,r16


; Hardware initialization

; Keyboard init:
	cbi DDRC, 2 ; set inputs for keyboard (COL1-3)
	cbi DDRC, 3
	cbi DDRC, 4

	sbi PORTC, 2 ; set internal Pull-Ups for keyboard
	sbi PORTC, 3
	sbi PORTC, 4

	sbi DDRD, 0 ; set driving outputs for keyboard
	sbi DDRD, 1 ; (ROW1-ROW4)
	sbi DDRD, 2
	sbi DDRD, 3

	cbi PORTD, 0	; log. 0 on all rows
	cbi PORTD, 1
	cbi PORTD, 2
	cbi PORTD, 3


; Timer controlled PWM init:
;
; There are registers TCCR0A and TCCR0B for config, DDRD for output bit
; enabling, OCR0A for counter lenght and OCR0A for pulse "high" lenght.
; Register TIMSK0 controlls interrupts - not used, 0x00 by default 

PWM_INIT:
	ldi R17, 0b00100011	; Fast PWM Mode, out on OC0B, non Inverting
	out TCCR0A, R17			; 
	ldi R17, 0b00001001	; WGM2:1 (Fast PWM), CS2..0:001 (internal clock, prescale=1)
	out TCCR0B, R17
	ldi R17, PWM_PULSES	; load number of clock pulses for 1 PWM period
	out OCR0A, R17   

	sbi DDRD, 5	; set pin 11 as PWM output OC0B (PD5 pin)


; Keyboard interrupt setup
;
; Activation of pin change interrupt - PROBLEM!!! PCICR and PCIMSK are 
; extended I/O registers (0x68 and 0x6C), and must be handled as 
; a memory location

	ldi r26, PCICR ; load address of PCICR in Y low
	clr r27 ; load high byte with 0
	ldi r16,0b00000010 ; activate PCINT1
	st X, r16 ; store new PCINT1

	ldi r26, PCMSK1 ; load address of PCMSK1 in Y low
	clr r27 ; load high byte with 0
	ldi r16,0b00011100 ; allow pin change interrupt on portC bits 2,3,4
	st X, r16 ; store new PCMSK1

	sei ; Enable interrupts


; Set sleep mode of the CPU
; SMCR: 0,0,0,0,SM2,SM1,SM0,SE   SM2..0: Sleep Mode Select Bits, SE: Sleep enabled)

	ldi r16,0b00000001 ; Idle mode 
	out SMCR,r16 ; Sleep mode set


; Sleep loop, wake up by the Interrupt, return with RETI back to sleep loop

loop:
	sleep ; now AVR sleeps
	nop ; return from Interrupt to this instruction
	rjmp loop ; and sleep again


; PCINT0 Service Routine:
;
; To do: *** rpm reading, regulation, etc.


isr1:
	rcall KEYPRESS

	mov R17, R16
	lsl R17
	lsl R17	; multiply key number by 4 for 10 steps in 40 clock pulses of PWM
	out OCR0B, R17  ; output pulse "high" lenght to PWM compare unit 

	reti ; return from Interrupt


; Keyboard decoding:

KEYPRESS:
KEY0:
	sbi 	PORTD, 0
	sbi 	PORTD, 1
	sbi 	PORTD, 2
	cbi 	PORTD, 3	;log. 0 to the row with *, 0, #, D	
	sbic	PINC, 3
	rjmp	KEY1
	ldi	R16, 0		;pressed key 0
	rjmp	KEYRET
KEY1:
	cbi 	PORTD, 0	;log. 0 to the row with 1, 2, 3, A
	sbi 	PORTD, 1	;log. 1 on another three
	sbi 	PORTD, 2
	sbi 	PORTD, 3	
	sbic	PINC, 2
	rjmp	KEY2
	ldi	R16, 1		;pressed key 1
	rjmp	KEYRET
KEY2:
	sbic	PINC, 3
	rjmp	KEY3
	ldi	R16, 2
	rjmp	KEYRET
KEY3:
	sbic	PINC, 4
	rjmp	KEY4
	ldi	R16, 3
	rjmp	KEYRET

KEY4:
	sbi 	PORTD, 0
	cbi 	PORTD, 1	;log. 0 to the row with 4, 5, 6, B
	sbi 	PORTD, 2
	sbi 	PORTD, 3	
	sbic	PINC, 2
	rjmp	KEY5
	ldi	R16, 4		;pressed key 4
	rjmp	KEYRET
KEY5:
	sbic	PINC, 3
	rjmp	KEY6
	ldi	R16, 5
	rjmp	KEYRET
KEY6:
	sbic	PINC, 4
	rjmp	KEY7
	ldi	R16, 6
	rjmp	KEYRET

KEY7:
	sbi 	PORTD, 0
	sbi 	PORTD, 1
	cbi 	PORTD, 2	;log. 0 to the row with 7, 8, 9, C
	sbi 	PORTD, 3	
	sbic	PINC, 2
	rjmp	KEY8
	ldi	R16, 7		;pressed key 7
	rjmp	KEYRET
KEY8:
	sbic	PINC, 3
	rjmp	KEY9
	ldi	R16, 8
	rjmp	KEYRET
KEY9:
	sbic	PINC, 4
	rjmp	KEYRET
	ldi	R16, 9

KEYRET:
	cbi PORTD, 0	; log. 0 on all rows for next key press catch
	cbi PORTD, 1
	cbi PORTD, 2
	cbi PORTD, 3
	ret

[editovat] Odkazy

Osobní nástroje