What if you alter the background color from BLUE to something else, does this happen as expected?
Can you add some code to push the BMP data down Serial
too, just to see if this looks OKish?
Have you checked that the bit depth of the BMP is 24?
If not, this should be flagged by a Serial.print()
output - AFAIK (remember).
So this is what I found so far:
My OLED is responsive BEFORE the “if(!SD.begin(cs_sd))”. Before that line, I can change the screen color multiple times no problem; but after, tft.fillScreen() no longer does anything. This seems very similar to Mike’s original problem
As for the pic, I’m using both the lily128.bmp (176x176) and another I found that is128x128. Both are 24bit are being read fine!
Also, the reading/writing portion of the code seems to be okay. I did a Serial.print() of the r, g, b values and they are within range and changing at each read, so I believe the problem lies in tft functions no longer working after the SD begins.
Thanks again for all the help!
Oh,here are both initializing lines:
Adafruit_SSD1351 tft = Adafruit_SSD1351(cs_oled, dc, rst); // HARDWARE SPI
if (!SD.begin(cs_sd)){ // HARDWARE SPI
@jahydin, looking at the SSD1351 display library from mikeseeh, the SPI is set to MODE3 while the SD library requires SPI MODE0. There is your conflict.
There is no simple way to fix this and this is a common problem with SPI. It is possible to switch modes on the fly but that tends to “glitch” the SPI bus causing problems.
The SSD1351 library only writes to the display, it does not read anything. You could use the software SPI mode for the display using only two extra pins for CLK and MOSI. The display CS line could be held low all the time since it is no longer on the common hardware SPI bus.
Oh no!
Okay, I changed my tft init to :
Adafruit_SSD1351 tft = Adafruit_SSD1351(cs_oled, dc, mosi, sclk, rst); // SOFTWARE SPI
How do I hold the display line low?
@jahydin, for now just use the cs_oled pin you have presently connected. You will need to specify different pins for mosi and sclk since they will conflict with the hardware SPI pins.
So here is my new pin setup:
#define sclk A0
#define mosi A1
#define miso A4
#define dc D7
#define cs_oled D6
#define cs_sd A2
#define rst D5
I have pins A5 and A3 still connected to sclk and mosi (for SD right?)
I also have pins A0 and A1 connected to sclk and mosi (for oled)
But now SD card failing to initialize.
@jahydin, here is the pin assignment as I believe it should be:
For microSD
------------
A5 = MOSI
A4 = MISO
A3 = SCLK
A2 = cs_sd
For OLED
------------
A1 = MOSI
A0 = SCLK
D7 = dc
D6 = cs_oled
D5 = rst
If you can, use a different pin for the OLED dc
pin since that pin has the onboard LED connected to it as well. The Adafruit constructor call should be:
Adafruit_SSD1351 tft = Adafruit_SSD1351(cs_oled, dc, A1, A0, rst); // SOFTWARE SPI
So I have:
With this config my screen works but SD fails to initialize.
Thanks for taking the time to help me btw. I really want to be able to use this screen for my Photon.
Well I finally got it to work, but it is far from stable. I’d say it works half the time, and only when I unplug my battery and pop it back in.The other times it will get to about half way than slow to a crawl then garble. Reset seems to cause the SD to fail init every time. I ordered a separate sd card reader in the hopes it will be better than the “all in one” package. Was really hoping to get this work though. ;(
Anyways, here is my code that got it to work:
#pragma SPARK_NO_PREPROCESSOR
#include "Particle.h"
#include "Adafruit_mfGFX.h"
#include "Adafruit_SSD1351.h"
#include "SdFat.h"
// SPI interface, these are the pins
#define sclk A3
#define mosi A5
#define miso A4
#define dc D4
#define cs_oled D6
#define cs_sd A2
#define rst D5
const uint8_t chipSelect = cs_sd;
// Color definitions
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
// for read16 and read32 to work properly
void bmpDraw(char *filename, uint8_t x, uint8_t y);
uint16_t read16(File &f);
uint32_t read32(File &f);
// instanciate an SD object
SdFat SD;
Adafruit_SSD1351 tft = Adafruit_SSD1351(cs_oled, dc, rst); // HARDWARE SPI
//Adafruit_SSD1351 tft = Adafruit_SSD1351(cs_oled, dc, mosi, sclk, rst); // SOFTWARE SPI
File bmpFile;
int bmpWidth, bmpHeight;
uint8_t bmpDepth, bmpImageoffset;
void setup(void) {
Serial.begin(9600);
//while (!Serial.available());
// initialize the OLED
Serial.println("init");
tft.begin();
//tft.setRotation(0);
tft.fillScreen(BLUE); //make it colorful
//delay(1000);
// initialize the SD Card
Serial.print("Initializing SD card...");
//if (!SD.begin(mosi, miso, sclk, cs_sd)) { // SOFTWARE SPI
if (!SD.begin(chipSelect, SPI_HALF_SPEED)){ // HARDWARE SPI
Serial.println("failed!");
return;
}
Serial.println("SD OK!");
// draw the image from the card
bmpDraw("logo.bmp", 0, 0);
}
void loop() {
}
// Code from https://github.com/adafruit/Adafruit-SSD1351-library/blob/master/examples/bmp/bmp.ino
// This function opens a Windows Bitmap (BMP) file and
// displays it at the given coordinates. It's sped up
// by reading many pixels worth of data at a time
// (rather than pixel by pixel). Increasing the buffer
// size takes more of the Arduino's precious RAM but
// makes loading a little faster. 20 pixels seems a
// good balance.
#define BUFFPIXEL 50
void bmpDraw(char *filename, uint8_t x, uint8_t y) {
File bmpFile;
int bmpWidth, bmpHeight; // W+H in pixels
uint8_t bmpDepth; // Bit depth (currently must be 24)
uint32_t bmpImageoffset; // Start of image data in file
uint32_t rowSize; // Not always = bmpWidth; may have padding
uint8_t sdbuffer[3*BUFFPIXEL]; // pixel buffer (R+G+B per pixel)
uint8_t buffidx = sizeof(sdbuffer); // Current position in sdbuffer
boolean goodBmp = false; // Set to true on valid header parse
boolean flip = true; // BMP is stored bottom-to-top
int w, h, row, col;
uint8_t r, g, b;
uint32_t pos = 0, startTime = millis();
if((x >= tft.width()) || (y >= tft.height())) return;
Serial.println();
Serial.print("Loading image '");
Serial.print(filename);
Serial.println('\'');
// Open requested file on SD card
if ((bmpFile = SD.open(filename)) == NULL) {
Serial.print("File not found");
return;
}
// Parse BMP header
if(read16(bmpFile) == 0x4D42) { // BMP signature
Serial.print("File size: "); Serial.println(read32(bmpFile));
(void)read32(bmpFile); // Read & ignore creator bytes
bmpImageoffset = read32(bmpFile); // Start of image data
Serial.print("Image Offset: "); Serial.println(bmpImageoffset, DEC);
// Read DIB header
Serial.print("Header size: "); Serial.println(read32(bmpFile));
bmpWidth = read32(bmpFile);
bmpHeight = read32(bmpFile);
if(read16(bmpFile) == 1) { // # planes -- must be '1'
bmpDepth = read16(bmpFile); // bits per pixel
Serial.print("Bit Depth: "); Serial.println(bmpDepth);
if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed
goodBmp = true; // Supported BMP format -- proceed!
Serial.print("Image size: ");
Serial.print(bmpWidth);
Serial.print('x');
Serial.println(bmpHeight);
// BMP rows are padded (if needed) to 4-byte boundary
rowSize = (bmpWidth * 3 + 3) & ~3;
// If bmpHeight is negative, image is in top-down order.
// This is not canon but has been observed in the wild.
if(bmpHeight < 0) {
bmpHeight = -bmpHeight;
flip = false;
}
// Crop area to be loaded
w = bmpWidth;
h = bmpHeight;
if((x+w-1) >= tft.width()) w = tft.width() - x;
if((y+h-1) >= tft.height()) h = tft.height() - y;
for (row=0; row<h; row++) { // For each scanline...
tft.goTo(x, y+row);
// Seek to start of scan line. It might seem labor-
// intensive to be doing this on every line, but this
// method covers a lot of gritty details like cropping
// and scanline padding. Also, the seek only takes
// place if the file position actually needs to change
// (avoids a lot of cluster math in SD library).
if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
else // Bitmap is stored top-to-bottom
pos = bmpImageoffset + row * rowSize;
if(bmpFile.position() != pos) { // Need seek?
bmpFile.seek(pos);
buffidx = sizeof(sdbuffer); // Force buffer reload
}
// optimize by setting pins now
for (col=0; col<w; col++) { // For each pixel...
// Time to read more pixel data?
if (buffidx >= sizeof(sdbuffer)) { // Indeed
bmpFile.read(sdbuffer, sizeof(sdbuffer));
buffidx = 0; // Set index to beginning
}
// Convert pixel from BMP to TFT format, push to display
b = sdbuffer[buffidx++];
g = sdbuffer[buffidx++];
r = sdbuffer[buffidx++];
tft.drawPixel(x+col, y+row, tft.Color565(r,g,b));
Serial.print(".");
// optimized!
//tft.pushColor(tft.Color565(r,g,b));
} // end pixel
} // end scanline
Serial.print("Loaded in ");
Serial.print(millis() - startTime);
Serial.println(" ms");
} // end goodBmp
}
}
bmpFile.close();
if(!goodBmp) Serial.println("BMP format not recognized.");
}
// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.
uint16_t read16(File &f) {
uint16_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read(); // MSB
return result;
}
uint32_t read32(File &f) {
uint32_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read();
((uint8_t *)&result)[2] = f.read();
((uint8_t *)&result)[3] = f.read(); // MSB
return result;
}
Reformatted by ScruffR
To get this just wrap your code in
```cpp
// your code
Sorry about the format. I have no idea how to get it in a nice window like everyone else…
Not sure if this would make a difference, but try
// either use
SYSTEM_THREAD(ENABLED)
// or
SYSTEM_MODE(SEMI_AUTOMATIC)
// and call Particle.connect() after you're done
// or use both ST & SM together
An ext card reader shouldn’t be required and might not help anyway.
Seemed about the same with STSTEM_THREAD, and I thought I was at 100% success (even with resets) using the SEMI_AUTOMATIC mode, but it stopped working consistantly after a a few resets or so. Thanks!
Which power supply do you use?
I had similar problems some time ago and after replacing the power supply it worked.
OMG, lol. Yup, that’s it. I’ve been using a little Li-ion 850mAh. I was using my usb for power (and the serial prompts) in the beginning, but that’s when I was getting my code sorted out (the SPI_HALF_SPEED is critical). Once I didn’t need the serial prompts I switched to my battery for convenience, got the code working, but never thought my battery could be the reason things were erratic.
Happy to say everything is running perfectly now. Multiple restarts and flashes and it draws the entire thing within a second!
Thanks to everyone who took the time to help me out, and especially you for putting this tutorial up to begin with.
Oh… so what power source did you end up using…?
BTW, I just bundled up @MikSeeH’s code with a spark.json and slightly tweaked demos from Adafruit and published it to particle build as Adafruit_1351_Photon. So you can now include it via the Libraries tab in the Web IDE and get a quick start from the example code.
I didn’t (yet) manage to get the SD card working though, so if anyone here would like to help add that and add in a second example, it would be much appreciated. My copy is at https://github.com/nfriedly/Adafruit_SSD1351_Photon
@nfriedly your test code is a huge help, right now I am trying to get the SDFat library working, but when I try to include it, it always gives this error:
If you are using Dev (as I assume by your screenshot) you’d need to have the library files locally stored in your project folder and #include "SdFat.h"
I’d say.
But actually I’d rather do this in Build since SdFat
is quite an elaborate library to include locally