/* ----------------------------------------------------- oled_demo.c Minimalprogramm fuer Textausgabe auf einem OLED-Display mit SSD1306 oder SSD1315 Display Damit das Programm grundsaetzlich mit jedem Mikrocontroller laufen soll, wurde I2C mittels Bitbanging realisiert, auch wenn hier ein direktes compilieren nur fuer einen AVR-Controller gegeben ist MCU : diverse Takt : nicht spezifiziert IDE : keine 25.02.2026 R. seelig ------------------------------------------------------ */ #include /* ------------------------------------------------------------ grundsaetzliche Konfiguration ------------------------------------------------------------ */ #define i2c_delay 0 // Verzoegerungszeit fuer die I2C Bitbanging Funktion, normalerweise 0 #define oled_addr 0x3c // Deviceadresse des Displays #define oled_width 128 // x-Aufloesung in Pixel #define oled_pages 8 // y-Aufloesung in Reihen. Jede Reihe hat 8 Pixel, daher 8 => 64 Pixel #define oled_is_1315 0 // 0 = ssd1306, 1 = ssd1315 // Define "entkommentieren", wenn das Programm fuer einen STM8S mittels SDCC uebersetzt // werden soll // #define STM8S /* ------------------------------------------------------------ Controllerspezifisches ------------------------------------------------------------ */ #ifdef __AVR__ /* ------------------------------------------------------------ AVR-Mikrocontroller ------------------------------------------------------------ */ #include #include #include #define delay_us(anz) _delay_us(anz) // Portpins fuer I2C (Bitbanging): SDA = PC4, SCL = PC5 #define i2c_port PORTC #define i2c_ddr DDRC #define i2c_pinr PINC #define sda_bit 4 #define scl_bit 5 // Bitbanging der Pins. Logische 1 wird durch setzen des Anschlusses auf Input !!! // bewirkt, der PullUp des Busses legt den Pin somit auf 1. // logisch 0 wird erreicht durch setzen des Ausganges auf 0 und setzen des Pins auf 0 #define sda_high() (i2c_ddr &= ~(1<> 4) // PB5 I/O #define PB5_output_init() { PB_DDR |= 0x20; \ PB_CR1 |= 0x20; \ PB_CR2 &= ~(0x20); } #define PB5_clr() ( PB_ODR &= ~(0x20) ) #define PB5_input_init() { PB_DDR &= ~(0x20); \ PB_CR1 |= 0x20; } #define sda_high() PB5_input_init() #define sda_low() { PB5_output_init(); PB5_clr(); } #define scl_high() PB4_input_init() #define scl_low() { PB4_output_init(); PB5_clr(); } #define sda_read() is_PB4() #define readfont(a,c) font8x8[(a)-32][c] #else // hier Makros fuer das Setzen und Loeschen der am Bitbanging I2C beteiligten Pins angeben #define sda_high() #define sda_low() #define scl_high() #define scl_low() #define sda_read() 0 #define readfont(a,c) font8x8[(a)-32][c] #error Beispielprogramm fuer anderen Mikrocontroller als AVR, bitte Programm um Bitbangingfunktionen erweitern #endif // Bitmapfont des Zeichensatzes ist am Ende der Datei zu finden extern const uint8_t font8x8[][8]; /* ------------------------------------------------------ STM8 "Gedoens" auch wenn das etwas "unuebersichtlicher" wird, wenn in dieser Datei noch ein weiterer Mikrocontroller verwendet werden kann, demonstriert dieses hier, wie in ein und derselben Datei Code geschrieben werden kann, der fuer unterschiedliche Controller gedacht ist. Hier der Einfachkeithalber fuer einen STM8, weil der Autor hierfuer auch eine Platine im r3 Format des AVR hat und somit das ganze sehr einfach zu testen ist (blosses Umstecken des Prototypenshieldes). ------------------------------------------------------ */ #ifdef STM8S /* ------------------------------------------------------ sysclock_init initialisiert einen STM8S fuer internen Takt ------------------------------------------------------ */ void sysclock_init(void) { volatile uint16_t i; CLK_ICKR = 0; // Reset Register interner clock CLK_ECKR = 0; // Reset Register externer clock (ext. clock disable) CLK_ICKR = HSIEN; // Interner clock enable while ((CLK_ICKR & (HSIRDY)) == 0); // warten bis int. Takt eingeschwungen ist CLK_CKDIVR = 0; // Taktteiler auf volle Geschwindigkeit CLK_PCKENR1 = 0xff; // alle Peripherietakte an CLK_PCKENR2 = 0xff; // dto. CLK_CCOR = 0; // CCO aus CLK_HSITRIMR = 0; // keine Taktjustierung CLK_SWIMCCR = 0; // SWIM = clock / 2. CLK_SWR = 0xe1; // int. Generator als Taktquelle CLK_SWCR = 0; // Reset clock switch control register. CLK_SWCR = SWEN; // Enable switching. while ((CLK_SWCR & SWBSY) != 0); // warten bis Peripherietakt stabil // Verzoegerungsschleife, damit sichergestellt ist, dass alles // "eingeschwungen" ist for (i= 0; i< 60000; i++) { } } /* ------------------------------------------------------ delay_us grobe Zeitverzoegerung in Mikrosekunden. Fuer genauere Zeitverzoegerung ist besser eine Interrupt- routine mittels Timer zu verwenden. ------------------------------------------------------ */ void delay_us(uint16_t anz) { volatile uint16_t count; for (count= 0; count< anz; count++) { __asm nop __endasm; } } /* ------------------------------------------------------ Ende STM8 "Gedoens" ------------------------------------------------------ */ #endif /* ------------------------------------------------------------------------------------------- ab hier Platformunabhaengige Funktionen fuer ein OLED-Display ------------------------------------------------------------------------------------------- */ /* ------------------------------------------------------------ I2C Bitbanging Funktionen ------------------------------------------------------------ */ #define i2c_wait() delay_us(i2c_delay) /* ------------------------------------------------------------ sendet Start-Condition auf dem Bus ------------------------------------------------------------ */ static void i2c_start(void) { sda_high(); scl_high(); i2c_wait(); sda_low(); i2c_wait(); scl_low(); } /* ------------------------------------------------------------ sendet Stop-Condition auf dem Bus ------------------------------------------------------------ */ static void i2c_stop(void) { sda_low(); scl_high(); i2c_wait(); sda_high(); i2c_wait(); } /* ------------------------------------------------------------ i2c_write schreibt ein Byte auf dem I2C-Bus und gibt Acknowledge zurueck. ------------------------------------------------------------ */ static uint8_t i2c_write(uint8_t data) { uint8_t i; for(i=0; i<8; i++) { if(data & 0x80) { sda_high(); } else { sda_low(); } scl_high(); i2c_wait(); scl_low(); data <<= 1; } sda_high(); // sda freigeben fuer ack scl_high(); i2c_wait(); uint8_t ack = !sda_read(); // ack ist low scl_low(); return ack; } /* ------------------------------------------------------------ oled_cmd schreibt ein Kommandobyte auf dem I2C-Bus ------------------------------------------------------------ */ static void oled_cmd(uint8_t cmd) { i2c_start(); i2c_write(oled_addr<<1); i2c_write(0x00); // 0x00 = Wert dass ein Kommandobyte folgt i2c_write(cmd); i2c_stop(); } /* ------------------------------------------------------------ oled_data schreibt ein Datenbyte (also ins Display-Ram) auf dem I2C-Bus ------------------------------------------------------------ */ static void oled_data(uint8_t data) { i2c_start(); i2c_write(oled_addr<<1); i2c_write(0x40); // 0x40 = Wert, dass ein Datenbyte folgt i2c_write(data); i2c_stop(); } /* ------------------------------------------------------------ oled_gotoxy positioniert die Ausgabepostition, an der ein nach- folgendes Textzeichen ausgegeben wird Bei einem 128x64 Pixel grossem Display entspricht das einem Textdisplay von 16x8 Zeichen. Somit sind erlaubte Koordinaten fuer x: 0..15 fuer y: 0..7 ------------------------------------------------------------ */ void oled_gotoxy(uint8_t x, uint8_t y) { oled_cmd(0xb0 | y); // Ausgabezeile setzen oled_cmd(0x00 | ((x*8) & 0x0f)); // niedriges Nibble der Spalte oled_cmd(0x10 | ((x*8) >> 4)); // hohes Nibble der Spalte } /* ------------------------------------------------------------ oled_clear loescht den Displayinhalt ------------------------------------------------------------ */ void oled_clear(void) { uint8_t i,p; for(p=0; p 127) { return; } for(col=0; col<8; col++) { oled_data(readfont(ascii,col)); } } /* ------------------------------------------------------------ oled_puts gibt einen String auf dem Display aus ------------------------------------------------------------ */ void oled_puts(const char *s) { while(*s) { oled_putchar(*s++); } } /* ------------------------------------------------------------ main ------------------------------------------------------------ */ int main(void) { #ifdef STM8S // fuer einen STM8 System initialisieren sysclock_init(); #endif oled_init(); oled_gotoxy(3,3); oled_puts("Hallo Welt"); while(1) { } } /* ------------------------------------------------------------ Bitmap eines 8x8 Pixel Zeichensatzes Arraydefinition unerscheidet zwischen einem AVR-Controller und anderen Controllern ------------------------------------------------------------ */ #ifdef __AVR__ const uint8_t font8x8[][8] PROGMEM = #else const uint8_t font8x8[][8] = #endif { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Ascii 32 = ' ' { 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x00, 0x00, 0x00 }, // Ascii 33 = '!' { 0x00, 0x07, 0x07, 0x00, 0x07, 0x07, 0x00, 0x00 }, // Ascii 34 = '"' { 0x14, 0x7f, 0x7f, 0x14, 0x7f, 0x7f, 0x14, 0x00 }, // Ascii 35 = '#' { 0x00, 0x24, 0x2a, 0x7f, 0x7f, 0x2a, 0x12, 0x00 }, // Ascii 36 = '$' { 0x00, 0x46, 0x66, 0x30, 0x18, 0x0c, 0x66, 0x62 }, // Ascii 37 = '%' { 0x00, 0x30, 0x7a, 0x4f, 0x5d, 0x37, 0x7a, 0x48 }, // Ascii 38 = '&' { 0x00, 0x00, 0x04, 0x07, 0x03, 0x00, 0x00, 0x00 }, // Ascii 39 = ''' { 0x00, 0x00, 0x1c, 0x3e, 0x63, 0x41, 0x00, 0x00 }, // Ascii 40 = '(' { 0x00, 0x00, 0x41, 0x63, 0x3e, 0x1c, 0x00, 0x00 }, // Ascii 41 = ')' { 0x08, 0x2a, 0x3e, 0x1c, 0x1c, 0x3e, 0x2a, 0x08 }, // Ascii 42 = '*' { 0x00, 0x08, 0x08, 0x3e, 0x3e, 0x08, 0x08, 0x00 }, // Ascii 43 = '+' { 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00 }, // Ascii 44 = ',' { 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00 }, // Ascii 45 = '-' { 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00 }, // Ascii 46 = '.' { 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x01, 0x00 }, // Ascii 47 = '/' { 0x3e, 0x7f, 0x51, 0x49, 0x45, 0x7f, 0x3e, 0x00 }, // Ascii 48 = '0' { 0x00, 0x00, 0x02, 0x7f, 0x7f, 0x00, 0x00, 0x00 }, // Ascii 49 = '1' { 0x00, 0x42, 0x63, 0x71, 0x59, 0x4f, 0x46, 0x00 }, // Ascii 50 = '2' { 0x00, 0x22, 0x63, 0x49, 0x49, 0x7f, 0x36, 0x00 }, // Ascii 51 = '3' { 0x18, 0x1c, 0x16, 0x13, 0x7f, 0x7f, 0x10, 0x00 }, // Ascii 52 = '4' { 0x00, 0x27, 0x67, 0x45, 0x45, 0x7d, 0x39, 0x00 }, // Ascii 53 = '5' { 0x00, 0x3e, 0x7f, 0x49, 0x49, 0x7b, 0x32, 0x00 }, // Ascii 54 = '6' { 0x00, 0x01, 0x01, 0x71, 0x79, 0x0f, 0x07, 0x00 }, // Ascii 55 = '7' { 0x00, 0x36, 0x7f, 0x49, 0x49, 0x7f, 0x36, 0x00 }, // Ascii 56 = '8' { 0x00, 0x06, 0x4f, 0x69, 0x39, 0x1f, 0x0e, 0x00 }, // Ascii 57 = '9' { 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x00, 0x00 }, // Ascii 58 = ':' { 0x00, 0x00, 0x01, 0x6d, 0x6c, 0x00, 0x00, 0x00 }, // Ascii 59 = ';' { 0x00, 0x08, 0x1c, 0x36, 0x63, 0x41, 0x00, 0x00 }, // Ascii 60 = '<' { 0x00, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x00 }, // Ascii 61 = '=' { 0x00, 0x41, 0x63, 0x36, 0x1c, 0x08, 0x00, 0x00 }, // Ascii 62 = '>' { 0x00, 0x02, 0x03, 0x51, 0x59, 0x0f, 0x06, 0x00 }, // Ascii 63 = '?' { 0x3e, 0x7f, 0x41, 0x5d, 0x5d, 0x5f, 0x1e, 0x00 }, // Ascii 64 = '@' { 0x7c, 0x7e, 0x13, 0x11, 0x13, 0x7e, 0x7c, 0x00 }, // Ascii 65 = 'A' { 0x7f, 0x7f, 0x49, 0x49, 0x49, 0x7f, 0x36, 0x00 }, // Ascii 66 = 'B' { 0x1c, 0x3e, 0x63, 0x41, 0x41, 0x63, 0x22, 0x00 }, // Ascii 67 = 'C' { 0x7f, 0x7f, 0x41, 0x41, 0x63, 0x3e, 0x1c, 0x00 }, // Ascii 68 = 'D' { 0x7f, 0x7f, 0x49, 0x49, 0x49, 0x41, 0x41, 0x00 }, // Ascii 69 = 'E' { 0x7f, 0x7f, 0x09, 0x09, 0x09, 0x01, 0x01, 0x00 }, // Ascii 70 = 'F' { 0x1c, 0x3e, 0x63, 0x41, 0x51, 0x73, 0x72, 0x00 }, // Ascii 71 = 'G' { 0x7f, 0x7f, 0x08, 0x08, 0x08, 0x7f, 0x7f, 0x00 }, // Ascii 72 = 'H' { 0x00, 0x41, 0x7f, 0x7f, 0x41, 0x00, 0x00, 0x00 }, // Ascii 73 = 'I' { 0x30, 0x70, 0x40, 0x40, 0x40, 0x7f, 0x3f, 0x00 }, // Ascii 74 = 'J' { 0x7f, 0x7f, 0x09, 0x1d, 0x36, 0x63, 0x41, 0x00 }, // Ascii 75 = 'K' { 0x7f, 0x7f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00 }, // Ascii 76 = 'L' { 0x7f, 0x7f, 0x06, 0x0c, 0x06, 0x7f, 0x7f, 0x00 }, // Ascii 77 = 'M' { 0x7f, 0x7f, 0x06, 0x0c, 0x18, 0x7f, 0x7f, 0x00 }, // Ascii 78 = 'N' { 0x1c, 0x3e, 0x63, 0x41, 0x63, 0x3e, 0x1c, 0x00 }, // Ascii 79 = 'O' { 0x7f, 0x7f, 0x09, 0x09, 0x09, 0x0f, 0x06, 0x00 }, // Ascii 80 = 'P' { 0x1c, 0x3e, 0x63, 0x51, 0x33, 0x6e, 0x5c, 0x00 }, // Ascii 81 = 'Q' { 0x7f, 0x7f, 0x09, 0x09, 0x19, 0x7f, 0x66, 0x00 }, // Ascii 82 = 'R' { 0x26, 0x6f, 0x49, 0x49, 0x49, 0x7b, 0x32, 0x00 }, // Ascii 83 = 'S' { 0x01, 0x01, 0x7f, 0x7f, 0x01, 0x01, 0x00, 0x00 }, // Ascii 84 = 'T' { 0x3f, 0x7f, 0x40, 0x40, 0x40, 0x7f, 0x3f, 0x00 }, // Ascii 85 = 'U' { 0x1f, 0x3f, 0x60, 0x40, 0x60, 0x3f, 0x1f, 0x00 }, // Ascii 86 = 'V' { 0x7f, 0x7f, 0x30, 0x18, 0x30, 0x7f, 0x7f, 0x00 }, // Ascii 87 = 'W' { 0x63, 0x77, 0x1c, 0x08, 0x1c, 0x77, 0x63, 0x00 }, // Ascii 88 = 'X' { 0x00, 0x07, 0x0f, 0x78, 0x78, 0x0f, 0x07, 0x00 }, // Ascii 89 = 'Y' { 0x41, 0x61, 0x71, 0x59, 0x4d, 0x47, 0x43, 0x00 }, // Ascii 90 = 'Z' { 0x00, 0x00, 0x7f, 0x7f, 0x41, 0x41, 0x00, 0x00 }, // Ascii 91 = '[' { 0x01, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00 }, // Ascii 92 = '\' { 0x00, 0x00, 0x41, 0x41, 0x7f, 0x7f, 0x00, 0x00 }, // Ascii 93 = ']' { 0x00, 0x08, 0x0c, 0x06, 0x03, 0x06, 0x0c, 0x08 }, // Ascii 94 = '^' { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, // Ascii 95 = '_' { 0x00, 0x00, 0x01, 0x03, 0x06, 0x04, 0x00, 0x00 }, // Ascii 96 = '`' { 0x20, 0x74, 0x54, 0x54, 0x54, 0x7c, 0x78, 0x00 }, // Ascii 97 = 'a' { 0x7f, 0x7f, 0x44, 0x44, 0x44, 0x7c, 0x38, 0x00 }, // Ascii 98 = 'b' { 0x38, 0x7c, 0x44, 0x44, 0x44, 0x6c, 0x28, 0x00 }, // Ascii 99 = 'c' { 0x38, 0x7c, 0x44, 0x44, 0x44, 0x7f, 0x7f, 0x00 }, // Ascii 100 = 'd' { 0x38, 0x7c, 0x54, 0x54, 0x54, 0x5c, 0x18, 0x00 }, // Ascii 101 = 'e' { 0x08, 0x7e, 0x7f, 0x09, 0x09, 0x03, 0x02, 0x00 }, // Ascii 102 = 'f' { 0x98, 0xbc, 0xa4, 0xa4, 0xa4, 0xfc, 0x7c, 0x00 }, // Ascii 103 = 'g' { 0x7f, 0x7f, 0x04, 0x04, 0x04, 0x7c, 0x78, 0x00 }, // Ascii 104 = 'h' { 0x00, 0x00, 0x00, 0x7d, 0x7d, 0x00, 0x00, 0x00 }, // Ascii 105 = 'i' { 0x40, 0xc0, 0x80, 0x80, 0xfd, 0x7d, 0x00, 0x00 }, // Ascii 106 = 'j' { 0x7f, 0x7f, 0x10, 0x10, 0x38, 0x6c, 0x44, 0x00 }, // Ascii 107 = 'k' { 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00 }, // Ascii 108 = 'l' { 0x78, 0x7c, 0x0c, 0x18, 0x0c, 0x7c, 0x78, 0x00 }, // Ascii 109 = 'm' { 0x7c, 0x7c, 0x04, 0x04, 0x04, 0x7c, 0x78, 0x00 }, // Ascii 110 = 'n' { 0x38, 0x7c, 0x44, 0x44, 0x44, 0x7c, 0x38, 0x00 }, // Ascii 111 = 'o' { 0xfc, 0xfc, 0x24, 0x24, 0x24, 0x3c, 0x18, 0x00 }, // Ascii 112 = 'p' { 0x18, 0x3c, 0x24, 0x24, 0x24, 0xfc, 0xfc, 0x00 }, // Ascii 113 = 'q' { 0x7c, 0x7c, 0x04, 0x04, 0x04, 0x0c, 0x08, 0x00 }, // Ascii 114 = 'r' { 0x48, 0x5c, 0x54, 0x54, 0x54, 0x74, 0x20, 0x00 }, // Ascii 115 = 's' { 0x04, 0x3f, 0x7f, 0x44, 0x44, 0x64, 0x20, 0x00 }, // Ascii 116 = 't' { 0x3c, 0x7c, 0x40, 0x40, 0x40, 0x7c, 0x7c, 0x00 }, // Ascii 117 = 'u' { 0x1c, 0x3c, 0x60, 0x40, 0x60, 0x3c, 0x1c, 0x00 }, // Ascii 118 = 'v' { 0x3c, 0x7c, 0x60, 0x30, 0x60, 0x7c, 0x3c, 0x00 }, // Ascii 119 = 'w' { 0x44, 0x6c, 0x38, 0x10, 0x38, 0x6c, 0x44, 0x00 }, // Ascii 120 = 'x' { 0x9c, 0xbc, 0xa0, 0xa0, 0xa0, 0xfc, 0x7c, 0x00 }, // Ascii 121 = 'y' { 0x00, 0x44, 0x64, 0x74, 0x5c, 0x4c, 0x44, 0x00 }, // Ascii 122 = 'z' { 0x00, 0x08, 0x08, 0x3e, 0x77, 0x41, 0x41, 0x00 }, // Ascii 123 = '{' { 0x00, 0x00, 0x00, 0x77, 0x77, 0x00, 0x00, 0x00 }, // Ascii 124 = '|' { 0x00, 0x41, 0x41, 0x77, 0x3e, 0x08, 0x08, 0x00 }, // Ascii 125 = '}' { 0x00, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x00 }, // Ascii 126 = '~' { 0x70, 0x78, 0x4c, 0x46, 0x46, 0x4c, 0x78, 0x70 }, // Ascii 127 = '' { 0x3c, 0x42, 0x81, 0x81, 0x81, 0x42, 0x3c, 0x00 }, // Ascii 128 abweichend: rundes o { 0x3c, 0x42, 0xbd, 0xbd, 0xbd, 0x42, 0x3c, 0x00 }, // Ascii 129 abweichend: ausgefuelltes o { 0x00, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e, 0x00 } // Ascii 130 abweichend: hochgestelltes o };