Arduino 1.8.3

Status
Not open for further replies.

PaulStoffregen

Well-known member
Looks like Arduino.cc will release version 1.8.3 today. I preparing Teensyduino 1.37-beta1 now to support it.

Relatively little has changed since 1.8.2, so I'm not expecting any issues. Still, here's a thread to discuss Arduino 1.8.3...
 
Looks like Arduino.cc will release version 1.8.3 today. I preparing Teensyduino 1.37-beta1 now to support it.

Relatively little has changed since 1.8.2, so I'm not expecting any issues. Still, here's a thread to discuss Arduino 1.8.3...

I think the link is missing.
 
Hi Paul,

My guess is you will need to get this release out soon, so probably won't have chance to try out the SPI changes?

Also may want to decide how much of it makes sense to include.
 
Yeah, 1.37-beta1 is going to have everything as it is right now.

My hope is to resolve this issue ASAP, but it can wait for beta2.

Will look at the SPI class stuff over the next few days. Looks like Arduino isn't going to make any API level decisions, so let's discuss this after beta1 and get something into beta2.
 
Yep - It is a bug with LSBFirst. Made comments on other thread. Could fix by checking LSBFIRST flag and packing/unpacking different or could revert to not packing at all and only output 8 bits per FIFO queue entry. A bit slower but less complex
 
Definitely will need testing!

Arduino.cc still hasn't put 1.8.3 on their website yet. I've never seen them delay this long between github to an official release.

On the plus side, it gives me a bit more time to merge this and do other stuff. My plan is to put 1.37-beta1 up as soon as they release 1.8.3 on their website. We'll probably have 3-4 days of merging stuff that adds features, with 1.37-beta2 sometime early next week. As usual, I'd like to freeze features after beta2 and focus on testing and fixes on beta3. The goal is a final 1.37 release 2 weeks after Arduino releases.
 
Can we switch to 'gnu++14'?

I'm trying to find any info about possible risks. Any ideas?

All the info I've found looks like C++14 is a pretty minor change from C++11. Also seems like gcc 5.4 should have fairly mature support. Maybe?

https://gcc.gnu.org/projects/cxx-status.html#cxx14

My main concern is the possibility of libraries which compile under C++11 (which Arduino is now using for AVR) but don't compile under C++14 for *any* reason. The Arduino world is filled with shoddy code, often written by people who never enable warnings.
 
I'm trying to find any info about possible risks. Any ideas?
They are very minor. A good discussion is here:
https://stackoverflow.com/questions...can-potentially-break-a-program-written-in-c1

Also seems like gcc 5.4 should have fairly mature support.
GCC 5.4 has very mature support. A lot of the features were already there in 4.9, the rest in 5.0.

All the info I've found looks like C++14 is a pretty minor change from C++11.

The relaxed constexpr rules are a pretty major convenience. It's now possible to write readable compile time computation code. E.g. to create a lookup table at compile time (it ends up in flash):

Code:
template<class T, size_t N>
struct array {
    T elems[N];
    constexpr T& operator[](size_t i)     {
        return elems[i];
    }
    constexpr const T& operator[](size_t i) const {
        return elems[i];
    }
    static constexpr size_t size() { return N; }
};

template <size_t size>
constexpr auto makeLut(){
    array<float , size > result {};
    for (size_t i = 0; i < size; ++i) {
        result[i] = sin(i) * i;
    }
    return result ;
}

constexpr auto my_lut = makeLut<100>();

auto returnLutValue(size_t i) {
    return my_lut[i];
}

Some compile time checking:
Code:
constexpr array<uint8_t, 10> ftm_pins = { 3, 4, 5, 6, 9, 10, 20, 21, 22, 23 };

constexpr bool isFtmPin(uint8_t pin_nr) {
    for(size_t i = 0; i < ftm_pins.size(); i++) 
        if(ftm_pins[i] == pin_nr) return true;
    return false;
}

constexpr uint8_t my_pin = 3;
static_assert(isFtmPin(my_pin), "Pin is not an FTM timer pin.");

// this would be a compile error:
// static_assert(isFtmPin(1), "Pin is not an FTM timer pin.");

The tables can actually be optimized away. If all usages can be resolved at compile time, they will be eliminated.
 
Last edited:
I have been using Teensyview displays (SSD1306) with a version of the Sparkfun library:
https://github.com/KurtE/SparkFun_TeensyView_Arduino_Library/tree/Multiple-SPI

But it uses some of the proposed stuff like SPI transfer with buffer and the async... Although I think the async is easy not to use.
One test program for that is:
Code:
/******************************************************************************
TeensyView_Demo.ino
SFE_TeensyView Library Demo
Jim Lindblom @ SparkFun Electronics
Original Creation Date: October 27, 2014
Modified Febuary 2, 2017
This sketch uses the TeensyView library to draw a 3-D projected
cube, and rotate it along all three axes.
Development environment specifics:
Arduino IDE 1.6.12 w/ Teensyduino 1.31
Arduino IDE 1.8.1 w/ Teensyduino 1.35
TeensyView v1.0
This code is beerware; if you see me (or any other SparkFun employee) at thewa
local, and you've found our code helpful, please buy us a round!
Distributed as-is; no warranty is given.
******************************************************************************/
#include <SPI.h>
#ifndef SPI_INTERFACES_COUNT
#define SPI_INTERFACES_COUNT 1
#endif
#include <TeensyView.h> // Include the SFE_TeensyView library
// /////////////////////////////////
// TeensyView Object Declaration //
// /////////////////////////////////
// #define TLC_SPI1
// #define T36_SPI1
// #define T36_SPI2
// #define DEFAULT_PINS

