/* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Joerg Wunsch * ---------------------------------------------------------------------------- * * Simple AVR demonstration. Controls a LED that can be directly * connected from OC1/OC1A to GND. The brightness of the LED is * controlled with the PWM. After each period of the PWM, the PWM * value is either incremented or decremented, that's all. * * $Id: demo.c,v 1.1 2002/09/30 18:16:07 troth Exp $ */ #include #include #include #include #if defined(__AVR_AT90S2313__) # define OC1 PB3 # define OCR OCR1 # define DDROC DDRB #elif defined(__AVR_AT90S2333__) || defined(__AVR_AT90S4433__) # define OC1 PB1 # define DDROC DDRB # define OCR OCR1 #elif defined(__AVR_AT90S4414__) || defined(__AVR_AT90S8515__) || \ defined(__AVR_AT90S4434__) || defined(__AVR_AT90S8535__) || \ defined(__AVR_ATmega16__) # define OC1 PD5 # define DDROC DDRD # define OCR OCR1A #else # error "Don't know what kind of MCU you are compiling for" #endif #if defined(COM11) # define XCOM11 COM11 #elif defined(COM1A1) # define XCOM11 COM1A1 #else # error "need either COM1A1 or COM11" #endif enum { UP, DOWN }; volatile uint16_t pwm; /* Note [1] */ volatile uint8_t direction; SIGNAL (SIG_OVERFLOW1) /* Note [2] */ { switch (direction) /* Note [3] */ { case UP: if (++pwm == 1023) direction = DOWN; break; case DOWN: if (--pwm == 0) direction = UP; break; } OCR = pwm; /* Note [4] */ } void ioinit (void) /* Note [5] */ { /* tmr1 is 10-bit PWM */ TCCR1A = _BV (WGM10) | _BV (WGM11) | _BV (XCOM11); /* tmr1 running on full MCU clock */ TCCR1B = _BV (CS10); /* set PWM value to 0 */ OCR = 0; /* enable OC1 and PB2 as output */ DDROC = _BV (OC1); timer_enable_int (_BV (TOIE1)); /* enable interrupts */ sei (); } int main (void) { ioinit (); /* loop forever, the interrupts are doing the rest */ for (;;) /* Note [6] */ ; return (0); } /* Some of the more important parts of the code are: Note [1]: The PWM is being used in 10-bit mode, so we need a 16-bit variable to remember the current value. Note [2]: SIGNAL() is a macro that marks the function as an interrupt routine. In this case, the function will get called when the timer overflows. Note [3]: This section determines the new value of the PWM. Note [4]: Here's where the newly computed value is loaded into the PWM register. Since we are in an interrupt routine, it is safe to use a 16-bit assignment to the register. Outside of an interrupt, the assignment should only be performed with interrupts disabled if there's a chance that an interrupt routine could also access this register (or another register that uses TEMP), see the appropriate FAQ entry. Note [5]: This routine gets called after a reset. It initializes the PWM and enables interrupts. Note [6]: The main loop of the program does nothing -- all the work is done by the interrupt routine! If this was a real product, we'd probably put a SLEEP instruction in this loop to conserve power. */