You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

213 lines
5.0 KiB

  1. /**
  2. * Backlighting code for PS2AVRGB boards (ATMEGA32A)
  3. * Kenneth A. (github.com/krusli | krusli.me)
  4. */
  5. #include "backlight.h"
  6. #include "quantum.h"
  7. #include <avr/pgmspace.h>
  8. #include <avr/interrupt.h>
  9. #include "backlight_custom.h"
  10. #include "breathing_custom.h"
  11. // DEBUG
  12. #include <stdlib.h>
  13. #include <stdio.h>
  14. // Port D: digital pins of the AVR chipset
  15. #define NUMLOCK_PORT (1 << 0) // D0
  16. #define CAPSLOCK_PORT (1 << 1) // D1
  17. #define BACKLIGHT_PORT (1 << 4) // D4
  18. #define SCROLLLOCK_PORT (1 << 6) // D6
  19. #define TIMER_CLK_DIV64 0x03 ///< Timer clocked at F_CPU/64
  20. #define TIMER1PRESCALE TIMER_CLK_DIV64 ///< timer 1 prescaler default
  21. #define TIMER_PRESCALE_MASK 0x07 ///< Timer Prescaler Bit-Mask
  22. #define PWM_MAX 0xFF
  23. #define TIMER_TOP 255 // 8 bit PWM
  24. extern backlight_config_t backlight_config;
  25. /**
  26. * References
  27. * Port Registers: https://www.arduino.cc/en/Reference/PortManipulation
  28. * TCCR1A: https://electronics.stackexchange.com/questions/92350/what-is-the-difference-between-tccr1a-and-tccr1b
  29. * Timers: http://www.avrbeginners.net/architecture/timers/timers.html
  30. * 16-bit timer setup: http://sculland.com/ATmega168/Interrupts-And-Timers/16-Bit-Timer-Setup/
  31. * PS2AVRGB firmware: https://github.com/showjean/ps2avrU/tree/master/firmware
  32. */
  33. // @Override
  34. // turn LEDs on and off depending on USB caps/num/scroll lock states.
  35. __attribute__ ((weak))
  36. void led_set_user(uint8_t usb_led) {
  37. if (usb_led & (1 << USB_LED_NUM_LOCK)) {
  38. // turn on
  39. DDRD |= NUMLOCK_PORT;
  40. PORTD |= NUMLOCK_PORT;
  41. } else {
  42. // turn off
  43. DDRD &= ~NUMLOCK_PORT;
  44. PORTD &= ~NUMLOCK_PORT;
  45. }
  46. if (usb_led & (1 << USB_LED_CAPS_LOCK)) {
  47. DDRD |= CAPSLOCK_PORT;
  48. PORTD |= CAPSLOCK_PORT;
  49. } else {
  50. DDRD &= ~CAPSLOCK_PORT;
  51. PORTD &= ~CAPSLOCK_PORT;
  52. }
  53. if (usb_led & (1 << USB_LED_SCROLL_LOCK)) {
  54. DDRD |= SCROLLLOCK_PORT;
  55. PORTD |= SCROLLLOCK_PORT;
  56. } else {
  57. DDRD &= ~SCROLLLOCK_PORT;
  58. PORTD &= ~SCROLLLOCK_PORT;
  59. }
  60. }
  61. #ifdef BACKLIGHT_ENABLE
  62. // sets up Timer 1 for 8-bit PWM
  63. void timer1PWMSetup(void) { // NOTE ONLY CALL THIS ONCE
  64. // default 8 bit mode
  65. TCCR1A &= ~(1 << 1); // cbi(TCCR1A,PWM11); <- set PWM11 bit to HIGH
  66. TCCR1A |= (1 << 0); // sbi(TCCR1A,PWM10); <- set PWM10 bit to LOW
  67. // clear output compare value A
  68. // outb(OCR1AH, 0);
  69. // outb(OCR1AL, 0);
  70. // clear output comparator registers for B
  71. OCR1BH = 0; // outb(OCR1BH, 0);
  72. OCR1BL = 0; // outb(OCR1BL, 0);
  73. }
  74. bool is_init = false;
  75. void timer1Init(void) {
  76. // timer1SetPrescaler(TIMER1PRESCALE)
  77. // set to DIV/64
  78. (TCCR1B) = ((TCCR1B) & ~TIMER_PRESCALE_MASK) | TIMER1PRESCALE;
  79. // reset TCNT1
  80. TCNT1H = 0; // outb(TCNT1H, 0);
  81. TCNT1L = 0; // outb(TCNT1L, 0);
  82. // TOIE1: Timer Overflow Interrupt Enable (Timer 1);
  83. TIMSK |= _BV(TOIE1); // sbi(TIMSK, TOIE1);
  84. is_init = true;
  85. }
  86. void timer1UnInit(void) {
  87. // set prescaler back to NONE
  88. (TCCR1B) = ((TCCR1B) & ~TIMER_PRESCALE_MASK) | 0x00; // TIMERRTC_CLK_STOP
  89. // disable timer overflow interrupt
  90. TIMSK &= ~_BV(TOIE1); // overflow bit?
  91. setPWM(0);
  92. is_init = false;
  93. }
  94. // handle TCNT1 overflow
  95. //! Interrupt handler for tcnt1 overflow interrupt
  96. ISR(TIMER1_OVF_vect, ISR_NOBLOCK)
  97. {
  98. // sei();
  99. // handle breathing here
  100. #ifdef BACKLIGHT_BREATHING
  101. if (is_breathing()) {
  102. custom_breathing_handler();
  103. }
  104. #endif
  105. // TODO call user defined function
  106. }
  107. // enable timer 1 PWM
  108. // timer1PWMBOn()
  109. void timer1PWMBEnable(void) {
  110. // turn on channel B (OC1B) PWM output
  111. // set OC1B as non-inverted PWM
  112. TCCR1A |= _BV(COM1B1);
  113. TCCR1A &= ~_BV(COM1B0);
  114. }
  115. // disable timer 1 PWM
  116. // timer1PWMBOff()
  117. void timer1PWMBDisable(void) {
  118. TCCR1A &= ~_BV(COM1B1);
  119. TCCR1A &= ~_BV(COM1B0);
  120. }
  121. void enableBacklight(void) {
  122. DDRD |= BACKLIGHT_PORT; // set digital pin 4 as output
  123. PORTD |= BACKLIGHT_PORT; // set digital pin 4 to high
  124. }
  125. void disableBacklight(void) {
  126. // DDRD &= ~BACKLIGHT_PORT; // set digital pin 4 as input
  127. PORTD &= ~BACKLIGHT_PORT; // set digital pin 4 to low
  128. }
  129. void startPWM(void) {
  130. timer1Init();
  131. timer1PWMBEnable();
  132. enableBacklight();
  133. }
  134. void stopPWM(void) {
  135. timer1UnInit();
  136. disableBacklight();
  137. timer1PWMBDisable();
  138. }
  139. void b_led_init_ports(void) {
  140. /* turn backlight on/off depending on user preference */
  141. #if BACKLIGHT_ON_STATE == 0
  142. // DDRx register: sets the direction of Port D
  143. // DDRD &= ~BACKLIGHT_PORT; // set digital pin 4 as input
  144. PORTD &= ~BACKLIGHT_PORT; // set digital pin 4 to low
  145. #else
  146. DDRD |= BACKLIGHT_PORT; // set digital pin 4 as output
  147. PORTD |= BACKLIGHT_PORT; // set digital pin 4 to high
  148. #endif
  149. timer1PWMSetup();
  150. startPWM();
  151. #ifdef BACKLIGHT_BREATHING
  152. breathing_enable();
  153. #endif
  154. }
  155. void b_led_set(uint8_t level) {
  156. if (level > BACKLIGHT_LEVELS) {
  157. level = BACKLIGHT_LEVELS;
  158. }
  159. setPWM((int)(TIMER_TOP * (float) level / BACKLIGHT_LEVELS));
  160. }
  161. // called every matrix scan
  162. void b_led_task(void) {
  163. // do nothing for now
  164. }
  165. void setPWM(uint16_t xValue) {
  166. if (xValue > TIMER_TOP) {
  167. xValue = TIMER_TOP;
  168. }
  169. OCR1B = xValue; // timer1PWMBSet(xValue);
  170. }
  171. #endif // BACKLIGHT_ENABLE