fw: Implement display output
This commit is contained in:
parent
69cb74b8dc
commit
633d2d0fe2
@ -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
|
||||
|
||||
|
@ -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
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">
|
Loading…
Reference in New Issue
Block a user