Compare commits

..

5 Commits

12 changed files with 1331 additions and 14 deletions

View File

@ -1,9 +1,10 @@
MCU=atmega88
CFLAGS=-g -Wall -mcall-prologues -mmcu=$(MCU) -Os -DF_CPU=8000000
CFLAGS=-g -Wall -mcall-prologues -mmcu=$(MCU) -Os -DF_CPU=8000000 -IAVR-SSD1306/Files/
LDFLAGS=-Wl,-gc-sections -Wl,-relax
CC=avr-gcc
TARGET=main
OBJECT_FILES=main.o
_OBJECT_FILES=main.o AVR-SSD1306/Files/SSD1306.c AVR-SSD1306/Files/TWI.c
OBJECT_FILES=main.o oled-display/font.o oled-display/i2c.o oled-display/lcd.o
all: $(TARGET).hex

View File

@ -1,6 +1,61 @@
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/delay.h>
#include "oled-display/lcd.h"
#define PORT_SPI(t) t##B
#define PIN_SPI_CS (1 << 2)
void spi_proc()
{
static uint8_t spi_cs_last = 0;
static uint8_t x = 0;
static uint8_t y = DISPLAY_HEIGHT / 8 - 1;
uint8_t spi_cs;
spi_cs = !(PORT_SPI(PIN) & PIN_SPI_CS);
if (spi_cs) {
if (SPSR & (1 << SPIF)) {
lcd_set_buffer(x, y, SPDR);
if (y == 0) {
y = DISPLAY_HEIGHT / 8 - 1;
x++;
if (x == DISPLAY_WIDTH)
x = 0;
} else {
y--;
}
}
} else {
if (spi_cs_last) {
lcd_display();
}
x = 0;
y = DISPLAY_HEIGHT / 8 - 1;
}
spi_cs_last = spi_cs;
}
int main()
{
lcd_init(LCD_DISP_ON);
lcd_gotoxy(0, 0);
lcd_puts_p(PSTR("== Internet Radio =="));
lcd_drawLine(0, 9, 120, 9, WHITE);
lcd_gotoxy(0, 2);
lcd_puts_p(PSTR("Starting up."));
lcd_gotoxy(0, 3);
lcd_puts_p(PSTR("Please wait..."));
lcd_display();
SPCR = (1 << SPE);
_delay_ms(10);
while (1) {
spi_proc();
}
return 0;
}

52
firmware/oled-display/.gitignore vendored Normal file
View File

@ -0,0 +1,52 @@
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf

View File