#ifdef DEFAULT_PINS
#define PIN_RESET 15
#define PIN_DC 5
#define PIN_CS 10
#define PIN_SCK 13
#define PIN_MOSI 11
#endif

#ifdef TLC_SPI1
#define PIN_RESET 15
#define PIN_SCK 20
#define PIN_MOSI 21
#define PIN_DC 4
#define PIN_CS 3
#endif

#ifdef T36_SPI1
#define PIN_RESET 15
#define PIN_SCK 20
#define PIN_MOSI 21
#define PIN_DC 31
#define PIN_CS 32
#endif

#ifdef T36_SPI2
#define PIN_RESET 15
#define PIN_SCK 53
#define PIN_MOSI 52
#define PIN_DC 55
#define PIN_CS 51
#endif

// Kurt's setup

#ifndef PIN_SCK

#if defined(KINETISK)
#define PIN_RESET 15
#define PIN_SCK 13
#define PIN_MOSI 11
#define PIN_DC 21
#define PIN_CS 20
// Setup 2nd one SPI1
#define PIN_RESET1 16
#define PIN_SCK1 32
#define PIN_MOSI1 0
#define PIN_DC1 31
#define PIN_CS1 30

#elif defined(KINETISL)  // Teensy LC
// For multiple need to change CS to not 20 as only valid sck1 on lc
#define PIN_RESET 15
#define PIN_SCK 13
#define PIN_MOSI 11
#define PIN_DC 21
#define PIN_CS 22
// #undef SPI_INTERFACES_COUNT
// #define SPI_INTERFACES_COUNT 1
#define PIN_RESET1 16
#define PIN_SCK1 20
#define PIN_MOSI1 0
#define PIN_DC1 6
#define PIN_CS1 7
#else // AVR... T2 SS(0), SCK(1), MOSI(2), MISO(3)
#define PIN_RESET 5
#define PIN_SCK 13
#define PIN_MOSI 11
#define PIN_DC 4
#define PIN_CS 0
#endif

// Pins on connector on Beta T3.6 board (3.3, GND)(48, 47)(57 56) (51 52) (53 55)
#define PIN_RESET2 48
// #define PIN_MISO2 51
#define PIN_MOSI2 52
#define PIN_SCK2 53
#define PIN_DC2 55
#define PIN_CS2 56
#endif

TeensyView32 oled(PIN_RESET, PIN_DC, PIN_CS, PIN_SCK, PIN_MOSI);

#if SPI_INTERFACES_COUNT > 1
TeensyView64 oled1(PIN_RESET1, PIN_DC1, PIN_CS1, PIN_SCK1, PIN_MOSI1);

    #if SPI_INTERFACES_COUNT > 2
TeensyView32 oled2(PIN_RESET2, PIN_DC2, PIN_CS2, PIN_SCK2, PIN_MOSI2);
    #endif

#endif


TeensyView * oleds[] =
{
#if 0
    & oled,
    & oled1
#else  
    & oled

#if SPI_INTERFACES_COUNT > 1
    , & oled1

    #if SPI_INTERFACES_COUNT > 2
    , & oled2
    #endif
#endif
#endif

};

uint8_t oled_which_test[] =
{
    0, 0, 0
};

uint16_t test_iterations_left[] =
{
    0xffff, 0xffff, 0xffff
};

uint32_t last_test_start_time[] =
{
    0, 0, 0
};

uint32_t next_test_start_time[] =
{
    0, 0, 0
};

extern void testRects(TeensyView * _oled, bool draw_async, uint16_t & iterations_left);
extern void testCircles(TeensyView * _oled, bool draw_async, uint16_t & iterations_left);
extern void TestpixelsAsync(TeensyView * _oled, uint16_t & iterations_left);
extern void TestFillRects(TeensyView * _oled, uint16_t & iterations_left);
extern void testdrawline(TeensyView * _oled, uint16_t & iterations_left, uint32_t & next_test_start_time);
extern void testscrolltext(TeensyView * _oled, uint16_t & iterations_left, uint32_t & next_test_start_time);
void setup()
{
    pinMode(2, OUTPUT);
    digitalWrite(2, LOW);
    pinMode(3, OUTPUT);
    digitalWrite(3, LOW);
    while (!Serial && millis() < 3000);
    Serial.begin(38400);
    oled.begin(); // Initialize the OLED
    oled.display(); // Display what's in the buffer (splashscreen)

#if SPI_INTERFACES_COUNT > 1
    oled1.begin(); // Initialize the OLED
    oled1.display(); // Display what's in the buffer (splashscreen)''
    Serial.println("oled1 displayed");
#endif

#if SPI_INTERFACES_COUNT > 2
    oled2.begin(); // Initialize the OLED
    oled2.display(); // Display what's in the buffer (splashscreen)''
    Serial.println("oled2 displayed");
#endif

    delay(1000); // Delay 1000 ms
    oled.clear(HARDWARE_MEM); // Clear the buffer.

#if SPI_INTERFACES_COUNT > 1
    oled1.clear(HARDWARE_MEM); // Clear the buffer.
#endif

#if SPI_INTERFACES_COUNT > 2
    oled2.clear(HARDWARE_MEM);
#endif
#ifdef A1
    randomSeed(analogRead(A0) + analogRead(A1));
#else
   randomSeed(analogRead(A0) + (analogRead(A0)<<1));
#endif    
}

