completely redid PWM: Now calculate 'microprograms', i.e. pairs of
[moodlight.git] / ledpwm.c
CommitLineData
b7a7cffc 1/* $Id: ledpwm.c,v 1.6 2010/08/08 18:09:40 simimeie Exp $
17aea8ef 2 * Functions for led brightness control via PWM (pulse width modulation).
3 */
4
5#include <avr/io.h>
4307b86a 6#include <avr/interrupt.h>
17aea8ef 7#include "ledpwm.h"
8
17aea8ef 9/*
4307b86a 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
17aea8ef 18 */
ddf1553f 19
4307b86a 20#define LEDPORT PORTD
21#define LEDDDR DDRD
22#define LEDPINR 6
23#define LEDPING 5
24#define LEDPINB 7
25
ddf1553f 26uint8_t ledpwm_re = 0xff, ledpwm_gr = 0xff, ledpwm_bl = 0xff;
27uint8_t ledpwm_bri = 128;
4307b86a 28/* Internal */
29volatile uint16_t ledpwm_val[3] = { 0x8000, 0x8000, 0x8000 };
b7a7cffc 30typedef struct {
31 uint16_t atcntval; /* at this counter value */
32 uint8_t orval; /* or this value onto the output */
33} ledpwm_microprog;
34ledpwm_microprog ledpwm_mprogs[2][10];
35volatile uint8_t ledpwm_activemprog = 0;
36volatile ledpwm_microprog * ledpwm_amprogptr = &ledpwm_mprogs[0][0];
37
ddf1553f 38
4307b86a 39/* To do a 16-bit write, the high byte must be written before the low byte.
40 * For a 16-bit read, the low byte must be read before the high byte */
b7a7cffc 41
42static inline uint16_t getnextminimum(uint16_t curmin) {
43 uint16_t res = 0xffff;
44 uint8_t i; uint8_t found = 0;
4307b86a 45 for (i = 0; i < 3; i++) {
b7a7cffc 46 if ((ledpwm_val[i] > curmin) && (ledpwm_val[i] <= res)) {
47 res = ledpwm_val[i];
48 found = 1;
4307b86a 49 }
50 }
b7a7cffc 51 if (found) {
52 return res;
53 } else {
54 return 0; /* We already had the maximum, no higher values there (EOL) */
6c58993d 55 }
17aea8ef 56}
57
b7a7cffc 58static void ledpwm_recalculateprogram(void) {
59 uint8_t nextprog = !ledpwm_activemprog;
60 uint8_t i = 0;
61 uint16_t nextminimum = 0;
62 uint8_t orval;
63
64 do {
65 orval = 0;
66 if (ledpwm_val[LEDPWM_REDLED] > nextminimum) {
67 orval |= _BV(LEDPINR);
68 }
69 if (ledpwm_val[LEDPWM_GREENLED] > nextminimum) {
70 orval |= _BV(LEDPING);
71 }
72 if (ledpwm_val[LEDPWM_BLUELED] > nextminimum) {
73 orval |= _BV(LEDPINB);
74 }
75 ledpwm_mprogs[nextprog][i].atcntval = nextminimum;
76 ledpwm_mprogs[nextprog][i].orval = orval;
77 /* now calculate nextminimum */
78 nextminimum = getnextminimum(nextminimum);
79 i++;
80 } while (nextminimum > 0);
81 while (i < 10) { /* Fill the rest with dummys that cause no harm */
82 ledpwm_mprogs[nextprog][i].atcntval = 0;
83 ledpwm_mprogs[nextprog][i].orval = 0;
84 i++;
4307b86a 85 }
b7a7cffc 86 /* activate the freshly calculated microprogram */
87 ledpwm_activemprog = nextprog;
88 return;
89}
90
91static inline void programnextpwmstep(void) {
92 uint16_t curcnt;
93 curcnt = TCNT1L;
94 curcnt |= (uint16_t)TCNT1H << 8;
95 while (((curcnt + 40) > ledpwm_amprogptr->atcntval) && (ledpwm_amprogptr->atcntval > 0)) {
96 /* Something to do in the next few CPU cycles, so wait for it! */
97 if (curcnt >= ledpwm_amprogptr->atcntval) { /* Execute! */
98 LEDPORT = (LEDPORT & (uint8_t)~(_BV(LEDPINR) | _BV(LEDPING) | _BV(LEDPINB))) | ledpwm_amprogptr->orval;
99 ledpwm_amprogptr++;
100 }
101 curcnt = TCNT1L;
102 curcnt |= (uint16_t)TCNT1H << 8;
4307b86a 103 }
b7a7cffc 104 /* Now program the overflow interrupt */
105 if (ledpwm_amprogptr->atcntval > 0) {
106 uint16_t nextval = ledpwm_amprogptr->atcntval;
107 OCR1AH = nextval >> 8;
108 OCR1AL = nextval & 0xff;
4307b86a 109 }
b7a7cffc 110}
111
112/* This gets called from timers.c which holds the 'main' (interrupt)
113 * handler for the timer1 overflow. */
114void ledpwm_TIMER1OVF_hook(void) {
115 /* (Re-)Start microprogram */
116 ledpwm_amprogptr = &ledpwm_mprogs[ledpwm_activemprog][0];
117 LEDPORT = (LEDPORT & (uint8_t)~(_BV(LEDPINR) | _BV(LEDPING) | _BV(LEDPINB))) | ledpwm_amprogptr->orval;
118 ledpwm_amprogptr++;
119 programnextpwmstep();
4307b86a 120}
121
122/* Called on compare match */
123ISR(TIMER1_COMPA_vect)
17aea8ef 124{
b7a7cffc 125 LEDPORT = (LEDPORT & (uint8_t)~(_BV(LEDPINR) | _BV(LEDPING) | _BV(LEDPINB))) | ledpwm_amprogptr->orval;
126 ledpwm_amprogptr++;
127 programnextpwmstep();
4307b86a 128}
129
130void ledpwm_init(void) {
131 /* Set our Port Pins to output */
132 LEDDDR |= _BV(LEDPINR) | _BV(LEDPING) | _BV(LEDPINB);
133 /* DO NOT initialize TIMER1 frequency and overflow interrupt.
134 * timers.c already does that. */
135 /* Enable TIMER1 output compare interrupt A */
136 TIMSK1 |= _BV(OCIE1A);
b7a7cffc 137 ledpwm_recalculateprogram();
4307b86a 138}
139
140void ledpwm_setled(uint8_t led, uint16_t val) {
141 if (led > 2) { return; /* ignore invalid values */ }
142 ledpwm_val[led] = val;
b7a7cffc 143 ledpwm_recalculateprogram();
17aea8ef 144}
de0e300b 145
146void ledpwm_set(uint8_t red, uint8_t green, uint8_t blue, uint8_t br) {
4307b86a 147 ledpwm_val[LEDPWM_REDLED] = red * br;
148 ledpwm_val[LEDPWM_GREENLED] = green * br;
149 ledpwm_val[LEDPWM_BLUELED] = blue * br;
b7a7cffc 150 ledpwm_recalculateprogram();
de0e300b 151}
This page took 0.059303 seconds and 4 git commands to generate.