@ -0,0 +1,136 @@
/*
* font.c
* i2c
*
* Created by Michael Köhler on 16.09.18.
* Copyright 2018 Skie-Systems. All rights reserved.
*
*/
#include "font.h"
const char ssd1306oled_font[][6] PROGMEM = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // sp
{0x00, 0x00, 0x00, 0x2f, 0x00, 0x00}, // !
{0x00, 0x00, 0x07, 0x00, 0x07, 0x00}, // "
{0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14}, // #
{0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12}, // $
{0x00, 0x62, 0x64, 0x08, 0x13, 0x23}, // %
{0x00, 0x36, 0x49, 0x55, 0x22, 0x50}, // &
{0x00, 0x00, 0x05, 0x03, 0x00, 0x00}, // '
{0x00, 0x00, 0x1c, 0x22, 0x41, 0x00}, // (
{0x00, 0x00, 0x41, 0x22, 0x1c, 0x00}, // )
{0x00, 0x14, 0x08, 0x3E, 0x08, 0x14}, // *
{0x00, 0x08, 0x08, 0x3E, 0x08, 0x08}, // +
{0x00, 0x00, 0x00, 0xA0, 0x60, 0x00}, // ,
{0x00, 0x08, 0x08, 0x08, 0x08, 0x08}, // -
{0x00, 0x00, 0x60, 0x60, 0x00, 0x00}, // .
{0x00, 0x20, 0x10, 0x08, 0x04, 0x02}, // /
{0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E}, // 0
{0x00, 0x00, 0x42, 0x7F, 0x40, 0x00}, // 1
{0x00, 0x42, 0x61, 0x51, 0x49, 0x46}, // 2
{0x00, 0x21, 0x41, 0x45, 0x4B, 0x31}, // 3
{0x00, 0x18, 0x14, 0x12, 0x7F, 0x10}, // 4
{0x00, 0x27, 0x45, 0x45, 0x45, 0x39}, // 5
{0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30}, // 6
{0x00, 0x01, 0x71, 0x09, 0x05, 0x03}, // 7
{0x00, 0x36, 0x49, 0x49, 0x49, 0x36}, // 8
{0x00, 0x06, 0x49, 0x49, 0x29, 0x1E}, // 9
{0x00, 0x00, 0x36, 0x36, 0x00, 0x00}, // :
{0x00, 0x00, 0x56, 0x36, 0x00, 0x00}, // ;
{0x00, 0x08, 0x14, 0x22, 0x41, 0x00}, // <
{0x00, 0x14, 0x14, 0x14, 0x14, 0x14}, // =
{0x00, 0x00, 0x41, 0x22, 0x14, 0x08}, // >
{0x00, 0x02, 0x01, 0x51, 0x09, 0x06}, // ?
{0x00, 0x32, 0x49, 0x59, 0x51, 0x3E}, // @
{0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C}, // A
{0x00, 0x7F, 0x49, 0x49, 0x49, 0x36}, // B
{0x00, 0x3E, 0x41, 0x41, 0x41, 0x22}, // C
{0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C}, // D
{0x00, 0x7F, 0x49, 0x49, 0x49, 0x41}, // E
{0x00, 0x7F, 0x09, 0x09, 0x09, 0x01}, // F
{0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A}, // G
{0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F}, // H
{0x00, 0x00, 0x41, 0x7F, 0x41, 0x00}, // I
{0x00, 0x20, 0x40, 0x41, 0x3F, 0x01}, // J
{0x00, 0x7F, 0x08, 0x14, 0x22, 0x41}, // K
{0x00, 0x7F, 0x40, 0x40, 0x40, 0x40}, // L
{0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F}, // M
{0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F}, // N
{0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E}, // O
{0x00, 0x7F, 0x09, 0x09, 0x09, 0x06}, // P
{0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E}, // Q
{0x00, 0x7F, 0x09, 0x19, 0x29, 0x46}, // R
{0x00, 0x46, 0x49, 0x49, 0x49, 0x31}, // S
{0x00, 0x01, 0x01, 0x7F, 0x01, 0x01}, // T
{0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F}, // U
{0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F}, // V
{0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F}, // W
{0x00, 0x63, 0x14, 0x08, 0x14, 0x63}, // X
{0x00, 0x07, 0x08, 0x70, 0x08, 0x07}, // Y
{0x00, 0x61, 0x51, 0x49, 0x45, 0x43}, // Z
{0x00, 0x00, 0x7F, 0x41, 0x41, 0x00}, // [
{0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55}, // backslash
{0x00, 0x00, 0x41, 0x41, 0x7F, 0x00}, // ]
{0x00, 0x04, 0x02, 0x01, 0x02, 0x04}, // ^
{0x00, 0x40, 0x40, 0x40, 0x40, 0x40}, // _
{0x00, 0x00, 0x01, 0x02, 0x04, 0x00}, // '
{0x00, 0x20, 0x54, 0x54, 0x54, 0x78}, // a
{0x00, 0x7F, 0x48, 0x44, 0x44, 0x38}, // b
{0x00, 0x38, 0x44, 0x44, 0x44, 0x20}, // c
{0x00, 0x38, 0x44, 0x44, 0x48, 0x7F}, // d
{0x00, 0x38, 0x54, 0x54, 0x54, 0x18}, // e
{0x00, 0x08, 0x7E, 0x09, 0x01, 0x02}, // f
{0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C}, // g
{0x00, 0x7F, 0x08, 0x04, 0x04, 0x78}, // h
{0x00, 0x00, 0x44, 0x7D, 0x40, 0x00}, // i
{0x00, 0x40, 0x80, 0x84, 0x7D, 0x00}, // j
{0x00, 0x7F, 0x10, 0x28, 0x44, 0x00}, // k
{0x00, 0x00, 0x41, 0x7F, 0x40, 0x00}, // l
{0x00, 0x7C, 0x04, 0x18, 0x04, 0x78}, // m
{0x00, 0x7C, 0x08, 0x04, 0x04, 0x78}, // n
{0x00, 0x38, 0x44, 0x44, 0x44, 0x38}, // o
{0x00, 0xFC, 0x24, 0x24, 0x24, 0x18}, // p
{0x00, 0x18, 0x24, 0x24, 0x18, 0xFC}, // q
{0x00, 0x7C, 0x08, 0x04, 0x04, 0x08}, // r
{0x00, 0x48, 0x54, 0x54, 0x54, 0x20}, // s
{0x00, 0x04, 0x3F, 0x44, 0x40, 0x20}, // t
{0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C}, // u
{0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C}, // v
{0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C}, // w
{0x00, 0x44, 0x28, 0x10, 0x28, 0x44}, // x
{0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C}, // y
{0x00, 0x44, 0x64, 0x54, 0x4C, 0x44}, // z
{0x00, 0x00, 0x08, 0x77, 0x41, 0x00}, // {
{0x00, 0x00, 0x00, 0x63, 0x00, 0x00}, // ¦
{0x00, 0x00, 0x41, 0x77, 0x08, 0x00}, // }
{0x00, 0x08, 0x04, 0x08, 0x08, 0x04}, // ~
/* end of normal char-set */
/* put your own signs/chars here, edit special_char too */
/* be sure that your first special char stand here */
{0x00, 0x3A, 0x40, 0x40, 0x20, 0x7A}, // ü, !!! Important: this must be special_char[0] !!!
{0x00, 0x3D, 0x40, 0x40, 0x40, 0x3D}, // Ü
{0x00, 0x21, 0x54, 0x54, 0x54, 0x79}, // ä
{0x00, 0x7D, 0x12, 0x11, 0x12, 0x7D}, // Ä
{0x00, 0x39, 0x44, 0x44, 0x44, 0x39}, // ö
{0x00, 0x3D, 0x42, 0x42, 0x42, 0x3D}, // Ö
{0x00, 0x02, 0x05, 0x02, 0x00, 0x00}, // °
{0x00, 0x7E, 0x01, 0x49, 0x55, 0x73}, // ß
{0x00, 0x7C, 0x10, 0x10, 0x08, 0x1C} // µ
};
const char special_char[][2] PROGMEM = {
// define position of special char in font
// {special char, position in font}
// be sure that last element of this
// array are {0xff, 0xff} and first element
// are {first special char, first element after normal char-set in font}
{'ü', 95}, // special_char[0]
{'Ü', 96},
{'ä', 97},
{'Ä', 98},
{'ö', 99},
{'Ö', 100},
{'°', 101},
{'ß', 102},
{'µ', 103},
{0xff, 0xff} // end of table special_char
};

View File

@ -0,0 +1,16 @@
/*
* font.h
* i2c
*
* Created by Michael Köhler on 13.09.18.
* Copyright 2018 Skie-Systems. All rights reserved.
*
*/
#ifndef _font_h_
#define _font_h_
#include <avr/pgmspace.h>
extern const char ssd1306oled_font[][6] PROGMEM;
extern const char special_char[][2] PROGMEM;
#endif

180
firmware/oled-display/i2c.c Normal file
View File