void loop()
{
    // Lets see which of our displays is ready to display something different
    for (uint8_t i = 0; i < sizeof(oleds) /sizeof(oleds[0]); i++)
    {
        if ((millis() > next_test_start_time[i]) && !oleds[i] -> displayAsyncActive())
        {
            last_test_start_time[i] = millis();
            switch (oled_which_test[i])
            {
                case 0:
                    testRects(oleds[i], true, test_iterations_left[i]);
                    break;
                case 1:
                    testRects(oleds[i], true, test_iterations_left[i]);
                    break;
                case 2:
                    TestpixelsAsync(oleds[i], test_iterations_left[i]);
                    break;
                case 3:
                    TestFillRects(oleds[i], test_iterations_left[i]);
                    break;
                case 4:
                    testdrawline(oleds[i], test_iterations_left[i], next_test_start_time[i]);
                    break;
                case 5:
                    testscrolltext(oleds[i], test_iterations_left[i], next_test_start_time[i]);
                    break;
            }
            if (test_iterations_left[i] == 0)
            {
                oled_which_test[i] ++;
                if (oled_which_test[i] > 5)
                    oled_which_test[i] = 0;
                test_iterations_left[i] = 0xffff; // mark it special for first call
                next_test_start_time[i] = millis() + 100;
            }
        }
        else
            if ((millis() - last_test_start_time[i]) > 2500)
            {
                Serial.printf("Oled %d hung test: %d iter: %d\n ", i, oled_which_test[i], test_iterations_left[i]);
                printDebugInfo(oleds[i]);
                last_test_start_time[i] = millis();
            }
    }
}

void printDebugInfo(TeensyView * _oled)
{

#ifdef KINETISK

  #ifdef SPI_DEBUG_ASYNC_T3X
    extern void dumpDMA_TCD(const char * psz, DMABaseClass * dmabc);
    dumpDMA_TCD("TX:", _oled -> _spi -> _dmaTX);
    dumpDMA_TCD("RX:", _oled -> _spi -> _dmaRX);
  #endif  
#else

        #ifdef SPI_DEBUG_ASYNC_LC
    extern void dumpDMA_CFG(const char * sz, DMABaseClass * dmabc);
    dumpDMA_CFG("TX:", _oled -> _spi -> _dmaTX);
    dumpDMA_CFG("RX:", _oled -> _spi -> _dmaRX);
        #endif

#endif

}

void testRects(TeensyView * _oled, bool draw_async, uint16_t & iterations_left)
{
    int n, i, i2;
    int cx = _oled -> getLCDWidth() / 2;
    int cy = _oled -> getLCDHeight() / 2;
    _oled -> clear(PAGE);
    n = min(_oled -> getLCDWidth(), _oled -> getLCDHeight());
    for (i = 2; i < n; i += 6)
    {
        i2 = i / 2;
        _oled -> rect(cx - i2, cy - i2, i, i);
    }
    if (draw_async)
    {
        _oled -> displayAsync();
    }
    else
    {
        _oled -> display();
    }
    iterations_left = 0;
}

void testCircles(TeensyView * _oled, bool draw_async, uint16_t & iterations_left)
{
    uint16_t radius = 10;
    int x, y, r2 = radius * 2,
    w = _oled -> getLCDWidth() + radius,
    h = _oled -> getLCDHeight() + radius;
    _oled -> clear(PAGE);
    for (x = 0; x < w; x += r2)
    {
        for (y = 0; y < h; y += r2)
        {
            _oled -> circle(x, y, radius);
        }
    }
    if (draw_async)
    {
        _oled -> displayAsync();
    }
    else
    {
        _oled -> display();
    }
    iterations_left = 0;
}

void TestpixelsAsync(TeensyView * _oled, uint16_t & iterations_left)
{
    if (iterations_left == 0xffff)
    {
        _oled -> clear(PAGE);
        iterations_left = 1024;
    }
    _oled -> pixel(random(_oled -> getLCDWidth()), random(_oled -> getLCDHeight()));
    _oled -> displayAsync();
    iterations_left--;
}

void TestFillRects(TeensyView * _oled, uint16_t & iterations_left)
{
    if (iterations_left == 0xffff)
    {
        _oled -> clear(PAGE);
        iterations_left = 0; // Not really, but makes it simple as we will update
    }
    _oled -> rectFill(iterations_left, iterations_left,
    _oled -> getLCDWidth() - iterations_left * 2, _oled -> getLCDHeight() - iterations_left * 2,(iterations_left & 1)? 0:1, NORM);
    _oled -> displayAsync();
    iterations_left += 3;
    if (iterations_left >= _oled -> getLCDHeight() /2)
    {
        iterations_left = 0; // we are done.
    }
}

