OLED 1306 128x32, ATTiny85, only printing every other line

Go To Last Post
2 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey guys.

 

Hope you're having a nice sunday.

 

I'm sitting here getting frustrated with a (small) issue that I just can't get my head around.

 

So, first. I'm using these two components:

 

- ATTiny85

- SSD1306 128x32 OLED (some common china model)

 

I'm using the Tinusaur library (made for 128x64 display). When I try to set the "cursor", only every other line is "available". Row 1 is y=2, row 2 is y = 4, etc. This means that if I try to draw a sine curve for example, it looks dashed almost.

 

Anyway, I've been googling and fiddling all day and it seems like: 1. It's a common problem. 2. It's supposed to be fixed by two things: Setting the multiplexing correctly and setting the com pins for the hardware configuration. Multiplexing I think I understand, com pins I dont however.

 

Now the biggest issue is that I don't understand what I'm seeing. As described below in the pictures, in the first case I'm telling the multiplexing that I'm using 64 lines when I only have 32 on the display. I understand that this doesn't work, so I set it to having 32 lines. Suddenly everything shifts "outside" of the screen and the gap between each pixel is bigger. Now I change the com pins from what's supposed to be for the 128x64 display (0x12) to the prescribed remedy for the 128x32 display (0x02). Now it's a bit better I guess, as the gap is smaller between lit up pixels. However, everything is still shifted.

 

What is it that I'm missing? Where can I look? I've tried to understand the datasheet but I can't find anything that points to what the issue is.

 

I've attached pictures for three cases when I'm plotting a sine wave:

 

1.

Com pins:    0xDA, 0x12,

Multiplexing:    0xA8, 0x3F,     //Multiplex ratio (0 to 63)

 

2.

Com pins:    0xDA, 0x12,

Multiplexing:    0xA8, 0x1F,     //Multiplex ratio (0 to 31)

 

3.

Com pins:    0xDA, 0x02,

Multiplexing:    0xA8, 0x1F,     //Multiplex ratio (0 to 31)

 

 

Here is the code for the AVR and below is the library:

 

#define F_CPU 1000000
#include <avr/io.h>
#include <util/delay.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include "cpufreq.h"
#include "ssd1306xled.h"
#include "font8x16.h"
#include "font6x8.h"
#include "ssd1306xledtx.h"

int main (void)
{
  _delay_ms(40);
  ssd1306_init();
  ssd1306_clear();

  for(int i = 0; i < 127; i++){
    ssd1306_setpos(i, (int) (sin(i/(6.28*2))*3)+4);
    ssd1306_byte(8);
  }
}

 

Library:

 

/**
 * SSD1306xLED - Library for the SSD1306 based OLED/PLED 128x64 displays
 *
 * @author Neven Boyanov
 *
 * This is part of the Tinusaur/SSD1306xLED project.
 *
 * Copyright (c) 2018 Neven Boyanov, The Tinusaur Team. All Rights Reserved.
 * Distributed as open source software under MIT License, see LICENSE.txt file.
 * Retain in your source code the link http://tinusaur.org to the Tinusaur project.
 *
 * Source code available at: https://bitbucket.org/tinusaur/ssd1306xled
 *
 */

// ============================================================================

#include <stdlib.h>
#include <avr/io.h>
#include <avr/pgmspace.h>

#include "ssd1306xled.h"

// ----------------------------------------------------------------------------

const uint8_t ssd1306_init_sequence [] PROGMEM = {	// Initialization Sequence
	0xAE,			// 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
	0x00,			// --set start line address
	0x81, 0xFF,		// 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, 0x1F,		// 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,	0x80,		// --set display clock divide ratio/oscillator frequency
	0xF0,			// --set divide ratio
	0xD9, 0x22,		// Set pre-charge period
	0xDA, 0x12,		// Set com pins hardware configuration
	0xDB,			// --set vcomh
	0x20,			// 0x20,0.77xVcc
	0x8D, 0x14,		// Set DC-DC enable
	0xAF			// Display ON in normal mode
};

// ============================================================================

// TODO: These functions could become separate library for handling I2C simplified output.
// IDEA: SI2CW - Simplified I2C Writer

// Some code based on "IIC_wtihout_ACK" by http://www.14blog.com/archives/1358

// These definitions are used only internally by the library
// Convenience definitions for PORTB
#define DIGITAL_WRITE_HIGH(PORT) PORTB |= (1 << PORT)
#define DIGITAL_WRITE_LOW(PORT) PORTB &= ~(1 << PORT)

// ----------------------------------------------------------------------------