@ -0,0 +1,180 @@
//
// i2c.c
// i2c
//
// Created by Michael Köhler on 09.10.17.
//
//
#include "i2c.h"
#if defined (__AVR_ATmega328__) || defined(__AVR_ATmega328P__) || \
defined(__AVR_ATmega168P__) || defined(__AVR_ATmega168PA__) || \
defined(__AVR_ATmega88P__) || defined(__AVR_ATmega88__) || \
defined(__AVR_ATmega8__) || \
defined(__AVR_ATmega48P__) || \
defined(__AVR_ATmega1284P__) || \
defined (__AVR_ATmega324A__) || defined (__AVR_ATmega324P__) || defined (__AVR_ATmega324PA__) || \
defined (__AVR_ATmega644__) || defined (__AVR_ATmega644A__) || defined (__AVR_ATmega644P__) || defined (__AVR_ATmega644PA__) || \
defined (__AVR_ATmega1284P__) || \
defined (__AVR_ATmega2560__)
#if PSC_I2C != 1 && PSC_I2C != 4 && PSC_I2C != 16 && PSC_I2C != 64
#error "Wrong prescaler for TWI !"
#elif SET_TWBR < 0 || SET_TWBR > 255
#error "TWBR out of range, change PSC_I2C or F_I2C !"
#endif
uint8_t I2C_ErrorCode;
/**********************************************
Public Function: i2c_init
Purpose: Initialise TWI/I2C interface
Input Parameter: none
Return Value: none
**********************************************/
void i2c_init(void){
// set clock
switch (PSC_I2C) {
case 4:
TWSR = 0x1;
break;
case 16:
TWSR = 0x2;
break;
case 64:
TWSR = 0x3;
break;
default:
TWSR = 0x00;
break;
}
TWBR = (uint8_t)SET_TWBR;
// enable
TWCR = (1 << TWEN);
}
/**********************************************
Public Function: i2c_start
Purpose: Start TWI/I2C interface
Input Parameter:
- uint8_t i2c_addr: Adress of reciever
Return Value: none
**********************************************/
void i2c_start(uint8_t i2c_addr){
// i2c start
TWCR = (1 << TWINT)|(1 << TWSTA)|(1 << TWEN);
uint16_t timeout = F_CPU/F_I2C*2.0;
while((TWCR & (1 << TWINT)) == 0 &&
timeout !=0){
timeout--;
if(timeout == 0){
I2C_ErrorCode |= (1 << I2C_START);
return;
}
};
// send adress
TWDR = 0x78;
TWCR = (1 << TWINT)|( 1 << TWEN);
timeout = F_CPU/F_I2C*2.0;
while((TWCR & (1 << TWINT)) == 0 &&
timeout !=0){
timeout--;
if(timeout == 0){
I2C_ErrorCode |= (1 << I2C_SENDADRESS);
return;
}
};
}
/**********************************************
Public Function: i2c_stop
Purpose: Stop TWI/I2C interface
Input Parameter: none
Return Value: none
**********************************************/
void i2c_stop(void){
// i2c stop
TWCR = (1 << TWINT)|(1 << TWSTO)|(1 << TWEN);
}
/**********************************************
Public Function: i2c_byte
Purpose: Send byte at TWI/I2C interface
Input Parameter:
- uint8_t byte: Byte to send to reciever
Return Value: none
**********************************************/
void i2c_byte(uint8_t byte){
TWDR = byte;
TWCR = (1 << TWINT)|( 1 << TWEN);
uint16_t timeout = F_CPU/F_I2C*2.0;
while((TWCR & (1 << TWINT)) == 0 &&
timeout !=0){
timeout--;
if(timeout == 0){
I2C_ErrorCode |= (1 << I2C_BYTE);
return;
}
};
}
/**********************************************
Public Function: i2c_readAck
Purpose: read acknowledge from TWI/I2C Interface
Input Parameter: none
Return Value: uint8_t
- TWDR: recieved value at TWI/I2C-Interface, 0 at timeout
- 0: Error at read
**********************************************/
uint8_t i2c_readAck(void){
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
uint16_t timeout = F_CPU/F_I2C*2.0;
while((TWCR & (1 << TWINT)) == 0 &&
timeout !=0){
timeout--;
if(timeout == 0){
I2C_ErrorCode |= (1 << I2C_READACK);
return 0;
}
};
return TWDR;
}
/**********************************************
Public Function: i2c_readNAck
Purpose: read non-acknowledge from TWI/I2C Interface
Input Parameter: none
Return Value: uint8_t
- TWDR: recieved value at TWI/I2C-Interface
- 0: Error at read
**********************************************/
uint8_t i2c_readNAck(void){
TWCR = (1<<TWINT)|(1<<TWEN);
uint16_t timeout = F_CPU/F_I2C*2.0;
while((TWCR & (1 << TWINT)) == 0 &&
timeout !=0){
timeout--;
if(timeout == 0){
I2C_ErrorCode |= (1 << I2C_READNACK);
return 0;
}
};
return TWDR;
}
#else
#error "Micorcontroller not supported now!"
#endif

View File

@ -0,0 +1,46 @@
//
// i2c.h
// i2c
//
// Created by Michael Köhler on 09.10.17.
//
//
#ifndef i2c_h
#define i2c_h
#ifdef __cplusplus
extern "C" {
#endif
/* TODO: setup i2c/twi */
#define F_I2C 500000UL// clock i2c
#define PSC_I2C 1 // prescaler i2c
#define SET_TWBR (F_CPU/F_I2C-16UL)/(PSC_I2C*2UL)
#include <stdio.h>
#include <avr/io.h>
extern uint8_t I2C_ErrorCode; // variable for communication error at twi
// ckeck it in your code
// 0 means no error
// define bits in I2C-ErrorCode:
#define I2C_START 0 // bit 0: timeout start-condition
#define I2C_SENDADRESS 1 // bit 0: timeout device-adress
#define I2C_BYTE 2 // bit 0: timeout byte-transmission
#define I2C_READACK 3 // bit 0: timeout read acknowledge
#define I2C_READNACK 4 // bit 0: timeout read nacknowledge
void i2c_init(void); // init hw-i2c
void i2c_start(uint8_t i2c_addr); // send i2c_start_condition
void i2c_stop(void); // send i2c_stop_condition
void i2c_byte(uint8_t byte); // send data_byte
uint8_t i2c_readAck(void); // read byte with ACK
uint8_t i2c_readNAck(void); // read byte with NACK
#ifdef __cplusplus
}
#endif
#endif /* i2c_h */

518
firmware/oled-display/lcd.c Normal file
View File

