aboutsummaryrefslogtreecommitdiffstats
path: root/keyboards/jj40/backlight.c
blob: fbd241fa9dee8314d1af5ae2fff3f7b42343ce21 (plain)
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
/**
 * Backlighting code for PS2AVRGB boards (ATMEGA32A)
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#include "backlight.h"
#include "quantum.h"

#include <avr/pgmspace.h>
#include <avr/interrupt.h>

#include "backlight_custom.h"
#include "breathing_custom.h"

// DEBUG
#include <stdlib.h>
#include <stdio.h>

// Port D: digital pins of the AVR chipset
#define NUMLOCK_PORT    (1 << 0)  // D0
#define CAPSLOCK_PORT   (1 << 1)  // D1
#define BACKLIGHT_PORT  (1 << 4)  // D4
#define SCROLLLOCK_PORT (1 << 6)  // D6

#define TIMER_CLK_DIV64			  0x03	///< Timer clocked at F_CPU/64
#define TIMER1PRESCALE	TIMER_CLK_DIV64 ///< timer 1 prescaler default

#define TIMER_PRESCALE_MASK		0x07	///< Timer Prescaler Bit-Mask

#define PWM_MAX 0xFF
#define TIMER_TOP 255 // 8 bit PWM

extern backlight_config_t backlight_config;

/**
 * References
 * Port Registers: https://www.arduino.cc/en/Reference/PortManipulation
 * TCCR1A: https://electronics.stackexchange.com/questions/92350/what-is-the-difference-between-tccr1a-and-tccr1b
 * Timers: http://www.avrbeginners.net/architecture/timers/timers.html
 * 16-bit timer setup: http://sculland.com/ATmega168/Interrupts-And-Timers/16-Bit-Timer-Setup/
 * PS2AVRGB firmware: https://github.com/showjean/ps2avrU/tree/master/firmware
 */

// @Override
// turn LEDs on and off depending on USB caps/num/scroll lock states.
__attribute__ ((weak))
void led_set_user(uint8_t usb_led) {
    if (usb_led & (1 << USB_LED_NUM_LOCK)) {
      // turn on
      DDRD  |= NUMLOCK_PORT;
      PORTD |= NUMLOCK_PORT;
    } else {
      // turn off
      DDRD  &= ~NUMLOCK_PORT;
      PORTD &= ~NUMLOCK_PORT;
    }

    if (usb_led & (1 << USB_LED_CAPS_LOCK)) {
      DDRD  |= CAPSLOCK_PORT;
      PORTD |= CAPSLOCK_PORT;
    } else {
      DDRD  &= ~CAPSLOCK_PORT;
      PORTD &= ~CAPSLOCK_PORT;
    }

    if (usb_led & (1 << USB_LED_SCROLL_LOCK)) {
      DDRD  |= SCROLLLOCK_PORT;
      PORTD |= SCROLLLOCK_PORT;
    } else {
      DDRD  &= ~SCROLLLOCK_PORT;
      PORTD &= ~SCROLLLOCK_PORT;
    }
}

#ifdef BACKLIGHT_ENABLE

// sets up Timer 1 for 8-bit PWM
void timer1PWMSetup(void) { // NOTE ONLY CALL THIS ONCE
  // default 8 bit mode
  TCCR1A &= ~(1 << 1); // cbi(TCCR1A,PWM11); <- set PWM11 bit to HIGH
  TCCR1A |= (1 << 0);  // sbi(TCCR1A,PWM10); <- set PWM10 bit to LOW

  // clear output compare value A
  // outb(OCR1AH, 0);
  // outb(OCR1AL, 0);

  // clear output comparator registers for B
	OCR1BH = 0; // outb(OCR1BH, 0);
	OCR1BL = 0; // outb(OCR1BL, 0);
}

bool is_init = false;
void timer1Init(void) {
  // timer1SetPrescaler(TIMER1PRESCALE)
  // set to DIV/64
  (TCCR1B) = ((TCCR1B) & ~TIMER_PRESCALE_MASK) | TIMER1PRESCALE;

  // reset TCNT1
  TCNT1H = 0;  // outb(TCNT1H, 0);
	TCNT1L = 0;  // outb(TCNT1L, 0);

  // TOIE1: Timer Overflow Interrupt Enable (Timer 1);
	TIMSK |= _BV(TOIE1); // sbi(TIMSK, TOIE1);

  is_init = true;
}

void timer1UnInit(void) {
  // set prescaler back to NONE
  (TCCR1B) = ((TCCR1B) & ~TIMER_PRESCALE_MASK) | 0x00;  // TIMERRTC_CLK_STOP

  // disable timer overflow interrupt
  TIMSK &= ~_BV(TOIE1); // overflow bit?

  setPWM(0);

  is_init = false;
}


// handle TCNT1 overflow
//! Interrupt handler for tcnt1 overflow interrupt
ISR(TIMER1_OVF_vect, ISR_NOBLOCK)
{
	// sei();
  // handle breathing here
  #ifdef BACKLIGHT_BREATHING
  if (is_breathing()) {
    custom_breathing_handler();
  }
  #endif

  // TODO call user defined function
}

// enable timer 1 PWM
// timer1PWMBOn()
void timer1PWMBEnable(void) {
  // turn on channel B (OC1B) PWM output
  // set OC1B as non-inverted PWM
  TCCR1A |= _BV(COM1B1);
  TCCR1A &= ~_BV(COM1B0);
}

// disable timer 1 PWM
// timer1PWMBOff()
void timer1PWMBDisable(void) {
  TCCR1A &= ~_BV(COM1B1);
  TCCR1A &= ~_BV(COM1B0);
}

void enableBacklight(void) {
  DDRD  |= BACKLIGHT_PORT;  // set digital pin 4 as output
  PORTD |= BACKLIGHT_PORT;  // set digital pin 4 to high
}

void disableBacklight(void) {
  // DDRD  &= ~BACKLIGHT_PORT;  // set digital pin 4 as input
  PORTD &= ~BACKLIGHT_PORT;  // set digital pin 4 to low
}

void startPWM(void) {
  timer1Init();
  timer1PWMBEnable();
  enableBacklight();
}

void stopPWM(void) {
  timer1UnInit();
  disableBacklight();
  timer1PWMBDisable();
}

void b_led_init_ports(void) {
  /* turn backlight on/off depending on user preference */
  #if BACKLIGHT_ON_STATE == 0
    // DDRx register: sets the direction of Port D
    // DDRD  &= ~BACKLIGHT_PORT;  // set digital pin 4 as input
    PORTD &= ~BACKLIGHT_PORT;  // set digital pin 4 to low
  #else
    DDRD  |= BACKLIGHT_PORT;  // set digital pin 4 as output
    PORTD |= BACKLIGHT_PORT;  // set digital pin 4 to high
  #endif

  timer1PWMSetup();
  startPWM();

  #ifdef BACKLIGHT_BREATHING
  breathing_enable();
  #endif
}

void b_led_set(uint8_t level) {
  if (level > BACKLIGHT_LEVELS) {
    level = BACKLIGHT_LEVELS;
  }

  setPWM((int)(TIMER_TOP * (float) level / BACKLIGHT_LEVELS));
}

// called every matrix scan
void b_led_task(void) {
  // do nothing for now
}

void setPWM(uint16_t xValue) {
  if (xValue > TIMER_TOP) {
    xValue = TIMER_TOP;
  }
  OCR1B = xValue; // timer1PWMBSet(xValue);
}

#endif  // BACKLIGHT_ENABLE