[tuxdroid-svn] r760 - firmware/tuxcore/trunk
Status: Beta
Brought to you by:
ks156
From: jaguarondi <c2m...@c2...> - 2007-12-06 17:33:37
|
Author: jaguarondi Date: 2007-12-06 18:32:44 +0100 (Thu, 06 Dec 2007) New Revision: 760 Modified: firmware/tuxcore/trunk/adc.c firmware/tuxcore/trunk/led.c firmware/tuxcore/trunk/led.h firmware/tuxcore/trunk/main.c firmware/tuxcore/trunk/parser.c firmware/tuxcore/trunk/standalone.c Log: * Complete rework of the led module to add intensity control and fading effects. This is major change which was not so trivial so expect bugs. The code is not always neat, some further optimizations have to be done but I want to try it out first. All in all it's a pretty nice effect which gives tux a much better impression. Modified: firmware/tuxcore/trunk/adc.c =================================================================== --- firmware/tuxcore/trunk/adc.c 2007-12-06 17:18:59 UTC (rev 759) +++ firmware/tuxcore/trunk/adc.c 2007-12-06 17:32:44 UTC (rev 760) @@ -29,7 +29,7 @@ #include "adc.h" -/** Union between 16 bits variables and the two 8 bits registers they're made +/* Union between 16 bits variables and the two 8 bits registers they're made * of. */ typedef union { Modified: firmware/tuxcore/trunk/led.c =================================================================== --- firmware/tuxcore/trunk/led.c 2007-12-06 17:18:59 UTC (rev 759) +++ firmware/tuxcore/trunk/led.c 2007-12-06 17:32:44 UTC (rev 760) @@ -24,72 +24,381 @@ \ingroup led */ +#include <stdbool.h> +#include <avr/interrupt.h> + #include "led.h" -/** Buffer the LEDs values before being applied on the I/O port. - * \ingroup led */ +/** Buffer the LEDs values before being applied on the I/O port. */ uint8_t leds_buffer = 0; -/** Counter for the delay between 2 toggles of the LEDs. */ -static uint8_t led_delay; -/** Delay between 2 toggles of the LEDs when blinking them. */ -static uint8_t led_blinking_pw; -/** Number of times the LEDs should be toggled when blinking. */ -static uint8_t led_blinking_cnt; +/** Left LED status structure. */ +led_t left_led = +{ + .command.step=2, + .command.rate=1, + .blink.max_intensity = 0xFF, +}; + +/** Right LED status structure. */ +led_t right_led = +{ + .command.step=2, + .command.rate=1, + .blink.max_intensity = 0xFF, +}; + /** - \brief Blink LEDs - \ingroup led - \param cnt Number of blinks - \param pulse_width Blinking pulse width + \brief Turn left LED on. + */ +#define turn_left_led_on() \ + do {leds_buffer |= LED_L_MK; \ + TIMSK1 |= _BV(OCIE1A); \ + } while(0) - The LEDs will be toggled 'cnt' times. The pulse width can be used to change - the toggle frequency. -*/ -void blink_leds(uint8_t cnt, uint8_t pulse_width) +/** + \brief Turn right LED on. + */ +#define turn_right_led_on() \ + do {leds_buffer |= LED_R_MK; \ + TIMSK1 |= _BV(OCIE1B); \ + } while(0) + +/** + \brief Turn left LED off. + */ +#define turn_left_led_off() \ + do {leds_buffer &= ~LED_L_MK; \ + TIMSK1 &= ~_BV(OCIE1A); \ + LED_PT &= ~LED_L_MK; \ + } while(0) + +/** + \brief Turn right LED off. + */ +#define turn_right_led_off() \ + do {leds_buffer &= ~LED_R_MK; \ + TIMSK1 &= ~_BV(OCIE1B); \ + LED_PT &= ~LED_R_MK; \ + } while(0) + +/** + * Timer1 initialization, used for applying PWM on the LEDs in order to change + * their intensity and have some fading effects. + * \ingroup led + * + * Settings: + * - Fast PWM, 8-bit mode. + * - CLK/8 = 1MHz + * + * Timer is clocked at 1MHz (CLK/8) as this frequency doesn't seem to disturb + * the IR receiver too much. Slower frequencies does. + * Output Compare A and B are used for toggling LEDs left and right so they can + * be controlled separately. + */ +void led_init(void) { - if (led_delay) /* cancel the blink if one is already happening */ - return; - if (!cnt) - return; - cnt--; /* the first toggle occurs here */ - toggle_leds(); - if (!cnt) /* if one toggle, nothing more to do */ - return; - led_delay = pulse_width; - led_blinking_pw = pulse_width; - led_blinking_cnt = cnt; + TCCR1A = _BV(WGM10); + TCCR1B = _BV(WGM12) | _BV(CS11); + TIMSK1 = _BV(TOIE1); } /** + * Shutting down the LEDs for power saving. + * \ingroup led + */ +void led_shutdown(void) +{ + TCCR1B = 0; + turn_left_led_off(); + turn_right_led_off(); +} + +/** \brief Refresh the LEDs I/O with the buffer value. - \ingroup led */ -void refresh_leds(void) +static inline void refresh_leds(void) { /* We should avoid glitches on the LEDs otherwise the IR receiver goes * crazy. So we can't simply set the output pins to 0 then set them with * leds_buffer. Only the pin that should be 0 has to be set to 0. */ - LED_PT &= ~(leds_buffer ^ LED_MK); + /* Commented until a better solution is found for driving the leds */ + //LED_PT &= ~(leds_buffer ^ LED_MK); // done in the 4ms loop for now LED_PT |= leds_buffer; } /** - \brief Periodic routine that controls the LEDs. + * Timer1 overflow interrupt, sets the LEDs for the PWM. + */ +ISR(SIG_OVERFLOW1) +{ + refresh_leds(); +} + +/** + * Timer1 compare A interrupt, clear the left LED for the PWM. + */ +ISR(SIG_OUTPUT_COMPARE1A) +{ + LED_PT &= ~LED_L_MK; +} + +/** + * Timer1 compare B interrupt, clear the right LED for the PWM. + */ +ISR(SIG_OUTPUT_COMPARE1B) +{ + LED_PT &= ~LED_R_MK; +} + +/** + \brief Blink LEDs \ingroup led + \param leds Which LEDs are affected by the command + \param cnt Number of blinks. 0 is ignored, 255 (0xFF) means infinite. + \param pulse_width Blinking pulse width. 0 is ignored. - This function should be called regularly as it controls the blinking. The - period this function is called will be the unit period of the led blinking. + The LEDs will be toggled 'cnt' times. The pulse width can be used to change + the toggle frequency. + If 'cnt' or 'pulse_width' are set to 0, the register is not updated so the + old value is kept. + 'cnt' can be set to 255 to trigger an infinite blinking effect. +*/ +void led_blink(LEDS_t leds, uint8_t const cnt, uint8_t const pulse_width) +{ + /* XXX we should really find a way to optimize this loop for the 2 leds. + * We don't want to add a new function call, bu don't want to have the code + * written twice. Maybe an inline function can do it with compiler + * optimizations. */ + led_t *led = &left_led; + + while (leds & LED_BOTH) + { + if (leds & LED_LEFT) + { + leds &= ~LED_LEFT; + /* Here, we should already have led = &left_led;*/ + } + else if (leds & LED_RIGHT) + { + leds &= ~LED_RIGHT; + led = &right_led; + } + led->status.blinking = true; + led->blink.pulse_width = pulse_width; + if (cnt) + { + led->blink.count = cnt; + /* We can't simply set the flag here otherwise we alter the ongoing + * blinking. So we need to check for blinking first. */ + if (!led->var.blink_tmr) + led->var.blink_flag = true; + } + } +} + +/** + \brief Set the intensity boundaries for the blink command. + \ingroup led + \param leds Which LEDs are affected by the command + \param max Maximum intensity + \param min Minimum intensity +*/ +void led_blink_range(LEDS_t leds, uint8_t const max, uint8_t const min) +{ + led_t *led = &left_led; + + while (leds & LED_BOTH) + { + if (leds & LED_LEFT) + { + leds &= ~LED_LEFT; + /* Here, we should already have led = &left_led;*/ + } + else if (leds & LED_RIGHT) + { + leds &= ~LED_RIGHT; + led = &right_led; + } + led->blink.max_intensity = max; + led->blink.min_intensity = min; + } +} + +/** + \brief Set the rate and step which determine the speed of the fading effect. + \ingroup led + \param leds Which LEDs are affected by the command + \param rate Rate at which the intensity is changed. See struct led_comand. + \param step Intenisty step applied when fading. */ -void led_control(void) +void led_set_fade_speed(LEDS_t leds, uint8_t const rate, uint8_t const step) { - if (led_delay) + led_t *led = &left_led; + + while (leds & LED_BOTH) { - led_delay--; - if (!led_delay) + if (leds & LED_LEFT) { - toggle_leds(); - if (--led_blinking_cnt) - led_delay = led_blinking_pw; + leds &= ~LED_LEFT; + /* Here, we should already have led = &left_led;*/ } + else if (leds & LED_RIGHT) + { + leds &= ~LED_RIGHT; + led = &right_led; + } + if (rate) + led->command.rate = rate; + if (step) + led->command.step = step; } } + +/** + \brief Set LEDs intensity with fading effect. + \ingroup led + \param leds Set left, right or both leds. + \param intensity 8 bits PWM value the led should be set to. + */ +void led_set_intensity(LEDS_t leds, uint8_t const intensity) +{ + led_t *led = &left_led; + + while (leds & LED_BOTH) + { + if (leds & LED_LEFT) + { + leds &= ~LED_LEFT; + /* Here, we should already have led = &left_led;*/ + } + else if (leds & LED_RIGHT) + { + leds &= ~LED_RIGHT; + led = &right_led; + } + led->command.setpoint = intensity; + led->status.fading = true; + } +} + +/** + \brief Handles intensity fading. + \param led Pointer to the led structure + + Set the new intensity and set the fading flag to false when fading is + completed. + */ +static void fading(led_t *led) +{ + if (!led->var.rate_cnt) + { + if (led->command.setpoint > led->status.intensity) + { + if (led->status.intensity + led->command.step + >= led->command.setpoint) + { + led->status.intensity = led->command.setpoint; + led->status.fading = false; + return; + } + else + led->status.intensity += led->command.step; + } + else + /* led->command.setpoint < led->status.intensity here */ + { + if (led->status.intensity - led->command.step + <= led->command.setpoint) + { + led->status.intensity = led->command.setpoint; + led->status.fading = false; + return; + } + else + led->status.intensity -= led->command.step; + } + led->var.rate_cnt = led->command.rate; + } + /* we decrease in all cases, i.e. if rate == 1, cnt will be set to 0 when + * leaving the function and so will only loop one time */ + led->var.rate_cnt--; +} + +/** + \brief Handles the blinking effect. + \param led Pointer to the led structure + + Set the new fading settings if more blinking has to be done. + */ +static void blinking(led_t *led) +{ + /* Infinite blinking or next blink. */ + if ((led->blink.count == 0xFF) || --led->blink.count) + led->var.blink_tmr = led->blink.pulse_width; + else + led->status.blinking = false; + if (led->status.intensity <= led->blink.min_intensity) + led->command.setpoint = led->blink.max_intensity; + else + (led->command.setpoint = led->blink.min_intensity); + led->status.fading = true; + /* Add the intermediate delay between 2 fades. */ + led->var.rate_cnt = led->command.rate; +} + +static inline void control_call(led_t *led) +{ + /* Do we need fading? */ + if (led->status.fading) + fading(led); + + /* Check the blinking pulse width. */ + if (led->var.blink_tmr) + { + led->var.blink_tmr--; + if (!led->var.blink_tmr) + led->var.blink_flag = true; + } + if (!led->status.fading) + /* When fading is done, check if we need to blink again. */ + if (led->var.blink_flag) + { + blinking(led); + led->var.blink_flag = false; + } +} + +/** + \brief Periodic routine that controls the LEDs. + \ingroup led + \param mask If set, masking is applied on the LEDs so they don't light. Used + when the eyes are closed, it's neater. + + This function should be called regularly as it controls the blinking. The + period this function is called will be the unit period of the led blinking. + */ +void led_control(bool mask) +{ + control_call(&left_led); + control_call(&right_led); + + /* This circumvent the bug we have when the OCR values are too low (<5 it + * seems), interrupt priority of the comparisons is higher than the + * overflow so when 2 interrupts occurs nearly simultaneously, the compare + * is done before the overflow which is not what we want. The result is + * that the led is ON instead of OFF with low values. XXX should find a way + * to fix that. */ + if (left_led.status.intensity <= 5 || mask) + turn_left_led_off(); + else + turn_left_led_on(); + if (right_led.status.intensity <= 5 || mask) + turn_right_led_off(); + else + turn_right_led_on(); + + /* Update the PWM registers. */ + OCR1AL = left_led.status.intensity; + OCR1BL = right_led.status.intensity; +} Modified: firmware/tuxcore/trunk/led.h =================================================================== --- firmware/tuxcore/trunk/led.h 2007-12-06 17:18:59 UTC (rev 759) +++ firmware/tuxcore/trunk/led.h 2007-12-06 17:32:44 UTC (rev 760) @@ -24,89 +24,64 @@ \ingroup led */ -/** \defgroup led Leds - The led module contains the functions that drive the eye LEDs. +/** \defgroup led LEDs + The LED module contains the functions that drive the eye LEDs. */ #ifndef _LED_H_ #define _LED_H_ +#include <stdbool.h> #include "hardware.h" +#include "common/defines.h" -extern uint8_t leds_buffer; - /** - \brief Turn both LEDs on. \ingroup led + LED status structure holding all information necessary to handle intensity, + fading and blinking effects. */ -#define turn_leds_on() (leds_buffer |= LED_MK) +typedef struct led_t +{ + struct led_status + { + uint8_t intensity; + bool fading; + bool blinking; + } status; + struct led_command + { + uint8_t setpoint; + uint8_t rate; + uint8_t step; + } command; + struct led_blink + { + uint8_t min_intensity; + uint8_t max_intensity; + /** Number of times the LEDs should be toggled when blinking. */ + uint8_t count; + /** Delay between 2 toggles of the LEDs when blinking them. */ + uint8_t pulse_width; + } blink; + struct led_var + { + uint8_t rate_cnt; + /** Timer for the delay between 2 toggles of the LEDs. */ + uint8_t blink_tmr; + bool blink_flag; + } var; +} led_t; -/** - \brief Turn both LEDs off. - \ingroup led - */ -#define turn_leds_off() (leds_buffer &= ~LED_MK) +extern led_t left_led; +extern led_t right_led; -/** - \brief Turn left led on. - \ingroup led - */ -#define turn_left_led_on() (leds_buffer |= LED_L_MK) +void led_init(void); +void led_shutdown(void); +void led_set_fade_speed(LEDS_t leds, uint8_t const rate, uint8_t const step); +void led_set_intensity(LEDS_t leds, uint8_t const intensity); +void led_blink_range(LEDS_t leds, uint8_t const max, uint8_t const min); +void blink_led(led_t *const led, uint8_t const cnt, uint8_t const pulse_width); +void led_blink(LEDS_t led, uint8_t const cnt, uint8_t const pulse_width); +void led_control(bool mask); -/** - \brief Turn right led on. - \ingroup led - */ -#define turn_right_led_on() (leds_buffer |= LED_R_MK) - -/** - \brief Turn left led off. - \ingroup led - */ -#define turn_left_led_off() (leds_buffer &= ~LED_L_MK) - -/** - \brief Turn right led off. - \ingroup led - */ -#define turn_right_led_off() (leds_buffer &= ~LED_R_MK) - -/** - \brief Read the value of the LEDs. - \ingroup led - */ -#define read_leds() (leds_buffer) - -/** - \brief Toggle LEDs. - \ingroup led - */ -#define toggle_leds() (leds_buffer ^= LED_MK) - -/** - \brief Toggle left led. - \ingroup led - */ -#define toggle_left_led() (leds_buffer ^= LED_L_MK) - -/** - \brief Toggle right led. - \ingroup led - */ -#define toggle_right_led() (leds_buffer ^= LED_R_MK) - -/** - \brief Turn both LEDs off directly on the I/O port. - \ingroup led - - This function is used to switch off the LEDs when the eyes are closed. - You shouldn't use this function elsewhere as the I/O port will be refreshed - immediately with the leds_buffer value. - */ -#define mask_leds() (LED_PT &= ~LED_MK) - -extern void blink_leds(uint8_t cnt, uint8_t pulse_width); -extern void refresh_leds(void); -extern void led_control(void); - #endif /* _LED_H_ */ Modified: firmware/tuxcore/trunk/main.c =================================================================== --- firmware/tuxcore/trunk/main.c 2007-12-06 17:18:59 UTC (rev 759) +++ firmware/tuxcore/trunk/main.c 2007-12-06 17:32:44 UTC (rev 760) @@ -140,6 +140,7 @@ initIO(); sensors_init(); config_init(); + led_init(); /* I2C communication initialization */ i2cCommunicationInit(); @@ -168,14 +169,10 @@ ir_delay--; /* Led blinking. */ - led_control(); + led_control(cond_flags.eyes_closed); /* Refresh the leds. When the eyes are closed, the leds are * switched off. They'll be refreshed again with the buffered value * when the eyes will reopen. */ - if (cond_flags.eyes_closed) - mask_leds(); - else - refresh_leds(); } if (t100ms_flag) { @@ -254,7 +251,7 @@ PCICR = 0; EIMSK = 0; ADCSRA = 0; - turn_leds_off(); + led_shutdown(); turnIrOff(); } Modified: firmware/tuxcore/trunk/parser.c =================================================================== --- firmware/tuxcore/trunk/parser.c 2007-12-06 17:18:59 UTC (rev 759) +++ firmware/tuxcore/trunk/parser.c 2007-12-06 17:32:44 UTC (rev 760) @@ -132,36 +132,23 @@ { stop_spinning(); } - /* Leds */ - else if (command[0] == LED_ON_CMD) + else if (command[0] == LED_BLINK_RANGE_CMD) { - turn_leds_on(); + led_blink_range(command[1], command[2], command[3]); } - else if (command[0] == LED_OFF_CMD) + else if (command[0] == LED_BLINK_CMD) { - turn_leds_off(); + led_blink(command[1], command[2], command[3]); } - else if (command[0] == LED_L_ON_CMD) + else if (command[0] == LED_FADE_SPEED_CMD) { - turn_left_led_on(); + led_set_fade_speed(command[1], command[2], command[3]); } - else if (command[0] == LED_L_OFF_CMD) + else if (command[0] == LED_SET_CMD) { - turn_left_led_off(); + led_set_intensity(command[1], command[2]); } - else if (command[0] == LED_R_ON_CMD) - { - turn_right_led_on(); - } - else if (command[0] == LED_R_OFF_CMD) - { - turn_right_led_off(); - } - else if (command[0] == LED_BLINK_CMD) - { - blink_leds(command[1], command[2]); - } else if (command[0] == IR_SEND_RC5_CMD) { irSendRC5(command[1], command[2]); @@ -220,6 +207,36 @@ return; } + /* Deprecated functions, may be removed in firmware v0.5. */ + else if (command[0] == LED_ON_CMD) + { + led_set_intensity(LED_BOTH, 0xFF); + } + else if (command[0] == LED_OFF_CMD) + { + led_set_intensity(LED_BOTH, 0x0); + } + else if (command[0] == LED_L_ON_CMD) + { + led_set_intensity(LED_LEFT, 0xFF); + } + else if (command[0] == LED_L_OFF_CMD) + { + led_set_intensity(LED_LEFT, 0x0); + } + else if (command[0] == LED_R_ON_CMD) + { + led_set_intensity(LED_RIGHT, 0xFF); + } + else if (command[0] == LED_R_OFF_CMD) + { + led_set_intensity(LED_RIGHT, 0x0); + } + else if (command[0] == 0x9A) /* old blink function */ + { + led_blink(3, command[1], command[2]); + } + /* Undefined commands */ else return; /* simply drop it */ Modified: firmware/tuxcore/trunk/standalone.c =================================================================== --- firmware/tuxcore/trunk/standalone.c 2007-12-06 17:18:59 UTC (rev 759) +++ firmware/tuxcore/trunk/standalone.c 2007-12-06 17:32:44 UTC (rev 760) @@ -212,13 +212,13 @@ if (gStatus. sw & (GSTATUS_LEFTWINGBTN_MK | GSTATUS_RIGHTWINGBTN_MK | GSTATUS_HEADBTN_MK)) - turn_left_led_on(); + led_set_intensity(LED_LEFT, 0xFF); else - turn_left_led_on(); + led_set_intensity(LED_LEFT, 0); if ((gStatus.sw & GSTATUS_POWERPLUGSW_MK)) - turn_right_led_on(); + led_set_intensity(LED_RIGHT, 0xFF); else - turn_right_led_off(); + led_set_intensity(LED_RIGHT, 0); } /* IR signal processing */ @@ -231,7 +231,7 @@ /* IR feedback signal */ if (tux_config.ir_feedback) - blink_leds(2, 4); + led_blink(LED_BOTH, 2, 4); /* ALT KEYS */ if (alt_mode) @@ -296,10 +296,10 @@ tux_config.ir_feedback = !tux_config.ir_feedback; break; case K_CHANNELPLUS: - toggle_right_led(); + led_blink(LED_RIGHT, 1, 0); break; case K_VOLUMEPLUS: - toggle_left_led(); + led_blink(LED_LEFT, 1, 0); break; case K_FASTREWIND: if (spin_PWM) |