2013-10-15 13:37:30 +02:00
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*
*/
/* This is the main part of the LED light controller program.
* It handles all peripherals ( power , fan , channels , IR , serial )
*/
/* This program is specifically designed for hardware version A,
* with schematic revision 2 , and layout revision 5.
*/
2013-10-12 17:05:01 +02:00
# include <stdint.h> /* Standard Integer Types */
# include <stdio.h> /* Standard IO facilities */
# include <stdlib.h> /* General utilities */
2013-10-14 16:47:25 +02:00
# include <stdbool.h> /* Boolean */
2013-10-12 17:05:01 +02:00
# include <string.h> /* Strings */
# include <avr/io.h> /* AVR device-specific IO definitions */
# include <util/delay.h> /* Convenience functions for busy-wait delay loops */
2013-10-13 16:47:46 +02:00
# include <avr/interrupt.h> /* Interrupts */
2013-10-19 19:51:18 +02:00
# include <avr/wdt.h> /* Watchdog timer handling */
2013-10-19 19:56:24 +02:00
# include <avr/pgmspace.h> /* Program Space Utilities */
2013-10-12 17:05:01 +02:00
2013-10-12 18:19:39 +02:00
# include "main.h"
# include "uart.h"
2013-10-14 16:47:25 +02:00
# include "ir_nec.h"
2013-10-17 13:29:01 +02:00
# include "settings.h"
2013-10-12 17:05:01 +02:00
2013-10-19 19:51:18 +02:00
/* help strings */
const char help_00 [ ] PROGMEM = " commands: \n " ;
const char help_01 [ ] PROGMEM = " \t help display this help \n " ;
const char help_02 [ ] PROGMEM = " \t reset reset boad and settings \n " ;
const char help_03 [ ] PROGMEM = " \t power show power state \n " ;
const char help_04 [ ] PROGMEM = " \t power on switch power on \n " ;
const char help_05 [ ] PROGMEM = " \t power off switch power off \n " ;
const char help_06 [ ] PROGMEM = " \t fan show fan speed \n " ;
2013-10-26 23:15:56 +02:00
const char help_07 [ ] PROGMEM = " \t temp show temperature \n " ;
const char help_08 [ ] PROGMEM = " \t mode show current mode \n " ;
const char help_09 [ ] PROGMEM = " \t mode N select mode \n " ;
const char help_10 [ ] PROGMEM = " \t ch X Y show channel [1-2].[1-5] brightness \n " ;
const char help_11 [ ] PROGMEM = " \t ch X Y Z set channel [1-2].[1-5] brightness [0-255] \n " ;
const char help_12 [ ] PROGMEM = " \t ir learn power learn the IR command to power on/off \n " ;
const char help_13 [ ] PROGMEM = " \t ir learn mode learn the IR command to change between modes \n " ;
const char help_14 [ ] PROGMEM = " \t ir learn brightness up learn the IR command to increase brightness \n " ;
const char help_15 [ ] PROGMEM = " \t ir learn brightness down learn the IR command to decrease brightness \n " ;
const char help_16 [ ] PROGMEM = " \t ir learn channel next learn the IR command to select next channel \n " ;
const char help_17 [ ] PROGMEM = " \t ir learn channel previous learn the IR command to select previous channel \n " ;
2013-10-19 19:51:18 +02:00
PGM_P const help_table [ ] PROGMEM = {
help_00 ,
help_01 ,
help_02 ,
help_03 ,
help_04 ,
help_05 ,
help_06 ,
help_07 ,
help_08 ,
help_09 ,
help_10 ,
help_11 ,
help_12 ,
help_13 ,
help_14 ,
2013-10-19 22:52:50 +02:00
help_15 ,
2013-10-26 23:15:56 +02:00
help_16 ,
help_17
2013-10-19 19:51:18 +02:00
} ;
2013-10-21 11:25:22 +02:00
//volatile uint8_t* PORTS[CHANNELS_1+CHANNELS_2] = {&PORTC,&PORTC,&PORTC,&PORTC,&PORTC,&PORTD,&PORTD,&PORTD,&PORTD,&PORTD};
volatile uint8_t * PORTS [ CHANNELS_1 + CHANNELS_2 ] = { & PORTC , & PORTC , & PORTC , & PORTD , & PORTD , & PORTD } ;
//volatile uint8_t* DDRS[CHANNELS_1+CHANNELS_2] = {&DDRC,&DDRC,&DDRC,&DDRC,&DDRC,&DDRD,&DDRD,&DDRD,&DDRD,&DDRD};
volatile uint8_t * DDRS [ CHANNELS_1 + CHANNELS_2 ] = { & DDRC , & DDRC , & DDRC , & DDRD , & DDRD , & DDRD } ;
//const uint8_t BITS[CHANNELS_1+CHANNELS_2] = {PC0,PC1,PC2,PC3,PC4,PD2,PD3,PD4,PD5,PD7};
const uint8_t BITS [ CHANNELS_1 + CHANNELS_2 ] = { PC0 , PC1 , PC2 , PD2 , PD3 , PD4 } ;
2013-10-17 16:35:58 +02:00
2013-10-13 16:47:46 +02:00
/* global variables */
2013-10-14 20:19:16 +02:00
# 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 */
2013-10-14 11:24:51 +02:00
volatile uint8_t pwr_ok ; /* is power ok */
volatile uint8_t fan ; /* fan signal state, to measure tachometer */
2013-10-26 17:31:04 +02:00
volatile uint8_t timer2_ovf = 0 ; /* to measure fan speed through the tachometer using timer 2 */
2013-10-17 13:29:01 +02:00
const uint16_t TIMER2_PRESCALE [ 8 ] = { 0 , 1 , 8 , 32 , 64 , 128 , 256 , 1024 } ; /* timer 2 CS2[2:0] values */
2013-10-14 11:24:51 +02:00
volatile uint16_t tachometer = 0 ; /* the tachometer time (from timer) */
volatile uint8_t ir ; /* IR signal state, to measure IR code */
2013-10-17 13:29:01 +02:00
const uint16_t TIMER1_PRESCALE [ 8 ] = { 0 , 1 , 8 , 64 , 256 , 1024 , 0 , 0 } ; /* timer 1 CS1[2:0] values */
2013-10-14 20:19:16 +02:00
volatile uint16_t ir_tick ; /* number of counter ticks per millisecond */
2013-10-14 16:47:25 +02:00
volatile uint8_t pulse = 0 ; /* pulse index within the burst */
2013-10-14 13:36:25 +02:00
# define PULSE_MAX 128 /* maximum number of pulses to save */
2013-10-14 16:47:25 +02:00
uint16_t burst [ PULSE_MAX ] ; /* pulse times forming a burst (from timer) */
2013-10-13 19:39:31 +02:00
2013-10-15 13:20:49 +02:00
/* channel variables */
# define LEVELS 10 /* the number of PWM levels */
2013-10-19 22:17:59 +02:00
volatile uint8_t ch_tick = 0 ; /* to tick for the channel PWM */
uint8_t on [ CHANNELS_1 + CHANNELS_2 ] ; /* at which tick turn the channel on */
uint8_t off [ CHANNELS_1 + CHANNELS_2 ] ; /* at which tick turn the channel off */
2013-10-15 13:20:49 +02:00
2013-10-14 20:19:16 +02:00
/* flags, set in the interrupts and handled in the main program */
2013-10-15 13:20:49 +02:00
volatile bool uart_flag = false ; /* an incoming activity on the UART */
volatile bool power_flag = false ; /* a change in the power or fan */
volatile bool ir_flag = false ; /* to process a burst */
volatile bool channel_flag = false ; /* indicate a change in the channel PWM values */
2013-10-19 13:27:21 +02:00
volatile bool learn_flag = false ; /* learn an IR command for an action */
enum IR_ACTIONS to_learn = IR_ACTION_END ; /* IR action to learn */
2013-10-14 20:19:16 +02:00
2013-10-26 17:31:04 +02:00
/* save the UART input into a string */
ISR ( USART_RX_vect ) { /* UART receive interrupt */
2013-10-14 20:19:16 +02:00
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 + + ;
}
2013-10-15 13:20:49 +02:00
uart_flag = true ; /* set flag */
2013-10-14 20:19:16 +02:00
}
2013-10-13 16:47:46 +02:00
2013-10-26 17:31:04 +02:00
/* power ok and IR interrupt
* store the new power start
* save the pulse time
*/
2013-10-13 19:39:31 +02:00
ISR ( PCINT0_vect ) { /* PCI0 Interrupt Vector for PCINT[7:0] */
2013-10-13 16:47:46 +02:00
if ( pwr_ok ! = ( PINB & ( 1 < < PWR_OK ) ) ) { /* did the PWR_OK pin state changed */
pwr_ok = PINB & ( 1 < < PWR_OK ) ; /* save new state */
2013-10-14 20:19:16 +02:00
power_flag = true ;
2013-10-14 11:24:51 +02:00
} else if ( ir ! = ( PINB & ( 1 < < IR ) ) ) { /* did the IR pin state changed */
ir = PINB & ( 1 < < IR ) ; /* save new state */
2013-10-14 13:36:25 +02:00
if ( pulse > 0 ) { /* save pulse, except the first */
2013-10-26 17:31:04 +02:00
burst [ pulse - 1 ] = ( TCNT1 * 1000UL ) / ir_tick ; /* pulse time in µs */
burst [ pulse ] = 0 ; /* end the burst with 0 */
2013-10-14 11:24:51 +02:00
}
2013-10-14 13:36:25 +02:00
if ( pulse < PULSE_MAX - 1 ) { /* prepare to save next pulse */
pulse + + ;
2013-10-14 11:24:51 +02:00
}
TCNT1 = 0 ; /* clear timer 1 */
2013-10-13 16:47:46 +02:00
}
}
2013-10-13 19:39:31 +02:00
/* 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 */
2013-10-14 11:24:51 +02:00
if ( fan ) { /* only react to rising edge */
tachometer = ( uint16_t ) ( timer2_ovf < < 8 ) + TCNT2 ; /* save time */
2013-10-13 19:39:31 +02:00
TCNT2 = 0 ; /* reset timer 2 */
2013-10-14 11:24:51 +02:00
timer2_ovf = 0 ; /* reset timer 2 */
2013-10-13 19:39:31 +02:00
}
}
}
2013-10-14 11:24:51 +02:00
/* 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 */
2013-10-26 17:31:04 +02:00
} else { /* timeout for tachometer (the tachometer counter should be reseted before this timeout if the fan is on) */
2013-10-14 20:40:52 +02:00
tachometer = 0 ; /* indicate no speed can be measured */
2013-10-14 20:45:08 +02:00
if ( pwr_ok ) { /* warn the fan is dead while the power in on */
power_flag = true ;
}
2013-10-26 17:31:04 +02:00
timer2_ovf = 0 ; /* reset the tachometer counter */
2013-10-14 11:24:51 +02:00
}
}
2013-10-26 17:31:04 +02:00
/* end of IR burst (using a timeout) */
2013-10-14 11:24:51 +02:00
ISR ( TIMER1_COMPA_vect ) { /* timer 1 OCR1A match interrupt vector */
2013-10-26 17:31:04 +02:00
if ( pulse > 0 & & ! ir_flag ) { /* warm a burst is ready to be processed */
2013-10-14 20:40:52 +02:00
ir_flag = true ;
2013-10-13 19:39:31 +02:00
}
}
2013-10-26 17:31:04 +02:00
/* generate a PWM for the channel outputs
* this is a bit long for an interrupt , but it ' s time critical
*/
2013-10-15 13:20:49 +02:00
ISR ( TIMER0_COMPA_vect ) { /* timer 0 OCR0A match interrupt vector */
2013-10-19 22:17:59 +02:00
ch_tick + + ;
2013-10-26 17:31:04 +02:00
if ( pwr_ok ) { /* only generate PWM if power is on */
for ( int i = 0 ; i < CHANNELS_1 + CHANNELS_2 ; i + + ) { /* generate PWM for every channel */
if ( on [ i ] = = ch_tick ) { /* time to switch on */
if ( on [ i ] ! = off [ i ] ) { /* switch on */
2013-10-19 22:17:59 +02:00
* ( PORTS [ i ] ) | = ( 1 < < BITS [ i ] ) ;
2013-10-26 17:31:04 +02:00
} else if ( brightness [ mode ] [ i ] = = 0 ) { /* switch off if it's also the off time and the brightness is 0 */
2013-10-19 22:17:59 +02:00
* ( PORTS [ i ] ) & = ~ ( 1 < < BITS [ i ] ) ;
2013-10-26 17:31:04 +02:00
} else { /* switch on if it's also the off time and the brightness is full */
2013-10-19 22:17:59 +02:00
* ( PORTS [ i ] ) | = ( 1 < < BITS [ i ] ) ;
}
2013-10-26 17:31:04 +02:00
} else if ( off [ i ] = = ch_tick & & brightness [ mode ] [ i ] ! = 0xff ) { /* time to switch off */
2013-10-19 22:17:59 +02:00
* ( PORTS [ i ] ) & = ~ ( 1 < < BITS [ i ] ) ;
}
}
}
2013-10-15 13:20:49 +02:00
}
2013-10-26 17:31:04 +02:00
/* disable watchdog when booting */
2013-10-19 19:51:18 +02:00
void wdt_init ( void ) __attribute__ ( ( naked ) ) __attribute__ ( ( section ( " .init3 " ) ) ) ;
void wdt_init ( void )
{
MCUSR = 0 ;
wdt_disable ( ) ;
return ;
}
2013-10-17 13:29:01 +02:00
void ioinit ( void )
2013-10-12 17:05:01 +02:00
{
2013-10-13 11:46:53 +02:00
/* configure power */
2013-10-13 16:47:46 +02:00
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 */
2013-10-13 11:46:53 +02:00
2013-10-14 11:24:51 +02:00
/* configure LED (on PD6/OC0A) */
DDRD | = ( 1 < < LED ) ; /* LED is output */
2013-10-15 13:20:49 +02:00
PORTD & = ~ ( 1 < < LED ) ; /* switch LED on */
2013-10-14 11:24:51 +02:00
2013-10-13 19:39:31 +02:00
/* 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 ) ;
2013-10-14 11:24:51 +02:00
/* clock/64 prescaler */
TCCR2B | = ( 1 < < CS22 ) ;
TCCR2B & = ~ ( ( 1 < < CS21 ) | ( 1 < < CS20 ) ) ;
2013-10-16 21:56:05 +02:00
TIFR2 = ( 1 < < TOV2 ) ; /* clear timer 2 overflow interrupt flag */
2013-10-13 19:39:31 +02:00
TIMSK2 | = ( 1 < < TOIE2 ) ; /* enable timer 2 overflow interrupt */
2013-10-13 11:46:53 +02:00
2013-10-14 11:24:51 +02:00
/* configure IR receiver */
DDRB & = ~ ( 1 < < IR ) ; /* IR (PB0/PCINT0) receiver is input */
ir = PINB & ( 1 < < IR ) ; /* save state */
2013-10-16 21:56:05 +02:00
PCIFR = ( 1 < < PCIF0 ) ; /* clear interrupt flag */
2013-10-14 11:24:51 +02:00
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 ) ) ;
2013-10-14 20:19:16 +02:00
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) */
}
2013-10-15 13:20:49 +02:00
TIFR1 & = ~ ( 1 < < OCF1A ) ; /* clear timer 1 compare interrupt flag */
TIMSK1 | = ( 1 < < OCIE1A ) ; /* enable timer 1 compare interrupt */
2013-10-14 11:24:51 +02:00
2013-10-13 11:46:53 +02:00
/* configure channels (used for powering LEDs using an nMOS) */
2013-10-17 16:54:13 +02:00
for ( uint8_t i = 0 ; i < CHANNELS_1 + CHANNELS_2 ; i + + ) {
* ( DDRS [ i ] ) | = ( 1 < < BITS [ i ] ) ;
* ( PORTS [ i ] ) & = ~ ( 1 < < BITS [ i ] ) ;
}
2013-10-13 11:46:53 +02:00
2013-10-15 13:20:49 +02:00
/* use timer 0 as source for the channel PWM */
/* use CTC mode */
TCCR0A | = ( 1 < < WGM01 ) ;
TCCR0A & = ~ ( 1 < < WGM00 ) ;
TCCR0B & = ~ ( 1 < < WGM02 ) ;
2013-10-19 19:51:18 +02:00
/* /8 prescale timer */
2013-10-15 13:20:49 +02:00
TCCR0B & = ~ ( 1 < < CS02 ) ;
2013-10-19 19:51:18 +02:00
TCCR0B | = ( 1 < < CS01 ) ;
TCCR0B & = ~ ( 1 < < CS00 ) ;
2013-10-19 22:17:59 +02:00
OCR0A = 0x40 ; /* set PWM frequency (with prescale=8, 0x00=2304kHz-0xff=9kHz) */
2013-10-16 21:56:05 +02:00
TIFR0 = ( 1 < < OCF0A ) ; /* clear timer 0 compare interrupt flag */
2013-10-15 13:20:49 +02:00
TIMSK0 | = ( 1 < < OCIE0A ) ; /* enable timer 0 compare interrupt */
2013-10-26 23:15:56 +02:00
/* configure internal temperature sensor */
ADMUX | = ( 1 < < REFS1 ) | ( 1 < < REFS0 ) ; /* select internal 1.1V voltage reference */
/* select ADC8 internal temperature sensor */
ADMUX | = ( 1 < < MUX3 ) ;
ADMUX & = ~ ( ( 1 < < MUX2 ) | ( 1 < < MUX1 ) | ( 1 < < MUX0 ) ) ;
ADCSRA | = ( 1 < < ADEN ) ; /* enable ADC */
ADCSRA | = ( ( 1 < < ADPS2 ) | ( 1 < < ADPS1 ) | ( 1 < < ADPS0 ) ) ; /* prescaler at 128 */
/* continuously do the conversion (then don't wait for ADCS to clear) */
//ADCSRB &= ~((1<<ADTS2)|(1<<ADTS1)|(1<<ADTS0)); /* free-running mode */
//ADCSRA |= (1<<ADATE); /* conversion can be triggered */
//ADCSRA |= (1<<ADSC); /* start analogue to digital conversion */
2013-10-13 11:46:53 +02:00
/* use UART as terminal */
uart_init ( ) ;
2013-10-12 18:19:39 +02:00
stdout = & uart_output ;
stdin = & uart_input ;
2013-10-13 11:46:53 +02:00
2013-10-13 16:47:46 +02:00
sei ( ) ; /* enable interrupts */
2013-10-12 17:05:01 +02:00
}
2013-10-19 13:27:21 +02:00
void help ( void )
{
2013-10-19 19:51:18 +02:00
char * str ;
2013-10-26 17:31:04 +02:00
for ( uint8_t i = 0 ; i < sizeof ( help_table ) / sizeof ( PGM_P ) ; i + + ) { /* display all help lines */
2013-10-19 19:51:18 +02:00
str = malloc ( strlen_PF ( ( uint_farptr_t ) pgm_read_word ( & ( help_table [ i ] ) ) ) ) ;
strcpy_PF ( str , ( uint_farptr_t ) pgm_read_word ( & ( help_table [ i ] ) ) ) ;
printf ( str ) ;
free ( str ) ;
2013-10-19 13:27:21 +02:00
}
}
2013-10-12 17:05:01 +02:00
int main ( void )
{
ioinit ( ) ; /* initialize IOs */
2013-10-14 20:19:16 +02:00
uint8_t command_i = 0 ; /* command index */
2013-10-26 17:31:04 +02:00
/* last IR data */
struct nec ir_data ;
2013-10-14 20:45:08 +02:00
ir_data . valid = false ;
ir_data . repeat = false ;
ir_data . address = 0 ;
ir_data . command = 0 ;
uint8_t ir_repeat = 0 ; /* number of times the IR data has been repeated */
2013-10-12 17:05:01 +02:00
2013-10-14 11:24:51 +02:00
puts ( " LED dimmer up & running " ) ;
2013-10-17 16:54:13 +02:00
2013-10-19 19:51:18 +02:00
/* load (or initialize) settings */
2013-10-17 16:54:13 +02:00
if ( ! verify_settings ( ) ) {
2013-10-17 16:35:58 +02:00
initialize_settings ( ) ;
save_settings ( ) ;
2013-10-17 16:54:13 +02:00
if ( ! verify_settings ( ) ) {
puts ( " can't store setting " ) ;
return 0 ;
2013-10-17 16:35:58 +02:00
}
2013-10-19 13:27:21 +02:00
puts ( " settings created " ) ;
} else {
load_settings ( ) ;
puts ( " settings loaded " ) ;
2013-10-17 16:35:58 +02:00
}
2013-10-19 22:17:59 +02:00
channel_flag = true ; /* calculate channel values later */
2013-10-17 16:54:13 +02:00
2013-10-19 19:51:18 +02:00
/* switch power supply as saved */
2013-10-19 14:36:03 +02:00
if ( power ) {
PORTB & = ~ ( 1 < < nPS_ON ) ;
} else {
PORTB | = ( 1 < < nPS_ON ) ;
}
2013-10-26 17:31:04 +02:00
2013-10-26 23:15:56 +02:00
PORTD & = ~ ( 1 < < LED ) ; /* switch on LED */
2013-10-14 20:19:16 +02:00
while ( true ) {
2013-10-26 17:31:04 +02:00
/* calculated PWM values (on/off times) */
2013-10-15 13:20:49 +02:00
while ( channel_flag ) {
2013-10-26 17:31:04 +02:00
/* the next channel goes on/starts when the previous goes off, so to distribute the power over the whole range, instead of only using a lot of it in the beginning */
2013-10-15 13:20:49 +02:00
uint8_t start = 0 ;
2013-10-17 13:29:01 +02:00
for ( uint8_t i = 0 ; i < CHANNELS_1 + CHANNELS_2 ; i + + ) {
2013-10-19 22:17:59 +02:00
on [ i ] = start ;
start + = brightness [ mode ] [ i ] ;
off [ i ] = start ;
2013-10-15 13:20:49 +02:00
}
channel_flag = false ;
}
/* handle UART input */
while ( uart_flag ) {
2013-10-26 23:15:56 +02:00
PORTD | = ( 1 < < LED ) ; /* switch off LED */
2013-10-14 20:40:52 +02:00
/* echo back */
char c = 0 ;
2013-10-14 20:19:16 +02:00
while ( command_i < input_i ) {
c = input [ command_i + + ] ;
2013-10-14 20:40:52 +02:00
putchar ( c ) ;
2013-10-14 20:19:16 +02:00
}
2013-10-26 17:31:04 +02:00
/* detect end of line */
2013-10-19 13:27:21 +02:00
if ( ' \n ' = = c | | ' \r ' = = c ) {
2013-10-26 17:31:04 +02:00
if ( ' \r ' = = c ) { /* display new line */
2013-10-19 13:27:21 +02:00
puts ( " " ) ;
}
2013-10-26 17:31:04 +02:00
/* process user command */
2013-10-19 13:27:21 +02:00
if ( command_i > 1 ) {
input [ command_i - 1 ] = ' \0 ' ;
uart_action ( input ) ;
2013-10-19 19:51:18 +02:00
save_settings ( ) ;
2013-10-19 13:27:21 +02:00
}
2013-10-26 17:31:04 +02:00
input_i = command_i = 0 ; /* reset input buffer */
2013-10-19 13:27:21 +02:00
}
2013-10-15 13:20:49 +02:00
uart_flag = false ;
2013-10-26 23:15:56 +02:00
PORTD & = ~ ( 1 < < LED ) ; /* switch on LED */
2013-10-14 20:19:16 +02:00
}
2013-10-14 20:40:52 +02:00
/* handle power state */
while ( power_flag ) {
if ( pwr_ok ) {
puts ( " power ok " ) ;
2013-10-19 14:36:03 +02:00
power = 1 ;
2013-10-14 20:40:52 +02:00
/* verify if FAN is present */
2013-10-14 20:45:08 +02:00
_delay_ms ( 500 ) ;
2013-10-14 20:40:52 +02:00
if ( 0 = = tachometer ) {
puts ( " FAN not on, switching power off " ) ;
PORTB | = ( 1 < < nPS_ON ) ;
2013-10-19 14:36:03 +02:00
power = 0 ;
2013-10-13 11:46:53 +02:00
}
2013-10-14 20:40:52 +02:00
} else {
2013-10-14 20:45:08 +02:00
puts ( " power off " ) ;
2013-10-19 19:51:18 +02:00
for ( uint8_t i = 0 ; i < CHANNELS_1 + CHANNELS_2 ; i + + ) { /* switch off output */
* ( PORTS [ i ] ) & = ~ ( 1 < < BITS [ i ] ) ;
}
2013-10-19 14:36:03 +02:00
power = 0 ;
2013-10-14 20:40:52 +02:00
}
2013-10-19 14:36:03 +02:00
save_settings ( ) ;
2013-10-14 20:45:08 +02:00
power_flag = false ;
2013-10-14 20:40:52 +02:00
}
/* handle IR input */
while ( ir_flag ) {
2013-10-26 23:15:56 +02:00
PORTD | = ( 1 < < LED ) ; /* switch off LED */
2013-10-14 20:40:52 +02:00
time2nec ( burst , pulse - 1 ) ; /* convert raw time burst in NEC format */
2013-10-14 20:45:08 +02:00
struct nec ir_tmp = nec2data ( burst , pulse - 1 ) ; /* decode NEC burst */
if ( ir_tmp . valid ) {
if ( ir_tmp . repeat ) {
if ( ir_repeat < 0xff ) {
ir_repeat + + ;
}
2013-10-13 11:46:53 +02:00
} else {
2013-10-14 20:45:08 +02:00
ir_data = ir_tmp ;
ir_repeat = 0 ;
}
2013-10-26 17:31:04 +02:00
if ( ir_repeat = = 0 | | ir_repeat > 3 ) { /* process command if new or repeated */
if ( learn_flag ) { /* learn command */
2013-10-19 13:27:21 +02:00
if ( to_learn < IR_ACTION_END ) {
ir_keys [ to_learn ] [ 0 ] = ir_data . address ;
ir_keys [ to_learn ] [ 1 ] = ir_data . command ;
}
puts ( " IR code learned " ) ;
to_learn = IR_ACTION_END ;
learn_flag = false ;
2013-10-26 17:31:04 +02:00
} else { /* trigger action */
2013-10-19 13:27:21 +02:00
ir_action ( ir_data . address , ir_data . command ) ;
}
2013-10-19 14:36:03 +02:00
save_settings ( ) ;
2013-10-13 12:19:17 +02:00
}
2013-10-14 20:40:52 +02:00
}
pulse = 0 ; /* reset burst */
ir_flag = false ;
2013-10-26 23:15:56 +02:00
PORTD & = ~ ( 1 < < LED ) ; /* switch on LED */
2013-10-14 20:40:52 +02:00
}
}
return 0 ;
}
2013-10-19 19:51:18 +02:00
void uart_action ( char * str )
{
2013-10-26 17:31:04 +02:00
/* split command */
2013-10-19 19:51:18 +02:00
const char * delimiter = " " ;
char * word = strtok ( str , delimiter ) ;
if ( ! word ) {
goto error ;
}
2013-10-26 17:31:04 +02:00
/* parse command */
2013-10-19 19:51:18 +02:00
if ( 0 = = strcmp ( word , " help " ) ) {
help ( ) ;
} else if ( 0 = = strcmp ( word , " reset " ) ) {
reset_settings ( ) ;
/* reset using watchdog */
do {
wdt_enable ( WDTO_15MS ) ;
for ( ; ; ) { }
} while ( 0 ) ;
} else if ( 0 = = strcmp ( word , " power " ) ) {
word = strtok ( NULL , delimiter ) ;
if ( ! word ) {
if ( PINB & ( 1 < < nPS_ON ) ) {
puts ( " power is off " ) ;
} else {
puts ( " power is on " ) ;
}
} else if ( 0 = = strcmp ( word , " on " ) ) {
PORTB & = ~ ( 1 < < nPS_ON ) ;
} else if ( 0 = = strcmp ( word , " off " ) ) {
PORTB | = ( 1 < < nPS_ON ) ;
} else {
goto error ;
}
} else if ( 0 = = strcmp ( word , " fan " ) ) {
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 is off (or not detected) \n " ) ;
}
} else {
printf ( " fan speed measurement not started \n " ) ;
}
} else {
printf ( " fan is off (or not detected) \n " ) ;
}
2013-10-26 23:15:56 +02:00
} else if ( 0 = = strcmp ( word , " temp " ) ) {
ADCSRA | = ( 1 < < ADSC ) ; /* start analogue to digital conversion */
loop_until_bit_is_clear ( ADCSRA , ADSC ) ; /* wait until conversion is finished */
printf ( " temperature: %u°C \n " , ( uint16_t ) ( ( ADCW - T_OFFSET ) * T_FACTOR ) ) ;
2013-10-19 22:52:50 +02:00
} else if ( 0 = = strcmp ( word , " mode " ) ) {
word = strtok ( NULL , delimiter ) ;
if ( ! word ) {
printf ( " %u/%u \n " , mode + 1 , MODES ) ;
} else {
/* expecting one digit mode N */
if ( strlen ( word ) ! = 1 ) {
goto error ;
}
/* expecting mode 1-MODES */
if ( word [ 0 ] < ' 1 ' ) {
goto error ;
}
uint8_t mode_temp = word [ 0 ] - ' 1 ' ;
if ( mode_temp > MODES ) {
goto error ;
}
mode = mode_temp ;
channel_flag = true ;
}
2013-10-19 19:51:18 +02:00
} else if ( 0 = = strcmp ( word , " ir " ) ) {
word = strtok ( NULL , delimiter ) ;
if ( 0 = = strcmp ( word , " learn " ) ) {
word = strtok ( NULL , delimiter ) ;
if ( 0 = = strcmp ( word , " power " ) ) {
to_learn = POWER ;
learn_flag = true ;
} else if ( 0 = = strcmp ( word , " mode " ) ) {
to_learn = MODE ;
learn_flag = true ;
} else if ( 0 = = strcmp ( word , " brightness " ) ) {
word = strtok ( NULL , delimiter ) ;
if ( 0 = = strcmp ( word , " up " ) ) {
to_learn = BRIGHTNESS_UP ;
learn_flag = true ;
} else if ( 0 = = strcmp ( word , " down " ) ) {
to_learn = BRIGHTNESS_DOWN ;
learn_flag = true ;
} else {
goto error ;
}
} else if ( 0 = = strcmp ( word , " channel " ) ) {
word = strtok ( NULL , delimiter ) ;
if ( 0 = = strcmp ( word , " next " ) ) {
to_learn = CHANNEL_NEXT ;
learn_flag = true ;
} else if ( 0 = = strcmp ( word , " previous " ) ) {
to_learn = CHANNEL_PREVIOUS ;
learn_flag = true ;
} else {
goto error ;
}
} else {
goto error ;
}
} else {
goto error ;
}
} else if ( 0 = = strcmp ( word , " ch " ) ) {
word = strtok ( NULL , delimiter ) ;
if ( ! word ) { /* expecting channel group */
goto error ;
}
if ( strlen ( word ) ! = 1 ) { /* expecting one digit channel group X */
goto error ;
}
uint8_t group = 0 ;
switch ( word [ 0 ] ) { /* expecting channel group X 1 or 2 */
case ' 1 ' :
group = 0 ;
break ;
case ' 2 ' :
group = 1 ;
break ;
default :
goto error ;
}
/* get channel output */
word = strtok ( NULL , delimiter ) ;
if ( ! word ) {
goto error ;
}
/* expecting one digit channel output Y */
if ( strlen ( word ) ! = 1 ) {
goto error ;
}
/* expecting channel group Y 1-CHANNELS_X */
if ( word [ 0 ] < ' 1 ' ) {
goto error ;
}
uint8_t output = word [ 0 ] - ' 1 ' ;
if ( group = = 0 & & output > CHANNELS_1 ) {
goto error ;
} else if ( group = = 1 & & output > CHANNELS_2 ) {
goto error ;
}
uint8_t channel = group * CHANNELS_1 + output ;
/* brightness setting */
word = strtok ( NULL , delimiter ) ;
if ( ! word ) {
printf ( " %u \n " , brightness [ mode ] [ channel ] ) ;
} else {
if ( strlen ( word ) > 3 ) {
goto error ;
}
uint16_t br = atoi ( word ) ;
if ( br > 0xff ) {
goto error ;
}
brightness [ mode ] [ channel ] = ( uint8_t ) br ;
2013-10-19 22:17:59 +02:00
channel_flag = true ;
2013-10-19 19:51:18 +02:00
}
} else {
goto error ;
}
if ( learn_flag ) {
puts ( " press button on remote to learn code " ) ;
}
return ;
error :
puts ( " command not recognized " ) ;
}
2013-10-19 13:27:21 +02:00
void ir_action ( uint8_t address , uint8_t command )
2013-10-14 20:40:52 +02:00
{
2013-10-19 13:27:21 +02:00
enum IR_ACTIONS ir_code = IR_ACTION_END ;
2013-10-19 23:36:48 +02:00
static uint8_t channel = CHANNELS_1 + CHANNELS_2 ;
2013-10-26 17:31:04 +02:00
uint8_t step = 0xff / LEVELS ; /* the brightness increase/decrease steps */
for ( ir_code = 0 ; ir_code < IR_ACTION_END ; ir_code + + ) { /* find the action for the current command */
2013-10-19 13:27:21 +02:00
if ( ir_keys [ ir_code ] [ 0 ] = = address & & ir_keys [ ir_code ] [ 1 ] = = command ) {
2013-10-14 20:40:52 +02:00
break ;
2013-10-19 13:27:21 +02:00
}
}
2013-10-26 17:31:04 +02:00
if ( ir_code < IR_ACTION_END ) { /* process action */
2013-10-19 13:27:21 +02:00
switch ( ir_code ) {
case POWER :
printf ( " switching power supply " ) ;
if ( PINB & ( 1 < < nPS_ON ) ) {
puts ( " on " ) ;
2013-10-13 19:39:31 +02:00
} else {
2013-10-19 13:27:21 +02:00
puts ( " off " ) ;
2013-10-13 19:39:31 +02:00
}
2013-10-19 13:27:21 +02:00
PINB | = ( 1 < < nPS_ON ) ;
break ;
case MODE :
2013-10-19 23:36:48 +02:00
mode = ( mode + 1 ) % MODES ;
printf ( " selecting mode %u/%u \n " , mode + 1 , MODES ) ;
channel_flag = true ;
2013-10-19 13:27:21 +02:00
break ;
case BRIGHTNESS_UP :
2013-10-19 23:36:48 +02:00
if ( channel < CHANNELS_1 + CHANNELS_2 ) {
if ( brightness [ mode ] [ channel ] < 0xff - step ) {
brightness [ mode ] [ channel ] + = step ;
} else {
brightness [ mode ] [ channel ] = 0xff ;
}
printf ( " increasing brightness ch %u %u: %u \n " , ( channel / CHANNELS_1 ) + 1 , ( channel % CHANNELS_1 ) + 1 , brightness [ mode ] [ channel ] ) ;
2013-10-26 17:31:04 +02:00
} else if ( channel = = CHANNELS_1 + CHANNELS_2 ) { /* increase all brightness if no channel is selected */
2013-10-21 11:25:22 +02:00
for ( uint8_t i = 0 ; i < CHANNELS_1 + CHANNELS_2 ; i + + ) {
if ( brightness [ mode ] [ i ] < 0xff - step ) {
brightness [ mode ] [ i ] + = step ;
} else {
brightness [ mode ] [ i ] = 0xff ;
}
}
printf ( " increasing brightness all channels \n " ) ;
2013-10-19 23:36:48 +02:00
}
channel_flag = true ;
2013-10-19 13:27:21 +02:00
break ;
case BRIGHTNESS_DOWN :
2013-10-19 23:36:48 +02:00
if ( channel < CHANNELS_1 + CHANNELS_2 ) {
if ( brightness [ mode ] [ channel ] > step ) {
brightness [ mode ] [ channel ] - = step ;
} else {
brightness [ mode ] [ channel ] = 0x00 ;
}
2013-10-21 11:25:22 +02:00
printf ( " decreasing brightness ch %u %u: %u \n " , ( channel / CHANNELS_1 ) + 1 , ( channel % CHANNELS_1 ) + 1 , brightness [ mode ] [ channel ] ) ;
2013-10-26 17:31:04 +02:00
} else if ( channel = = CHANNELS_1 + CHANNELS_2 ) { /* decrease all brightness if no channel is selected */
2013-10-21 11:25:22 +02:00
for ( uint8_t i = 0 ; i < CHANNELS_1 + CHANNELS_2 ; i + + ) {
if ( brightness [ mode ] [ i ] > step ) {
brightness [ mode ] [ i ] - = step ;
} else {
brightness [ mode ] [ i ] = 0x00 ;
}
}
printf ( " decreasing brightness all channels \n " ) ;
2013-10-19 23:36:48 +02:00
}
channel_flag = true ;
2013-10-19 13:27:21 +02:00
break ;
case CHANNEL_NEXT :
2013-10-21 11:25:22 +02:00
if ( channel < = CHANNELS_1 + CHANNELS_2 ) {
channel = ( channel + 1 ) % ( CHANNELS_1 + CHANNELS_2 + 1 ) ;
2013-10-19 23:36:48 +02:00
} else {
channel = 0x00 ;
}
printf ( " selecting channel ch %u %u (%u) \n " , ( channel / CHANNELS_1 ) + 1 , ( channel % CHANNELS_1 ) + 1 , brightness [ mode ] [ channel ] ) ;
2013-10-19 13:27:21 +02:00
break ;
case CHANNEL_PREVIOUS :
2013-10-19 23:36:48 +02:00
if ( 0 = = channel ) {
2013-10-21 11:25:22 +02:00
channel = CHANNELS_1 + CHANNELS_2 ;
2013-10-19 23:36:48 +02:00
} else {
channel - - ;
}
printf ( " selecting channel ch %u %u (%u) \n " , ( channel / CHANNELS_1 ) + 1 , ( channel % CHANNELS_1 ) + 1 , brightness [ mode ] [ channel ] ) ;
2013-10-19 13:27:21 +02:00
break ;
default :
printf ( " unhandled IR action: %u \n " , ir_code ) ;
break ;
2013-10-14 20:45:08 +02:00
}
} else {
2013-10-26 17:31:04 +02:00
puts ( " unknown IR command " ) ;
2013-10-14 20:45:08 +02:00
}
}