comments added
This commit is contained in:
parent
fc48ec6c2e
commit
e4ed9649b5
|
@ -37,6 +37,7 @@ void time2nec(uint16_t* burst, uint8_t pulses)
|
|||
return;
|
||||
}
|
||||
if (0==i%2) { /* mark */
|
||||
/* fine the nearest NEC mark time */
|
||||
bool found = false;
|
||||
for (j=0; j<(sizeof(MARKS)/sizeof(uint16_t))-1; j++) {
|
||||
if (burst[i]>((MARKS[j]+MARKS[j+1])/2)) {
|
||||
|
@ -49,6 +50,7 @@ void time2nec(uint16_t* burst, uint8_t pulses)
|
|||
burst[i] = j;
|
||||
}
|
||||
} else { /* space */
|
||||
/* fine the nearest NEC space time */
|
||||
bool found = false;
|
||||
for (j=0; j<(sizeof(SPACES)/sizeof(uint16_t))-1; j++) {
|
||||
if (burst[i]>((SPACES[j]+SPACES[j+1])/2)) {
|
||||
|
@ -83,17 +85,18 @@ struct nec nec2data(uint16_t* burst, uint8_t pulses)
|
|||
to_return.valid = false;
|
||||
}
|
||||
}
|
||||
/* decode the valid data into address and command */
|
||||
if (to_return.valid) {
|
||||
uint8_t address = (data>>24)&0xff;
|
||||
uint8_t naddress = (data>>16)&0xff;
|
||||
uint8_t command = (data>>8)&0xff;
|
||||
uint8_t ncommand = (data>>0)&0xff;
|
||||
if (0xff==(address^naddress)) {
|
||||
uint8_t address = (data>>24)&0xff; /* first byte is the address */
|
||||
uint8_t naddress = (data>>16)&0xff; /* second byte is the inverted address */
|
||||
uint8_t command = (data>>8)&0xff; /* third byte is the command */
|
||||
uint8_t ncommand = (data>>0)&0xff; /* fourth byte is the inverted command */
|
||||
if (0xff==(address^naddress)) { /* check if the data is not corrupted (the address comes also inverted) */
|
||||
to_return.address = address;
|
||||
} else {
|
||||
to_return.valid = false;
|
||||
}
|
||||
if (0xff==(command^ncommand)) {
|
||||
if (0xff==(command^ncommand)) { /* check if the data is not corrupted (the command comes also inverted) */
|
||||
to_return.command = command;
|
||||
} else {
|
||||
to_return.valid = false;
|
||||
|
|
|
@ -17,9 +17,10 @@
|
|||
* More information at http://www.sbprojects.com/knowledge/ir/nec.php
|
||||
*/
|
||||
|
||||
extern const uint16_t MARKS[2]; /* mark duration in us [start,bit] */
|
||||
extern const uint16_t SPACES[4]; /* space duration in us [start,repeat,1,0] */
|
||||
extern const uint16_t MARKS[2]; /* mark duration in µs [start,bit] */
|
||||
extern const uint16_t SPACES[4]; /* space duration in µs [start,repeat,1,0] */
|
||||
|
||||
/* a decoded NEC IR command */
|
||||
struct nec {
|
||||
bool valid;
|
||||
bool repeat;
|
||||
|
@ -27,5 +28,7 @@ struct nec {
|
|||
uint8_t command;
|
||||
};
|
||||
|
||||
/* convert the pulse from µs into NEC mark and spaces */
|
||||
void time2nec(uint16_t* burst, uint8_t pulses);
|
||||
/* decode the NEC mark and space into IR command data */
|
||||
struct nec nec2data(uint16_t* burst, uint8_t pulses);
|
||||
|
|
|
@ -85,7 +85,7 @@ 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 */
|
||||
volatile uint8_t timer2_ovf = 0; /* to measure fan speed through the tachometer using timer 2 */
|
||||
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 */
|
||||
|
@ -109,8 +109,8 @@ volatile bool channel_flag = false; /* indicate a change in the channel PWM valu
|
|||
volatile bool learn_flag = false; /* learn an IR command for an action */
|
||||
enum IR_ACTIONS to_learn = IR_ACTION_END; /* IR action to learn */
|
||||
|
||||
/* UART receive interrupt */
|
||||
ISR(USART_RX_vect) {
|
||||
/* save the UART input into a string */
|
||||
ISR(USART_RX_vect) { /* UART receive interrupt */
|
||||
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 */
|
||||
|
@ -119,7 +119,10 @@ ISR(USART_RX_vect) {
|
|||
uart_flag = true; /* set flag */
|
||||
}
|
||||
|
||||
/* power ok interrupt */
|
||||
/* power ok and IR interrupt
|
||||
* store the new power start
|
||||
* save the pulse time
|
||||
*/
|
||||
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 */
|
||||
|
@ -127,8 +130,8 @@ ISR(PCINT0_vect) { /* PCI0 Interrupt Vector for PCINT[7:0] */
|
|||
} 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;
|
||||
burst[pulse-1] = (TCNT1*1000UL)/ir_tick; /* pulse time in µs */
|
||||
burst[pulse] = 0; /* end the burst with 0 */
|
||||
}
|
||||
if (pulse<PULSE_MAX-1) { /* prepare to save next pulse */
|
||||
pulse++;
|
||||
|
@ -153,44 +156,45 @@ ISR(PCINT1_vect) { /* PCI0 Interrupt Vector for PCINT[14:8] */
|
|||
ISR(TIMER2_OVF_vect) { /* timer 2 overflow interrupt vector */
|
||||
if (timer2_ovf<0xff) { /* prevent overflow */
|
||||
timer2_ovf++; /* increase tachometer counter */
|
||||
} else {
|
||||
} else { /* timeout for tachometer (the tachometer counter should be reseted before this timeout if the fan is on) */
|
||||
tachometer = 0; /* indicate no speed can be measured */
|
||||
if (pwr_ok) { /* warn the fan is dead while the power in on */
|
||||
power_flag = true;
|
||||
}
|
||||
timer2_ovf = 0;
|
||||
timer2_ovf = 0; /* reset the tachometer counter */
|
||||
}
|
||||
}
|
||||
|
||||
/* timer 1 interrupt used to timeout IR burst */
|
||||
/* end of IR burst (using a timeout) */
|
||||
ISR(TIMER1_COMPA_vect) { /* timer 1 OCR1A match interrupt vector */
|
||||
if (pulse>0 && !ir_flag) { /* warm an burst is ready when the timeout triggered */
|
||||
if (pulse>0 && !ir_flag) { /* warm a burst is ready to be processed */
|
||||
ir_flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* timer 0 interrupt used generate a PWM for the channels */
|
||||
/* generate a PWM for the channel outputs
|
||||
* this is a bit long for an interrupt, but it's time critical
|
||||
*/
|
||||
ISR(TIMER0_COMPA_vect) { /* timer 0 OCR0A match interrupt vector */
|
||||
ch_tick++;
|
||||
if (pwr_ok) {
|
||||
for (int i=0; i<CHANNELS_1+CHANNELS_2; i++) {
|
||||
if (on[i]==ch_tick) {
|
||||
if (on[i]!=off[i]) {
|
||||
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 */
|
||||
*(PORTS[i]) |= (1<<BITS[i]);
|
||||
} else if (brightness[mode][i]==0) {
|
||||
} else if (brightness[mode][i]==0) { /* switch off if it's also the off time and the brightness is 0 */
|
||||
*(PORTS[i]) &= ~(1<<BITS[i]);
|
||||
} else {
|
||||
} else { /* switch on if it's also the off time and the brightness is full */
|
||||
*(PORTS[i]) |= (1<<BITS[i]);
|
||||
}
|
||||
} else if (off[i]==ch_tick && brightness[mode][i]!=0xff) {
|
||||
} else if (off[i]==ch_tick && brightness[mode][i]!=0xff) { /* time to switch off */
|
||||
*(PORTS[i]) &= ~(1<<BITS[i]);
|
||||
}
|
||||
}
|
||||
PIND = (1<<LED);
|
||||
}
|
||||
}
|
||||
|
||||
/* disable watched when booting */
|
||||
/* disable watchdog when booting */
|
||||
void wdt_init(void) __attribute__((naked)) __attribute__((section(".init3")));
|
||||
void wdt_init(void)
|
||||
{
|
||||
|
@ -283,7 +287,7 @@ void ioinit(void)
|
|||
void help(void)
|
||||
{
|
||||
char* str;
|
||||
for (uint8_t i=0; i<sizeof(help_table)/sizeof(PGM_P); i++) {
|
||||
for (uint8_t i=0; i<sizeof(help_table)/sizeof(PGM_P); i++) { /* display all help lines */
|
||||
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);
|
||||
|
@ -296,7 +300,8 @@ int main(void)
|
|||
ioinit(); /* initialize IOs */
|
||||
|
||||
uint8_t command_i = 0; /* command index */
|
||||
struct nec ir_data; /* last IR data */
|
||||
/* last IR data */
|
||||
struct nec ir_data;
|
||||
ir_data.valid = false;
|
||||
ir_data.repeat = false;
|
||||
ir_data.address = 0;
|
||||
|
@ -326,10 +331,12 @@ int main(void)
|
|||
} else {
|
||||
PORTB |= (1<<nPS_ON);
|
||||
}
|
||||
|
||||
|
||||
PIND |= ~(1<<LED); /* switch on LED */
|
||||
while (true) {
|
||||
/* calculated PWM values */
|
||||
/* calculated PWM values (on/off times) */
|
||||
while (channel_flag) {
|
||||
/* 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 */
|
||||
uint8_t start = 0;
|
||||
for (uint8_t i=0; i<CHANNELS_1+CHANNELS_2; i++) {
|
||||
on[i] = start;
|
||||
|
@ -340,24 +347,28 @@ int main(void)
|
|||
}
|
||||
/* handle UART input */
|
||||
while (uart_flag) {
|
||||
PIND &= (1<<LED); /* switch off LED */
|
||||
/* echo back */
|
||||
char c = 0;
|
||||
while (command_i<input_i) {
|
||||
c = input[command_i++];
|
||||
putchar(c);
|
||||
}
|
||||
/* detect end of line */
|
||||
if ('\n'==c || '\r'==c) {
|
||||
if ('\r'==c) {
|
||||
if ('\r'==c) { /* display new line */
|
||||
puts("");
|
||||
}
|
||||
/* process user command */
|
||||
if (command_i>1) {
|
||||
input[command_i-1] = '\0';
|
||||
uart_action(input);
|
||||
save_settings();
|
||||
}
|
||||
input_i = command_i = 0;
|
||||
input_i = command_i = 0; /* reset input buffer */
|
||||
}
|
||||
uart_flag = false;
|
||||
PIND |= ~(1<<LED); /* switch on LED */
|
||||
}
|
||||
/* handle power state */
|
||||
while (power_flag) {
|
||||
|
@ -383,6 +394,7 @@ int main(void)
|
|||
}
|
||||
/* handle IR input */
|
||||
while (ir_flag) {
|
||||
PIND &= (1<<LED); /* switch off LED */
|
||||
time2nec(burst,pulse-1); /* convert raw time burst in NEC format */
|
||||
struct nec ir_tmp = nec2data(burst,pulse-1); /* decode NEC burst */
|
||||
if (ir_tmp.valid) {
|
||||
|
@ -394,8 +406,8 @@ int main(void)
|
|||
ir_data = ir_tmp;
|
||||
ir_repeat = 0;
|
||||
}
|
||||
if (ir_repeat==0 || ir_repeat>3) {
|
||||
if (learn_flag) {
|
||||
if (ir_repeat==0 || ir_repeat>3) { /* process command if new or repeated */
|
||||
if (learn_flag) { /* learn command */
|
||||
if (to_learn<IR_ACTION_END) {
|
||||
ir_keys[to_learn][0] = ir_data.address;
|
||||
ir_keys[to_learn][1] = ir_data.command;
|
||||
|
@ -403,7 +415,7 @@ int main(void)
|
|||
puts("IR code learned");
|
||||
to_learn = IR_ACTION_END;
|
||||
learn_flag = false;
|
||||
} else {
|
||||
} else { /* trigger action */
|
||||
ir_action(ir_data.address,ir_data.command);
|
||||
}
|
||||
save_settings();
|
||||
|
@ -411,6 +423,7 @@ int main(void)
|
|||
}
|
||||
pulse = 0; /* reset burst */
|
||||
ir_flag = false;
|
||||
PIND |= ~(1<<LED); /* switch on LED */
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -418,11 +431,13 @@ int main(void)
|
|||
|
||||
void uart_action(char* str)
|
||||
{
|
||||
/* split command */
|
||||
const char* delimiter = " ";
|
||||
char* word = strtok(str,delimiter);
|
||||
if (!word) {
|
||||
goto error;
|
||||
}
|
||||
/* parse command */
|
||||
if (0==strcmp(word,"help")) {
|
||||
help();
|
||||
} else if (0==strcmp(word,"reset")) {
|
||||
|
@ -463,12 +478,6 @@ void uart_action(char* str)
|
|||
} else {
|
||||
printf("fan is off (or not detected)\n");
|
||||
}
|
||||
reset_settings();
|
||||
/* reset using watchdog */
|
||||
do {
|
||||
wdt_enable(WDTO_15MS);
|
||||
for(;;) {}
|
||||
} while(0);
|
||||
} else if (0==strcmp(word,"mode")) {
|
||||
word = strtok(NULL,delimiter);
|
||||
if (!word) {
|
||||
|
@ -597,13 +606,13 @@ void ir_action(uint8_t address, uint8_t command)
|
|||
{
|
||||
enum IR_ACTIONS ir_code = IR_ACTION_END;
|
||||
static uint8_t channel = CHANNELS_1+CHANNELS_2;
|
||||
uint8_t step = 0xff/LEVELS;
|
||||
for (ir_code=0; ir_code<IR_ACTION_END; ir_code++) {
|
||||
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 */
|
||||
if (ir_keys[ir_code][0]==address && ir_keys[ir_code][1]==command) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ir_code<IR_ACTION_END) {
|
||||
if (ir_code<IR_ACTION_END) { /* process action */
|
||||
switch (ir_code) {
|
||||
case POWER:
|
||||
printf("switching power supply ");
|
||||
|
@ -627,7 +636,7 @@ void ir_action(uint8_t address, uint8_t command)
|
|||
brightness[mode][channel] = 0xff;
|
||||
}
|
||||
printf("increasing brightness ch %u %u: %u\n",(channel/CHANNELS_1)+1,(channel%CHANNELS_1)+1,brightness[mode][channel]);
|
||||
} else if (channel==CHANNELS_1+CHANNELS_2) {
|
||||
} else if (channel==CHANNELS_1+CHANNELS_2) { /* increase all brightness if no channel is selected */
|
||||
for (uint8_t i=0; i<CHANNELS_1+CHANNELS_2; i++) {
|
||||
if (brightness[mode][i]<0xff-step) {
|
||||
brightness[mode][i] += step;
|
||||
|
@ -647,7 +656,7 @@ void ir_action(uint8_t address, uint8_t command)
|
|||
brightness[mode][channel] = 0x00;
|
||||
}
|
||||
printf("decreasing brightness ch %u %u: %u\n",(channel/CHANNELS_1)+1,(channel%CHANNELS_1)+1,brightness[mode][channel]);
|
||||
} else if (channel==CHANNELS_1+CHANNELS_2) {
|
||||
} else if (channel==CHANNELS_1+CHANNELS_2) { /* decrease all brightness if no channel is selected */
|
||||
for (uint8_t i=0; i<CHANNELS_1+CHANNELS_2; i++) {
|
||||
if (brightness[mode][i]>step) {
|
||||
brightness[mode][i] -= step;
|
||||
|
@ -680,6 +689,6 @@ void ir_action(uint8_t address, uint8_t command)
|
|||
break;
|
||||
}
|
||||
} else {
|
||||
puts("IR command not learned");
|
||||
puts("unknown IR command");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,11 @@ extern volatile uint8_t* PORTS[CHANNELS_1+CHANNELS_2]; /* channel ports */
|
|||
extern volatile uint8_t* DDRS[CHANNELS_1+CHANNELS_2]; /* channel I/O configuration registers */
|
||||
extern const uint8_t BITS[CHANNELS_1+CHANNELS_2]; /* channel bits */
|
||||
|
||||
/* initialize I/O, timers, … */
|
||||
void ioinit(void);
|
||||
/* display the help */
|
||||
void help(void);
|
||||
/* process user command coming from UART */
|
||||
void uart_action(char* str);
|
||||
/* process user command coming from IR */
|
||||
void ir_action(uint8_t address, uint8_t command);
|
||||
|
|
|
@ -6,27 +6,28 @@
|
|||
#include "main.h"
|
||||
#include "settings.h"
|
||||
|
||||
/* initialize variable */
|
||||
/* initialize variables */
|
||||
uint8_t power;
|
||||
uint8_t mode;
|
||||
uint8_t brightness[MODES][CHANNELS_1+CHANNELS_2];
|
||||
uint8_t ir_keys[IR_ACTION_END][2];
|
||||
|
||||
const uint8_t MAGIC = 0x42; // magic header
|
||||
const uint8_t MAGIC = 0x42; /* magic header */
|
||||
|
||||
bool verify_settings(void)
|
||||
{
|
||||
bool to_return;
|
||||
uint8_t checksum = 0;
|
||||
uint8_t byte;
|
||||
uint16_t settings_size = sizeof(MAGIC)+sizeof(power)+sizeof(mode)+sizeof(brightness)+sizeof(ir_keys)+1; // the byte used for the checksum (magic header and checksum included)
|
||||
for (uint16_t i=0; i<settings_size; i++) {
|
||||
uint16_t settings_size = sizeof(MAGIC)+sizeof(power)+sizeof(mode)+sizeof(brightness)+sizeof(ir_keys)+1; /* the bytes used for the checksum (magic header and checksum included) */
|
||||
for (uint16_t i=0; i<settings_size; i++) { /* calculate checksum */
|
||||
byte = eeprom_read_byte((const uint8_t*)i);
|
||||
if (0==i && byte!=MAGIC) {
|
||||
if (0==i && byte!=MAGIC) { /* verify magic header first */
|
||||
return false;
|
||||
}
|
||||
checksum ^= byte;
|
||||
}
|
||||
/* verify checkecum */
|
||||
if (0==checksum) {
|
||||
to_return = true;
|
||||
} else {
|
||||
|
@ -37,14 +38,14 @@ bool verify_settings(void)
|
|||
|
||||
void initialize_settings(void)
|
||||
{
|
||||
power = 0;
|
||||
mode = 0;
|
||||
for (uint8_t i=0; i<MODES; i++) {
|
||||
power = 0; /* power is off */
|
||||
mode = 0; /* first mode used */
|
||||
for (uint8_t i=0; i<MODES; i++) { /* channel brightness set to 0 */
|
||||
for (uint8_t j=0; j<CHANNELS_1+CHANNELS_2; j++) {
|
||||
brightness[i][j] = 0;
|
||||
}
|
||||
}
|
||||
for (uint8_t i=0; i<IR_ACTION_END; i++) {
|
||||
for (uint8_t i=0; i<IR_ACTION_END; i++) { /* codes for IR actions set to 0 */
|
||||
ir_keys[i][0] = 0;
|
||||
ir_keys[i][1] = 0;
|
||||
}
|
||||
|
@ -52,8 +53,8 @@ void initialize_settings(void)
|
|||
|
||||
void save_settings(void)
|
||||
{
|
||||
uint16_t addr = 0; // the address in the EEPROM
|
||||
uint8_t checksum = 0;
|
||||
uint16_t addr = 0; /* address in the EEPROM */
|
||||
uint8_t checksum = 0; /* checksum of settings (including the magic header, else all 0 settings are calculated as valid) */
|
||||
eeprom_update_byte((uint8_t*)addr,MAGIC);
|
||||
checksum ^= MAGIC;
|
||||
addr++;
|
||||
|
@ -83,7 +84,7 @@ void save_settings(void)
|
|||
|
||||
void load_settings(void)
|
||||
{
|
||||
uint16_t addr = 1; // the address in the EEPROM (skip magic header)
|
||||
uint16_t addr = 1; /* the address in the EEPROM (skip magic header) */
|
||||
power = eeprom_read_byte((uint8_t*)addr);
|
||||
addr++;
|
||||
mode = eeprom_read_byte((uint8_t*)addr);
|
||||
|
@ -104,6 +105,5 @@ void load_settings(void)
|
|||
|
||||
void reset_settings(void)
|
||||
{
|
||||
/* invalidate magic header */
|
||||
eeprom_update_byte((uint8_t*)0,0);
|
||||
eeprom_update_byte((uint8_t*)0,0); /* invalidate magic header so the verification fails */
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ extern uint8_t power; /* power state */
|
|||
extern uint8_t mode; /* the current mode */
|
||||
extern uint8_t brightness[MODES][CHANNELS_1+CHANNELS_2]; /* the mode brightness settings for the channels */
|
||||
|
||||
/* IR settings */
|
||||
enum IR_ACTIONS { /* the actions for the infrared remote control */
|
||||
/* IR settings actions from the infrared remote control */
|
||||
enum IR_ACTIONS {
|
||||
POWER = 0,
|
||||
MODE,
|
||||
BRIGHTNESS_UP,
|
||||
|
@ -14,11 +14,15 @@ enum IR_ACTIONS { /* the actions for the infrared remote control */
|
|||
CHANNEL_PREVIOUS,
|
||||
IR_ACTION_END
|
||||
};
|
||||
extern uint8_t ir_keys[IR_ACTION_END][2]; // the IR NEC values (address+command) for the actions
|
||||
extern uint8_t ir_keys[IR_ACTION_END][2]; /* the IR NEC values (address+command) for the actions */
|
||||
|
||||
/* function to load/save the settings */
|
||||
/* verify integrity of the settings */
|
||||
bool verify_settings(void);
|
||||
/* create settings */
|
||||
void initialize_settings(void);
|
||||
/* save current settings into EEPROM */
|
||||
void save_settings(void);
|
||||
/* load settings from EEPROM */
|
||||
void load_settings(void);
|
||||
/* invalidate settings in EEPROM */
|
||||
void reset_settings(void);
|
||||
|
|
Loading…
Reference in New Issue