void testdrawline(TeensyView * _oled, uint16_t & iterations_left, uint32_t & next_test_start_time)
{
    // Serial.printf("testDrawline %x %x\n", _oled, iterations_left);
    if (iterations_left == 0xffff)
    {
        _oled -> clear(PAGE);
        iterations_left = 0; // Not really, but makes it simple as we will update
    }
    uint8_t line_test = iterations_left >> 8;
    uint8_t i = iterations_left & 0xff;
    switch (line_test)
    {
        case 0:
            _oled -> line(0, 0, i, _oled -> getLCDHeight() - 1);
            _oled -> displayAsync();
            i += 4;
            if (i >= _oled -> getLCDWidth())
            {
                i = 0;
                line_test++;
            }
            break;
        case 1:
            _oled -> line(0, 0, _oled -> getLCDWidth() - 1, i);
            _oled -> displayAsync();
            i += 4;
            if (i >= _oled -> getLCDHeight())
            {
                i = 0;
                line_test++;
                next_test_start_time = millis() + 250;
            }
        case 2:
            if (i == 0)
            {
                _oled -> clear(PAGE);
            }
            _oled -> line(0, _oled -> getLCDHeight() - 1, i, 0);
            _oled -> displayAsync();
            i += 4;
            if (i >= _oled -> getLCDWidth())
            {
                i = 0;
                line_test++;
            }
            break;
        case 3:
            _oled -> line(0, _oled -> getLCDHeight() - 1, _oled -> getLCDWidth() - 1, i);
            _oled -> displayAsync();
            i += 4;
            if (i >= _oled -> getLCDHeight())
            {
                i = 0;
                line_test++;
                next_test_start_time = millis() + 250;
            }
            break;
        case 4:
            if (i == 0)
            {
                _oled -> clear(PAGE);
            }
            _oled -> line(_oled -> getLCDWidth() - 1, _oled -> getLCDHeight() - 1, i, 0);
            _oled -> displayAsync();
            i += 4;
            if (i >= _oled -> getLCDWidth())
            {
                i = 0;
                line_test++;
            }
            break;
        case 5:
            _oled -> line(_oled -> getLCDWidth() - 1, _oled -> getLCDHeight() - 1, 0, i);
            _oled -> displayAsync();
            i += 4;
            if (i >= _oled -> getLCDHeight())
            {
                i = 0;
                line_test++;
                next_test_start_time = millis() + 250;
            }
            break;
        case 6:
            if (i == 0)
            {
                _oled -> clear(PAGE);
            }
            _oled -> line(_oled -> getLCDWidth() - 1, 0, 0, i);
            _oled -> displayAsync();
            i += 4;
            if (i >= _oled -> getLCDHeight())
            {
                i = 0;
                line_test++;
            }
            break;
        case 7:
            _oled -> line(_oled -> getLCDWidth() - 1, 0, i, _oled -> getLCDHeight() - 1);
            _oled -> displayAsync();
            i += 4;
            if (i >= _oled -> getLCDWidth())
            {
                i = 0;
                line_test = 0; // Say we are done
            }
    }
    iterations_left = (line_test << 8) | i;
}

void testscrolltext(TeensyView * _oled, uint16_t & iterations_left, uint32_t & next_test_start_time)
{
    if (iterations_left == 0xffff)
    {
        int middleX = _oled -> getLCDWidth() / 2;
        int middleY = _oled -> getLCDHeight() / 2;
        _oled -> clear(PAGE);
        _oled -> setFontType(1);
        // Try to set the cursor in the middle of the screen
        _oled -> setCursor(middleX - (_oled -> getFontWidth() * (6 / 2)),
        middleY - (_oled -> getFontWidth() / 2));
        // Print the title:
        _oled -> print("Scroll");
        _oled -> displayAsync();
        iterations_left = 1; // Not really, but makes it simple as we will update
        return;
    }
    switch (iterations_left)
    {
        case 1:
            _oled -> scrollRight(0x00, 0x0f, true);
            next_test_start_time = millis() + 2000;
            break;
        case 2:
            _oled -> scrollStop();
            next_test_start_time = millis() + 1000;
            break;
        case 3:
            _oled -> scrollLeft(0x00, 0x0f, true);
            next_test_start_time = millis() + 2000;
            break;
        case 4:
            _oled -> scrollStop();
            next_test_start_time = millis() + 1000;
            break;
        case 5:
            _oled -> scrollVertRight(0x00, 0x07, true);
            next_test_start_time = millis() + 2000;
            break;
        case 6:
            _oled -> scrollVertLeft(0x00, 0x07, true);
            next_test_start_time = millis() + 2000;
            break;
        case 7:
            _oled -> scrollStop();
            next_test_start_time = millis() + 50;
            break;
    }
    if (++iterations_left > 7)
        iterations_left = 0;
}


