summaryrefslogtreecommitdiffstats
path: root/fw/pwm.c
blob: 60f8b5cf7b69090fc67097a5723e50566894547e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <pic16f84a.h>

typedef unsigned char uint8_t;

/*
 * Breathing LED via bit-bang PWM on PORTA(0) and PORTB(0).
 *
 * The PWM period is 256 counts of the inner counter 'pwm'.
 * Each duty level is held for HOLD_PERIODS full PWM periods before
 * stepping, giving a smooth fade up then down (heartbeat pattern).
 *
 * At 50 MHz with SDCC's output (~10 instructions/iteration) the inner
 * loop runs at roughly 5 MHz, so one 256-count PWM period ≈ 51 µs
 * → ~20 kHz PWM.  With HOLD_PERIODS = 32 each brightness step lasts
 * ~1.6 ms and the full 0→255→0 sweep takes ~820 ms — a natural breath.
 * Tweak HOLD_PERIODS to taste.
 */

#define HOLD_PERIODS 32

void main(void) {
    uint8_t duty    = 0;   /* current duty cycle, 0..255              */
    uint8_t dir     = 1;   /* 1 = ramping up, 0 = ramping down        */
    uint8_t pwm     = 0;   /* free-running counter within one period  */
    uint8_t periods = 0;   /* how many full periods at this duty level */

    while (1) {
        /* PWM compare: LED on when we're inside the duty window */
        if (pwm < duty) {
            PORTA = 0x01;
            PORTB = 0x01;
        } else {
            PORTA = 0x00;
            PORTB = 0x00;
        }

        pwm++;  /* wraps 255→0, completing one 256-count PWM period */

        if (pwm == 0) {
            periods++;
            if (periods == HOLD_PERIODS) {
                periods = 0;
                if (dir) {
                    duty++;
                    if (duty == 255) dir = 0;   /* peak: start ramping down */
                } else {
                    duty--;
                    if (duty == 0)   dir = 1;   /* floor: start ramping up  */
                }
            }
        }
    }
}