Vor ein paar Monaten habe ich mir ein kleines OLED-Display aus China bestellt, mit einem SSD1306 Controller (Ebay). Die Größe des Displays beträgt 0,96″ und hat eine Auflösung von 128x64px. Das Display ist auf einem fertigen Modul gelötet, man braucht sich also nicht mehr um die restliche Beschaltung von dem Display zu kümmern. Der Display-Controller (SSD1306) bietet verschiedene Schnittstellen (Parallel, SPI, I2C). Leider ist die Schnittstelle bei diesem Modul per 0 ohm SMD-Widerstände auf die parallele Schnittstelle eingestellt. Da ich keine vernünftige Spitze für meinen Lötkolben hatte, und auch keine Motivation um die SMD-Widerstände umzulöten (auf SPI), habe ich das Display also parallel angesteuert. Ich habe hauptsächlich die gleiche Bibliothek benutzt wie beim Nokia-Display (PCD8544), allerdings mit ein paar neuen Funktionen und Verbesserungen. Angesteuert wird das Display wieder per MSP430G2553.
Video
Modul
Schaltplan
Ich poste hier keinen “richtigen” Schaltplan, sondern nur eine Übersicht der Beschaltung zwischen MSP430 und dem SSD1306-Modul, beide werden über einen externen LDO (3V3) betrieben:
// P1 is Data-Port (P1.7=D7...P1.0=D0) #define CS 0x0010 // P2.4 #define RES 0x0008 // P2.3 #define DC 0x0004 // P2.2 #define WR 0x0002 // P2.1 #define RD 0x0001 // P2.0
Source-Code
(25.03.2014, 00:35): SOURCE-CODE (Interface 8bit 8080)
(02.04.2018, 22:51) SOURCE-CODE (Interface 4-wire SPI)
Ein kurzer Ausschnitt:
SSD1306.c
/*******************************
* __
* /\ \
* __ _ \_\ \ __ ___
* /\ \/'\ /'_' \ /'__'\ /'___\
* \/> <//\ \ \ \/\ __//\ \__/
* /\_/\_\ \___,_\ \____\ \____\
* \//\/_/\/__,_ /\/____/\/____/
*
* Author: declis (xdec.de)
********************************/
#include "MSP430G2553.h"
#include "lib_lcd.h"
#include "delay.h"
const char init_cmd_array[]={0xAE, // DISPLAY OFF
0xD5, // SET OSC FREQUENY
0x80, // divide ratio = 1 (bit 3-0), OSC (bit 7-4)
0xA8, // SET MUX RATIO
0x3F, // 64MUX
0xD3, // SET DISPLAY OFFSET
0x00, // offset = 0
0x40, // set display start line, start line = 0
0x8D, // ENABLE CHARGE PUMP REGULATOR
0x14, //
0x20, // SET MEMORY ADDRESSING MODE
0x02, // horizontal addressing mode
0xA1, // set segment re-map, column address 127 is mapped to SEG0
0xC8, // set COM/Output scan direction, remapped mode (COM[N-1] to COM0)
0xDA, // SET COM PINS HARDWARE CONFIGURATION
0x12, // alternative COM pin configuration
0x81, // SET CONTRAST CONTROL
0xCF, //
0xD9, // SET PRE CHARGE PERIOD
0xF1, //
0xDB, // SET V_COMH DESELECT LEVEL
0x40, //
0xA4, // DISABLE ENTIRE DISPLAY ON
0xA6, // NORMAL MODE (A7 for inverse display)
0xAF}; // DISPLAY ON
void init_LCD(void)
{
unsigned char byte=sizeof(init_cmd_array);
wait_ms(100);
P2OUT|=RES;
while(byte)
{
// really dirty stuff
set_instruction(0,init_cmd_array[sizeof(init_cmd_array)-byte]);
byte--;
}
// display RAM is undefined after reset, clean dat shit
fill_display(lcd_width,lcd_height,0x00);
}
void init_ports(void)
{
P1DIR|=0xFF; // parallel port for OLED-Display
P1OUT&=~0xFF;
P2DIR|=CS+DC+WR+RD+RES;
P2OUT&=~RES+WR+RD; // reset, write, read LOW
P2OUT|=CS; // chip select HIGH
}
main.c
/*******************************
* __
* /\ \
* __ _ \_\ \ __ ___
* /\ \/'\ /'_' \ /'__'\ /'___\
* \/> <//\ \ \ \/\ __//\ \__/
* /\_/\_\ \___,_\ \____\ \____\
* \//\/_/\/__,_ /\/____/\/____/
*
* Author: declis (xdec.de)
********************************/
#include <msp430g2553.h>
#include "init_SSD1306.h"
#include "lib_lcd.h"
#include "lib_math.h"
#include "delay.h"
#include "fuck.h"
#define meta_size 3
void scene_05_scroller_help(const char*,const char*);
void scene_00_typer_help(unsigned char);
signed int x=128,y=0,x1=128,y1=0,x2=128,y2=127;
unsigned char time_ms=80;
struct meta_b m_balls[meta_size];
void main(void)
{
WDTCTL=WDTPW+WDTHOLD;
BCSCTL1=CALBC1_16MHZ;
DCOCTL=CALDCO_16MHZ;
m_balls[0].x=10; m_balls[0].y=20; m_balls[0].direction=4;
m_balls[1].x=30; m_balls[1].y=50; m_balls[1].direction=2;
m_balls[2].x=45; m_balls[2].y=25; m_balls[2].direction=3;
init_ports();
init_LCD();
// SCENE 00 - TYPER INTRO ---------------------------------------
x=0;
scene_00_typer_help(0);
scene_00_typer_help(4);
wait_ms(time_ms);
write_h_string(4,0,str_scene00_3,0);
wait_ms(time_ms);
write_h_string(17,0,str_scene00_3,0);
wait_ms(500);
string_typer(5,1,str_scene00_0,3,1500);
x=lcd_width;
while(x!=-60)
{
draw_string_sin(x--,44,str_scene00_1,3);
wait_ms(30);
clean_area(0,lcd_width,5,lcd_height_b);
}
wait_ms(2000);
fill_display(lcd_width,lcd_height,0x00);
wait_ms(1000);
// --------------------------------------------------------------
// SCENE 01 - CITY-SCROLLER -------------------------------------
x=lcd_width,y=0,x1=lcd_width,y1=0,x2=lcd_width,y2=lcd_width-1;
while(x>=-400)
{
if(x<1) // start normal scroller
{
draw_string(x1,y,str_scene01_2);
if(y1) y--; // y1 = direction
else y++;
if(y==7) y1=1; // moving up
if(!y) y1=0; // moving down
x1-=2;
}
draw_bitmap(x2--,16,cityb_w,cityb_h,cityb);
if(x2+(signed int)cityb_w<lcd_width)
draw_bitmap(y2--,16,cityb_w,cityb_h,cityb);
x--;
wait_ms(30);
clean_area(0,lcd_width,0,1); // scroller area
}
// --------------------------------------------------------------
// SCENE 02 - BOUNCE DAT BMP ------------------------------------
bouncy_bmp(85,64,10,baabis_w,baabis_h,baabis);
wait_ms(1000);
write_h_string(12,0,str_scene02_hoo,0);
wait_ms(1000);
// --------------------------------------------------------------
// SCENE 03 - METABALLS PHYSICS ---------------------------------
y=0;
while(y<=14000)
{
draw_metaballs(m_balls,meta_size,y);
y+=100;
}
y1=0;y2=3;x=1;
while(y2)
{
draw_metaballs(m_balls,meta_size,y);
meta_ball_physics(m_balls,meta_size,x,5);
if(y<=2000)
{
y1=1;
y2--; // cycles
x++; // pos_change
set_instruction(0,0xA7); // invert
}
if(y>=25000)
{
y1=0;
set_instruction(0,0xA6); // normal
}
if(!y1) y-=200; // bigger
else y+=200; // smaller
}
// --------------------------------------------------------------
set_instruction(0,0xA6);
clean_area(0,84,0,8);
wait_ms(1000);
// SCENE 04 - BMP SCROLL OUT + SINE SCROLLER --------------------
x=85;x1=lcd_width;x2=0;
while(x!=-75)
{
draw_bitmap(x--,0,baabis_w,baabis_h,baabis);
if(x<40)
draw_string_sin_fixed(x1--,24,str_scene05_4,x2++,2);
wait_ms(20);
clean_area(x1,lcd_width,3,5);
}
// --------------------------------------------------------------
// SCENE 05 - FIXED SINE STRING + NORMAL SCROLLER ---------------
scene_05_scroller_help(str_scene05_4,str_scene05_5);
// transition 00
y1=24;y2=lcd_height+10;
while(y2>=24)
{
draw_string_sin_fixed(x1,y1--,str_scene05_4,x2++,2);
if(y1<10)
draw_string_sin_fixed(x1,y2--,str_scene05_6,x2++,2);
wait_ms(20);
clean_area(x1,lcd_width,0,lcd_height_b);
}
scene_05_scroller_help(str_scene05_6,str_scene05_5);
// transition 01
y1=-(6*(sizeof(str_scene05_6)-1));
while(x1>=y1)
{
draw_string_sin_fixed(x1--,y2,str_scene05_6,x2++,2);
wait_ms(15);
clean_area(0,lcd_width,2,5);
}
// --------------------------------------------------------------
// SCENE 06 - OUTRO SINE SCROLLER -------------------------------
bouncy_bmp(1,64,5,smiley1b_w,smiley1b_h,smiley1b);
draw_bitmap(1,32,smiley1b_w,smiley1b_h,smiley1b); // move 1px down
x=lcd_width;x1=lcd_width;x2=-(6*(sizeof(str_scene06_7)-1));y=0;
while(x1>=x2)
{
if(x>=40)
{
draw_string(x,32,str_scene06_8);
draw_string(x,40,str_scene06_9);
draw_string(x,48,str_scene06_10);
draw_string(x,56,str_scene06_11);
x--;
}
draw_string_sin_fixed(x1--,0,str_scene06_7,y++,2);
wait_ms(17);
clean_area(0,lcd_width,0,3);
}
wait_ms(1000);
fill_display(lcd_width,lcd_height,0x00);
// --------------------------------------------------------------
write_string(2,3,str_scene06_12,3);
wait_ms(4000);
fill_display(lcd_width,lcd_height,0x00);
while(1);
}
// HELP FUNCTIONS for smaller code size (a liitle bit dirty :D)--
// SCENE 05
void scene_05_scroller_help(const char *str_mid, const char *str_up_down)
{
x=lcd_width;y=-(6*(sizeof(str_scene05_5)-1));y1=2;
while(y1)
{
draw_string_sin_fixed(x1,24,str_mid,x2++,2);
draw_string(x--,0,str_up_down);
draw_string(y++,56,str_up_down);
if(y>lcd_width)
{
x=lcd_width;
y=-(6*sizeof(str_scene05_5)-1);
y1--;
}
wait_ms(15);
clean_area(x1,lcd_width,3,5);
}
}
// SCENE 00
void scene_00_typer_help(unsigned char y_pos)
{
while(x<3)
{
string_typer(5+(x*(sizeof(str_scene00_0)-1)),y_pos,str_scene00_0,0,time_ms);
x++;
}
x=0;
}
// --------------------------------------------------------------
Thanks for sharing. That is very nice.
Hi,
ich habe eine I2C Version mit 4 Pins (“GND VDD SCK SDA”) (für ~5€ inkl. Versandkosten). Hast du da vielleicht auch einen Quellcode (MSP430G2553) und kannst ihn hier posten? Würde bestimmt auch andere freuen.
Hi,
einfach USCI auf I2C initialiseren und set_instruction in lib_lcd.c dementsprechend für I2C abändern, ggf. noch ISR hinzufügen. Der restliche Quellcode kann weiterhin benutzt werden. I2C Beispiele gibt es auch direkt auf TI oder einfach im User Guide die Register nachschlagen.
Da stimme ich Swordancer absolut zu, sorry, aber das hilft leider gar nicht. Wäre super wenn du das und auch größere Schriftarten hinzufügen könntest.
Die 5×7 Font kann vergrößert werden.
Achja, der Source-Code kann gerne modifiziert werden -> Fonts etc. (User-Guide zur Hand nehmen oder direkt die I2C Beispiele von TI laden). Vielleicht mache ich es in der nächsten Zeit.
Hmm, this is very nice, but I can’t make it work with I2C. I have I2C code for SDA and SCL, but I don’t know how to write data in to oled corectly.
Please share i2c the library through github or something if possible. I want to implement it in my MSP430G2553.
Hi,
ich habe eine I2C Version mit 4 Pins (“GND VDD SCK SDA”) (für ~5€ inkl. Versandkosten).
Ich habe alles schon versucht. Kannst du mir da weiter helfen?
Über eine Antwort deinerseits werde ich mich sehr freuen. 🙂