Compare commits
5 Commits
1cbc6063d6
...
633d2d0fe2
Author | SHA1 | Date | |
---|---|---|---|
633d2d0fe2 | |||
69cb74b8dc | |||
a618fff822 | |||
a8d947544b | |||
7efac00348 |
@ -1,9 +1,10 @@
|
|||||||
MCU=atmega88
|
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
|
LDFLAGS=-Wl,-gc-sections -Wl,-relax
|
||||||
CC=avr-gcc
|
CC=avr-gcc
|
||||||
TARGET=main
|
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
|
all: $(TARGET).hex
|
||||||
|
|
||||||
|
@ -1,6 +1,61 @@
|
|||||||
#include <avr/io.h>
|
#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()
|
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
52
firmware/oled-display/.gitignore
vendored
Normal 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
|
136
firmware/oled-display/font.c
Normal file
136
firmware/oled-display/font.c
Normal 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
|
||||||
|
};
|
16
firmware/oled-display/font.h
Normal file
16
firmware/oled-display/font.h
Normal 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
180
firmware/oled-display/i2c.c
Normal 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
|
46
firmware/oled-display/i2c.h
Normal file
46
firmware/oled-display/i2c.h
Normal 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
518
firmware/oled-display/lcd.c
Normal 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
150
firmware/oled-display/lcd.h
Normal 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 */
|
19
firmware/oled-display/main.c
Normal file
19
firmware/oled-display/main.c
Normal 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;
|
||||||
|
}
|
132
firmware/oled-display/readme.md
Normal file
132
firmware/oled-display/readme.md
Normal 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">
|
@ -3,8 +3,9 @@
|
|||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
import asyncio
|
import asyncio
|
||||||
from mpd import MPDClient
|
from mpd import MPDClient
|
||||||
|
import spidev
|
||||||
|
|
||||||
IO_DELAY = (1.0 / 20.0)
|
IO_DELAY = (1.0 / 25.0)
|
||||||
MPD_DELAY = 0.5
|
MPD_DELAY = 0.5
|
||||||
|
|
||||||
class display(object):
|
class display(object):
|
||||||
@ -17,7 +18,6 @@ class display(object):
|
|||||||
self.img = Image.new('1', (self.width, self.height), color = not self.fg)
|
self.img = Image.new('1', (self.width, self.height), color = not self.fg)
|
||||||
self.d = ImageDraw.Draw(self.img)
|
self.d = ImageDraw.Draw(self.img)
|
||||||
self.bar_redraw = True
|
self.bar_redraw = True
|
||||||
self.dbg = 0
|
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
print("render NYI")
|
print("render NYI")
|
||||||
@ -26,9 +26,8 @@ class display(object):
|
|||||||
print("animate NYI")
|
print("animate NYI")
|
||||||
|
|
||||||
def get_image(self):
|
def get_image(self):
|
||||||
self.img.save("/tmp/wr/frame%d.png" % self.dbg)
|
temp = self.img
|
||||||
self.dbg += 1
|
return temp.rotate(-90,expand=True).tobytes()
|
||||||
return self.img.tobytes()
|
|
||||||
|
|
||||||
class display_playback(display):
|
class display_playback(display):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -133,8 +132,8 @@ class radioserv:
|
|||||||
while True:
|
while True:
|
||||||
self.disp_mgr.animate(1)
|
self.disp_mgr.animate(1)
|
||||||
self.disp_mgr.render()
|
self.disp_mgr.render()
|
||||||
self.disp_mgr.get_image()
|
data = list(self.disp_mgr.get_image())
|
||||||
print("x")
|
self.spi.xfer(data)
|
||||||
await self.io_event.wait()
|
await self.io_event.wait()
|
||||||
self.io_event.clear()
|
self.io_event.clear()
|
||||||
|
|
||||||
@ -155,7 +154,11 @@ class radioserv:
|
|||||||
|
|
||||||
# Song title
|
# Song title
|
||||||
try:
|
try:
|
||||||
title = currentsong['title'] + " by " + currentsong['artist']
|
title = currentsong['title']
|
||||||
|
try:
|
||||||
|
title += " by " + currentsong['artist']
|
||||||
|
except:
|
||||||
|
pass
|
||||||
except:
|
except:
|
||||||
try:
|
try:
|
||||||
title = currentsong['file'];
|
title = currentsong['file'];
|
||||||
@ -175,8 +178,11 @@ class radioserv:
|
|||||||
# Playback info
|
# Playback info
|
||||||
try:
|
try:
|
||||||
cm, cs = divmod(round(float(status['elapsed'])), 60)
|
cm, cs = divmod(round(float(status['elapsed'])), 60)
|
||||||
dm, ds = divmod(round(float(status['duration'])), 60)
|
if "duration" in status:
|
||||||
info = "#%d/%d %d:%02d/%d:%02d" % (int(status['song']) + 1, int(status['playlistlength']), cm, cs, dm, ds)
|
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:
|
except:
|
||||||
info = "Stopped"
|
info = "Stopped"
|
||||||
if (info != last_info):
|
if (info != last_info):
|
||||||
@ -186,6 +192,12 @@ class radioserv:
|
|||||||
await asyncio.sleep(MPD_DELAY)
|
await asyncio.sleep(MPD_DELAY)
|
||||||
|
|
||||||
async def main(self):
|
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
|
# Connect to MPD
|
||||||
self.mpd = MPDClient()
|
self.mpd = MPDClient()
|
||||||
self.mpd.connect("yuki", 6600)
|
self.mpd.connect("yuki", 6600)
|
||||||
@ -205,7 +217,7 @@ class radioserv:
|
|||||||
mpd_task = asyncio.create_task(self.mpd_main())
|
mpd_task = asyncio.create_task(self.mpd_main())
|
||||||
|
|
||||||
# TODO: Wait for exit condition (shutdown command)
|
# TODO: Wait for exit condition (shutdown command)
|
||||||
await asyncio.sleep(10)
|
await asyncio.sleep(60)
|
||||||
|
|
||||||
io_timer_task.cancel()
|
io_timer_task.cancel()
|
||||||
io_task.cancel()
|
io_task.cancel()
|
||||||
|
Loading…
Reference in New Issue
Block a user