MSP430 OLED-Display SSD1306 (128×64)

msp430_ssd1306_actionVor 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

ssd1306_module_pins ssd1306_module_backSchaltplan

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;	
}
// --------------------------------------------------------------

 

8 thoughts on “MSP430 OLED-Display SSD1306 (128×64)

  1. gast

    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.

    Reply
    1. declis Post author

      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.

      Reply
      1. StimmeSZu

        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.

        Reply
        1. declis Post author

          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.

          Reply
  2. Swordancer

    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.

    Reply
  3. Asia

    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. 🙂

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

This site uses Akismet to reduce spam. Learn how your comment data is processed.