void ssd1306_xfer_start(void);
void ssd1306_xfer_stop(void);
void ssd1306_send_byte(uint8_t byte);
void ssd1306_send_command(uint8_t command);
void ssd1306_send_data_start(void);
void ssd1306_send_data_stop(void);

// ----------------------------------------------------------------------------

void ssd1306_xfer_start(void)
{
	DIGITAL_WRITE_HIGH(SSD1306_SCL);	// Set to HIGH
	DIGITAL_WRITE_HIGH(SSD1306_SDA);	// Set to HIGH
	DIGITAL_WRITE_LOW(SSD1306_SDA);		// Set to LOW
	DIGITAL_WRITE_LOW(SSD1306_SCL);		// Set to LOW
}

void ssd1306_xfer_stop(void)
{
	DIGITAL_WRITE_LOW(SSD1306_SCL);		// Set to LOW
	DIGITAL_WRITE_LOW(SSD1306_SDA);		// Set to LOW
	DIGITAL_WRITE_HIGH(SSD1306_SCL);	// Set to HIGH
	DIGITAL_WRITE_HIGH(SSD1306_SDA);	// Set to HIGH
}

void ssd1306_send_byte(uint8_t byte)
{
	uint8_t i;
	for (i = 0; i < 8; i++)
	{
		if ((byte << i) & 0x80)
			DIGITAL_WRITE_HIGH(SSD1306_SDA);
		else
			DIGITAL_WRITE_LOW(SSD1306_SDA);

		DIGITAL_WRITE_HIGH(SSD1306_SCL);
		DIGITAL_WRITE_LOW(SSD1306_SCL);
	}
	DIGITAL_WRITE_HIGH(SSD1306_SDA);
	DIGITAL_WRITE_HIGH(SSD1306_SCL);
	DIGITAL_WRITE_LOW(SSD1306_SCL);
}

void ssd1306_send_command_start(void) {
	ssd1306_xfer_start();
	ssd1306_send_byte(SSD1306_SA);  // Slave address, SA0=0
	ssd1306_send_byte(0x00);	// write command
}

void ssd1306_send_command_stop(void) {
	ssd1306_xfer_stop();
}

void ssd1306_send_command(uint8_t command)
{
	ssd1306_send_command_start();
	ssd1306_send_byte(command);
	ssd1306_send_command_stop();
}

void ssd1306_send_data_start(void)
{
	ssd1306_xfer_start();
	ssd1306_send_byte(SSD1306_SA);
	ssd1306_send_byte(0x40);	//write data
}

void ssd1306_send_data_stop(void)
{
	ssd1306_xfer_stop();
}

// ============================================================================

void ssd1306_init(void)
{
	DDRB |= (1 << SSD1306_SDA);	// Set port as output
	DDRB |= (1 << SSD1306_SCL);	// Set port as output

	for (uint8_t i = 0; i < sizeof (ssd1306_init_sequence); i++) {
		ssd1306_send_command(pgm_read_byte(&ssd1306_init_sequence[i]));
	}
}

void ssd1306_setpos(uint8_t x, uint8_t y)
{
	ssd1306_send_command_start();
	ssd1306_send_byte(0xb0 + y);
	ssd1306_send_byte(((x & 0xf0) >> 4) | 0x10); // | 0x10
/* TODO: Verify correctness */	ssd1306_send_byte((x & 0x0f)); // | 0x01
	ssd1306_send_command_stop();
}

void ssd1306_fill4(uint8_t p1, uint8_t p2, uint8_t p3, uint8_t p4) {
	ssd1306_setpos(0, 0);
	ssd1306_send_data_start();
	for (uint16_t i = 0; i < 128 * 8 / 4; i++) {
		ssd1306_send_byte(p1);
		ssd1306_send_byte(p2);
		ssd1306_send_byte(p3);
		ssd1306_send_byte(p4);
	}
	ssd1306_send_data_stop();
}

// ----------------------------------------------------------------------------

void ssd1306_byte(uint8_t b) {
	ssd1306_send_data_start();
	ssd1306_send_byte(b);
	ssd1306_send_data_stop();
}

// ============================================================================

 

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your "I2C" code is crap.   You should never "drive" the I2C lines high.

 

I have a similar display.   It works just fine with respected Arduino libraries on regular Arduinos.

I would be fairly confident that it would work with SpenceKonde's TinyCore and TinyWire library.

 

If you want to use non-Arduino code,   Fleury's i2cmaster.S is a bit-banged Master that will work on any pins of any regular Mega or Tiny.

 

David.