some fixes for PWM, but the resolution in the lower end still sucks.
[moodlight.git] / ledpwm.c
CommitLineData
6c58993d 1/* $Id: ledpwm.c,v 1.5 2010/08/08 17:06:11 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 };
30volatile uint16_t ledpwm_curoreg = 0;
ddf1553f 31
4307b86a 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 */
34static void ledpwm_programnextstep(void) {
35 uint16_t nextval = 0xffff;
6c58993d 36 uint16_t tmp1 = ledpwm_curoreg; /* Copy so the compiler can place it into
37 * a register despite the volatile. */
4307b86a 38 uint8_t i;
39 for (i = 0; i < 3; i++) {
6c58993d 40 if ((ledpwm_val[i] > tmp1) && (ledpwm_val[i] <= nextval)) {
4307b86a 41 nextval = ledpwm_val[i];
42 }
43 }
6c58993d 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 }
4307b86a 52 OCR1AH = nextval >> 8;
53 OCR1AL = nextval & 0xff;
54 ledpwm_curoreg = nextval;
17aea8ef 55}
56
4307b86a 57/* This gets called from timers.c which holds the 'main' (interrupt)
58 * handler for the timer1 overflow. */
59void 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 */
75ISR(TIMER1_COMPA_vect)
17aea8ef 76{
6c58993d 77 uint8_t orval = 0x00;
78 if (ledpwm_val[LEDPWM_REDLED] > ledpwm_curoreg) {
79 orval |= _BV(LEDPINR);
4307b86a 80 }
6c58993d 81 if (ledpwm_val[LEDPWM_GREENLED] > ledpwm_curoreg) {
82 orval |= _BV(LEDPING);
4307b86a 83 }
6c58993d 84 if (ledpwm_val[LEDPWM_BLUELED] > ledpwm_curoreg) {
85 orval |= _BV(LEDPINB);
17aea8ef 86 }
6c58993d 87 LEDPORT = (LEDPORT & (uint8_t)~(_BV(LEDPINR) | _BV(LEDPING) | _BV(LEDPINB))) | orval;
4307b86a 88 ledpwm_programnextstep();
89}
90
91void 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);
4307b86a 98}
99
100void ledpwm_setled(uint8_t led, uint16_t val) {
101 if (led > 2) { return; /* ignore invalid values */ }
102 ledpwm_val[led] = val;
17aea8ef 103}
de0e300b 104
105void ledpwm_set(uint8_t red, uint8_t green, uint8_t blue, uint8_t br) {
4307b86a 106 ledpwm_val[LEDPWM_REDLED] = red * br;
107 ledpwm_val[LEDPWM_GREENLED] = green * br;
108 ledpwm_val[LEDPWM_BLUELED] = blue * br;
de0e300b 109}
This page took 0.071338 seconds and 4 git commands to generate.