diff options
Diffstat (limited to 'quantum/quantum.c')
-rw-r--r-- | quantum/quantum.c | 406 |
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" |
27 | extern backlight_config_t backlight_config; | 31 | extern 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 | ||
832 | static const uint8_t backlight_pin = BACKLIGHT_PIN; | 846 | static 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)) |
852 | void backlight_init_ports(void) | 869 | void 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)) |
891 | void backlight_set(uint8_t level) | 884 | void 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 | ||
937 | uint8_t backlight_tick = 0; | 886 | uint8_t backlight_tick = 0; |
938 | 887 | ||
939 | void backlight_task(void) { | 888 | void 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 |
965 | void breathing_defaults(void) {} | 914 | |
966 | void breathing_intensity_default(void) {} | 915 | #define TIMER_TOP 0xFFFFU |
967 | #else | 916 | |
917 | // See http://jared.geek.nz/2013/feb/linear-led-pwm | ||
918 | static 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. | ||
934 | static inline void set_pwm(uint16_t val) { | ||
935 | OCR1x = val; | ||
936 | } | ||
937 | |||
938 | __attribute__ ((weak)) | ||
939 | void 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 | |||
954 | void 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 | ||
973 | static uint8_t breath_intensity; | 963 | static uint8_t breathing_period = BREATHING_PERIOD; |
974 | static uint8_t breath_speed; | 964 | static uint8_t breathing_halt = BREATHING_NO_HALT; |
975 | static uint16_t breathing_index; | 965 | static uint16_t breathing_counter = 0; |
976 | static uint8_t breathing_halt; | ||
977 | 966 | ||
978 | void breathing_enable(void) | 967 | bool 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 | 976 | void 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 | ||
996 | void breathing_pulse(void) | 983 | void 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 | ||
1014 | void breathing_disable(void) | 993 | void 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 | ||
1021 | void breathing_self_disable(void) | 1000 | void 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 | ||
1035 | void breathing_toggle(void) | 1008 | void 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 | ||
1062 | bool is_breathing(void) | 1015 | void 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 | ||
1067 | void breathing_intensity_default(void) | 1022 | void 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 | ||
1073 | void breathing_intensity_set(uint8_t value) | 1026 | void breathing_period_inc(void) |
1074 | { | 1027 | { |
1075 | breath_intensity = value; | 1028 | breathing_period_set(breathing_period+1); |
1076 | } | 1029 | } |
1077 | 1030 | ||
1078 | void breathing_speed_default(void) | 1031 | void breathing_period_dec(void) |
1079 | { | 1032 | { |
1080 | breath_speed = 4; | 1033 | breathing_period_set(breathing_period-1); |
1081 | } | 1034 | } |
1082 | 1035 | ||
1083 | void 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; | 1039 | static 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 | ||
1107 | void breathing_speed_inc(uint8_t value) | 1041 | // Use this before the cie_lightness function. |
1108 | { | 1042 | static 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 | ||
1119 | void 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 | */ | ||
1049 | ISR(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 | ||
1131 | void 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 | */ | ||
1144 | static 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, | ||
1147 | 255, 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 | ||
1151 | ISR(TIMER1_COMPA_vect) | 1067 | __attribute__ ((weak)) |
1068 | void 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)) |
1174 | void backlight_init_ports(void) | 1112 | void backlight_init_ports(void) {} |
1175 | { | ||
1176 | |||
1177 | } | ||
1178 | 1113 | ||
1179 | __attribute__ ((weak)) | 1114 | __attribute__ ((weak)) |
1180 | void backlight_set(uint8_t level) | 1115 | void backlight_set(uint8_t level) {} |
1181 | { | ||
1182 | |||
1183 | } | ||
1184 | 1116 | ||
1185 | #endif // backlight | 1117 | #endif // backlight |
1186 | 1118 | ||