led-controller/src/main.c

347 lines
9.4 KiB
C

#include <stdint.h> /* Standard Integer Types */
#include <stdio.h> /* Standard IO facilities */
#include <stdlib.h> /* General utilities */
#include <stdbool.h> /* Boolean */
#include <string.h> /* Strings */
#include <avr/io.h> /* AVR device-specific IO definitions */
#include <util/delay.h> /* Convenience functions for busy-wait delay loops */
#include <avr/interrupt.h> /* Interrupts */
#include "main.h"
#include "uart.h"
#include "ir_nec.h"
/* global variables */
#define INPUT_MAX 255 /* max length for user input string */
char input[INPUT_MAX+2]; /* user input from USART */
volatile uint8_t input_i = 0; /* user input index */
volatile uint8_t pwr_ok; /* is power ok */
volatile uint8_t fan; /* fan signal state, to measure tachometer */
volatile uint8_t timer2_ovf = 0; /* to measure fan speed using timer 2 */
static const uint16_t TIMER2_PRESCALE[8] = {0,1,8,32,64,128,256,1024}; /* timer 2 CS2[2:0] values */
volatile uint16_t tachometer = 0; /* the tachometer time (from timer) */
volatile uint8_t ir; /* IR signal state, to measure IR code */
static const uint16_t TIMER1_PRESCALE[8] = {0,1,8,64,256,1024,0,0}; /* timer 1 CS1[2:0] values */
volatile uint16_t ir_tick; /* number of counter ticks per millisecond */
volatile uint8_t pulse = 0; /* pulse index within the burst */
#define PULSE_MAX 128 /* maximum number of pulses to save */
uint16_t burst[PULSE_MAX]; /* pulse times forming a burst (from timer) */
/* flags, set in the interrupts and handled in the main program */
volatile bool input_flag = false;
volatile bool power_flag = false;
/* UART receive interrupt */
ISR(USART_RX_vect) {
input[input_i] = getchar(); /* save input */
input[input_i+1] = 0; /* always end the string */
if (input_i<INPUT_MAX) { /* next character, if space is available */
input_i++;
}
input_flag = true; /* set flag */
}
/* power ok interrupt */
ISR(PCINT0_vect) { /* PCI0 Interrupt Vector for PCINT[7:0] */
if (pwr_ok!=(PINB&(1<<PWR_OK))) { /* did the PWR_OK pin state changed */
pwr_ok = PINB&(1<<PWR_OK); /* save new state */
power_flag = true;
} else if (ir!=(PINB&(1<<IR))) { /* did the IR pin state changed */
ir = PINB&(1<<IR); /* save new state */
if (pulse>0) { /* save pulse, except the first */
burst[pulse-1] = (TCNT1*1000UL)/ir_tick;
burst[pulse] = 0;
}
if (pulse<PULSE_MAX-1) { /* prepare to save next pulse */
pulse++;
}
TCNT1 = 0; /* clear timer 1 */
}
}
/* fan tachometer interrupt */
ISR(PCINT1_vect) { /* PCI0 Interrupt Vector for PCINT[14:8] */
if (fan!=(PINC&(1<<FAN))) { /* did the FAN pin state changed */
fan = PINC&(1<<FAN); /* save new state */
if (fan) { /* only react to rising edge */
tachometer = (uint16_t)(timer2_ovf<<8)+TCNT2; /* save time */
TCNT2 = 0; /* reset timer 2 */
timer2_ovf = 0; /* reset timer 2 */
}
}
}
/* timer 2 interrupt used to measure fan speed based on tachometer */
ISR(TIMER2_OVF_vect) { /* timer 2 overflow interrupt vector */
if (timer2_ovf<0xff) { /* prevent overflow */
timer2_ovf++; /* increase tachometer counter */
}
}
/* timer 1 interrupt used to measure IR pulse */
ISR(TIMER1_COMPA_vect) { /* timer 1 OCR1A match interrupt vector */
if (pulse>0) {
time2nec(burst,pulse-1);
struct nec ir_data = nec2data(burst,pulse-1);
if (ir_data.valid) {
if (ir_data.repeat) {
printf("IR signal repeated\n");
} else {
printf("IR addr: %u, command: %u\n",ir_data.address,ir_data.command);
}
}
pulse = 0;
}
}
static void ioinit(void)
{
/* configure power */
DDRB |= (1<<nPS_ON); /* nPS_ON is output */
PORTB |= (1<<nPS_ON); /* switch off power supply */
DDRB &= ~(1<<PWR_OK); /* PWR_ON (PB1/PCINT1) is input */
pwr_ok = PINB&(1<<PWR_OK); /* save state */
PCIFR &= ~(1<<PCIF0); /* clear interrupt flag */
PCICR |= (1<<PCIE0); /* enable interrupt for PCINT[7:0] */
PCMSK0 |= (1<<PCINT1); /* enable interrupt for PCINT1 */
/* configure LED (on PD6/OC0A) */
DDRD |= (1<<LED); /* LED is output */
/* use timer 1 to produce a PWM */
/* use phase correct PWM mode (because fast PWM is always on for at least 1 cycle) */
TCCR0A &= ~(1<<WGM01);
TCCR0A |= (1<<WGM00);
TCCR0B &= ~(1<<WGM02);
/* /64 prescale timer */
TCCR0B &= ~(1<<CS02);
TCCR0B |= (1<<CS01)|(1<<CS00);
/* clear on match, because the pin is used as sink */
TCCR0A |= (1<<COM0A1)|(1<<COM0A0);
/* start with LED on */
OCR0A = 0xff;
/* configure FAN */
DDRC &= ~(1<<FAN); /* FAN (PC5/PCINT13) is input */
fan = PINC&(1<<FAN); /* save state */
PCIFR &= ~(1<<PCIF1); /* clear interrupt flag */
PCICR |= (1<<PCIE1); /* enable interrupt for PCINT[14:8] */
PCMSK1 |= (1<<PCINT13); /* enable interrupt for PCINT1 */
/* use timer 2 to measure the tachometer */
/* use normal mode */
TCCR2A &= ~((1<<WGM20)|(1<<WGM21));
TCCR2B &= ~(1<<WGM22);
/* clock/64 prescaler */
TCCR2B |= (1<<CS22);
TCCR2B &= ~((1<<CS21)|(1<<CS20));
TIFR2 &= ~(1<<TOV2); /* clear timer 2 overflow interrupt flag */
TIMSK2 |= (1<<TOIE2); /* enable timer 2 overflow interrupt */
/* configure IR receiver */
DDRB &= ~(1<<IR); /* IR (PB0/PCINT0) receiver is input */
ir = PINB&(1<<IR); /* save state */
PCIFR &= ~(1<<PCIF0); /* clear interrupt flag */
PCICR |= (1<<PCIE0); /* enable interrupt for PCINT[7:0] */
PCMSK0 |= (1<<PCINT0); /* enable interrupt for PCINT0 */
/* use timer 1 to measure IR pulse */
/* use CTC mode */
TCCR1A &= ~((1<<WGM10)|(1<<WGM11));
TCCR1B |= (1<<WGM12);
TCCR1B &= ~(1<<WGM13);
/* clock/8 prescaler, offers most precision for 15ms (up to 28.5ms) */
TCCR1B |= (1<<CS11);
TCCR1B &= ~((1<<CS12)|(1<<CS10));
uint16_t prescale = TIMER1_PRESCALE[(TCCR1B&((1<<CS12)|(1<<CS11)|(1<<CS10)))>>CS10]; /* timer 1 presacler */
if (0!=prescale) {
ir_tick = F_CPU/(1000*prescale); /* ticks per ms */
OCR1A = (uint32_t)(15*F_CPU)/(1000*prescale); /* set clear time to 15 ms (no IR toggle should be longer) */
}
TIFR1 &= ~(1<<OCF1A); /* clear timer 1 overflow interrupt flag */
TIMSK1 |= (1<<OCIE1A); /* enable timer 1 overflow interrupt */
/* configure channels (used for powering LEDs using an nMOS) */
DDRC |= (1<<CH1_1)|(1<<CH1_2)|(1<<CH1_3)|(1<<CH1_4)|(1<<CH1_5); /* CH1_x is output */
PORTC &= ~((1<<CH1_1)|(1<<CH1_2)|(1<<CH1_3)|(1<<CH1_4)|(1<<CH1_5)); /* switch off CH1_x */
DDRD |= (1<<CH2_1)|(1<<CH2_2)|(1<<CH2_3)|(1<<CH2_4)|(1<<CH2_5); /* CH2_x is output */
PORTD &= ~((1<<CH2_1)|(1<<CH2_2)|(1<<CH2_3)|(1<<CH2_4)|(1<<CH2_5)); /* switch off CH2_x */
/* use UART as terminal */
uart_init();
stdout = &uart_output;
stdin = &uart_input;
sei(); /* enable interrupts */
}
int main(void)
{
ioinit(); /* initialize IOs */
uint8_t command_i = 0; /* command index */
char c = 0;
puts("LED dimmer up & running");
while (true) {
/* handle user input */
while (input_flag) {
while (command_i<input_i) {
c = input[command_i++];
putchar(c); /* echo back */
}
input_flag = false;
}
}
{
_delay_ms(1000);
switch (c) {
case 'l':
PIND |= (1<<LED);
printf("LED: ");
if (PIND&(1<<LED)) {
puts("off");
} else {
puts("on");
}
break;
case 'a':
printf("switching power supply ");
if (PINB&(1<<nPS_ON)) {
puts("on");
} else {
puts("off");
}
PINB |= (1<<nPS_ON);
break;
case 's':
printf("power: ");
if (PINB&(1<<PWR_OK)) {
puts("ok");
} else {
puts("ko");
}
break;
case '1':
PINC |= (1<<CH1_1);
printf("CH1_1: ");
if (PINC&(1<<CH1_1)) {
puts("on");
} else {
puts("off");
}
break;
case '2':
PINC |= (1<<CH1_2);
printf("CH1_2: ");
if (PINC&(1<<CH1_2)) {
puts("on");
} else {
puts("off");
}
break;
case '3':
PINC |= (1<<CH1_3);
printf("CH1_3: ");
if (PINC&(1<<CH1_3)) {
puts("on");
} else {
puts("off");
}
break;
case '4':
PINC |= (1<<CH1_4);
printf("CH1_4: ");
if (PINC&(1<<CH1_4)) {
puts("on");
} else {
puts("off");
}
break;
case '5':
PINC |= (1<<CH1_5);
printf("CH1_5: ");
if (PINC&(1<<CH1_5)) {
puts("on");
} else {
puts("off");
}
break;
case '6':
PIND |= (1<<CH2_1);
printf("CH2_1: ");
if (PIND&(1<<CH2_1)) {
puts("on");
} else {
puts("off");
}
break;
case '7':
PIND |= (1<<CH2_2);
printf("CH2_2: ");
if (PIND&(1<<CH2_2)) {
puts("on");
} else {
puts("off");
}
break;
case '8':
PIND |= (1<<CH2_3);
printf("CH2_3: ");
if (PIND&(1<<CH2_3)) {
puts("on");
} else {
puts("off");
}
break;
case '9':
PIND |= (1<<CH2_4);
printf("CH2_4: ");
if (PIND&(1<<CH2_4)) {
puts("on");
} else {
puts("off");
}
break;
case '0':
PIND |= (1<<CH2_5);
printf("CH2_5: ");
if (PIND&(1<<CH2_5)) {
puts("on");
} else {
puts("off");
}
break;
case '-':
if (OCR0A>0) {
OCR0A--;
}
printf("decreasing LED: %u\n",OCR0A);
break;
case '+':
if (OCR0A<0xff) {
OCR0A++;
}
printf("increasing LED: %u\n",OCR0A);
break;
case 't':
if (tachometer) {
uint16_t prescale = TIMER2_PRESCALE[TCCR2B&((1<<CS22)|(1<<CS21)|(1<<CS20))];
if (prescale) {
if (timer2_ovf<0xff) {
uint32_t speed = ((60*F_CPU)/(prescale*(uint32_t)tachometer))/2; /* calculate speed. 2 pulses per revolution */
printf("fan speed: %lurpm\n",speed);
} else {
printf("fan off (or not detected)\n");
}
} else {
printf("fan speed measurement not started\n");
}
} else {
printf("fan off (or not detected)\n");
}
break;
}
}
return 0;
}