ledbar.cpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. /* 7 rows LED bar firmware / library
  2. * Copyright (C) 2019 asdrea
  3. *
  4. * This program is free software: you can redistribute it and/or modify it under
  5. * the terms of the GNU General Public License as published by the Free Software
  6. * Foundation, either version 3 of the License, or (at your option) any later
  7. * version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but WITHOUT
  10. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  11. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  12. * details.
  13. *
  14. * You should have received a copy of the GNU General Public License along with
  15. * this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include <SPI.h>
  18. #include <string.h>
  19. #include "charset.h"
  20. #include "ledbar.h"
  21. static SPISettings spi_settings(1000000, LSBFIRST, SPI_MODE2);
  22. // Double buffer ROWSxCOLS bits
  23. static uint8_t ledbar_framebuf[2][LEDBAR_ROWS][LEDBAR_COLS >> 3];
  24. static volatile uint8_t cur_buf = 0, swap_buf = 0;
  25. static inline uint8_t getbuf(ledbar_buf_t b) { return (b == LEDBAR_FRONTBUF ? cur_buf : (1 - cur_buf)); }
  26. void ledbar_init(void)
  27. {
  28. uint8_t i;
  29. for(i = 0; i < LEDBAR_ROWS; ++i) {
  30. pinMode(LEDBAR_PIN_ROWS[i], OUTPUT);
  31. digitalWrite(LEDBAR_PIN_ROWS[i], LOW);
  32. }
  33. pinMode(LEDBAR_PIN_STROBE, OUTPUT);
  34. digitalWrite(LEDBAR_PIN_STROBE, LOW);
  35. SPI.begin();
  36. memset(ledbar_framebuf, 0, 2 * LEDBAR_ROWS * (LEDBAR_COLS >> 3));
  37. // Set timer1 interrupt at ~350Hz
  38. cli();
  39. TCCR1A = 0;
  40. TCCR1B = 0;
  41. TCNT1 = 0;
  42. OCR1A = 44; // = 16e6 / (350 * 1024) - 1 (must be <65536)
  43. TCCR1B |= (1 << WGM12); // turn on CTC mode
  44. TCCR1B |= (1 << CS12) | (1 << CS10); // Set CS10 and CS12 bits for 1024 prescaler
  45. TIMSK1 |= (1 << OCIE1A); // Enable timer compare interrupt
  46. sei();
  47. }
  48. void ledbar_wait_vsync(void)
  49. {
  50. while(swap_buf);
  51. }
  52. void ledbar_swap_buf(void)
  53. {
  54. swap_buf = 1;
  55. }
  56. void ledbar_clear(ledbar_buf_t buf)
  57. {
  58. memset(ledbar_framebuf[getbuf(buf)], 0, LEDBAR_ROWS * (LEDBAR_COLS >> 3));
  59. }
  60. #define _LEDBAR_SETCHAR_SETCOL(X) \
  61. if(b < (8 - X)) { \
  62. c0 |= ((chbuf[X] >> k) & 0x1) << ((7 - X) - b); \
  63. } \
  64. else { \
  65. c1 |= ((chbuf[X] >> k) & 0x1) << ((15 - X) - b); \
  66. }
  67. void ledbar_setchar(ledbar_buf_t buf, int16_t col, uint8_t ch)
  68. {
  69. uint8_t i, k, *p;
  70. uint8_t chbuf[5];
  71. uint8_t c0 = 0, c1 = 0;
  72. int8_t block = (col >> 3);
  73. int8_t b = col - (block << 3);
  74. uint8_t buf_id = getbuf(buf);
  75. if(block < -1 || block >= (LEDBAR_COLS >> 3))
  76. return;
  77. memcpy_P(chbuf, CHARSET[ch] + i, 5);
  78. for(i = 0; i < LEDBAR_ROWS; ++i) {
  79. p = ledbar_framebuf[buf_id][i] + block;
  80. if(block >= 0)
  81. c0 = p[0];
  82. if(block < ((LEDBAR_COLS >> 3) - 1))
  83. c1 = p[1];
  84. k = LEDBAR_ROWS - i - 1;
  85. // Optimize this shit?
  86. _LEDBAR_SETCHAR_SETCOL(4);
  87. _LEDBAR_SETCHAR_SETCOL(3);
  88. _LEDBAR_SETCHAR_SETCOL(2);
  89. _LEDBAR_SETCHAR_SETCOL(1);
  90. c0 |= ((chbuf[0] >> k) & 0x1) << (7 - b); // == _LEDBAR_SETCHAR_SETCOL(0)
  91. if(block >= 0)
  92. p[0] = c0;
  93. if(block < ((LEDBAR_COLS >> 3) - 1))
  94. p[1] = c1;
  95. }
  96. }
  97. void ledbar_settext(ledbar_buf_t buf, int16_t col, const char* text, uint8_t spacing)
  98. {
  99. uint16_t i;
  100. for(i = 0; text[i]; ++i) {
  101. ledbar_setchar(buf, col, text[i]);
  102. col += 5 + spacing;
  103. }
  104. }
  105. ISR(TIMER1_COMPA_vect)
  106. {
  107. static uint8_t cur_row = LEDBAR_ROWS - 1;
  108. uint8_t i, rowbuf[LEDBAR_COLS >> 3];
  109. // Power-off previous row
  110. digitalWrite(LEDBAR_PIN_ROWS[cur_row++], LOW);
  111. if(cur_row >= LEDBAR_ROWS) {
  112. cur_row = 0;
  113. if(swap_buf) {
  114. // Swap buffers only on the first row (i.e. sync @50Hz)
  115. cur_buf = 1 - cur_buf;
  116. swap_buf = 0;
  117. }
  118. }
  119. // Buffer column data (SPI.transfer() modify the content)
  120. for(i = 0; i < (LEDBAR_COLS >> 3); ++i)
  121. rowbuf[i] = ledbar_framebuf[cur_buf][cur_row][(LEDBAR_COLS >> 3) - i - 1];
  122. // Write column data
  123. SPI.beginTransaction(spi_settings);
  124. SPI.transfer(rowbuf, LEDBAR_COLS >> 3);
  125. SPI.endTransaction();
  126. // Strobe
  127. digitalWrite(LEDBAR_PIN_STROBE, HIGH);
  128. digitalWrite(LEDBAR_PIN_STROBE, LOW);
  129. // Need a delay here? Glitchy?
  130. // Power-on current row
  131. digitalWrite(LEDBAR_PIN_ROWS[cur_row], HIGH);
  132. }