I have also been testing with ILI9341 code, which I have tried to make a version that uses all of the new stuff as to try to verify it helps and test things out. I included a zip file of it here. I updated graphic test to work with it... Plus another example test program:
Code:
#include <ILI9341_t3ns_font_Arial.h>
#include <ILI9341_t3ns_font_ArialBold.h>
#include <ILI9341_t3ns.h>

#include "SPI.h"
//#define KURTS_FLEXI
#ifdef KURTS_FLEXI
#define TFT_DC 22
#define TFT_CS 15
#define TFT_RST -1
#define TFT_SCK 14
#define TFT_MISO 12
#define TFT_MOSI 7
#define DEBUG_PIN 13
#else
#define TFT_DC  9
#define TFT_CS 10
#define TFT_RST 7
#define TFT_SCK 13
#define TFT_MISO 12
#define TFT_MOSI 11
#endif
ILI9341_t3ns tft = ILI9341_t3ns(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCK, TFT_MISO);
Adafruit_GFX_Button button;
uint8_t use_dma = 0;
uint8_t use_clip_rect = 0;
uint8_t use_set_origin = 0;
uint8_t use_fb = 0;

#define ORIGIN_TEST_X 50
#define ORIGIN_TEST_Y 50

void setup() {
  while (!Serial && (millis() < 4000)) ;
  Serial.begin(115200);


  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(ILI9341_BLACK);

  // read diagnostics (optional but can help debug problems)
  uint8_t x = tft.readcommand8(ILI9341_RDMODE);
  Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDMADCTL);
  Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDPIXFMT);
  Serial.print("Pixel Format: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDIMGFMT);
  Serial.print("Image Format: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDSELFDIAG);
  Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX);
#ifdef DEBUG_PIN
  pinMode(DEBUG_PIN, OUTPUT);
#endif

  button.initButton(&tft, 200, 125, 100, 40, ILI9341_GREEN, ILI9341_YELLOW, ILI9341_RED, "UP", 1);

  drawTestScreen();
}

void SetupOrClearClipRectAndOffsets() {
  if (use_clip_rect) {
    tft.setClipRect();  // make sure we clear the whole screen
    tft.setOrigin();    // make sure none are set yet

    tft.fillScreen(ILI9341_LIGHTGREY);

    // Now lets set origin.
    if (use_set_origin)
      tft.setOrigin(ORIGIN_TEST_X, ORIGIN_TEST_Y);
    int x = tft.width() / 4;
    int y = tft.height() / 4;
    int w = tft.width() / 2;
    int h = tft.height() / 2;
    tft.drawRect(x, y, w, h, ILI9341_ORANGE);
    tft.updateScreen();
    tft.setClipRect(x + 1, y + 1, w - 2, h - 2);
    delay(250);

  } else {
    tft.setClipRect();
    if (use_set_origin)
      tft.setOrigin(ORIGIN_TEST_X, ORIGIN_TEST_Y);
    else
      tft.setOrigin();
  }
}


uint16_t palette[256];  // Should probably be 256, but I don't use many colors...
uint16_t pixel_data[2500];
const uint8_t pict1bpp[] = {0xff, 0xff, 0xc0, 0x03, 0xa0, 0x05, 0x90, 0x9, 0x88, 0x11, 0x84, 0x21, 0x82, 0x41, 0x81, 0x81,
                            0x81, 0x81, 0x82, 0x41, 0x84, 0x21, 0x88, 0x11, 0x90, 0x09, 0xa0, 0x05, 0xc0, 0x03, 0xff, 0xff
                           };
