Ich habe ATMEGA328P und I2C-Display (SSD1306). Ich versuche, einfach ein einzelnes Pixel mit möglichst wenig Code auf einen Bildschirm zu bringen, damit ich daraus lernen kann.
Ich konnte dies mit Himbeere mit dem folgenden Code tun:
// gcc ssd1306.c -lwiringPi -o ssd1306
#include <stdio.h>
#include <string.h>
#include <wiringPiI2C.h>
#define WIDTH 128
#define HEIGHT 64
int buffer[ WIDTH * HEIGHT / 8 ];
int i2cd;
void ssd1306_command(unsigned int c)
{
unsigned int control = 0x00;
wiringPiI2CWriteReg8( i2cd, control, c );
}
void ssd1306_byte(unsigned int c)
{
unsigned int control = 0x40;
wiringPiI2CWriteReg8( i2cd, control, c );
}
void drawPixel( int x, int y, unsigned int color )
{
switch (color)
{
case 1: // white
buffer[x + ( y / 8 ) * WIDTH ] = 1;
break;
case 0: // black
buffer[x + ( y / 8 ) * WIDTH ] = 0;
break;
}
}
void init()
{
i2cd = wiringPiI2CSetup( 0x3C ); // address
ssd1306_command(0xAE); // 0xAE // display off
ssd1306_command(0xD5); // 0xD5 // set display clock division
ssd1306_command(0x80); // the suggested ratio 0x80
ssd1306_command(0xA8); // 0xA8 set multiplex
ssd1306_command(63); // set height
ssd1306_command(0xD3); // set display offset
ssd1306_command(0x0); // no offset
ssd1306_command(64); // line #0 setstartline
ssd1306_command(0x8D); // 0x8D // chargepump
ssd1306_command(0x14);
ssd1306_command(0x20); // memory mode
ssd1306_command(0x00); // 0x0 act like ks0108
ssd1306_command(161); // segremap
ssd1306_command(0xC8); // comscandec
ssd1306_command(0xDA); // 0xDA set com pins
ssd1306_command(0x12);
ssd1306_command(0x81); // 0x81 // set contract
ssd1306_command(0xCF);
ssd1306_command(0xD9); // 0xd9 set pre-charge
ssd1306_command(0xF1);
ssd1306_command(0xDB); // SSD1306_SETVCOMDETECT
ssd1306_command(0x40);
ssd1306_command(0xA4); // 0xA4 // display all on resume
ssd1306_command(0xA6); // 0xA6 // normal display
ssd1306_command(0x2E); // deactivate scroll
ssd1306_command(0xAF); // --turn on oled panel
}
void renderBuffer(void)
{
ssd1306_command(0x21); // column address
ssd1306_command(0); // Column start address (0 = reset)
ssd1306_command(127); // Column end address (127
ssd1306_command(0x22); // page address
ssd1306_command(0x00); // Page start address (0 = reset)
ssd1306_command(7); // Page end address
int i;
for (i = 0; i < ( 128 * 64 / 8 ); i++)
{
ssd1306_byte( buffer[i] );
}
}
void clearBuffer(void)
{
memset( buffer, 0, ( 128 * 64 / 8 ) * sizeof( int ) );
}
void main()
{
init();
clearBuffer();
drawPixel( 10, 10, 1 );
renderBuffer();
}
Bisher funktioniert alles perfekt, wenn ich versuche, dasselbe mit i2c_master.c für mein ATMEGA zu tun: https://github.com/g4lvanix/rgbtime/tree/master/firmware
#ifndef F_CPU
#define F_CPU 8000000UL
#endif
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include "i2c_master.c"
#include "i2c_master.h"
#define SSD1306_ADDRESS 0x3C
void initDisplay()
{
i2c_start(SSD1306_ADDRESS);
i2c_write(0xAE); // 0xAE // display off
i2c_write(0xD5); // 0xD5 // set display clock division
i2c_write(0x80); // the suggested ratio 0x80
i2c_write(0xA8); // 0xA8 set multiplex
i2c_write(63); // set height
i2c_write(0xD3); // set display offset
i2c_write(0x0); // no offset
i2c_write(64); // line #0 setstartline
i2c_write(0x8D); // 0x8D // chargepump
i2c_write(0x14);
i2c_write(0x20); // memory mode
i2c_write(0x00); // 0x0 act like ks0108
i2c_write(161); // segremap
i2c_write(0xC8); // comscandec
i2c_write(0xDA); // 0xDA set com pins
i2c_write(0x12);
i2c_write(0x81); // 0x81 // set contract
i2c_write(0xCF);
i2c_write(0xD9); // 0xd9 set pre-charge
i2c_write(0xF1);
i2c_write(0xDB); // SSD1306_SETVCOMDETECT
i2c_write(0x40);
i2c_write(0xA4); // 0xA4 // display all on resume
i2c_write(0xA6); // 0xA6 // normal display
i2c_write(0x2E); // deactivate scroll
i2c_write(0xAF); // --turn on oled panel
i2c_stop();
}
void drawPixel()
{
i2c_start( SSD1306_ADDRESS );
i2c_write(0x21); // column address
i2c_write(0); // Column start address (0 = reset)
i2c_write(127); // Column end address (127
i2c_write(0x22); // page address
i2c_write(0x00); // Page start address (0 = reset)
i2c_write(7); // Page end address
int i;
int z=0;
for ( i = 0; i < ( 128 * 64 / 8 ); i++ )
{
if ( z == 0 )
{
i2c_write( 0xff );
z = 1;
}
else
{
i2c_write( 0x00 );
z = 0;
}
}
i2c_stop();
}
int main(void){
i2c_init();
initDisplay();
drawPixel();
return 0;
}
aber das Pixel wird nicht gezeichnet..
Ich verwende die folgenden Sicherungseinstellungen (ich verwende 8 MHz intern) lfuse:w:0xe2:m -U hfuse:w:0xd9:m
Ich habe folgende Hardwarekonfiguration:
-ATMEGA328P connected to a 4.0v power source
-ADC5 (SCL) connected to OLED's SCL (with additional line to VCC with 15k resistor in between)
-ADC4 (SDA) connected to OLED's SDA (with additional line to VCC with 15k resistor in between)
Ich habe auch versucht, Widerstände und die "zusätzliche vcc-Verbindung" zu entfernen, und sie genauso angeschlossen wie Himbeere, aber es hat keinen Unterschied gemacht. Irgendwelche Hinweise was ich falsch mache? Ich stecke seit Tagen daran fest. Danke!
Weitere Informationen:
(a) Bitte liefern Sie ein Foto der H/W.
(b) Bitte liefern Sie das Datenblatt für das OLED-Modul.
https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
(c) Haben Sie gemessen, ob das OLED-Modul I2C-Pullups eingebaut und aktiviert hat, oder sind Ihre 15k-Pullups die einzigen auf dem Bus?
Ich glaube, es gibt keine eingebauten Klimmzüge. Hier ein Zitat aus dem Datenblatt:
Both the data line (SDA) and the clock line (SCL) should be pulled up by external resistors
(d) Haben Sie Zugang zu einem Oszilloskop und Erfahrung damit?
Leider habe ich kein Oszilloskop...
(e) Wenn ja, können Sie Spuren bereitstellen, die den Anstieg und Abfall einer kleinen Stichprobe beider I2C-Signale zeigen?
-
(f) Obwohl in diesem Stadium weniger nützlich als ein Oszilloskop, haben Sie Zugang zu einem Logikanalysator, auch zu einem billigen?
Leider habe ich auch keinen Logikanalysator..
Das erste Byte, das in einer I2C-Transaktion übertragen wird, ist die Slave-Adresse (7 Bit) plus das einzelne Lese-/Schreibbit. Wenn die Slave-Adresse also 0x3c ist, wird als erstes Byte Folgendes übertragen:
0x78 // for starting a write transaction
0x79 // for starting a read transaction
Das ist 0x3c um 1 Bit verschoben plus - für Lesetransaktionen - das gesetzte LSB.
Die Raspberry-Bibliothek scheint dies automatisch zu handhaben. Die ATMEGA328P-Bibliothek scheint zu erfordern, dass Sie dies manuell tun.
Verwenden Sie also 0x78
als Adresse und versuchen Sie es erneut.
Aktualisieren
Es gibt zusätzliche Probleme in Ihrem Code: Sie müssen dem Display-Controller angeben, ob Sie Befehle (und Befehlsparameter) oder Daten senden. Grundsätzlich müssen Sie 0x80 vor jedem Befehlsbyte und 0x40 vor dem Senden von Daten voranstellen. Sobald Sie 0x40 verwenden, werden alle Bytes bis zur Stoppbedingung als Datenbytes behandelt. Siehe Abbildung 8-7 und Kapitel 8.1.5.2 lit. 5 im Datenblatt.
Deshalb gibt es ssd1306_command
und ssd1306_byte
im Raspberry-Code. (Offensichtlich funktioniert auch 0x00 anstelle von 0x80.) Sie müssen also 0x80 vor jedem Initialisierungsbyte sowie vor den Adressbefehlen beim Rendern eines Puffers hinzufügen. Und Sie müssen 0x40 hinzufügen, bevor Sie das erste Datenbyte senden.
Beachten Sie, dass Sie auch bei den Transaktionen einen Unterschied haben. (Eine Transaktion beginnt mit der START-Bedingung und endet mit der STOP-Bedingung.) Im Raspberry-Code wird jedes Befehlsbyte und jedes Datenbyte in einer separaten Transaktion gesendet. Im ATMEGA328P-Code kombinieren Sie es zu weit weniger, aber größeren Transaktionen. Beide Ansätze funktionieren. Letzteres ist effizienter. Dies kann für einige geringfügige Unterschiede wie 0x00 vs. 0x80 relevant sein.
Nur als Referenz: Sie finden meinen Referenzcode für das OLED-Display in Swift und C# . Es ist Testcode für Wirekite – eine Open-Source-Lösung zum Anschließen von I/Os einschließlich I2C an Ihren Mac oder PC mit einem kostengünstigen Teensy-Board und einem USB-Kabel. Der anwendungsspezifische Code wird dann auf Ihrem Mac oder PC ausgeführt; der Code auf dem Teensy steht fest.
Ich bin mir nicht ganz sicher, und da ich noch nicht das Privileg habe, einen Kommentar abzugeben,
Denken Sie nicht, dass Sie die TWI-Kommunikation nach jedem Befehl starten oder neu starten oder einen Stopp senden und einen Neustart veranlassen sollten?
Wenn das Ihre eigene Bibliothek ist, könnten Sie den TWI- oder I2C-Statuscode aus dem TWSR-Register lesen, und das würde Ihnen beim Debuggen dieses Problems helfen. Sie können wirklich überprüfen, welcher Teil Ihrer Anweisungen fehlschlägt, indem Sie mit dem im Datenblatt angegebenen Statuscode vergleichen.
EDIT: Keine Vermutungen mehr, der erste Teil der Antwort ist falsch. Nein, Sie brauchen kein Start- und Stoppbit, nachdem Sie gelesen und geschrieben haben. Um also einen Befehl an Ihren Chip zu senden, müssen Sie 16-Bit-Daten senden, von denen die ersten 8 Bits von MSB bestimmen, ob Sie die Daten oder den Befehl senden. Machst du das?
Der einfachste Weg zur Portierung besteht darin, eine eigene Version von WiringPiI2CWriteReg8 und die entsprechende Setup-Funktion zu schreiben.
Kompilieren Sie neu und Sie sollten bereit sein zu gehen.
SamGibson
0x29a
SamGibson
SamGibson
0x29a
SamGibson
0x29a
0x29a
0x29a
Wolodymyr Smotesko