4.8.1 asmdemo.c
/* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * Joerg Wunsch 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 * ---------------------------------------------------------------------------- * * Demo combining C and assembly source files. * * This demo implements an RC model type PWM decoder. The incoming * PWM signal consists of a pulse sequence with a pulse width of 920 * microseconds up to 2120 microseconds (1520 microseconds being the * neutral point). Depending on the value of the decoded incoming * PWM, an outgoing PWM is controlled between 0 and 100 %. * * The project is intented to be run on an ATtiny13 that has only one * timer channel (timer 0), so both the incoming signal discrimination * as well as the outgoing PWM need to run on the same timer. * * For verification purposes, the same project can also be run on an * ATtiny25/45/85, where timer 1 can be used to evaluate the incoming * PWM signal, and timer 0 to generate the outgoing PWM. In that * case, no additional assembly code is needed. * * $Id$ */ /* * This is the main C source file for the demo. */ #include <avr/interrupt.h> #include <avr/io.h> #include <avr/sleep.h> #include "project.h" volatile uint16_t pwm_incoming; volatile struct { uint8_t pwm_received: 1; } intbits; void ioinit(void) { counter_hi = 0; flags = 0; /* * Timer 0 runs as phase-correct PWM at full clock, OC0B connects to * the PWM engine. */ TCCR0A = (1 << COM0B1) | (1 << WGM00); TCCR0B = (1 << CS00); OCR0A = 255; #if defined(__AVR_ATtiny13__) TIMSK0 = (1 << TOIE0) | (1 << OCIE0A); # define F_CPU 1200000ul /* Minimal PWM pulse width is 920 us. */ # define MIN_PWM_VAL ((920ul * F_CPU) / 1000000ul) /* Maximal PWM pulse width is 2120 us */ # define MAX_PWM_VAL ((2120ul * F_CPU) / 1000000ul) #elif defined(__AVR_ATtiny25__) ||\ defined(__AVR_ATtiny45__) ||\ defined(__AVR_ATtiny85__) # define F_CPU 1000000ul /* * We use a prescaler of 16 here to avoid the 32-bit calculations * below. */ /* Minimal PWM pulse width is 920 us. */ # define MIN_PWM_VAL ((920ul * F_CPU) / 16 / 1000000ul) /* Maximal PWM pulse width is 2120 us */ # define MAX_PWM_VAL ((2120ul * F_CPU) / 16 / 1000000ul) #else # error "Don't know how to run on your MCU_TYPE." #endif PCMSK = (1 << 4); GIFR = (1 << PCIF); GIMSK = (1 << PCIE); DDRB = (1 << PB1); PORTB = 0; sei(); } #if defined(__AVR_ATtiny25__) ||\ defined(__AVR_ATtiny45__) ||\ defined(__AVR_ATtiny85__) ISR(PCINT0_vect) { uint8_t tcnt1; if (PINB & (1 << 4)) { /* Start timer 1 with a prescaler of 16. */ TCNT1 = 0; TCCR1 = (1 << CS12) | (1 << CS10); return; } /* Stop timer 1, current value is pulse width. */ tcnt1 = TCNT1; TCCR1 = 0; GIMSK &= ~(1 << PCIE); pwm_incoming = tcnt1; intbits.pwm_received = 1; } #endif /* ATtinyX5 */ int main(void) { ioinit(); for (;;) { if (intbits.pwm_received) { intbits.pwm_received = 0; #if defined(__AVR_ATtiny13__) if (pwm_incoming < MIN_PWM_VAL) pwm_incoming = MIN_PWM_VAL; else if (pwm_incoming > MAX_PWM_VAL) pwm_incoming = MAX_PWM_VAL; OCR0B = (pwm_incoming - MIN_PWM_VAL) * 255ul / (MAX_PWM_VAL - MIN_PWM_VAL); #else OCR0B = (pwm_incoming - MIN_PWM_VAL) * 255u / (MAX_PWM_VAL - MIN_PWM_VAL); #endif GIFR = (1 << PCIF); GIMSK |= (1 << PCIE); } sleep_mode(); } }