#include <stdint.h>
#include <stdlib.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#define CONTROL_PORT PORTD
#define CONTROL_DDR DDRD
#if defined(__AVR_ATtiny2313__)
# define TRIGGER_DOWN PD2
# define TRIGGER_UP PD3
# define FLASH PD4
# define CLOCKOUT PD6
#else
# define TRIGGER_DOWN PD2
# define TRIGGER_UP PD3
# define TRIGGER_ADC PD4
# define CLOCKOUT PD6
# define FLASH PD7
#endif
#if defined(__AVR_ATmega16__)
# define PWMDDR DDRD
# define PWMOUT PD5
#elif defined(__AVR_ATmega8__) || defined(__AVR_ATmega48__) ||\
defined(__AVR_ATmega88__) || defined(__AVR_ATmega168__)
# define PWMDDR DDRB
# define PWMOUT PB1
#elif defined(__AVR_ATtiny2313__)
# define PWMDDR DDRB
# define PWMOUT PB3
# define HAVE_ADC 0
# define USART_RXC_vect USART_RX_vect
# define MCUCSR MCUSR
#else
# error "Unsupported MCU type"
#endif
#if defined(__AVR_ATmega48__) || defined(__AVR_ATmega88__) ||\
defined(__AVR_ATmega168__)
# define USART_RXC_vect USART_RX_vect
# define UDR UDR0
# define UCSRA UCSR0A
# define UCSRB UCSR0B
# define FE FE0
# define TXEN TXEN0
# define RXEN RXEN0
# define RXCIE RXCIE0
# define UDRE UDRE0
# define U2X U2X0
# define UBRRL UBRR0L
# define TIMSK TIMSK1
# define MCUCSR MCUSR
#endif
#if !defined(HAVE_ADC)
# define HAVE_ADC 1
#endif
#define F_CPU 1000000UL
#define SOFTCLOCK_FREQ 100
#define EE_UPDATE_TIME (3 * SOFTCLOCK_FREQ)
#define TMR1_SCALE ((F_CPU * 10) / (2048UL * SOFTCLOCK_FREQ) + 9) / 10
volatile struct
{
uint8_t tmr_int: 1;
uint8_t adc_int: 1;
uint8_t rx_int: 1;
}
intflags;
volatile char rxbuff;
volatile uint16_t adcval;
uint16_t ee_pwm __attribute__((section(".eeprom"))) = 42;
int16_t pwm;
int16_t pwm_backup_tmr;
uint8_t mcucsr __attribute__((section(".noinit")));
ISR(TIMER1_OVF_vect)
{
static uint8_t scaler = TMR1_SCALE;
if (--scaler == 0)
{
scaler = TMR1_SCALE;
intflags.tmr_int = 1;
}
}
#if HAVE_ADC
ISR(ADC_vect)
{
adcval = ADCW;
ADCSRA &= ~_BV(ADIE);
intflags.adc_int = 1;
}
#endif
ISR(USART_RXC_vect)
{
uint8_t c;
c = UDR;
if (bit_is_clear(UCSRA, FE))
{
rxbuff = c;
intflags.rx_int = 1;
}
}
void handle_mcucsr(void)
__attribute__((section(".init3")))
__attribute__((naked));
void handle_mcucsr(void)
{
mcucsr = MCUCSR;
MCUCSR = 0;
}
static void
ioinit(void)
{
uint16_t pwm_from_eeprom;
TCCR1A = _BV(WGM10) | _BV(WGM11) | _BV(COM1A1) | _BV(COM1A0);
TCCR1B = _BV(CS10);
OCR1A = 0;
#if HAVE_ADC
CONTROL_PORT = _BV(TRIGGER_DOWN) | _BV(TRIGGER_UP) | _BV(TRIGGER_ADC);
#else
CONTROL_PORT = _BV(TRIGGER_DOWN) | _BV(TRIGGER_UP);
#endif
CONTROL_DDR = _BV(CLOCKOUT) | _BV(FLASH);
PWMDDR |= _BV(PWMOUT);
UCSRA = _BV(U2X);
UCSRB = _BV(TXEN)|_BV(RXEN)|_BV(RXCIE);
UBRRL = (F_CPU / (8 * 9600UL)) - 1;
#if HAVE_ADC
ADCSRA = _BV(ADEN) | _BV(ADPS1) | _BV(ADPS0);
#endif
TIMSK = _BV(TOIE1);
sei();
wdt_enable(WDTO_2S);
if ((pwm_from_eeprom = eeprom_read_word(&ee_pwm)) != 0xffff)
OCR1A = (pwm = pwm_from_eeprom);
}
static void
putchr(char c)
{
loop_until_bit_is_set(UCSRA, UDRE);
UDR = c;
}
static void
printstr(const char *s)
{
while (*s)
{
if (*s == '\n')
putchr('\r');
putchr(*s++);
}
}
static void
printstr_p(const char *s)
{
char c;
for (c = pgm_read_byte(s); c; ++s, c = pgm_read_byte(s))
{
if (c == '\n')
putchr('\r');
putchr(c);
}
}
static void
set_pwm(int16_t new)
{
char s[8];
if (new < 0)
new = 0;
else if (new > 1000)
new = 1000;
if (new != pwm)
{
OCR1A = (pwm = new);
new /= 10;
itoa(new, s, 10);
printstr(s);
putchr(' ');
pwm_backup_tmr = EE_UPDATE_TIME;
}
}
int
main(void)
{
enum
{
MODE_UPDOWN,
MODE_ADC,
MODE_SERIAL
} __attribute__((packed)) mode = MODE_UPDOWN;
uint8_t flash = 0;
ioinit();
if ((mcucsr & _BV(WDRF)) == _BV(WDRF))
printstr_p(PSTR("\nOoops, the watchdog bit me!"));
printstr_p(PSTR("\nHello, this is the avr-gcc/libc "
"demo running on an "
#if defined(__AVR_ATmega16__)
"ATmega16"
#elif defined(__AVR_ATmega8__)
"ATmega8"
#elif defined(__AVR_ATmega48__)
"ATmega48"
#elif defined(__AVR_ATmega88__)
"ATmega88"
#elif defined(__AVR_ATmega168__)
"ATmega168"
#elif defined(__AVR_ATtiny2313__)
"ATtiny2313"
#else
"unknown AVR"
#endif
"\n"));
for (;;)
{
wdt_reset();
if (intflags.tmr_int)
{
intflags.tmr_int = 0;
CONTROL_PORT ^= _BV(CLOCKOUT);
flash++;
if (flash == 5)
CONTROL_PORT |= _BV(FLASH);
else if (flash == 100)
{
flash = 0;
CONTROL_PORT &= ~_BV(FLASH);
}
switch (mode)
{
case MODE_SERIAL:
break;
case MODE_UPDOWN:
if (bit_is_clear(PIND, TRIGGER_DOWN))
set_pwm(pwm - 10);
else if (bit_is_clear(PIND, TRIGGER_UP))
set_pwm(pwm + 10);
#if HAVE_ADC
else if (bit_is_clear(PIND, TRIGGER_ADC))
mode = MODE_ADC;
#endif
break;
case MODE_ADC:
#if HAVE_ADC
if (bit_is_set(PIND, TRIGGER_ADC))
mode = MODE_UPDOWN;
else
{
ADCSRA |= _BV(ADIE);
ADCSRA |= _BV(ADSC);
}
#endif
break;
}
if (pwm_backup_tmr && --pwm_backup_tmr == 0)
{
eeprom_write_word(&ee_pwm, pwm);
printstr_p(PSTR("[EEPROM updated] "));
}
}
#if HAVE_ADC
if (intflags.adc_int)
{
intflags.adc_int = 0;
set_pwm(adcval);
}
#endif
if (intflags.rx_int)
{
intflags.rx_int = 0;
if (rxbuff == 'q')
{
printstr_p(PSTR("\nThank you for using serial mode."
" Good-bye!\n"));
mode = MODE_UPDOWN;
}
else
{
if (mode != MODE_SERIAL)
{
printstr_p(PSTR("\nWelcome at serial control, "
"type +/- to adjust, or 0/1 to turn on/off\n"
"the LED, q to quit serial mode, "
"r to demonstrate a watchdog reset\n"));
mode = MODE_SERIAL;
}
switch (rxbuff)
{
case '+':
set_pwm(pwm + 10);
break;
case '-':
set_pwm(pwm - 10);
break;
case '0':
set_pwm(0);
break;
case '1':
set_pwm(1000);
break;
case 'r':
printstr_p(PSTR("\nzzzz... zzz..."));
for (;;)
;
}
}
}
sleep_mode();
}
}