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
- }
/******************************* * __ * /\ \ * __ _ \_\ \ __ ___ * /\ \/'\ /'_' \ /'__'\ /'___\ * \/> <//\ \ \ \/\ __//\ \__/ * /\_/\_\ \___,_\ \____\ \____\ * \//\/_/\/__,_ /\/____/\/____/ * * 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;
- }
- // --------------------------------------------------------------
/******************************* * __ * /\ \ * __ _ \_\ \ __ ___ * /\ \/'\ /'_' \ /'__'\ /'___\ * \/> <//\ \ \ \/\ __//\ \__/ * /\_/\_\ \___,_\ \____\ \____\ * \//\/_/\/__,_ /\/____/\/____/ * * 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. 🙂