dc22-wp-badge-firmware/fw_attiny88/src/led.c

165 lines
4.4 KiB
C

/*
* led.c
*
* Created: 6/13/2014 1:44:28 AM
* Author: true
*/
#include "led.h"
/* led */
uint8_t rgbled_pwm_lf[4]; // pwm value for TIMER1 OCA LED outputs
uint8_t rgbled_pwm_rt[4]; // pwm value for TIMER1 OCB LED outputs
static uint8_t rgbled_read_sel; // LED light sensor select
static uint8_t rgbled_sensitivity[4] = {1, 1, 1, 1};
/* adc */
volatile uint8_t adc_busy;
volatile uint16_t adc_result[ADC_MAX_FEEDBACK + 1];
volatile uint8_t adc_read_mode;
/* functions */
// sets up the IO pins for LED output function.
void rgbled_io_init()
{
// PortD[4:7] (left) and PortC[0:3] (right) are LEDs
// Also, Right LEDs are capable of ADC input on ALL pins (ADC[0:3])
// set all pins as outputs
DDRD |= 0xf0;
DDRC |= 0x0f;
#ifdef LED_COMMON_ANODE
// com-anode: pin is high to disable
PORTD |= 0xf0;
PORTC |= 0x0f;
#else
// com-cathode: pin is low to disable
PORTD &= 0x0f;
PORTC &= 0xf0;
#endif
}
// sets the appropriate pins and PWM values for the LED index specified in rgbled_idx.
// note: this index is updated externally, and not by this function.
void rgbled_update()
{
// clear all selected LED pins
// we have to clear the select line then set it because we can't fully disable the PWM for some reason
#ifdef LED_COMMON_ANODE
// make sure all pins are high
PORTD |= 0xf0;
PORTC |= 0x0f;
#else
// make sure all pins are low
PORTD &= 0x0f;
PORTC &= 0xf0;
#endif
// set pwm OCx registers to max
timer1_set_oca(TIMER1_COMPARE);
timer1_set_ocb(TIMER1_COMPARE);
// and set pwm counter value to 1 before expiry
timer1_set(TIMER1_COMPARE - 1);
// now we set the desired LED
// turn on one LED in each group
#ifdef LED_COMMON_ANODE
// make sure active pin is output low and others are high
PORTD &= ~(_BV(rgbled_idx + 4));
PORTC &= ~(_BV(rgbled_idx));
#else
// make sure active pin is output high and others are low
PORTD |= _BV(rgbled_idx) << 4;
PORTC |= _BV(rgbled_idx);
#endif
// load new pwm OCx values for this LED
timer1_set_oca(rgbled_pwm_lf[rgbled_idx]);
timer1_set_ocb(rgbled_pwm_rt[rgbled_idx]);
}
// sets up the PortC LED matrix as a diode light sensor.
// configures the specified LED on the ADC.
// this function assumes common anode.
void rgbled_sensor_init(uint8_t led)
{
// set the left eye high (fixes color flash/tearing)
PORTD |= 0xf0;
// disable both eye's PWM (required for right eye, fixes color flash/tearing in left eye)
TCCR1A &= ~(_BV(COM1A1) | _BV(COM1A0) | _BV(COM1B1) | _BV(COM1B0));
// ground the LED
DDRC &= 0xf0; // set all LEDs cathode as inputs
MCUCR |= (_BV(PUD)); // globally disable pullups
DDRC |= (_BV(led)); // set our LED as an output
PORTC = (PORTC & 0xf0); // set all LEDs cathode low
PORTB |= (_BV(PORTB2)); // set anode high
// reverse LED bias
PORTC |= (_BV(led)); // set cathode high
PORTB &= ~(_BV(PORTB2)); // set anode low
_delay_us(3); // allow it to charge fully
// set LED as input
DDRC &= ~(_BV(led)); // set led cathode as input
PORTC &= ~(_BV(led)); // set led cathode pullup off
MCUCR &= ~(_BV(PUD)); // re-enable global pullups
}
// starts ADC to read the value of charge remaining on the LED
void rgbled_sensor_sensitivity(uint8_t ledidx, uint8_t sensitivity)
{
if (ledidx <= 0x03) {
if (sensitivity) {
rgbled_sensitivity[ledidx] = sensitivity;
}
}
}
void rgbled_sensor_read_idx(uint8_t ledidx)
{
rgbled_read_sel = ledidx & 0x03;
}
void rgbled_sensor_read()
{
uint8_t sens;
sens = rgbled_sensitivity[rgbled_read_sel];
if (!sens) sens = 1;
if (adc_read_step == 0) {
// adc_init();
// clear ADC bias by reading ground
adc_channel(ADC_CHAN_GND, ADC_REF_AVCC);
} else if (adc_read_step == 1) {
adc_start(0, 0);
} else if (adc_read_step == 2) {
// if the ADC is done, charge up LED for reading
if (adc_busy) {
adc_read_step--;
} else {
rgbled_sensor_init(rgbled_read_sel);
adc_channel(rgbled_read_sel, ADC_REF_NO_SET);
}
} else if (adc_read_step == (2 + sens)) {
// if vref has changed, it should be stable by now; start reading LED value
adc_start(3, 1);
} else if (adc_read_step > (2 + sens)) {
adc_read_step--;
if (!adc_busy) {
// we are done! :) finish up...
adc_channel(ADC_CHAN_GND, ADC_REF_NO_SET); // change ADC to not look at a real pin
rgbled_io_init(); // set LED pins up again
timer1_pwm_reinit(); // and set up PWM again
adc_read_mode = 0; // clear ADC read mode
}
}
}