/* Scrolling Text Display David Johnson-Davies - www.technoblogy.com - 11th September 2024 ATtiny85 @ 8 MHz (internal oscillator; BOD disabled) CC BY 4.0 Licensed under a Creative Commons Attribution 4.0 International license: http://creativecommons.org/licenses/by/4.0/ */ #include #include // Keyboard ********************************************** const int ClockPin = 4; // PB4 = D+ const int DataPin = 1; // PB1 = D- const int ADCPin = 3; // PB3 const int EndMessageGap = 5; const int MessageSize = 256; uint8_t KybdBuf[MessageSize+EndMessageGap]; volatile uint8_t WritePtr = 0, Escape = 0; volatile bool DisplayMode = true; // true: scrolling, false: editing volatile int Scroll = 0; volatile int MessageChars = MessageSize; const int KeymapSize = 132; const uint8_t Keymap[] PROGMEM = // Without shift " \011` q1 zsaw2 cxde43 vftr5 nbhgy6 mju78 ,kio09" " ./l;p- \' [= \015] \\ \010 1 47 0.2568\033 +3-*9 " // With shift " \011~ Q! ZSAW@ CXDE$# VFTR% NBHGY^ MJU&* ?L:P_ \" {+ \015} | \010 1 47 0.2568\033 +3-*9 "; ISR(PCINT0_vect) { static uint8_t Break = 0, Modifier = 0, Shift = 0; static int ScanCode = 0, ScanBit = 1; if (PINB & 1<> 1; ScanCode = 0, ScanBit = 1; if (s == 0xAA) return; // BAT completion code // if (s == 0xF0) { Break = 1; return; } if (s == 0xE0) { Modifier = 1; return; } if (Break) { if ((s == 0x12) || (s == 0x59)) Shift = 0; Break = 0; Modifier = 0; return; } if ((s == 0x12) || (s == 0x59)) Shift = 1; if (Modifier) return; char c = pgm_read_byte(&Keymap[s + KeymapSize*Shift]); if (c == 32 && s != 0x29) return; ProcessKey(c); return; } void ProcessKey (char c) { if (c == 27) { // Escape key goes to edit mode DisplayMode = false; Scroll = 6 * max(WritePtr-5, 0); MessageChars = MessageSize; return; } if (DisplayMode) return; // Ignore all other keys // Edit buffer if (c == '\r') { // Return key goes to display mode DisplayMode = true; MessageChars = WritePtr + EndMessageGap; EEPROM.write(0, WritePtr); EEPROM.write(1, MessageChars); EEPROM.put(2, KybdBuf); // Save our message return; } if (c == 9) { // Tab key for (int i=0; i 0) { if (WritePtr <= 5) Scroll = Scroll - 6; WritePtr--; KybdBuf[WritePtr] = ' '; } } else if (WritePtr < MessageSize) { KybdBuf[WritePtr++] = c; } Scroll = 6 * max(WritePtr-5, 0); return; } void SetupKeyboard() { PORTB = PORTB | 1<> 1) & 0x55) | ((x << 1) & 0xaa); x = ((x >> 2) & 0x33) | ((x << 2) & 0xcc); x = ((x >> 4) & 0x0f) | ((x << 4) & 0xf0); return x; } void SetupDisplay (int addr) { Wire.beginTransmission(addr); Wire.write(0x21); Wire.endTransmission(); Wire.beginTransmission(addr); Wire.write(0x81); Wire.endTransmission(); } // Brightness from 1 to 15 void SetBrightness (int addr, int bri) { Wire.beginTransmission(addr); Wire.write(0xe0 + bri); Wire.endTransmission(); } const int Brightness = 2; // Setup ********************************************** void setup() { SetupKeyboard(); Wire.begin(); for (int disp=0; disp<4; disp++) { int addr = Addresses[disp]; SetupDisplay(addr); SetBrightness(addr, Brightness); } WritePtr = EEPROM.read(0); MessageChars = EEPROM.read(1); EEPROM.get(2, KybdBuf); } void loop() { for (int disp=0; disp<4; disp++) { for (int c=0; c < 8; c++) { int dcol = disp*8 + c; int column = dcol + Scroll; int nchar = (column/6)%MessageChars; int cchar = column%6; int addr = Addresses[disp]; Wire.beginTransmission(addr); Wire.write(c<<1); int segs = ReverseByte(pgm_read_byte(&CharMap[KybdBuf[nchar]-32][cchar])); Wire.write(segs>>1 | segs<<7); Wire.endTransmission(); } } delay(analogRead(ADCPin)>>3); if (DisplayMode) Scroll++; }