Nile University Embedded Real Time Systems
School of Engineering and Applied Sciences
Lecture 4
3- Fast PWM Mode
The TOP is fixed to 255 for Timer0 and Timer2 and 65535 for Timer1
OCn is set or cleared on match but the timer resumes counting till reaching the
TOP value then rollover
The actual OCn value will only be visible on the port pin if the data direction for the
port pin is set as output.
OCFn flag is set when match and used to trigger the ISR (Timern_OCIE_vect)
TIMSK |= (1 << OCIEn); sei();
TOVn is set on overflow and used to trigger the ISR (Timern_OVF_vect)
TIMSK |= (1 << TOIEn); sei();
The generated frequency at OCn:
Fast PWM mode is useful for generating high-frequency PWM signals.
You can change the frequency by only changing the prescaler
OCR is used to determine the duty cycle as follows:
OCRn = (2n * duty%) – 1 , where OCRn <= 2n - 1 = 255 for Timer0 and 2 (The MAX)
By changing the duty cycle of the generated waveform you can control motor speed, LED
dimming levels, Encoding information and Audio Signal Generation and other various
applications.
Example: Generate a 60 Hz PWM signal to control the speed of a motor. Two
speed levels are provided based on the state of an active-high switch connected
to PortA0.
When the switch is ON (high), the motor speed is high (80% duty cycle
PWM).
When the switch is OFF (low), the motor speed is low (20% duty cycle
PWM).
The motor initially rotates at the highest speed (100%) . The CPU clock
frequency is 1 MHz.
Sol: use Timer2 in Fast PWM, non inverting (OC2 is cleared on match)
1- Estimate the suitable prescaller N:
N = 1000000/(256*60) = 65.10 ~ 64
Ocr80 = (256*80/100 - 1 ) = 203.8 ~ 204
Ocr20 = (256*20/100 - 1) = 51.2 ~ 51
ECEN435 Dr. Noha Younis
#define PRESCALER 64 // Prescaler setting for Timer2
# define OCR80 204
#define OCR20 51
#define LSW 2
#define PWM-PIN 7
void Timer2_FAST_PWM_NONINV_INIT(void) {
// FAST PWM mode/ non-inverted mode ( OC2 cleared on match)
TCCR2 |= (1 << WGM20) | (1 << WGM21) | (1 << COM21);}
void Timer2_FAST_PWM_NONINV( uint8_t duty) {
OCR2 = duty;
TCCR2 |= (1 << CS22) ;} // prescaler = 64
int main(void) {
DDRA &= ~(1 << HSW); // Set SW as input switch
DDRD |= (1 << PWM-PIN); // OC2 OUTPUT
PORTD |= (1 << PWM-PIN); // initially set OC2 ( highest speed)
Not necessary as it is pull down
Timer2_FAST_PWM_NONINV_INIT(); // initialize TIMER2
uint8_t last_state = 1 ;
uint8_t current_state ;
// Main loop
while (1) {
current_state = (PINA >> HSW)& 1 ;
if (current_state != last_state )
{ last_state = current_state ;
TCCR2 &= ~(1 << CS22) ; // Just stop Timer2 without affecting the
//initial settings
// Check the state of switch connected to PORTA2
if ((PINA >> HSW)& 1)
4- Event Counter
Counters are used for even counting–based tasks in ERTS
Example: A heartbeat sensor connected to PB0 of an ATmega32 generates a positive edge
trigger with every new heartbeat. Develop a part of the firmware for an embedded real-time
pacemaker to calculate the number of heartbeats within one minute. After one minute, the
number of heartbeats should be loaded onto PortA, ready to be sent via Bluetooth to a mobile
device for real-time tracking. The CPU clock frequency is 4 kHz.
Answer:
Use Timer2 in CTC mode , inverted or non inverted mode to generate the one
minute duration
OCR2 =( 60 X 4000/1024)-1 = 233
Use counter0 in normal mode and +ve edge triggering at T0 (PB0) to count the
heartbeats ( we don’t need initial value of TCNT0 as it doesn’t count a limited
number of heart beats)
ECEN435 Dr. Noha Younis
USE ISR ISR(TIMER2_COMP_vect) to stop the timer and counter after one
minute
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 4000UL
#define SENSOR PB0
// To estimate the desired 60 second delay
void timer2_ctc_mode() {
// Clear the timer
TCNT2 = 0;
// Set CTC mode
TCCR2 |= (1 << WGM21);
TCCR2 &= ~(1 << WGM20);
// Set OCR2 value to 233
OCR2 = 233;
TIMSK |= (1 << OCIE2); // enable as once MATCH, 60 Sec done
// Enable global interrupts
sei();
// Set OC2 on compare match
TCCR2 |= (1 << COM21) | (1 << COM20);
TCCR2 |= (1 << CS22) | (1 << CS21)| (1 << CS20); // Pre-scaler = 1024
void count0() // just set it to count from zero
{
TCCR0 |= (1 << CS02)|(1 << CS00) | (1 << CS00); // counter, clock on rising edge
ISR(TIMER2_COMP_vect) // stop conter0 and timer2 after one minute and load the number
of heart beats on PORTD
{ TCCR0 = 0 ;
TCCR2 = 0;
PORTD = TCNT0 ; }
ECEN435 Dr. Noha Younis
int main(void) {
DDRD = 0XFF ; // load no of heart beats after 1 sec on PORTD
DDRB &= ~(1 << SENSOR); // Force PINB0(T0)to be an input to receive the sensor
//pulses
timer2_ctc_mode(); // start timer2 for one minute duration
count0(); // start Counter0 for counting the heartbeat
while (1) {
// Do other things :)!
}
return 0;}
ECEN435 Dr. Noha Younis
Timer1 : 16-bit Timer1/Counter1
In most microcontrollers (MCUs), different timers of various resolutions are available.to
handle a wide range of timing-related tasks, from simple delays to complex pulse-width
modulation (PWM) and event counting.
It is generally a good practice to use low bit timers as long as they satisfy requirements to save
on power consumption and potentially reduce software complexity.
Case study: 16-bit Timer/Counter1
ECEN435 Dr. Noha Younis
ECEN435 Dr. Noha Younis
ECEN435 Dr. Noha Younis
ECEN435 Dr. Noha Younis
TOV1 is used to trigger the ISR (Timer1_OVF_vect)
TIMSK |= (1 << TOIE1); sei();
Timer tick delay= N/fcpu
No. of ticks for D delay = (Dxfcpu)/N : 2n ≥ NOT > 0
TCNTn = (2n - No. of ticks for D delay) : 2n -1 ≥ TCNTn ≥ 0
ECEN435 Dr. Noha Younis
Example: Write a program to continually turn on a device connected
to atmega32 for 2 seconds then turn it off for 2 seconds the device is
connected to Portb0 of the microcontroller. The CPU frequency is
8MHz. The device is active high and initially switched on.
Sol.:
No. of ticks for 2 delay = (2x 8000000)/N = 216 > 15625 > 28 for N= 1024
Use Timer1 better than Timer0 or 2
TCNT1 = (216 - 15625) = 49911
#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#define DIV 0
#define DDR DDRA
#define PORT PORTA
void TIMER1 (){
TCNT1 = 49911 ;
TCCR1A = 0 ; // Normal mode
TIMSK |= (1 << TOIE1); // Enable Timer1 interrupt when TOV1 = 1
sei(); // Enable global interrupts
TCCR1B |=(1 << CS12) | (1 << CS10); // PRESCAL = 1024
ISR(TIMER1_OVF_vect)
{
TCNT1 = 49910 ;
PORT ^=(1 << DIV);
int main(void){
DDR |= (1 << DIV);
PORT |= (1 << DIV); // initially high
TIMER1();
while (1)
{
// Do something else concurrently
}
return 0 ;}
ECEN435 Dr. Noha Younis
Example:
In a nuclear reactor, vibration sensors are essential for monitoring the
condition of various components. Occasionally, a vibration alarm may be
triggered by minor, non-critical events. However, a rapid succession of
vibration alarms within a short period can indicate an imminent mechanical
failure that requires immediate intervention.
Design a system using the ATmega32 microcontroller to monitor the frequency
of vibration alarms. The system should count the number of vibration alarm
positive edge pulses received at PORTB0 and stop counting when a predefined
limit is reached (10 in this example). The elapsed time to reach this limit should
be measured and store the elapsed time preparing to convert it to a digital
number. The CPU frequency = 4MHz. Simplifying the process, assume that the
timer will not overflow.
Set Timer0 as a counter that stops once counting 10 vibration events
TCNT0 = 256 – 10 = 246
Use Timer1 in normal mode to estimate the duration of the 10 pulses
TCNT1 = 0 , use Prescaler = 1024 : use high prescaler to enable estimation of large durations
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 4000000UL
#define ALARM_PULSE PB0
float TIME = 0 ;
// To estimate the desired 60 second delay
void timer1_NORM_mode() {
TCCR1A = 0 ; TCCR1B = 0 ;
// Clear the timer to start estimating the elapsed time
TCNT1 = 0;
TCCR1B |= (1 << CS12) | (1 << CS10); // Pre-scaler = 1024
ECEN435 Dr. Noha Younis
void count0() // just set it to count only up to 10 counts on rising edge received at PB0
{ TCNT0=246;
TIMSK |= (1 << TOIE0); // TO enable TOV0 interrupt after 10 times of alarm pulses
// Enable global interrupts
sei();
TCCR0 |= (1 << CS02)|(1 << CS00) | (1 << CS00); // counter, clock on rising edge
ISR(TIMER0_OVF_vect) // stop conter0 and timer1 after counting 10 vibration alarm
pulses
{ TCCR1B = 0 ; TCCR0 = 0; // stop Timer1 and Counter0
TIME = TCNT1 * (1024/4000) ; // time in ms
}
int main(void) {
DDRB &= ~(1 << ALARM_PULSE); // Force PINB0(T0)to be an input to receive the
Vibration alarm pulses
count0(); // start Counter0 for counting the vibration alarm pulses
timer1_NORM_mode(); // start timer1 to estimate the elapsed time for 10 counts
while (1) {
// Do something else concurrently
}
return 0;
}
ECEN435 Dr. Noha Younis