const uint8_t pict2bpp[] = {
  0x00, 0x00, 0x55, 0x55, 0xaa, 0xaa, 0xff, 0xff,
  0x00, 0x00, 0x55, 0x55, 0xaa, 0xaa, 0xff, 0xff,
  0x55, 0x55, 0xaa, 0xaa, 0xff, 0xff, 0x00, 0x00, 
  0x55, 0x55, 0xaa, 0xaa, 0xff, 0xff, 0x00, 0x00, 
  0xaa, 0xaa, 0xff, 0xff, 0x00, 0x00, 0x55, 0x55, 
  0xaa, 0xaa, 0xff, 0xff, 0x00, 0x00, 0x55, 0x55, 
  0xff, 0xff, 0x00, 0x00, 0x55, 0x55, 0xaa, 0xaa, 
  0xff, 0xff, 0x00, 0x00, 0x55, 0x55, 0xaa, 0xaa, 
  0x00, 0x00, 0x55, 0x55, 0xaa, 0xaa, 0xff, 0xff,
  0x00, 0x00, 0x55, 0x55, 0xaa, 0xaa, 0xff, 0xff,
  0x55, 0x55, 0xaa, 0xaa, 0xff, 0xff, 0x00, 0x00, 
  0x55, 0x55, 0xaa, 0xaa, 0xff, 0xff, 0x00, 0x00, 
  0xaa, 0xaa, 0xff, 0xff, 0x00, 0x00, 0x55, 0x55, 
  0xaa, 0xaa, 0xff, 0xff, 0x00, 0x00, 0x55, 0x55, 
  0xff, 0xff, 0x00, 0x00, 0x55, 0x55, 0xaa, 0xaa, 
  0xff, 0xff, 0x00, 0x00, 0x55, 0x55, 0xaa, 0xaa, 
};
const uint8_t pict4bpp[] = {  
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00,  
  0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00,  
  0x00, 0x11, 0x22, 0x22, 0x22, 0x22, 0x11, 0x00,
  0x00, 0x11, 0x22, 0x22, 0x22, 0x22, 0x11, 0x00,
  0x00, 0x11, 0x22, 0x33, 0x33, 0x22, 0x11, 0x00,
  0x00, 0x11, 0x22, 0x33, 0x33, 0x22, 0x11, 0x00,
  0x00, 0x11, 0x22, 0x33, 0x33, 0x22, 0x11, 0x00,
  0x00, 0x11, 0x22, 0x33, 0x33, 0x22, 0x11, 0x00,
  0x00, 0x11, 0x22, 0x22, 0x22, 0x22, 0x11, 0x00,
  0x00, 0x11, 0x22, 0x22, 0x22, 0x22, 0x11, 0x00,
  0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00,  
  0x00, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00,  
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};


                           
void drawTestScreen() {
  Serial.printf("Use FB: %d ", use_fb); Serial.flush();
  tft.useFrameBuffer(use_fb);
  SetupOrClearClipRectAndOffsets();
  uint32_t start_time = millis();
  tft.fillScreen(use_fb ? ILI9341_RED : ILI9341_BLACK);
  //tft.setFont(Inconsolata_60);
  tft.setFont(Arial_24_Bold);
  tft.setTextColor(ILI9341_WHITE);
  tft.setCursor(0, 0);
  tft.println("Test");
  tft.setTextColor(ILI9341_WHITE, ILI9341_RED);
  tft.println("text");
  tft.setCursor(85, 65);
  tft.print("XYZ");
  tft.setFontAdafruit();
  tft.setTextSize(2);
  tft.setTextColor(ILI9341_WHITE);
  tft.println("01234");
  tft.setTextColor(ILI9341_WHITE, ILI9341_GREEN);
  tft.println("56789!@#$%");

  tft.drawRect(0, 150, 100, 50, ILI9341_WHITE);
  tft.drawLine(0, 150, 100, 50, ILI9341_GREEN);

  tft.fillRectVGradient(125, 150, 50, 50, ILI9341_GREEN, ILI9341_YELLOW);
  tft.fillRectHGradient(200, 150, 50, 50, ILI9341_YELLOW, ILI9341_GREEN);
  // Try a read rect and write rect
#ifdef DEBUG_PIN
  digitalWrite(DEBUG_PIN, HIGH);
#endif

  delay(5);
  tft.readRect(0, 0, 50, 50, pixel_data);

#ifdef DEBUG_PIN
  digitalWrite(DEBUG_PIN, LOW);
#endif
  tft.writeRect(250, 0, 50, 50, pixel_data);

  // Lets try to pack this rectangle of data into 8 byte
  tft.readRect(85, 65, 50, 50, pixel_data);
  uint16_t *ppd16 = pixel_data;
  uint8_t *ppd8 = (uint8_t*)pixel_data;
  uint8_t palette_cnt = 0;
  int palette_index;
  for (int i = 0; i < 2500; i++) {
    for (palette_index = 0; palette_index < palette_cnt; palette_index++) {
      if (*ppd16 == palette[palette_index])
        break;
    }
    if (palette_index >= palette_cnt) {
      palette[palette_cnt++] = *ppd16;  // save away the color
    }
    *ppd8++ = palette_index;
    ppd16++;
  }
  tft.writeRect8BPP(200, 50, 50, 50, (uint8_t*)pixel_data, palette);
  palette[0] = ILI9341_CYAN; 
  palette[1] = ILI9341_OLIVE; 
  tft.writeRect1BPP(75, 100, 16, 16, pict1bpp, palette);
  tft.writeRect1BPP(320-90, 75, 16, 16, pict1bpp, palette);
  
  palette[2] = ILI9341_MAROON; 
  palette[3] = ILI9341_PINK; 
  tft.writeRect2BPP(75, 125, 32, 16, pict2bpp, palette);

  tft.writeRectNBPP(15, 125, 32, 16, 2, pict2bpp, palette);
  tft.writeRectNBPP(75, 150, 16, 16, 4, pict4bpp, palette);

  // Try drawing button
  tft.setFontAdafruit();
  button.drawButton();

  if (use_dma) {
    tft.startUpdateScreenAsync(); 
  } else {
    tft.updateScreen();
  }
  Serial.println(millis() - start_time, DEC);

  if (use_dma && use_fb) {
    delay(500);
    Serial.printf("DMA error status: %x\n", DMA_ES);
  }

  use_fb = use_fb ? 0 : 1 ;
  Serial.println(use_fb, DEC);


}

