aboutsummaryrefslogtreecommitdiff
path: root/quantum/quantum.c
diff options
context:
space:
mode:
Diffstat (limited to 'quantum/quantum.c')
-rw-r--r--quantum/quantum.c406
1 files changed, 169 insertions, 237 deletions
diff --git a/quantum/quantum.c b/quantum/quantum.c
index c80975f21..88617412c 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -23,6 +23,10 @@
23#define TAPPING_TERM 200 23#define TAPPING_TERM 200
24#endif 24#endif
25 25
26#ifndef BREATHING_PERIOD
27#define BREATHING_PERIOD 6
28#endif
29
26#include "backlight.h" 30#include "backlight.h"
27extern backlight_config_t backlight_config; 31extern backlight_config_t backlight_config;
28 32
@@ -618,7 +622,17 @@ bool process_record_quantum(keyrecord_t *record) {
618 } 622 }
619 623
620 send_keyboard_report(); 624 send_keyboard_report();
625 return false;
621 } 626 }
627
628#if defined(BACKLIGHT_ENABLE) && defined(BACKLIGHT_BREATHING)
629 case BL_BRTG: {
630 if (record->event.pressed)
631 breathing_toggle();
632 return false;
633 }
634#endif
635
622 default: { 636 default: {
623 shift_interrupted[0] = true; 637 shift_interrupted[0] = true;
624 shift_interrupted[1] = true; 638 shift_interrupted[1] = true;
@@ -831,6 +845,7 @@ void matrix_scan_quantum() {
831 845
832static const uint8_t backlight_pin = BACKLIGHT_PIN; 846static const uint8_t backlight_pin = BACKLIGHT_PIN;
833 847
848// depending on the pin, we use a different output compare unit
834#if BACKLIGHT_PIN == B7 849#if BACKLIGHT_PIN == B7
835# define COM1x1 COM1C1 850# define COM1x1 COM1C1
836# define OCR1x OCR1C 851# define OCR1x OCR1C
@@ -841,17 +856,18 @@ static const uint8_t backlight_pin = BACKLIGHT_PIN;
841# define COM1x1 COM1A1 856# define COM1x1 COM1A1
842# define OCR1x OCR1A 857# define OCR1x OCR1A
843#else 858#else
844# define NO_BACKLIGHT_CLOCK 859# define NO_HARDWARE_PWM
845#endif 860#endif
846 861
847#ifndef BACKLIGHT_ON_STATE 862#ifndef BACKLIGHT_ON_STATE
848#define BACKLIGHT_ON_STATE 0 863#define BACKLIGHT_ON_STATE 0
849#endif 864#endif
850 865
866#ifdef NO_HARDWARE_PWM // pwm through software
867
851__attribute__ ((weak)) 868__attribute__ ((weak))
852void backlight_init_ports(void) 869void backlight_init_ports(void)
853{ 870{
854
855 // Setup backlight pin as output and output to on state. 871 // Setup backlight pin as output and output to on state.
856 // DDRx |= n 872 // DDRx |= n
857 _SFR_IO8((backlight_pin >> 4) + 1) |= _BV(backlight_pin & 0xF); 873 _SFR_IO8((backlight_pin >> 4) + 1) |= _BV(backlight_pin & 0xF);
@@ -862,83 +878,15 @@ void backlight_init_ports(void)
862 // PORTx |= n 878 // PORTx |= n
863 _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF); 879 _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
864 #endif 880 #endif
865
866 #ifndef NO_BACKLIGHT_CLOCK
867 // Use full 16-bit resolution.
868 ICR1 = 0xFFFF;
869
870 // I could write a wall of text here to explain... but TL;DW
871 // Go read the ATmega32u4 datasheet.
872 // And this: http://blog.saikoled.com/post/43165849837/secret-konami-cheat-code-to-high-resolution-pwm-on
873
874 // Pin PB7 = OCR1C (Timer 1, Channel C)
875 // Compare Output Mode = Clear on compare match, Channel C = COM1C1=1 COM1C0=0
876 // (i.e. start high, go low when counter matches.)
877 // WGM Mode 14 (Fast PWM) = WGM13=1 WGM12=1 WGM11=1 WGM10=0
878 // Clock Select = clk/1 (no prescaling) = CS12=0 CS11=0 CS10=1
879
880 TCCR1A = _BV(COM1x1) | _BV(WGM11); // = 0b00001010;
881 TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001;
882 #endif
883
884 backlight_init();
885 #ifdef BACKLIGHT_BREATHING
886 breathing_defaults();
887 #endif
888} 881}
889 882
890__attribute__ ((weak)) 883__attribute__ ((weak))
891void backlight_set(uint8_t level) 884void backlight_set(uint8_t level) {}
892{
893 // Prevent backlight blink on lowest level
894 // #if BACKLIGHT_ON_STATE == 0
895 // // PORTx &= ~n
896 // _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
897 // #else
898 // // PORTx |= n
899 // _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
900 // #endif
901
902 if ( level == 0 ) {
903 #ifndef NO_BACKLIGHT_CLOCK
904 // Turn off PWM control on backlight pin, revert to output low.
905 TCCR1A &= ~(_BV(COM1x1));
906 OCR1x = 0x0;
907 #else
908 // #if BACKLIGHT_ON_STATE == 0
909 // // PORTx |= n
910 // _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
911 // #else
912 // // PORTx &= ~n
913 // _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
914 // #endif
915 #endif
916 }
917 #ifndef NO_BACKLIGHT_CLOCK
918 else if ( level == BACKLIGHT_LEVELS ) {
919 // Turn on PWM control of backlight pin
920 TCCR1A |= _BV(COM1x1);
921 // Set the brightness
922 OCR1x = 0xFFFF;
923 }
924 else {
925 // Turn on PWM control of backlight pin
926 TCCR1A |= _BV(COM1x1);
927 // Set the brightness
928 OCR1x = 0xFFFF >> ((BACKLIGHT_LEVELS - level) * ((BACKLIGHT_LEVELS + 1) / 2));
929 }
930 #endif
931
932 #ifdef BACKLIGHT_BREATHING
933 breathing_intensity_default();
934 #endif
935}
936 885
937uint8_t backlight_tick = 0; 886uint8_t backlight_tick = 0;
938 887
939void backlight_task(void) { 888void backlight_task(void) {
940 #ifdef NO_BACKLIGHT_CLOCK 889 if ((0xFFFF >> ((BACKLIGHT_LEVELS - get_backlight_level()) * ((BACKLIGHT_LEVELS + 1) / 2))) & (1 << backlight_tick)) {
941 if ((0xFFFF >> ((BACKLIGHT_LEVELS - backlight_config.level) * ((BACKLIGHT_LEVELS + 1) / 2))) & (1 << backlight_tick)) {
942 #if BACKLIGHT_ON_STATE == 0 890 #if BACKLIGHT_ON_STATE == 0
943 // PORTx &= ~n 891 // PORTx &= ~n
944 _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF); 892 _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
@@ -955,232 +903,216 @@ void backlight_task(void) {
955 _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF); 903 _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
956 #endif 904 #endif
957 } 905 }
958 backlight_tick = (backlight_tick + 1) % 16; 906 backlight_tick = backlight_tick + 1 % 16;
959 #endif
960} 907}
961 908
962#ifdef BACKLIGHT_BREATHING 909#ifdef BACKLIGHT_BREATHING
910#error "Backlight breathing only available with hardware PWM. Please disable."
911#endif
963 912
964#ifdef NO_BACKLIGHT_CLOCK 913#else // pwm through timer
965void breathing_defaults(void) {} 914
966void breathing_intensity_default(void) {} 915#define TIMER_TOP 0xFFFFU
967#else 916
917// See http://jared.geek.nz/2013/feb/linear-led-pwm
918static uint16_t cie_lightness(uint16_t v) {
919 if (v <= 5243) // if below 8% of max
920 return v / 9; // same as dividing by 900%
921 else {
922 uint32_t y = (((uint32_t) v + 10486) << 8) / (10486 + 0xFFFFUL); // add 16% of max and compare
923 // to get a useful result with integer division, we shift left in the expression above
924 // and revert what we've done again after squaring.
925 y = y * y * y >> 8;
926 if (y > 0xFFFFUL) // prevent overflow
927 return 0xFFFFU;
928 else
929 return (uint16_t) y;
930 }
931}
932
933// range for val is [0..TIMER_TOP]. PWM pin is high while the timer count is below val.
934static inline void set_pwm(uint16_t val) {
935 OCR1x = val;
936}
937
938__attribute__ ((weak))
939void backlight_set(uint8_t level) {
940 if (level > BACKLIGHT_LEVELS)
941 level = BACKLIGHT_LEVELS;
942
943 if (level == 0) {
944 // Turn off PWM control on backlight pin
945 TCCR1A &= ~(_BV(COM1x1));
946 } else {
947 // Turn on PWM control of backlight pin
948 TCCR1A |= _BV(COM1x1);
949 }
950 // Set the brightness
951 set_pwm(cie_lightness(TIMER_TOP * (uint32_t)level / BACKLIGHT_LEVELS));
952}
953
954void backlight_task(void) {}
955
956#ifdef BACKLIGHT_BREATHING
968 957
969#define BREATHING_NO_HALT 0 958#define BREATHING_NO_HALT 0
970#define BREATHING_HALT_OFF 1 959#define BREATHING_HALT_OFF 1
971#define BREATHING_HALT_ON 2 960#define BREATHING_HALT_ON 2
961#define BREATHING_STEPS 128
972 962
973static uint8_t breath_intensity; 963static uint8_t breathing_period = BREATHING_PERIOD;
974static uint8_t breath_speed; 964static uint8_t breathing_halt = BREATHING_NO_HALT;
975static uint16_t breathing_index; 965static uint16_t breathing_counter = 0;
976static uint8_t breathing_halt;
977 966
978void breathing_enable(void) 967bool is_breathing(void) {
979{ 968 return !!(TIMSK1 & _BV(TOIE1));
980 if (get_backlight_level() == 0) 969}
981 {
982 breathing_index = 0;
983 }
984 else
985 {
986 // Set breathing_index to be at the midpoint (brightest point)
987 breathing_index = 0x20 << breath_speed;
988 }
989 970
990 breathing_halt = BREATHING_NO_HALT; 971#define breathing_interrupt_enable() do {TIMSK1 |= _BV(TOIE1);} while (0)
972#define breathing_interrupt_disable() do {TIMSK1 &= ~_BV(TOIE1);} while (0)
973#define breathing_min() do {breathing_counter = 0;} while (0)
974#define breathing_max() do {breathing_counter = breathing_period * 244 / 2;} while (0)
991 975
992 // Enable breathing interrupt 976void breathing_enable(void)
993 TIMSK1 |= _BV(OCIE1A); 977{
978 breathing_counter = 0;
979 breathing_halt = BREATHING_NO_HALT;
980 breathing_interrupt_enable();
994} 981}
995 982
996void breathing_pulse(void) 983void breathing_pulse(void)
997{ 984{
998 if (get_backlight_level() == 0) 985 if (get_backlight_level() == 0)
999 { 986 breathing_min();
1000 breathing_index = 0;
1001 }
1002 else 987 else
1003 { 988 breathing_max();
1004 // Set breathing_index to be at the midpoint + 1 (brightest point)
1005 breathing_index = 0x21 << breath_speed;
1006 }
1007
1008 breathing_halt = BREATHING_HALT_ON; 989 breathing_halt = BREATHING_HALT_ON;
1009 990 breathing_interrupt_enable();
1010 // Enable breathing interrupt
1011 TIMSK1 |= _BV(OCIE1A);
1012} 991}
1013 992
1014void breathing_disable(void) 993void breathing_disable(void)
1015{ 994{
1016 // Disable breathing interrupt 995 breathing_interrupt_disable();
1017 TIMSK1 &= ~_BV(OCIE1A); 996 // Restore backlight level
1018 backlight_set(get_backlight_level()); 997 backlight_set(get_backlight_level());
1019} 998}
1020 999
1021void breathing_self_disable(void) 1000void breathing_self_disable(void)
1022{ 1001{
1023 if (get_backlight_level() == 0) 1002 if (get_backlight_level() == 0)
1024 { 1003 breathing_halt = BREATHING_HALT_OFF;
1025 breathing_halt = BREATHING_HALT_OFF; 1004 else
1026 } 1005 breathing_halt = BREATHING_HALT_ON;
1027 else
1028 {
1029 breathing_halt = BREATHING_HALT_ON;
1030 }
1031
1032 //backlight_set(get_backlight_level());
1033} 1006}
1034 1007
1035void breathing_toggle(void) 1008void breathing_toggle(void) {
1036{ 1009 if (is_breathing())
1037 if (!is_breathing()) 1010 breathing_disable();
1038 { 1011 else
1039 if (get_backlight_level() == 0) 1012 breathing_enable();
1040 {
1041 breathing_index = 0;
1042 }
1043 else
1044 {
1045 // Set breathing_index to be at the midpoint + 1 (brightest point)
1046 breathing_index = 0x21 << breath_speed;
1047 }
1048
1049 breathing_halt = BREATHING_NO_HALT;
1050 }
1051
1052 // Toggle breathing interrupt
1053 TIMSK1 ^= _BV(OCIE1A);
1054
1055 // Restore backlight level
1056 if (!is_breathing())
1057 {
1058 backlight_set(get_backlight_level());
1059 }
1060} 1013}
1061 1014
1062bool is_breathing(void) 1015void breathing_period_set(uint8_t value)
1063{ 1016{
1064 return (TIMSK1 && _BV(OCIE1A)); 1017 if (!value)
1018 value = 1;
1019 breathing_period = value;
1065} 1020}
1066 1021
1067void breathing_intensity_default(void) 1022void breathing_period_default(void) {
1068{ 1023 breathing_period_set(BREATHING_PERIOD);
1069 //breath_intensity = (uint8_t)((uint16_t)100 * (uint16_t)get_backlight_level() / (uint16_t)BACKLIGHT_LEVELS);
1070 breath_intensity = ((BACKLIGHT_LEVELS - get_backlight_level()) * ((BACKLIGHT_LEVELS + 1) / 2));
1071} 1024}
1072 1025
1073void breathing_intensity_set(uint8_t value) 1026void breathing_period_inc(void)
1074{ 1027{
1075 breath_intensity = value; 1028 breathing_period_set(breathing_period+1);
1076} 1029}
1077 1030
1078void breathing_speed_default(void) 1031void breathing_period_dec(void)
1079{ 1032{
1080 breath_speed = 4; 1033 breathing_period_set(breathing_period-1);
1081} 1034}
1082 1035
1083void breathing_speed_set(uint8_t value) 1036/* To generate breathing curve in python:
1084{ 1037 * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)]
1085 bool is_breathing_now = is_breathing(); 1038 */
1086 uint8_t old_breath_speed = breath_speed; 1039static const uint8_t breathing_table[BREATHING_STEPS] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1087
1088 if (is_breathing_now)
1089 {
1090 // Disable breathing interrupt
1091 TIMSK1 &= ~_BV(OCIE1A);
1092 }
1093
1094 breath_speed = value;
1095
1096 if (is_breathing_now)
1097 {
1098 // Adjust index to account for new speed
1099 breathing_index = (( (uint8_t)( (breathing_index) >> old_breath_speed ) ) & 0x3F) << breath_speed;
1100
1101 // Enable breathing interrupt
1102 TIMSK1 |= _BV(OCIE1A);
1103 }
1104
1105}
1106 1040
1107void breathing_speed_inc(uint8_t value) 1041// Use this before the cie_lightness function.
1108{ 1042static inline uint16_t scale_backlight(uint16_t v) {
1109 if ((uint16_t)(breath_speed - value) > 10 ) 1043 return v / BACKLIGHT_LEVELS * get_backlight_level();
1110 {
1111 breathing_speed_set(0);
1112 }
1113 else
1114 {
1115 breathing_speed_set(breath_speed - value);
1116 }
1117} 1044}
1118 1045
1119void breathing_speed_dec(uint8_t value) 1046/* Assuming a 16MHz CPU clock and a timer that resets at 64k (ICR1), the following interrupt handler will run
1047 * about 244 times per second.
1048 */
1049ISR(TIMER1_OVF_vect)
1120{ 1050{
1121 if ((uint16_t)(breath_speed + value) > 10 ) 1051 uint16_t interval = (uint16_t) breathing_period * 244 / BREATHING_STEPS;
1122 { 1052 // resetting after one period to prevent ugly reset at overflow.
1123 breathing_speed_set(10); 1053 breathing_counter = (breathing_counter + 1) % (breathing_period * 244);
1124 } 1054 uint8_t index = breathing_counter / interval % BREATHING_STEPS;
1125 else 1055
1126 { 1056 if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) ||
1127 breathing_speed_set(breath_speed + value); 1057 ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1)))
1128 } 1058 {
1129} 1059 breathing_interrupt_disable();
1060 }
1130 1061
1131void breathing_defaults(void) 1062 set_pwm(cie_lightness(scale_backlight((uint16_t) pgm_read_byte(&breathing_table[index]) * 0x0101U)));
1132{
1133 breathing_intensity_default();
1134 breathing_speed_default();
1135 breathing_halt = BREATHING_NO_HALT;
1136} 1063}
1137 1064
1138/* Breathing Sleep LED brighness(PWM On period) table 1065#endif // BACKLIGHT_BREATHING
1139 * (64[steps] * 4[duration]) / 64[PWM periods/s] = 4 second breath cycle
1140 *
1141 * http://www.wolframalpha.com/input/?i=%28sin%28+x%2F64*pi%29**8+*+255%2C+x%3D0+to+63
1142 * (0..63).each {|x| p ((sin(x/64.0*PI)**8)*255).to_i }
1143 */
1144static const uint8_t breathing_table[64] PROGMEM = {
1145 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 6, 10,
1146 15, 23, 32, 44, 58, 74, 93, 113, 135, 157, 179, 199, 218, 233, 245, 252,
1147255, 252, 245, 233, 218, 199, 179, 157, 135, 113, 93, 74, 58, 44, 32, 23,
1148 15, 10, 6, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1149};
1150 1066
1151ISR(TIMER1_COMPA_vect) 1067__attribute__ ((weak))
1068void backlight_init_ports(void)
1152{ 1069{
1153 // OCR1x = (pgm_read_byte(&breathing_table[ ( (uint8_t)( (breathing_index++) >> breath_speed ) ) & 0x3F ] )) * breath_intensity; 1070 // Setup backlight pin as output and output to on state.
1154 1071 // DDRx |= n
1155 1072 _SFR_IO8((backlight_pin >> 4) + 1) |= _BV(backlight_pin & 0xF);
1156 uint8_t local_index = ( (uint8_t)( (breathing_index++) >> breath_speed ) ) & 0x3F; 1073 #if BACKLIGHT_ON_STATE == 0
1157 1074 // PORTx &= ~n
1158 if (((breathing_halt == BREATHING_HALT_ON) && (local_index == 0x20)) || ((breathing_halt == BREATHING_HALT_OFF) && (local_index == 0x3F))) 1075 _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
1159 { 1076 #else
1160 // Disable breathing interrupt 1077 // PORTx |= n
1161 TIMSK1 &= ~_BV(OCIE1A); 1078 _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
1162 } 1079 #endif
1163 1080 // I could write a wall of text here to explain... but TL;DW
1164 OCR1x = (uint16_t)(((uint16_t)pgm_read_byte(&breathing_table[local_index]) * 257)) >> breath_intensity; 1081 // Go read the ATmega32u4 datasheet.
1082 // And this: http://blog.saikoled.com/post/43165849837/secret-konami-cheat-code-to-high-resolution-pwm-on
1083
1084 // Pin PB7 = OCR1C (Timer 1, Channel C)
1085 // Compare Output Mode = Clear on compare match, Channel C = COM1C1=1 COM1C0=0
1086 // (i.e. start high, go low when counter matches.)
1087 // WGM Mode 14 (Fast PWM) = WGM13=1 WGM12=1 WGM11=1 WGM10=0
1088 // Clock Select = clk/1 (no prescaling) = CS12=0 CS11=0 CS10=1
1089
1090 /*
1091 14.8.3:
1092 "In fast PWM mode, the compare units allow generation of PWM waveforms on the OCnx pins. Setting the COMnx1:0 bits to two will produce a non-inverted PWM [..]."
1093 "In fast PWM mode the counter is incremented until the counter value matches either one of the fixed values 0x00FF, 0x01FF, or 0x03FF (WGMn3:0 = 5, 6, or 7), the value in ICRn (WGMn3:0 = 14), or the value in OCRnA (WGMn3:0 = 15)."
1094 */
1095
1096 TCCR1A = _BV(COM1x1) | _BV(WGM11); // = 0b00001010;
1097 TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001;
1098 // Use full 16-bit resolution. Counter counts to ICR1 before reset to 0.
1099 ICR1 = TIMER_TOP;
1165 1100
1101 backlight_init();
1102 #ifdef BACKLIGHT_BREATHING
1103 breathing_enable();
1104 #endif
1166} 1105}
1167 1106
1168#endif // NO_BACKLIGHT_CLOCK 1107#endif // NO_HARDWARE_PWM
1169#endif // breathing
1170 1108
1171#else // backlight 1109#else // backlight
1172 1110
1173__attribute__ ((weak)) 1111__attribute__ ((weak))
1174void backlight_init_ports(void) 1112void backlight_init_ports(void) {}
1175{
1176
1177}
1178 1113
1179__attribute__ ((weak)) 1114__attribute__ ((weak))
1180void backlight_set(uint8_t level) 1115void backlight_set(uint8_t level) {}
1181{
1182
1183}
1184 1116
1185#endif // backlight 1117#endif // backlight
1186 1118