@ -0,0 +1,518 @@
/*
* This file is part of lcd library for ssd1306/ssd1309/sh1106 oled-display.
*
* lcd library for ssd1306/ssd1309/sh1106 oled-display is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any later version.
*
* lcd library for ssd1306/ssd1309/sh1106 oled-display is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar. If not, see <http://www.gnu.org/licenses/>.
*
* Diese Datei ist Teil von lcd library for ssd1306/ssd1309/sh1106 oled-display.
*
* lcd library for ssd1306/ssd1309/sh1106 oled-display ist Freie Software: Sie können es unter den Bedingungen
* der GNU General Public License, wie von der Free Software Foundation,
* Version 3 der Lizenz oder jeder späteren
* veröffentlichten Version, weiterverbreiten und/oder modifizieren.
*
* lcd library for ssd1306/ssd1309/sh1106 oled-display wird in der Hoffnung, dass es nützlich sein wird, aber
* OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite
* Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK.
* Siehe die GNU General Public License für weitere Details.
*
* Sie sollten eine Kopie der GNU General Public License zusammen mit diesem
* Programm erhalten haben. Wenn nicht, siehe <http://www.gnu.org/licenses/>.
*
* lcd.h
*
* Created by Michael Köhler on 22.12.16.
* Copyright 2016 Skie-Systems. All rights reserved.
*
* lib for OLED-Display with ssd1306/ssd1309/sh1106-Controller
* first dev-version only for I2C-Connection
* at ATMega328P like Arduino Uno
*
* at GRAPHICMODE lib needs static SRAM for display:
* DISPLAY-WIDTH * DISPLAY-HEIGHT + 2 bytes
*
* at TEXTMODE lib need static SRAM for display:
* 2 bytes (cursorPosition)
*/
#include "lcd.h"
#include "font.h"
#include <string.h>
#if defined SPI
#include <util/delay.h>
#endif
static struct {
uint8_t x;
uint8_t y;
} cursorPosition;
static uint8_t charMode = NORMALSIZE;
#if defined GRAPHICMODE
#include <stdlib.h>
static uint8_t displayBuffer[DISPLAY_HEIGHT/8][DISPLAY_WIDTH];
#elif defined TEXTMODE
#else
#error "No valid displaymode! Refer lcd.h"
#endif
const uint8_t init_sequence [] PROGMEM = { // Initialization Sequence
LCD_DISP_OFF, // Display OFF (sleep mode)
0x20, 0b00, // Set Memory Addressing Mode
// 00=Horizontal Addressing Mode; 01=Vertical Addressing Mode;
// 10=Page Addressing Mode (RESET); 11=Invalid
0xB0, // Set Page Start Address for Page Addressing Mode, 0-7
0xC8, // Set COM Output Scan Direction
0x00, // --set low column address
0x10, // --set high column address
0x40, // --set start line address
0x81, 0x3F, // Set contrast control register
0xA1, // Set Segment Re-map. A0=address mapped; A1=address 127 mapped.
0xA6, // Set display mode. A6=Normal; A7=Inverse
0xA8, DISPLAY_HEIGHT-1, // Set multiplex ratio(1 to 64)
0xA4, // Output RAM to Display
// 0xA4=Output follows RAM content; 0xA5,Output ignores RAM content
0xD3, 0x00, // Set display offset. 00 = no offset
0xD5, // --set display clock divide ratio/oscillator frequency
0xF0, // --set divide ratio
0xD9, 0x22, // Set pre-charge period
// Set com pins hardware configuration
#if DISPLAY_HEIGHT==64
0xDA, 0x12,
#elif DISPLAY_HEIGHT==32
0xDA, 0x02,
#endif
0xDB, // --set vcomh
0x20, // 0x20,0.77xVcc
0x8D, 0x14, // Set DC-DC enable
};
#pragma mark LCD COMMUNICATION
void lcd_command(uint8_t cmd[], uint8_t size) {
#if defined I2C
i2c_start((LCD_I2C_ADR << 1) | 0);
i2c_byte(0x00); // 0x00 for command, 0x40 for data
for (uint8_t i=0; i<size; i++) {
i2c_byte(cmd[i]);
}
i2c_stop();
#elif defined SPI
LCD_PORT &= ~(1 << CS_PIN);
LCD_PORT &= ~(1 << DC_PIN);
for (uint8_t i=0; i<size; i++) {
SPDR = cmd[i];
while(!(SPSR & (1<<SPIF)));
}
LCD_PORT |= (1 << CS_PIN);
#endif
}
void lcd_data(uint8_t data[], uint16_t size) {
#if defined I2C
i2c_start((LCD_I2C_ADR << 1) | 0);
i2c_byte(0x40); // 0x00 for command, 0x40 for data
for (uint16_t i = 0; i<size; i++) {
i2c_byte(data[i]);
}
i2c_stop();
#elif defined SPI
LCD_PORT &= ~(1 << CS_PIN);
LCD_PORT |= (1 << DC_PIN);
for (uint16_t i = 0; i<size; i++) {
SPDR = data[i];
while(!(SPSR & (1<<SPIF)));
}
LCD_PORT |= (1 << CS_PIN);
#endif
}
#pragma mark -
#pragma mark GENERAL FUNCTIONS
void lcd_init(uint8_t dispAttr){
#if defined I2C
i2c_init();
#elif defined SPI
DDRB |= (1 << PB2)|(1 << PB3)|(1 << PB5);
SPCR = (1 << SPE)|(1<<MSTR)|(1<<SPR0);
LCD_DDR |= (1 << CS_PIN)|(1 << DC_PIN)|(1 << RES_PIN);
LCD_PORT |= (1 << CS_PIN)|(1 << DC_PIN)|(1 << RES_PIN);
LCD_PORT &= ~(1 << RES_PIN);
_delay_ms(10);
LCD_PORT |= (1 << RES_PIN);
#endif
uint8_t commandSequence[sizeof(init_sequence)+1];
for (uint8_t i = 0; i < sizeof (init_sequence); i++) {
commandSequence[i] = (pgm_read_byte(&init_sequence[i]));
}
commandSequence[sizeof(init_sequence)]=(dispAttr);
lcd_command(commandSequence, sizeof(commandSequence));
lcd_clrscr();
}
void lcd_gotoxy(uint8_t x, uint8_t y){
x = x * sizeof(FONT[0]);
lcd_goto_xpix_y(x,y);
}
void lcd_goto_xpix_y(uint8_t x, uint8_t y){
if( x > (DISPLAY_WIDTH) || y > (DISPLAY_HEIGHT/8-1)) return;// out of display
cursorPosition.x=x;
cursorPosition.y=y;
#if defined (SSD1306) || defined (SSD1309)
uint8_t commandSequence[] = {0xb0+y, 0x21, x, 0x7f};
#elif defined SH1106
uint8_t commandSequence[] = {0xb0+y, 0x21, 0x00+((2+x) & (0x0f)), 0x10+( ((2+x) & (0xf0)) >> 4 ), 0x7f};
#endif
lcd_command(commandSequence, sizeof(commandSequence));
}
void lcd_set_buffer(uint8_t x, uint8_t y, uint8_t value) {
displayBuffer[y][x] = value;
}
void lcd_clrscr(void){
#ifdef GRAPHICMODE
for (uint8_t i = 0; i < DISPLAY_HEIGHT/8; i++){
memset(displayBuffer[i], 0x00, sizeof(displayBuffer[i]));
}
lcd_display();
#elif defined TEXTMODE
uint8_t displayBuffer[DISPLAY_WIDTH];
memset(displayBuffer, 0x00, sizeof(displayBuffer));
for (uint8_t i = 0; i < DISPLAY_HEIGHT/8; i++){
lcd_gotoxy(0,i);
lcd_data(displayBuffer, sizeof(displayBuffer));
}
#endif
lcd_home();
}
void lcd_home(void){
lcd_gotoxy(0, 0);
}
void lcd_invert(uint8_t invert){
uint8_t commandSequence[1];
if (invert != YES) {
commandSequence[0] = 0xA6;
} else {
commandSequence[0] = 0xA7;
}
lcd_command(commandSequence, 1);
}
void lcd_sleep(uint8_t sleep){
uint8_t commandSequence[1];
if (sleep != YES) {
commandSequence[0] = 0xAF;
} else {
commandSequence[0] = 0xAE;
}
lcd_command(commandSequence, 1);
}
void lcd_set_contrast(uint8_t contrast){
uint8_t commandSequence[2] = {0x81, contrast};
lcd_command(commandSequence, sizeof(commandSequence));
}
void lcd_putc(char c){
switch (c) {
case '\b':
// backspace
lcd_gotoxy(cursorPosition.x-charMode, cursorPosition.y);
lcd_putc(' ');
lcd_gotoxy(cursorPosition.x-charMode, cursorPosition.y);
break;
case '\t':
// tab
if( (cursorPosition.x+charMode*4) < (DISPLAY_WIDTH/ sizeof(FONT[0])-charMode*4) ){
lcd_gotoxy(cursorPosition.x+charMode*4, cursorPosition.y);
}else{
lcd_gotoxy(DISPLAY_WIDTH/ sizeof(FONT[0]), cursorPosition.y);
}
break;
case '\n':
// linefeed
if(cursorPosition.y < (DISPLAY_HEIGHT/8-1)){
lcd_gotoxy(cursorPosition.x, cursorPosition.y+charMode);
}
break;
case '\r':
// carrige return
lcd_gotoxy(0, cursorPosition.y);
break;
default:
// char doesn't fit in line
if( (cursorPosition.x >= DISPLAY_WIDTH-sizeof(FONT[0])) || (c < ' ') ) break;
// mapping char
c -= ' ';
if (c >= pgm_read_byte(&special_char[0][1]) ) {
char temp = c;
c = 0xff;
for (uint8_t i=0; pgm_read_byte(&special_char[i][1]) != 0xff; i++) {
if ( pgm_read_byte(&special_char[i][0])-' ' == temp ) {
c = pgm_read_byte(&special_char[i][1]);
break;
}
}
if ( c == 0xff ) break;
}
// print char at display
#ifdef GRAPHICMODE
if (charMode == DOUBLESIZE) {
uint16_t doubleChar[sizeof(FONT[0])];
uint8_t dChar;
if ((cursorPosition.x+2*sizeof(FONT[0]))>DISPLAY_WIDTH) break;
for (uint8_t i=0; i < sizeof(FONT[0]); i++) {
doubleChar[i] = 0;
dChar = pgm_read_byte(&(FONT[(uint8_t)c][i]));
for (uint8_t j=0; j<8; j++) {
if ((dChar & (1 << j))) {
doubleChar[i] |= (1 << (j*2));
doubleChar[i] |= (1 << ((j*2)+1));
}
}
}
for (uint8_t i = 0; i < sizeof(FONT[0]); i++)
{
// load bit-pattern from flash
displayBuffer[cursorPosition.y+1][cursorPosition.x+(2*i)] = doubleChar[i] >> 8;
displayBuffer[cursorPosition.y+1][cursorPosition.x+(2*i)+1] = doubleChar[i] >> 8;
displayBuffer[cursorPosition.y][cursorPosition.x+(2*i)] = doubleChar[i] & 0xff;
displayBuffer[cursorPosition.y][cursorPosition.x+(2*i)+1] = doubleChar[i] & 0xff;
}
cursorPosition.x += sizeof(FONT[0])*2;
} else {
if ((cursorPosition.x+sizeof(FONT[0]))>DISPLAY_WIDTH) break;
for (uint8_t i = 0; i < sizeof(FONT[0]); i++)
{
// load bit-pattern from flash
displayBuffer[cursorPosition.y][cursorPosition.x+i] =pgm_read_byte(&(FONT[(uint8_t)c][i]));
}
cursorPosition.x += sizeof(FONT[0]);
}
#elif defined TEXTMODE
if (charMode == DOUBLESIZE) {
uint16_t doubleChar[sizeof(FONT[0])];
uint8_t dChar;
if ((cursorPosition.x+2*sizeof(FONT[0]))>DISPLAY_WIDTH) break;
for (uint8_t i=0; i < sizeof(FONT[0]); i++) {
doubleChar[i] = 0;
dChar = pgm_read_byte(&(FONT[(uint8_t)c][i]));
for (uint8_t j=0; j<8; j++) {
if ((dChar & (1 << j))) {
doubleChar[i] |= (1 << (j*2));
doubleChar[i] |= (1 << ((j*2)+1));
}
}
}
uint8_t data[sizeof(FONT[0])*2];
for (uint8_t i = 0; i < sizeof(FONT[0]); i++)
{
// print font to ram, print 6 columns
data[i<<1]=(doubleChar[i] & 0xff);
data[(i<<1)+1]=(doubleChar[i] & 0xff);
}
lcd_data(data, sizeof(FONT[0])*2);
#if defined (SSD1306) || defined (SSD1309)
uint8_t commandSequence[] = {0xb0+cursorPosition.y+1,
0x21,
cursorPosition.x,
0x7f};
#elif defined SH1106
uint8_t commandSequence[] = {0xb0+cursorPosition.y+1,
0x21,
0x00+((2+cursorPosition.x) & (0x0f)),
0x10+( ((2+cursorPosition.x) & (0xf0)) >> 4 ),
0x7f};
#endif
lcd_command(commandSequence, sizeof(commandSequence));
for (uint8_t i = 0; i < sizeof(FONT[0]); i++)
{
// print font to ram, print 6 columns
data[i<<1]=(doubleChar[i] >> 8);
data[(i<<1)+1]=(doubleChar[i] >> 8);
}
lcd_data(data, sizeof(FONT[0])*2);
commandSequence[0] = 0xb0+cursorPosition.y;
#if defined (SSD1306) || defined (SSD1309)
commandSequence[2] = cursorPosition.x+(2*sizeof(FONT[0]));
#elif defined SH1106
commandSequence[2] = 0x00+((2+cursorPosition.x+(2*sizeof(FONT[0]))) & (0x0f));
commandSequence[3] = 0x10+( ((2+cursorPosition.x+(2*sizeof(FONT[0]))) & (0xf0)) >> 4 );
#endif
lcd_command(commandSequence, sizeof(commandSequence));
cursorPosition.x += sizeof(FONT[0])*2;
} else {
uint8_t data[sizeof(FONT[0])];
if ((cursorPosition.x+sizeof(FONT[0]))>DISPLAY_WIDTH) break;
for (uint8_t i = 0; i < sizeof(FONT[0]); i++)
{
// print font to ram, print 6 columns
data[i]=(pgm_read_byte(&(FONT[(uint8_t)c][i])));
}
lcd_data(data, sizeof(FONT[0]));
cursorPosition.x += sizeof(FONT[0]);
}
#endif
break;
}
}
void lcd_charMode(uint8_t mode){
charMode = mode;
}
void lcd_puts(const char* s){
while (*s) {
lcd_putc(*s++);
}
}
void lcd_puts_p(const char* progmem_s){
register uint8_t c;
while ((c = pgm_read_byte(progmem_s++))) {
lcd_putc(c);
}
}
#ifdef GRAPHICMODE
#pragma mark -
#pragma mark GRAPHIC FUNCTIONS
void lcd_drawPixel(uint8_t x, uint8_t y, uint8_t color){
if( x > DISPLAY_WIDTH-1 || y > (DISPLAY_HEIGHT-1)) return; // out of Display
if( color == WHITE){
displayBuffer[(y / 8)][x] |= (1 << (y % 8));
} else {
displayBuffer[(y / 8)][x] &= ~(1 << (y % 8));
}
}
void lcd_drawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color){
if( x1 > DISPLAY_WIDTH-1 ||
x2 > DISPLAY_WIDTH-1 ||
y1 > DISPLAY_HEIGHT-1 ||
y2 > DISPLAY_HEIGHT-1) return;
int dx = abs(x2-x1), sx = x1<x2 ? 1 : -1;
int dy = -abs(y2-y1), sy = y1<y2 ? 1 : -1;
int err = dx+dy, e2; /* error value e_xy */
while(1){
lcd_drawPixel(x1, y1, color);
if (x1==x2 && y1==y2) break;
e2 = 2*err;
if (e2 > dy) { err += dy; x1 += sx; } /* e_xy+e_x > 0 */
if (e2 < dx) { err += dx; y1 += sy; } /* e_xy+e_y < 0 */
}
}
void lcd_drawRect(uint8_t px1, uint8_t py1, uint8_t px2, uint8_t py2, uint8_t color){
if( px1 > DISPLAY_WIDTH-1 ||
px2 > DISPLAY_WIDTH-1 ||
py1 > DISPLAY_HEIGHT-1 ||
py2 > DISPLAY_HEIGHT-1) return;
lcd_drawLine(px1, py1, px2, py1, color);
lcd_drawLine(px2, py1, px2, py2, color);
lcd_drawLine(px2, py2, px1, py2, color);
lcd_drawLine(px1, py2, px1, py1, color);
}
void lcd_fillRect(uint8_t px1, uint8_t py1, uint8_t px2, uint8_t py2, uint8_t color){
if( px1 > px2){
uint8_t temp = px1;
px1 = px2;
px2 = temp;
temp = py1;
py1 = py2;
py2 = temp;
}
for (uint8_t i=0; i<=(py2-py1); i++){
lcd_drawLine(px1, py1+i, px2, py1+i, color);
}
}
void lcd_drawCircle(uint8_t center_x, uint8_t center_y, uint8_t radius, uint8_t color){
if( ((center_x + radius) > DISPLAY_WIDTH-1) ||
((center_y + radius) > DISPLAY_HEIGHT-1) ||
center_x < radius ||
center_y < radius) return;
int16_t f = 1 - radius;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * radius;
int16_t x = 0;
int16_t y = radius;
lcd_drawPixel(center_x , center_y+radius, color);
lcd_drawPixel(center_x , center_y-radius, color);
lcd_drawPixel(center_x+radius, center_y , color);
lcd_drawPixel(center_x-radius, center_y , color);
while (x<y) {
if (f >= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
lcd_drawPixel(center_x + x, center_y + y, color);
lcd_drawPixel(center_x - x, center_y + y, color);
lcd_drawPixel(center_x + x, center_y - y, color);
lcd_drawPixel(center_x - x, center_y - y, color);
lcd_drawPixel(center_x + y, center_y + x, color);
lcd_drawPixel(center_x - y, center_y + x, color);
lcd_drawPixel(center_x + y, center_y - x, color);
lcd_drawPixel(center_x - y, center_y - x, color);
}
}
void lcd_fillCircle(uint8_t center_x, uint8_t center_y, uint8_t radius, uint8_t color) {
for(uint8_t i=0; i<= radius;i++){
lcd_drawCircle(center_x, center_y, i, color);
}
}
void lcd_drawBitmap(uint8_t x, uint8_t y, const uint8_t *picture, uint8_t width, uint8_t height, uint8_t color){
uint8_t i,j, byteWidth = (width+7)/8;
for (j = 0; j < height; j++) {
for(i=0; i < width;i++){
if(pgm_read_byte(picture + j * byteWidth + i / 8) & (128 >> (i & 7))){
lcd_drawPixel(x+i, y+j, color);
} else {
lcd_drawPixel(x+i, y+j, !color);
}
}
}
}
void lcd_display() {
#if defined (SSD1306) || defined (SSD1309)
lcd_gotoxy(0,0);
lcd_data(&displayBuffer[0][0], DISPLAY_WIDTH*DISPLAY_HEIGHT/8);
#elif defined SH1106
for (uint8_t i = 0; i < DISPLAY_HEIGHT/8; i++){
lcd_gotoxy(0,i);
lcd_data(displayBuffer[i], sizeof(displayBuffer[i]));
}
#endif
}
void lcd_clear_buffer() {
for (uint8_t i = 0; i < DISPLAY_HEIGHT/8; i++){
memset(displayBuffer[i], 0x00, sizeof(displayBuffer[i]));
}
}
uint8_t lcd_check_buffer(uint8_t x, uint8_t y) {
if( x > DISPLAY_WIDTH-1 || y > (DISPLAY_HEIGHT-1)) return 0; // out of Display
return displayBuffer[(y / (DISPLAY_HEIGHT/8))][x] & (1 << (y % (DISPLAY_HEIGHT/8)));
}
void lcd_display_block(uint8_t x, uint8_t line, uint8_t width) {
if (line > (DISPLAY_HEIGHT/8-1) || x > DISPLAY_WIDTH - 1){return;}
if (x + width > DISPLAY_WIDTH) { // no -1 here, x alone is width 1
width = DISPLAY_WIDTH - x;
}
lcd_goto_xpix_y(x,line);
lcd_data(&displayBuffer[line][x], width);
}
#endif

150
firmware/oled-display/lcd.h Normal file
View File

@ -0,0 +1,150 @@
/*
* This file is part of lcd library for ssd1306/ssd1309/sh1106 oled-display.
*
* lcd library for ssd1306/ssd1309/sh1106 oled-display is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any later version.
*
* lcd library for ssd1306/ssd1309/sh1106 oled-display is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar. If not, see <http://www.gnu.org/licenses/>.
*
* Diese Datei ist Teil von lcd library for ssd1306/ssd1309/sh1106 oled-display.
*
* lcd library for ssd1306/ssd1309/sh1106 oled-display ist Freie Software: Sie können es unter den Bedingungen
* der GNU General Public License, wie von der Free Software Foundation,
* Version 3 der Lizenz oder jeder späteren
* veröffentlichten Version, weiterverbreiten und/oder modifizieren.
*
* lcd library for ssd1306/ssd1309/sh1106 oled-display wird in der Hoffnung, dass es nützlich sein wird, aber
* OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite
* Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK.
* Siehe die GNU General Public License für weitere Details.
*
* Sie sollten eine Kopie der GNU General Public License zusammen mit diesem
* Programm erhalten haben. Wenn nicht, siehe <http://www.gnu.org/licenses/>.
*
* lcd.h
*
* Created by Michael Köhler on 22.12.16.
* Copyright 2016 Skie-Systems. All rights reserved.
*
* lib for OLED-Display with ssd1306/ssd1309/sh1106-Controller
* first dev-version only for I2C-Connection
* at ATMega328P like Arduino Uno
*
* at GRAPHICMODE lib needs SRAM for display
* DISPLAY-WIDTH * DISPLAY-HEIGHT + 2 bytes
*/
#ifndef LCD_H
#define LCD_H
#ifdef __cplusplus
extern "C" {
#endif
#if (__GNUC__ * 100 + __GNUC_MINOR__) < 303
#error "This library requires AVR-GCC 3.3 or later, update to newer AVR-GCC compiler !"
#endif
#include <inttypes.h>
#include <avr/pgmspace.h>
/* TODO: define bus */
#define I2C // I2C or SPI
/* TODO: define displaycontroller */
#define SSD1306 // SH1106 or SSD1306, check datasheet of your display
/* TODO: define displaymode */
#define GRAPHICMODE // TEXTMODE for only text to display,
// GRAPHICMODE for text and graphic
/* TODO: define font */
#define FONT ssd1306oled_font// set font here, refer font-name at font.h/font.c
/* TODO: define I2C-adress for display */
// using 7-bit-adress for lcd-library
// if you use your own library for twi check I2C-adress-handle
#define LCD_I2C_ADR (0x7a >> 1) // 7 bit slave-adress without r/w-bit
// r/w-bit are set/unset by library
// e.g. 8 bit slave-adress:
// 0x78 = adress 0x3C with cleared r/w-bit (write-mode)
#ifdef I2C
#include "i2c.h" // library for I2C-communication
// if you want to use other lib for I2C
// edit i2c_xxx commands in this library
// i2c_start(), i2c_byte(), i2c_stop()
#elif defined SPI
// if you want to use your other lib/function for SPI replace SPI-commands
#define LCD_PORT PORTB
#define LCD_DDR DDRB
#define RES_PIN PB0
#define DC_PIN PB1
#define CS_PIN PB2
#endif
#ifndef YES
#define YES 1
#endif
#define NORMALSIZE 1
#define DOUBLESIZE 2
#define LCD_DISP_OFF 0xAE
#define LCD_DISP_ON 0xAF
#define WHITE 0x01
#define BLACK 0x00
#define DISPLAY_WIDTH 128
#define DISPLAY_HEIGHT 32
void lcd_command(uint8_t cmd[], uint8_t size); // transmit command to display
void lcd_data(uint8_t data[], uint16_t size); // transmit data to display
void lcd_init(uint8_t dispAttr);
void lcd_home(void); // set cursor to 0,0
void lcd_invert(uint8_t invert); // invert display
void lcd_sleep(uint8_t sleep); // display goto sleep (power off)
void lcd_set_contrast(uint8_t contrast); // set contrast for display
void lcd_puts(const char* s); // print string, \n-terminated, from ram on screen (TEXTMODE)
// or buffer (GRAPHICMODE)
void lcd_puts_p(const char* progmem_s); // print string from flash on screen (TEXTMODE)
// or buffer (GRAPHICMODE)
void lcd_clrscr(void); // clear screen (and buffer at GRFAICMODE)
void lcd_gotoxy(uint8_t x, uint8_t y); // set curser at pos x, y. x means character,
// y means line (page, refer lcd manual)
void lcd_goto_xpix_y(uint8_t x, uint8_t y); // set curser at pos x, y. x means pixel,
// y means line (page, refer lcd manual)
void lcd_putc(char c); // print character on screen at TEXTMODE
// at GRAPHICMODE print character to buffer
void lcd_charMode(uint8_t mode); // set size of chars
#if defined GRAPHICMODE
void lcd_set_buffer(uint8_t x, uint8_t y, uint8_t value);
void lcd_drawPixel(uint8_t x, uint8_t y, uint8_t color);
void lcd_drawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color);
void lcd_drawRect(uint8_t px1, uint8_t py1, uint8_t px2, uint8_t py2, uint8_t color);
void lcd_fillRect(uint8_t px1, uint8_t py1, uint8_t px2, uint8_t py2, uint8_t color);
void lcd_drawCircle(uint8_t center_x, uint8_t center_y, uint8_t radius, uint8_t color);
void lcd_fillCircle(uint8_t center_x, uint8_t center_y, uint8_t radius, uint8_t color);
void lcd_drawBitmap(uint8_t x, uint8_t y, const uint8_t picture[], uint8_t width, uint8_t height, uint8_t color);
void lcd_display(void); // copy buffer to display RAM
void lcd_clear_buffer(void); // clear display buffer
uint8_t lcd_check_buffer(uint8_t x, uint8_t y); // read a pixel value from the display buffer
void lcd_display_block(uint8_t x, uint8_t line, uint8_t width); // display (part of) a display line
#endif
#ifdef __cplusplus
}
#endif
#endif /* LCD_H */