void drawTextScreen(bool fOpaque) {
  SetupOrClearClipRectAndOffsets();
  uint32_t start_time = millis();
  tft.useFrameBuffer(use_fb);
  tft.fillScreen(use_fb ? ILI9341_RED : ILI9341_BLACK);
  tft.setFont(Arial_40_Bold);
  if (fOpaque)
    tft.setTextColor(ILI9341_WHITE, use_fb ? ILI9341_BLACK : ILI9341_RED);
  else
    tft.setTextColor(ILI9341_WHITE);
  tft.setCursor(0, 5);
  tft.println("AbCdEfGhIj");
  tft.setFont(Arial_28_Bold);
  tft.println("0123456789!@#$");
#if 1
  tft.setFont(Arial_20_Bold);
  tft.println("abcdefghijklmnopq");
  tft.setFont(Arial_14_Bold);
  tft.println("ABCDEFGHIJKLMNOPQRST");
  tft.setFont(Arial_10_Bold);
  tft.println("0123456789zyxwvutu");
#endif
  tft.updateScreen();

  Serial.printf("Use FB: %d OP: %d, DT: %d OR: %d\n", use_fb, fOpaque, use_set_origin, millis() - start_time);
}

//=============================================================================
// Try continuous update
//=============================================================================
void testDMAContUpdate() {
  // Force frame buffer on
  Serial.printf("continuous DMA udpate test - Frame mode on\n"); Serial.flush();
      use_fb = 1; //
  
  tft.useFrameBuffer(use_fb);
  tft.fillScreen(ILI9341_GREEN);

  tft.startUpdateScreenAsync(true);

  uint32_t frame_count;  

  while ((frame_count = tft.frameCount()) < 10) yield();
  tft.fillScreen(ILI9341_YELLOW);

  while ((frame_count = tft.frameCount()) < 20) yield();
  tft.fillScreen(ILI9341_RED);

  while ((frame_count = tft.frameCount()) < 30) yield();
  tft.fillScreen(ILI9341_BLACK);

  while ((frame_count = tft.frameCount()) < 40) yield();

  tft.setCursor(0, 100);
  tft.setFont(Arial_20_Bold);
  tft.println("DONE");

  tft.endUpdateScreenAsync();

  Serial.println("Finished test");


}

void loop(void) {
  // See if any text entered
  int ich;
  if ((ich = Serial.read()) != -1) {
    while (Serial.read() != -1) delay(1);
    if (ich == 'c') {
      use_clip_rect = !use_clip_rect;
      if (use_clip_rect) Serial.println("Clip Rectangle Turned on");
      else Serial.println("Clip Rectangle turned off");
      return;
    }
    if (ich == 'd') {
      use_dma = !use_dma;
      if (use_dma) Serial.println("DMA Turned on");
      else Serial.println("DMA turned off");
      return;
    }

    if (ich == 's') {
      use_set_origin = !use_set_origin;
      if (use_set_origin) Serial.printf("Set origin to %d, %d\n", ORIGIN_TEST_X, ORIGIN_TEST_Y);
      else Serial.println("Clear origin");
      return;
    }
    if (ich == 'o')
      drawTextScreen(1);
    else if (ich == 't')
      drawTextScreen(0);
    else if (ich == 'r') {
      testDMAContUpdate();
    }
    else
      drawTestScreen();
  }

}

Testing wise I have run the SSD1306 on
all 3 SPI devices on T3.5/6
all 2 SPI devices on TLC
The 1 on T3.2
and 1 on T2.0

I have not done an extensive test on all of the alternative pins for all of these, but have done some.

Likewise I have done some testing of different ILI9341 displays for different boards, but not sure if I tested all of the different ones as much as I did with the SSD1306
 

Attachments

  • ili9341_t3ns.zip
    809.5 KB · Views: 231
I should mention I also had a few other simple test apps, that I would use with Logic Analyzer to watch the SPI streams, like:
Code:
#include <SPI.h>
#define SPICLOCK 30000000
#define CS_PIN 10
void setup() {
  // put your setup code here, to run once:
  while (!Serial && (millis() < 4000)) ;
  Serial.begin(115200);
  delay(250);
  pinMode(10, OUTPUT);
  digitalWrite(CS_PIN, HIGH);
  Serial.print("SPI Speed test Begin");
  SPI.begin();
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
  SPI1.begin();

  SPI2.setMOSI(52);
  SPI2.setMISO(51);
  SPI2.setSCK(53);
  SPI2.begin();
#endif  
}
uint8_t buf[27];

