dc8b7bddf5e894308dd4c061d72a5139271861d3
[moodlight.git] / ledpwm.c
1 /* $Id: ledpwm.c,v 1.5 2010/08/08 17:06:11 simimeie Exp $
2  * Functions for led brightness control via PWM (pulse width modulation).
3  */
4
5 #include <avr/io.h>
6 #include <avr/interrupt.h>
7 #include "ledpwm.h"
8
9 /*
10  * Our hardware connections on the first prototype are as follows:
11  *  Red ->   PD6 (OC0A)
12  *  Green -> PD5 (OC0B)
13  *  Blue ->  PD3 (OC2B)  CHANGED umgeloetet durch Julian jetzt PD7
14  * In the next hardware revision this will be:
15  *  Red ->   PC3
16  *  Green -> PC2
17  *  Blue ->  PC1
18  */
19
20 #define LEDPORT PORTD
21 #define LEDDDR DDRD
22 #define LEDPINR 6
23 #define LEDPING 5
24 #define LEDPINB 7
25
26 uint8_t ledpwm_re = 0xff, ledpwm_gr = 0xff, ledpwm_bl = 0xff;
27 uint8_t ledpwm_bri = 128;
28 /* Internal */
29 volatile uint16_t ledpwm_val[3] = { 0x8000, 0x8000, 0x8000 };
30 volatile uint16_t ledpwm_curoreg = 0;
31
32 /* To do a 16-bit write, the high byte must be written before the low byte.
33  * For a 16-bit read, the low byte must be read before the high byte */
34 static void ledpwm_programnextstep(void) {
35   uint16_t nextval = 0xffff;
36   uint16_t tmp1 = ledpwm_curoreg; /* Copy so the compiler can place it into
37                  * a register despite the volatile. */
38   uint8_t i;
39   for (i = 0; i < 3; i++) {
40     if ((ledpwm_val[i] > tmp1) && (ledpwm_val[i] <= nextval)) {
41       nextval = ledpwm_val[i];
42     }
43   }
44   /* tmp1 now reused */
45   tmp1 = TCNT1L;
46   tmp1 |= (uint16_t)TCNT1H << 8;
47   if ((tmp1 + 30) > nextval) { /* 30 cycles is more or less a guess.
48     * This needs to be set to the number of cycles the code takes from
49     * here until the iret. */
50     nextval = tmp1 + 30;
51   }
52   OCR1AH = nextval >> 8;
53   OCR1AL = nextval & 0xff;
54   ledpwm_curoreg = nextval;
55 }
56
57 /* This gets called from timers.c which holds the 'main' (interrupt)
58  * handler for the timer1 overflow. */
59 void ledpwm_TIMER1OVF_hook(void) {
60   /* Turn on all LEDs */
61   if (ledpwm_val[LEDPWM_REDLED] > 0) {
62     LEDPORT |= _BV(LEDPINR);
63   }
64   if (ledpwm_val[LEDPWM_GREENLED] > 0) {
65     LEDPORT |= _BV(LEDPING);
66   }
67   if (ledpwm_val[LEDPWM_BLUELED] > 0) {
68     LEDPORT |= _BV(LEDPINB);
69   }
70   ledpwm_curoreg = 0;
71   ledpwm_programnextstep();
72 }
73
74 /* Called on compare match */
75 ISR(TIMER1_COMPA_vect)
76 {
77   uint8_t orval = 0x00;
78   if (ledpwm_val[LEDPWM_REDLED] > ledpwm_curoreg) {
79     orval |= _BV(LEDPINR);
80   }
81   if (ledpwm_val[LEDPWM_GREENLED] > ledpwm_curoreg) {
82     orval |= _BV(LEDPING);
83   }
84   if (ledpwm_val[LEDPWM_BLUELED] > ledpwm_curoreg) {
85     orval |= _BV(LEDPINB);
86   }
87   LEDPORT = (LEDPORT & (uint8_t)~(_BV(LEDPINR) | _BV(LEDPING) | _BV(LEDPINB))) | orval;
88   ledpwm_programnextstep();
89 }
90
91 void ledpwm_init(void) {
92   /* Set our Port Pins to output */
93   LEDDDR |= _BV(LEDPINR) | _BV(LEDPING) | _BV(LEDPINB);
94   /* DO NOT initialize TIMER1 frequency and overflow interrupt.
95    * timers.c already does that. */
96   /* Enable TIMER1 output compare interrupt A */
97   TIMSK1 |= _BV(OCIE1A);
98 }
99
100 void ledpwm_setled(uint8_t led, uint16_t val) {
101   if (led > 2) { return; /* ignore invalid values */ }
102   ledpwm_val[led] = val;
103 }
104
105 void ledpwm_set(uint8_t red, uint8_t green, uint8_t blue, uint8_t br) {
106   ledpwm_val[LEDPWM_REDLED]   = red * br;
107   ledpwm_val[LEDPWM_GREENLED] = green * br;
108   ledpwm_val[LEDPWM_BLUELED]  = blue * br;
109 }
This page took 0.046407 seconds and 2 git commands to generate.