View File

@ -0,0 +1,19 @@
//****main.c****//
#include "lcd.h"
int main(void){
lcd_init(LCD_DISP_ON); // init lcd and turn on
lcd_puts("Hello World"); // put string from RAM to display (TEXTMODE) or buffer (GRAPHICMODE)
lcd_gotoxy(0,2); // set cursor to first column at line 3
lcd_puts_p(PSTR("String from flash")); // puts string form flash to display (TEXTMODE) or buffer (GRAPHICMODE)
#if defined GRAPHICMODE
lcd_drawCircle(64,32,7,WHITE); // draw circle to buffer white lines
lcd_display(); // send buffer to display
#endif
for(;;){
//main loop
}
return 0;
}

View File

@ -0,0 +1,132 @@
# OLED for AVR mikrocontrollers
Library for oled-displays with SSD1306, SSD1309 or SH1106 display-controller connected with I2C or SPI at an AVR Atmel Atmega like Atmega328P.
Upstream: [https://github.com/Sylaina/oled-display/](https://github.com/Sylaina/oled-display/)
<img src="https://github.com/Sylaina/oled-display/blob/master/oled.jpg?raw=true" width="500">
This library allows you to display text or/and graphic at oled-display.
The library need less than 2 kilobytes flash-memory and 3 bytes sram in textmode, in graphicmode library need less than 3 kilobytes flash-memory and 1027 bytes static sram so you can use oled-displays e.g with Atmega48PA (only with textmode).
Library is only tested with 128x64 Pixel display, lower resolution not tested but should work too.
If you want to use your own I2C library you have to fit i2c-function at lcd-library.
Settings for I2C-bus have to set at i2c.h
Settings for display have to set at lcd.h
If you want to use characters like e.g. ä set your compiler input-charset to utf-8 and your compiler exec-charset to iso-8859-15 (look at makefile line 115).
Testcondition: Display: SSD1306 OLED, Compiler Optimizelevel: -Os, µC: Atmega328p @ 8 MHz internal RC
Memory:
<table>
<tr>
<th>Modul</th>
<th>Flash</th>
<th>Static RAM</th>
</tr>
<tr>
<td>I2C-Core</td>
<td>234 Bytes</td>
<td>0 Bytes</td>
</tr>
<tr>
<td>FONT</td>
<td>644 Bytes</td>
<td>0 Bytes</td>
</tr>
<tr>
<td>OLED (Text-Mode)</td>
<td>1449 Bytes</td>
<td>3 Bytes</td>
</tr>
<tr>
<td>OLED (Graphic-Mode)</td>
<td>2403 Bytes</td>
<td>1027 Bytes</td>
</tr>
</table>
Speed (print 20 charaters (1 line) in normal size to display):
<table>
<tr>
<th>Mode</th>
<th>Time</th>
<th>I2C-Speed</th>
</tr>
<tr>
<td>OLED (Text-Mode)</td>
<td>4.411 ms</td>
<td>400 kHz</td>
</tr>
<tr>
<td>OLED (Text-Mode)</td>
<td>15.384 ms</td>
<td>100 kHz</td>
</tr>
<tr>
<td>OLED (Graphic-Mode)</td>
<td>26.603 ms</td>
<td>400 kHz</td>
</tr>
<tr>
<td>OLED (Graphic-Mode)</td>
<td>96.294 ms</td>
<td>100 kHz</td>
</tr>
</table>
example:
```c
//****main.c****//
#include "lcd.h"
int main(void){
lcd_init(LCD_DISP_ON); // init lcd and turn on
lcd_puts("Hello World"); // put string from RAM to display (TEXTMODE) or buffer (GRAPHICMODE)
lcd_gotoxy(0,2); // set cursor to first column at line 3
lcd_puts_p(PSTR("String from flash")); // puts string form flash to display (TEXTMODE) or buffer (GRAPHICMODE)
#if defined GRAPHICMODE
lcd_drawCircle(64,32,7,WHITE); // draw circle to buffer
lcd_display(); // send buffer to display
#endif
for(;;){
//main loop
}
return 0;
}
```
example for chars with double height:
```c
//****main.c****//
#include "lcd.h"
int main(void){
lcd_init(LCD_DISP_ON);
lcd_clrscr();
lcd_set_contrast(0x00);
lcd_gotoxy(4,1);
lcd_puts("Normal Size");
lcd_charMode(DOUBLESIZE);
lcd_gotoxy(0,4);
lcd_puts(" Double \r\n Size");
lcd_charMode(NORMALSIZE);
#ifdef GRAPHICMODE
lcd_display();
#endif
for(;;){
//main loop
}
return 0;
}
```
<img src="https://github.com/Sylaina/oled-display/blob/master/bigchars.JPG?raw=true" width="500">

View File

@ -3,8 +3,9 @@
from PIL import Image, ImageDraw, ImageFont
import asyncio
from mpd import MPDClient
import spidev
IO_DELAY = (1.0 / 20.0)
IO_DELAY = (1.0 / 25.0)
MPD_DELAY = 0.5
class display(object):
@ -17,7 +18,6 @@ class display(object):
self.img = Image.new('1', (self.width, self.height), color = not self.fg)
self.d = ImageDraw.Draw(self.img)
self.bar_redraw = True
self.dbg = 0
def render(self):
print("render NYI")
@ -26,9 +26,8 @@ class display(object):
print("animate NYI")
def get_image(self):
self.img.save("/tmp/wr/frame%d.png" % self.dbg)
self.dbg += 1
return self.img.tobytes()
temp = self.img
return temp.rotate(-90,expand=True).tobytes()
class display_playback(display):
def __init__(self):
@ -133,8 +132,8 @@ class radioserv:
while True:
self.disp_mgr.animate(1)
self.disp_mgr.render()
self.disp_mgr.get_image()
print("x")
data = list(self.disp_mgr.get_image())
self.spi.xfer(data)
await self.io_event.wait()
self.io_event.clear()
@ -155,7 +154,11 @@ class radioserv:
# Song title
try:
title = currentsong['title'] + " by " + currentsong['artist']
title = currentsong['title']
try:
title += " by " + currentsong['artist']
except:
pass
except:
try:
title = currentsong['file'];
@ -175,8 +178,11 @@ class radioserv:
# Playback info
try:
cm, cs = divmod(round(float(status['elapsed'])), 60)
if "duration" in status:
dm, ds = divmod(round(float(status['duration'])), 60)
info = "#%d/%d %d:%02d/%d:%02d" % (int(status['song']) + 1, int(status['playlistlength']), cm, cs, dm, ds)
else:
info = "Radio %02d:%02d" % (cm, cs)
except:
info = "Stopped"
if (info != last_info):
@ -186,6 +192,12 @@ class radioserv:
await asyncio.sleep(MPD_DELAY)
async def main(self):
# Connect to SPI
self.spi = spidev.SpiDev()
self.spi.open(0, 0)
self.spi.max_speed_hz = 500000
self.spi.mode = 0b00
# Connect to MPD
self.mpd = MPDClient()
self.mpd.connect("yuki", 6600)
@ -205,7 +217,7 @@ class radioserv:
mpd_task = asyncio.create_task(self.mpd_main())
# TODO: Wait for exit condition (shutdown command)
await asyncio.sleep(10)
await asyncio.sleep(60)
io_timer_task.cancel()
io_task.cancel()