void loop() {
  // put your main code here, to run repeatedly:
  Serial.println();
  initBuf();
  digitalWrite(CS_PIN, LOW);
  uint32_t time_start = micros();
  SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
  for (uint8_t i = 0; i < sizeof(buf); i++) {
    buf[i] = SPI.transfer(buf[i]);
  }
  digitalWrite(CS_PIN, HIGH);
  SPI.endTransaction();
  Serial.printf("elapsed single: %d\n", micros() - time_start);
  printBuf();

  delay(10);  // give a little gap.
  initBuf();
  time_start = micros();
  digitalWrite(CS_PIN, LOW);
  SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
  SPI.transfer(buf, sizeof(buf));
  digitalWrite(CS_PIN, HIGH);
  SPI.endTransaction();
  Serial.printf("elapsed buf: %d\n", micros() - time_start);
  printBuf();

#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
  delay(10);
  initBuf();
  digitalWrite(CS_PIN, LOW);
  time_start = micros();
  SPI1.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
  for (uint8_t i = 0; i < sizeof(buf); i++) {
    buf[i] = SPI1.transfer(buf[i]);
  }
  digitalWrite(CS_PIN, HIGH);
  SPI1.endTransaction();
  Serial.printf("SPI1: elapsed single: %d\n", micros() - time_start);
  printBuf();

  delay(10);  // give a little gap.
  initBuf();
  time_start = micros();
  digitalWrite(CS_PIN, LOW);
  SPI1.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
  SPI1.transfer(buf, sizeof(buf));
  digitalWrite(CS_PIN, HIGH);
  SPI1.endTransaction();
  Serial.printf("SPI1 elapsed buf: %d\n", micros() - time_start);
  printBuf();

  delay(10);
  initBuf();
  digitalWrite(CS_PIN, LOW);
  time_start = micros();
  SPI2.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
  for (uint8_t i = 0; i < sizeof(buf); i++) {
    buf[i] = SPI2.transfer(buf[i]);
  }
  digitalWrite(CS_PIN, HIGH);
  SPI2.endTransaction();
  Serial.printf("SPI2: elapsed single: %d\n", micros() - time_start);
  printBuf();

  delay(10);  // give a little gap.
  initBuf();
  time_start = micros();
  digitalWrite(CS_PIN, LOW);
  SPI2.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
  SPI2.transfer(buf, sizeof(buf));
  digitalWrite(CS_PIN, HIGH);
  SPI2.endTransaction();
  Serial.printf("SPI2 elapsed buf: %d\n", micros() - time_start);
  printBuf();

#endif  
  Serial.println("Enter something to repeat");
  while (Serial.read() == -1) ; 
  while (Serial.read() != -1) ;

  
}

void initBuf(void) {
  for (uint8_t i = 0; i < sizeof(buf); i++) {
    buf[i] = 'A' + i;
  }
  buf[sizeof(buf) - 1] = 0; // make sure to null terminate...
}

void printBuf(void) {
  for (uint8_t i = 0; i < sizeof(buf); i++) {
    if (buf[i] >= 'A' && buf[i] <= 'Z')
      Serial.write(buf[i]);
    else if (buf[i] == 0)
      Serial.print("0");
    else if (buf[i] == 0xff)
      Serial.print("1");  // maybe when we tie to +v
    else
      Serial.print("?");  // maybe when we tie to +v
  }
  Serial.println();

}
 
I've merged all the changes for a single SPIClass, but so far none of the new API stuff. Still undecided how much of that should go into 1.37, or wait for 1.38. Normally I try to release every 3 months, but I'm considering doing 1.38 early to roll up a lot of new features. I really wanted to get at least the unified SPIClass change into 1.37, so libraries can start having configurable SPI port options.
 
I totally hear you! If you had not already done the stuff, I was going to make another pass in the code and do things like conditionally include.
That is probably define a couple of new defines, that code could test if defined and the library would have #ifdef to remove if not...

Like already have: #define SPI_HAS_TRANSFER_BUF 1

Was going to add: #define SPI_HAS_TRANSFER_ASYNC 1

I was hoping to sneak in a few of the new APIS, to make it easier for Apps/libraries to adapt to multiple SPI busses. In particular the ones for checking if a pin is valid:
bool pinIsMISO(uint8_t pin);
bool pinIsMOSI(uint8_t pin);
bool pinIsSCK(uint8_t pin);
 
I'm trying to find any info about possible risks. Any ideas?
All the info I've found looks like C++14 is a pretty minor change from C++11. Also seems like gcc 5.4 should have fairly mature support. Maybe?

We're going through this exact decision at work right now, we currently compile to c++11 and are looking at c++14. The biggest deciding factor for us is C++11 is the beginning of what the industry calls "Modern C++". One of the biggest things in Modern C++ is proper memory management regarding dead references and memory leaks:
- raw pointers are a no-no, malloc() / free() is practically verboten.
- things should be managed with smart pointers, raw pointers are only used when dealing with legacy libraries
- Calling new() yourself is bad practice. That's exactly what smart pointers are for, like std::unique_ptr and std::shared_ptr
- in fact, calling delete() is bad practice! That's exactly what make_unique<> and make_shared<> are for.

In a nutshell, smart pointers take over allocation for you, so you don't mess it up. The std::make_* templates take over deallocation for you, so you dont' mess it up. Together, this his how C++ has achieved 'garbage collection' which is common in languages like Java.

And now we come to the ++14 vs ++11 thing. The macro std::make_unique was left out of c++11 (make_shared is there however), and make_unique was added in c++14. So, using c++11 and following "Modern C++" practices means you have to roll your own make_unique template which has potential issues with everyone making their own slightly different one.

A lot of developers are moving to c++14 just to get a proper standardized make_unqiue<> macro which is essential to proper modern memory management practices.
 
May I ask, if that is the same concept as 'smart pointers' used by VB (with possible differences)?

I'm not familiar with VB. My understanding that the smart pointers introduced to std:: in C++11/14 are based heavily on the smart pointers from the Boost library and were designed to replace the awful auto_ptr which was deprecated.
 
Looks like Arduino *still* hasn't released 1.8.3. If it doesn't appear within the next few hours, I'm going to make a 1.37-beta1 release anyway so we can start using and testing all this new stuff.
 
Status
Not open for further replies.
Back
Top