aboutsummaryrefslogtreecommitdiff
path: root/quantum
diff options
context:
space:
mode:
Diffstat (limited to 'quantum')
-rw-r--r--quantum/quantum.c256
-rw-r--r--quantum/quantum.h4
2 files changed, 217 insertions, 43 deletions
diff --git a/quantum/quantum.c b/quantum/quantum.c
index 9aa498dad..0fb798a74 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -1138,30 +1138,38 @@ void matrix_scan_quantum() {
1138 1138
1139 matrix_scan_kb(); 1139 matrix_scan_kb();
1140} 1140}
1141#if defined(BACKLIGHT_ENABLE) && defined(BACKLIGHT_PIN) 1141#if defined(BACKLIGHT_ENABLE) && (defined(BACKLIGHT_PIN) || defined(BACKLIGHT_PINS))
1142 1142
1143static const uint8_t backlight_pin = BACKLIGHT_PIN; 1143// The logic is a bit complex, we support 3 setups:
1144// 1. hardware PWM when backlight is wired to a PWM pin
1145// depending on this pin, we use a different output compare unit
1146// 2. software PWM with hardware timers, but the used timer depends
1147// on the audio setup (audio wins other backlight)
1148// 3. full software PWM
1144 1149
1145// depending on the pin, we use a different output compare unit
1146#if BACKLIGHT_PIN == B7 1150#if BACKLIGHT_PIN == B7
1151# define HARDWARE_PWM
1147# define TCCRxA TCCR1A 1152# define TCCRxA TCCR1A
1148# define TCCRxB TCCR1B 1153# define TCCRxB TCCR1B
1149# define COMxx1 COM1C1 1154# define COMxx1 COM1C1
1150# define OCRxx OCR1C 1155# define OCRxx OCR1C
1151# define ICRx ICR1 1156# define ICRx ICR1
1152#elif BACKLIGHT_PIN == B6 1157#elif BACKLIGHT_PIN == B6
1158# define HARDWARE_PWM
1153# define TCCRxA TCCR1A 1159# define TCCRxA TCCR1A
1154# define TCCRxB TCCR1B 1160# define TCCRxB TCCR1B
1155# define COMxx1 COM1B1 1161# define COMxx1 COM1B1
1156# define OCRxx OCR1B 1162# define OCRxx OCR1B
1157# define ICRx ICR1 1163# define ICRx ICR1
1158#elif BACKLIGHT_PIN == B5 1164#elif BACKLIGHT_PIN == B5
1165# define HARDWARE_PWM
1159# define TCCRxA TCCR1A 1166# define TCCRxA TCCR1A
1160# define TCCRxB TCCR1B 1167# define TCCRxB TCCR1B
1161# define COMxx1 COM1A1 1168# define COMxx1 COM1A1
1162# define OCRxx OCR1A 1169# define OCRxx OCR1A
1163# define ICRx ICR1 1170# define ICRx ICR1
1164#elif BACKLIGHT_PIN == C6 1171#elif BACKLIGHT_PIN == C6
1172# define HARDWARE_PWM
1165# define TCCRxA TCCR3A 1173# define TCCRxA TCCR3A
1166# define TCCRxB TCCR3B 1174# define TCCRxB TCCR3B
1167# define COMxx1 COM1A1 1175# define COMxx1 COM1A1
@@ -1175,28 +1183,115 @@ static const uint8_t backlight_pin = BACKLIGHT_PIN;
1175# define ICRx ICR1 1183# define ICRx ICR1
1176# define TIMSK1 TIMSK 1184# define TIMSK1 TIMSK
1177#else 1185#else
1178# define NO_HARDWARE_PWM 1186# if !defined(BACKLIGHT_CUSTOM_DRIVER)
1187# if !defined(B5_AUDIO) && !defined(B6_AUDIO) && !defined(B7_AUDIO)
1188 // timer 1 is not used by audio , backlight can use it
1189#pragma message "Using hardware timer 1 with software PWM"
1190# define HARDWARE_PWM
1191# define BACKLIGHT_PWM_TIMER
1192# define TCCRxA TCCR1A
1193# define TCCRxB TCCR1B
1194# define OCRxx OCR1A
1195# define OCRxAH OCR1AH
1196# define OCRxAL OCR1AL
1197# define TIMERx_COMPA_vect TIMER1_COMPA_vect
1198# define TIMERx_OVF_vect TIMER1_OVF_vect
1199# define OCIExA OCIE1A
1200# define TOIEx TOIE1
1201# define ICRx ICR1
1202# ifndef TIMSK
1203# define TIMSK TIMSK1
1204# endif
1205# elif !defined(C6_AUDIO) && !defined(C5_AUDIO) && !defined(C4_AUDIO)
1206#pragma message "Using hardware timer 3 with software PWM"
1207// timer 3 is not used by audio, backlight can use it
1208# define HARDWARE_PWM
1209# define BACKLIGHT_PWM_TIMER
1210# define TCCRxA TCCR3A
1211# define TCCRxB TCCR3B
1212# define OCRxx OCR3A
1213# define OCRxAH OCR3AH
1214# define OCRxAL OCR3AL
1215# define TIMERx_COMPA_vect TIMER3_COMPA_vect
1216# define TIMERx_OVF_vect TIMER3_OVF_vect
1217# define OCIExA OCIE3A
1218# define TOIEx TOIE3
1219# define ICRx ICR1
1220# ifndef TIMSK
1221# define TIMSK TIMSK3
1222# endif
1223# else
1224#pragma message "Audio in use - using pure software PWM"
1225#define NO_HARDWARE_PWM
1226# endif
1227# else
1228#pragma message "Custom driver defined - using pure software PWM"
1229#define NO_HARDWARE_PWM
1230# endif
1179#endif 1231#endif
1180 1232
1181#ifndef BACKLIGHT_ON_STATE 1233#ifndef BACKLIGHT_ON_STATE
1182#define BACKLIGHT_ON_STATE 0 1234#define BACKLIGHT_ON_STATE 0
1183#endif 1235#endif
1184 1236
1185#ifdef NO_HARDWARE_PWM // pwm through software 1237void backlight_on(uint8_t backlight_pin) {
1238#if BACKLIGHT_ON_STATE == 0
1239 writePinLow(backlight_pin);
1240#else
1241 writePinHigh(backlight_pin);
1242#endif
1243}
1186 1244
1187__attribute__ ((weak)) 1245void backlight_off(uint8_t backlight_pin) {
1246#if BACKLIGHT_ON_STATE == 0
1247 writePinHigh(backlight_pin);
1248#else
1249 writePinLow(backlight_pin);
1250#endif
1251}
1252
1253
1254#if defined(NO_HARDWARE_PWM) || defined(BACKLIGHT_PWM_TIMER) // pwm through software
1255
1256// we support multiple backlight pins
1257#ifndef BACKLIGHT_LED_COUNT
1258#define BACKLIGHT_LED_COUNT 1
1259#endif
1260
1261#if BACKLIGHT_LED_COUNT == 1
1262#define BACKLIGHT_PIN_INIT { BACKLIGHT_PIN }
1263#else
1264#define BACKLIGHT_PIN_INIT BACKLIGHT_PINS
1265#endif
1266
1267#define FOR_EACH_LED(x) \
1268 for (uint8_t i = 0; i < BACKLIGHT_LED_COUNT; i++) \
1269 { \
1270 uint8_t backlight_pin = backlight_pins[i]; \
1271 { \
1272 x \
1273 } \
1274 }
1275
1276static const uint8_t backlight_pins[BACKLIGHT_LED_COUNT] = BACKLIGHT_PIN_INIT;
1277
1278#else // full hardware PWM
1279
1280// we support only one backlight pin
1281static const uint8_t backlight_pin = BACKLIGHT_PIN;
1282#define FOR_EACH_LED(x) x
1283
1284#endif
1285
1286#ifdef NO_HARDWARE_PWM
1287__attribute__((weak))
1188void backlight_init_ports(void) 1288void backlight_init_ports(void)
1189{ 1289{
1190 // Setup backlight pin as output and output to on state. 1290 // Setup backlight pin as output and output to on state.
1191 // DDRx |= n 1291 FOR_EACH_LED(
1192 _SFR_IO8((backlight_pin >> 4) + 1) |= _BV(backlight_pin & 0xF); 1292 setPinOutput(backlight_pin);
1193 #if BACKLIGHT_ON_STATE == 0 1293 backlight_on(backlight_pin);
1194 // PORTx &= ~n 1294 )
1195 _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
1196 #else
1197 // PORTx |= n
1198 _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
1199 #endif
1200} 1295}
1201 1296
1202__attribute__ ((weak)) 1297__attribute__ ((weak))
@@ -1207,21 +1302,14 @@ uint8_t backlight_tick = 0;
1207#ifndef BACKLIGHT_CUSTOM_DRIVER 1302#ifndef BACKLIGHT_CUSTOM_DRIVER
1208void backlight_task(void) { 1303void backlight_task(void) {
1209 if ((0xFFFF >> ((BACKLIGHT_LEVELS - get_backlight_level()) * ((BACKLIGHT_LEVELS + 1) / 2))) & (1 << backlight_tick)) { 1304 if ((0xFFFF >> ((BACKLIGHT_LEVELS - get_backlight_level()) * ((BACKLIGHT_LEVELS + 1) / 2))) & (1 << backlight_tick)) {
1210 #if BACKLIGHT_ON_STATE == 0 1305 FOR_EACH_LED(
1211 // PORTx &= ~n 1306 backlight_on(backlight_pin);
1212 _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF); 1307 )
1213 #else 1308 }
1214 // PORTx |= n 1309 else {
1215 _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF); 1310 FOR_EACH_LED(
1216 #endif 1311 backlight_off(backlight_pin);
1217 } else { 1312 )
1218 #if BACKLIGHT_ON_STATE == 0
1219 // PORTx |= n
1220 _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
1221 #else
1222 // PORTx &= ~n
1223 _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
1224 #endif
1225 } 1313 }
1226 backlight_tick = (backlight_tick + 1) % 16; 1314 backlight_tick = (backlight_tick + 1) % 16;
1227} 1315}
@@ -1233,7 +1321,52 @@ void backlight_task(void) {
1233 #endif 1321 #endif
1234#endif 1322#endif
1235 1323
1236#else // pwm through timer 1324#else // hardware pwm through timer
1325
1326#ifdef BACKLIGHT_PWM_TIMER
1327
1328// The idea of software PWM assisted by hardware timers is the following
1329// we use the hardware timer in fast PWM mode like for hardware PWM, but
1330// instead of letting the Output Match Comparator control the led pin
1331// (which is not possible since the backlight is not wired to PWM pins on the
1332// CPU), we do the LED on/off by oursleves.
1333// The timer is setup to count up to 0xFFFF, and we set the Output Compare
1334// register to the current 16bits backlight level (after CIE correction).
1335// This means the CPU will trigger a compare match interrupt when the counter
1336// reaches the backlight level, where we turn off the LEDs,
1337// but also an overflow interrupt when the counter rolls back to 0,
1338// in which we're going to turn on the LEDs.
1339// The LED will then be on for OCRxx/0xFFFF time, adjusted every 244Hz.
1340
1341// Triggered when the counter reaches the OCRx value
1342ISR(TIMERx_COMPA_vect) {
1343 FOR_EACH_LED(
1344 backlight_off(backlight_pin);
1345 )
1346}
1347
1348// Triggered when the counter reaches the TOP value
1349// this one triggers at F_CPU/65536 =~ 244 Hz
1350ISR(TIMERx_OVF_vect) {
1351#ifdef BACKLIGHT_BREATHING
1352 breathing_task();
1353#endif
1354 // for very small values of OCRxx (or backlight level)
1355 // we can't guarantee this whole code won't execute
1356 // at the same time as the compare match interrupt
1357 // which means that we might turn on the leds while
1358 // trying to turn them off, leading to flickering
1359 // artifacts (especially while breathing, because breathing_task
1360 // takes many computation cycles).
1361 // so better not turn them on while the counter TOP is very low.
1362 if (OCRxx > 256) {
1363 FOR_EACH_LED(
1364 backlight_on(backlight_pin);
1365 )
1366 }
1367}
1368
1369#endif
1237 1370
1238#define TIMER_TOP 0xFFFFU 1371#define TIMER_TOP 0xFFFFU
1239 1372
@@ -1265,11 +1398,28 @@ void backlight_set(uint8_t level) {
1265 level = BACKLIGHT_LEVELS; 1398 level = BACKLIGHT_LEVELS;
1266 1399
1267 if (level == 0) { 1400 if (level == 0) {
1401 #ifdef BACKLIGHT_PWM_TIMER
1402 if (OCRxx) {
1403 TIMSK &= ~(_BV(OCIExA));
1404 TIMSK &= ~(_BV(TOIEx));
1405 FOR_EACH_LED(
1406 backlight_off(backlight_pin);
1407 )
1408 }
1409 #else
1268 // Turn off PWM control on backlight pin 1410 // Turn off PWM control on backlight pin
1269 TCCRxA &= ~(_BV(COMxx1)); 1411 TCCRxA &= ~(_BV(COMxx1));
1412 #endif
1270 } else { 1413 } else {
1414 #ifdef BACKLIGHT_PWM_TIMER
1415 if (!OCRxx) {
1416 TIMSK |= _BV(OCIExA);
1417 TIMSK |= _BV(TOIEx);
1418 }
1419 #else
1271 // Turn on PWM control of backlight pin 1420 // Turn on PWM control of backlight pin
1272 TCCRxA |= _BV(COMxx1); 1421 TCCRxA |= _BV(COMxx1);
1422 #endif
1273 } 1423 }
1274 // Set the brightness 1424 // Set the brightness
1275 set_pwm(cie_lightness(TIMER_TOP * (uint32_t)level / BACKLIGHT_LEVELS)); 1425 set_pwm(cie_lightness(TIMER_TOP * (uint32_t)level / BACKLIGHT_LEVELS));
@@ -1289,12 +1439,25 @@ static uint8_t breathing_period = BREATHING_PERIOD;
1289static uint8_t breathing_halt = BREATHING_NO_HALT; 1439static uint8_t breathing_halt = BREATHING_NO_HALT;
1290static uint16_t breathing_counter = 0; 1440static uint16_t breathing_counter = 0;
1291 1441
1442#ifdef BACKLIGHT_PWM_TIMER
1443static bool breathing = false;
1444
1445bool is_breathing(void) {
1446 return breathing;
1447}
1448
1449#define breathing_interrupt_enable() do { breathing = true; } while (0)
1450#define breathing_interrupt_disable() do { breathing = false; } while (0)
1451#else
1452
1292bool is_breathing(void) { 1453bool is_breathing(void) {
1293 return !!(TIMSK1 & _BV(TOIE1)); 1454 return !!(TIMSK1 & _BV(TOIE1));
1294} 1455}
1295 1456
1296#define breathing_interrupt_enable() do {TIMSK1 |= _BV(TOIE1);} while (0) 1457#define breathing_interrupt_enable() do {TIMSK1 |= _BV(TOIE1);} while (0)
1297#define breathing_interrupt_disable() do {TIMSK1 &= ~_BV(TOIE1);} while (0) 1458#define breathing_interrupt_disable() do {TIMSK1 &= ~_BV(TOIE1);} while (0)
1459#endif
1460
1298#define breathing_min() do {breathing_counter = 0;} while (0) 1461#define breathing_min() do {breathing_counter = 0;} while (0)
1299#define breathing_max() do {breathing_counter = breathing_period * 244 / 2;} while (0) 1462#define breathing_max() do {breathing_counter = breathing_period * 244 / 2;} while (0)
1300 1463
@@ -1368,10 +1531,14 @@ static inline uint16_t scale_backlight(uint16_t v) {
1368 return v / BACKLIGHT_LEVELS * get_backlight_level(); 1531 return v / BACKLIGHT_LEVELS * get_backlight_level();
1369} 1532}
1370 1533
1534#ifdef BACKLIGHT_PWM_TIMER
1535void breathing_task(void)
1536#else
1371/* Assuming a 16MHz CPU clock and a timer that resets at 64k (ICR1), the following interrupt handler will run 1537/* Assuming a 16MHz CPU clock and a timer that resets at 64k (ICR1), the following interrupt handler will run
1372 * about 244 times per second. 1538 * about 244 times per second.
1373 */ 1539 */
1374ISR(TIMER1_OVF_vect) 1540ISR(TIMER1_OVF_vect)
1541#endif
1375{ 1542{
1376 uint16_t interval = (uint16_t) breathing_period * 244 / BREATHING_STEPS; 1543 uint16_t interval = (uint16_t) breathing_period * 244 / BREATHING_STEPS;
1377 // resetting after one period to prevent ugly reset at overflow. 1544 // resetting after one period to prevent ugly reset at overflow.
@@ -1393,19 +1560,21 @@ __attribute__ ((weak))
1393void backlight_init_ports(void) 1560void backlight_init_ports(void)
1394{ 1561{
1395 // Setup backlight pin as output and output to on state. 1562 // Setup backlight pin as output and output to on state.
1396 // DDRx |= n 1563 FOR_EACH_LED(
1397 _SFR_IO8((backlight_pin >> 4) + 1) |= _BV(backlight_pin & 0xF); 1564 setPinOutput(backlight_pin);
1398 #if BACKLIGHT_ON_STATE == 0 1565 backlight_on(backlight_pin);
1399 // PORTx &= ~n 1566 )
1400 _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF); 1567
1401 #else
1402 // PORTx |= n
1403 _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
1404 #endif
1405 // I could write a wall of text here to explain... but TL;DW 1568 // I could write a wall of text here to explain... but TL;DW
1406 // Go read the ATmega32u4 datasheet. 1569 // Go read the ATmega32u4 datasheet.
1407 // And this: http://blog.saikoled.com/post/43165849837/secret-konami-cheat-code-to-high-resolution-pwm-on 1570 // And this: http://blog.saikoled.com/post/43165849837/secret-konami-cheat-code-to-high-resolution-pwm-on
1408 1571
1572#ifdef BACKLIGHT_PWM_TIMER
1573 // TimerX setup, Fast PWM mode count to TOP set in ICRx
1574 TCCRxA = _BV(WGM11); // = 0b00000010;
1575 // clock select clk/1
1576 TCCRxB = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001;
1577#else // hardware PWM
1409 // Pin PB7 = OCR1C (Timer 1, Channel C) 1578 // Pin PB7 = OCR1C (Timer 1, Channel C)
1410 // Compare Output Mode = Clear on compare match, Channel C = COM1C1=1 COM1C0=0 1579 // Compare Output Mode = Clear on compare match, Channel C = COM1C1=1 COM1C0=0
1411 // (i.e. start high, go low when counter matches.) 1580 // (i.e. start high, go low when counter matches.)
@@ -1417,8 +1586,9 @@ void backlight_init_ports(void)
1417 "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 [..]." 1586 "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 [..]."
1418 "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)." 1587 "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)."
1419 */ 1588 */
1420 TCCRxA = _BV(COMxx1) | _BV(WGM11); // = 0b00001010; 1589 TCCRxA = _BV(COMxx1) | _BV(WGM11); // = 0b00001010;
1421 TCCRxB = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001; 1590 TCCRxB = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001;
1591#endif
1422 // Use full 16-bit resolution. Counter counts to ICR1 before reset to 0. 1592 // Use full 16-bit resolution. Counter counts to ICR1 before reset to 0.
1423 ICRx = TIMER_TOP; 1593 ICRx = TIMER_TOP;
1424 1594
@@ -1428,9 +1598,9 @@ void backlight_init_ports(void)
1428 #endif 1598 #endif
1429} 1599}
1430 1600
1431#endif // NO_HARDWARE_PWM 1601#endif // hardware backlight
1432 1602
1433#else // backlight 1603#else // no backlight
1434 1604
1435__attribute__ ((weak)) 1605__attribute__ ((weak))
1436void backlight_init_ports(void) {} 1606void backlight_init_ports(void) {}
diff --git a/quantum/quantum.h b/quantum/quantum.h
index 987516ded..17cb90274 100644
--- a/quantum/quantum.h
+++ b/quantum/quantum.h
@@ -260,8 +260,12 @@ void tap_code16(uint16_t code);
260#ifdef BACKLIGHT_ENABLE 260#ifdef BACKLIGHT_ENABLE
261void backlight_init_ports(void); 261void backlight_init_ports(void);
262void backlight_task(void); 262void backlight_task(void);
263void backlight_task_internal(void);
264void backlight_on(uint8_t backlight_pin);
265void backlight_off(uint8_t backlight_pin);
263 266
264#ifdef BACKLIGHT_BREATHING 267#ifdef BACKLIGHT_BREATHING
268void breathing_task(void);
265void breathing_enable(void); 269void breathing_enable(void);
266void breathing_pulse(void); 270void breathing_pulse(void);
267void breathing_disable(void); 271void breathing_disable(void);