Teensy 3.0 and u8glib

Status
Not open for further replies.
Hi, I was trying to test u8glib with this lcd
http://www.electrodragon.com/product/12864min-graphic-lcd/
and a Teensy 3.0.
Just installed the latest Arduino 1.6.5-r5 and teensyduino beta compatible with -r5. Verified simple blink sketch on the Teensy is running. Then downloaded u8glib, replaced u8g.h, deleted hw_spi.c and added the hw_spi.cpp from Hoss's post. I copied the latest u8g_com_arduino_hw_spi.cpp from Hoss's post on 09/15.

Tried the Hello World sketch from the u8glib examples, after adding an "#include <SPI.h> at the top of the file, and uncommented
U8GLIB_NHD_C12864 u8g(13, 11, 10, 9, 8); // SPI Com: SCK = 13, MOSI = 11, CS = 10, A0 = 9, RST = 8

This matches the lcd class from the Electrodragon sample .ino on their website. But I get compilation errors pasted below ...

D:\arduino\arduino-1.6.5-r5\libraries\U8glib\utility\u8g_com_arduino_sw_spi.c: In function 'u8g_digital_write_sam_high':
D:\arduino\arduino-1.6.5-r5\libraries\U8glib\utility\u8g_com_arduino_sw_spi.c:170:5: warning: implicit declaration of function 'PIO_Set' [-Wimplicit-function-declaration]
PIO_Set( g_APinDescription[pin].pPort, g_APinDescription[pin].ulPin) ;
^
D:\arduino\arduino-1.6.5-r5\libraries\U8glib\utility\u8g_com_arduino_sw_spi.c:170:14: error: 'g_APinDescription' undeclared (first use in this function)
PIO_Set( g_APinDescription[pin].pPort, g_APinDescription[pin].ulPin) ;
^
D:\arduino\arduino-1.6.5-r5\libraries\U8glib\utility\u8g_com_arduino_sw_spi.c:170:14: note: each undeclared identifier is reported only once for each function it appears in
D:\arduino\arduino-1.6.5-r5\libraries\U8glib\utility\u8g_com_arduino_sw_spi.c: In function 'u8g_digital_write_sam_low':
D:\arduino\arduino-1.6.5-r5\libraries\U8glib\utility\u8g_com_arduino_sw_spi.c:175:5: warning: implicit declaration of function 'PIO_Clear' [-Wimplicit-function-declaration]
PIO_Clear( g_APinDescription[pin].pPort, g_APinDescription[pin].ulPin) ;
^
D:\arduino\arduino-1.6.5-r5\libraries\U8glib\utility\u8g_com_arduino_sw_spi.c:175:16: error: 'g_APinDescription' undeclared (first use in this function)
PIO_Clear( g_APinDescription[pin].pPort, g_APinDescription[pin].ulPin) ;
^
Error compiling.


Any ideas on what I need to do to fix this ?
Thanks !
 
Last edited:
Before going into too many details, what version of u8glib you got (1.18.1 ?), Hoss fixes are tested on u8glib version 1.15

Try this constructor:
Code:
U8GLIB_NHD_C12864 u8g(10, 9, 8); // HW SPI Com: CS = 10, A0 = 9, 8 = Reset(Optional) (Hardware Pins are  SCK = 13 and MOSI = 11)
 
Last edited:
Thanks a ton ! That was the problem. Both the HW spi constructor and SW spi constructor are working fine now. For anyone trying out this Electrodragon lcd ( 12864MIN ), the demo code on their website uses
U8GLIB_NHD_C12864 which is incorrect. That assumes ST7565 lcd controller and resulted in a reversed text display on this lcd.
The correct constructor is
U8GLIB_MINI12864 u8g(10,9,8);
And the contrast setting in the uc1701 driver file needed to be changed to 0x024, the driver setting of 0x027 gave me a black screen after a few seconds.

Before going into too many details, what version of u8glib you got (1.18.1 ?), Hoss fixes are tested on u8glib version 1.15

Try this constructor:
Code:
U8GLIB_NHD_C12864 u8g(10, 9, 8); // HW SPI Com: CS = 10, A0 = 9, 8 = Reset(Optional) (Hardware Pins are  SCK = 13 and MOSI = 11)
 
Hello,

Does anybody knows if there is a reliable and easy way to make work a SSD1322 driver (256x64 OLED from New Heaven or BuyDisplay), working with U8glib in HW SPI on Teensy 3.1/3.2 ? All the tests I made until now with the current U8Glib library are unsuccessful.
Thanks a lot.
 
[SOLVED] SSD1322 running fast on Teensy 3.2 with HW SPI

Following my last post, I got had a chat with Chris O. who helped me a lot by resolving the issues I had on my SSD1322 display and Teensy 3.2 board.
My display is a BuyDisplay ER-OLEDM032-1W, but it seems fully compatible with the New Haven Display NHD-3.12-25664UCB2.

Thanks to him, everything is working well now in HW SPI, and it blast !
I'm running the U8Glib 1.5 with Hoss modifications he has posted on page 3 of this thread. So the first requirement is to install the correct files as he wrote. The trick is the IDE will refuse to build because of a SPI0 that which cannot be resolve in scope.

So here the fix that Chris O. gave me :

Open the file u8g_com_arduino_hw_spi.cpp and modify the were the builder create error on SPI0.XXX by KINETIC_SPI0.XXX.
It will give you the following file. After that changes, build and upload will run normally.

Code:
/*
 
 u8g_com_arduino_hw_spi.c
 
 Universal 8bit Graphics Library
 
 Copyright (c) 2011, olikraus@gmail.com
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without modification,
 are permitted provided that the following conditions are met:
 
 * Redistributions of source code must retain the above copyright notice, this list
 of conditions and the following disclaimer.
 
 * Redistributions in binary form must reproduce the above copyright notice, this
 list of conditions and the following disclaimer in the documentation and/or other
 materials provided with the distribution.
 
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 SPI Clock Cycle Type
 
 SSD1351	  50ns		20 MHz
 SSD1322	300ns		  3.3 MHz
 SSD1327	300ns
 SSD1306	300ns
 ST7565		400ns 		  2.5 MHz
 ST7920		400ns
 
 Arduino DUE
 
 PA25	MISO
 PA26	MOSI	75
 PA27	SCLK	76
 
 
 typedef struct {
 WoReg SPI_CR;        (Spi Offset: 0x00) Control Register
 RwReg SPI_MR;        (Spi Offset: 0x04) Mode Register
 RoReg SPI_RDR;       (Spi Offset: 0x08) Receive Data Register
 WoReg SPI_TDR;       (Spi Offset: 0x0C) Transmit Data Register
 RoReg SPI_SR;        (Spi Offset: 0x10) Status Register
 WoReg SPI_IER;       (Spi Offset: 0x14) Interrupt Enable Register
 WoReg SPI_IDR;       (Spi Offset: 0x18) Interrupt Disable Register
 RoReg SPI_IMR;       (Spi Offset: 0x1C) Interrupt Mask Register
 RoReg Reserved1[4];
 RwReg SPI_CSR[4];    (Spi Offset: 0x30) Chip Select Register
 RoReg Reserved2[41];
 RwReg SPI_WPMR;      (Spi Offset: 0xE4) Write Protection Control Register
 RoReg SPI_WPSR;      (Spi Offset: 0xE8) Write Protection Status Register
 } Spi;
 
 Power Management Controller (PMC)
 arduino-1.5.2/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/instance/instance_pmc.h
 - enable PIO
 
 REG_PMC_PCER0 = 1UL << ID_PIOA
 - enable SPI
 REG_PMC_PCER0 = 1UL << ID_SPI0
 
 
 - enable PIOA and SPI0
 REG_PMC_PCER0 = (1UL << ID_PIOA) | (1UL << ID_SPI0);
 
 Parallel Input/Output Controller (PIO)
 arduino-1.5.2/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/instance/instance_pioa.h
 - enable special function of the pin: disable PIO on A26 and A27:
 REG_PIOA_PDR = 0x0c000000
 PIOA->PIO_PDR = 0x0c000000
 
 SPI
 SPI0->SPI_CR = SPI_CR_SPIDIS
 SPI0->SPI_CR = SPI_CR_SWRST ;
 SPI0->SPI_CR = SPI_CR_SWRST ;
 SPI0->SPI_CR = SPI_CR_SPIEN
 
 Bit 0: Master Mode = 1 (active)
 Bit 1: Peripheral Select = 0 (fixed)
 Bit 2: Chip Select Decode Mode = 1 (4 to 16)
 Bit 4: Mode Fault Detection = 1 (disabled)
 Bit 5: Wait Data Read = 0 (disabled)
 Bit 7: Loop Back Mode = 0 (disabled)
 Bit 16-19: Peripheral Chip Select = 0 (chip select 0)
 SPI0->SPI_MR = SPI_MR_MSTR | SPI_MR_PCSDEC | SPI_MR_MODFDIS
 
 Bit 0: Clock Polarity = 0
 Bit 1: Clock Phase = 0
 Bit 4-7: Bits = 0 (8 Bit)
 Bit 8-15: SCBR = 1
 SPI0->SPI_CSR[0] = SPI_CSR_SCBR(x)	Serial Baud Rate
 SCBR / 84000000 > 50 / 1000000000
 SCBR / 84 > 5 / 100
 SCBR  > 50 *84 / 1000 --> SCBR=5
 SCBR  > 300*84 / 1000 --> SCBR=26
 SCBR  > 400*84 / 1000 --> SCBR=34
 
 Arduino Due test code:
 REG_PMC_PCER0 = (1UL << ID_PIOA) | (1UL << ID_SPI0);
 REG_PIOA_PDR = 0x0c000000;
 SPI0->SPI_CR = SPI_CR_SPIDIS;
 SPI0->SPI_CR = SPI_CR_SWRST;
 SPI0->SPI_CR = SPI_CR_SWRST;
 SPI0->SPI_CR = SPI_CR_SPIEN;
 SPI0->SPI_MR = SPI_MR_MSTR | SPI_MR_PCSDEC | SPI_MR_MODFDIS;
 SPI0->SPI_CSR[0] = SPI_CSR_SCBR(30);
 
 for(;;)
 {
 while( (SPI0->SPI_SR & SPI_SR_TDRE) == 0 )
 ;
 SPI0->SPI_TDR = 0x050;
 }
 
 */

#include "u8g.h"

#if defined(ARDUINO)

#if defined(__AVR__)
#define U8G_ARDUINO_ATMEGA_HW_SPI
/* remove the definition for attiny */
#if __AVR_ARCH__ == 2
#undef U8G_ARDUINO_ATMEGA_HW_SPI
#endif
#if __AVR_ARCH__ == 25
#undef U8G_ARDUINO_ATMEGA_HW_SPI
#endif
#endif

#if defined(U8G_ARDUINO_ATMEGA_HW_SPI)

#include <avr/interrupt.h>
#include <avr/io.h>

#if ARDUINO < 100
#include <WProgram.h>

/* fixed pins */
#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) // Sanguino.cc board
#define PIN_SCK         7
#define PIN_MISO        6
#define PIN_MOSI        5
#define PIN_CS          4
#else                                   // Arduino Board
#define PIN_SCK 13
#define PIN_MISO  12
#define PIN_MOSI 11
#define PIN_CS 10
#endif // (__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)

#else

#include <Arduino.h>

/* use Arduino pin definitions */
#define PIN_SCK SCK
#define PIN_MISO  MISO
#define PIN_MOSI MOSI
#define PIN_CS SS

#endif



//static uint8_t u8g_spi_out(uint8_t data) U8G_NOINLINE;
static uint8_t u8g_spi_out(uint8_t data)
{
    /* unsigned char x = 100; */
    /* send data */
    SPDR = data;
    /* wait for transmission */
    while (!(SPSR & (1<<SPIF)))
        ;
    /* clear the SPIF flag by reading SPDR */
    return  SPDR;
}


uint8_t u8g_com_arduino_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{
    switch(msg)
    {
        case U8G_COM_MSG_STOP:
            break;
            
        case U8G_COM_MSG_INIT:
            u8g_com_arduino_assign_pin_output_high(u8g);
            pinMode(PIN_SCK, OUTPUT);
            digitalWrite(PIN_SCK, LOW);
            pinMode(PIN_MOSI, OUTPUT);
            digitalWrite(PIN_MOSI, LOW);
            /* pinMode(PIN_MISO, INPUT); */
            
            pinMode(PIN_CS, OUTPUT);			/* system chip select for the atmega board */
            digitalWrite(PIN_CS, HIGH);
            
            
            
            /*
             SPR1 SPR0
             0	0		fclk/4
             0	1		fclk/16
             1	0		fclk/64
             1	1		fclk/128
             */
            SPCR = 0;
            SPCR =  (1<<SPE) | (1<<MSTR)|(0<<SPR1)|(0<<SPR0)|(0<<CPOL)|(0<<CPHA);
#ifdef U8G_HW_SPI_2X
            SPSR = (1 << SPI2X);  /* double speed, issue 89 */
#else
            if ( arg_val  <= U8G_SPI_CLK_CYCLE_50NS )
            {
                SPSR = (1 << SPI2X);  /* double speed, issue 89 */
            }
#endif
            
            
            break;
            
        case U8G_COM_MSG_ADDRESS:                     /* define cmd (arg_val = 0) or data mode (arg_val = 1) */
            u8g_com_arduino_digital_write(u8g, U8G_PI_A0, arg_val);
            break;
            
        case U8G_COM_MSG_CHIP_SELECT:
            if ( arg_val == 0 )
            {
                /* disable */
                u8g_com_arduino_digital_write(u8g, U8G_PI_CS, HIGH);
            }
            else
            {
                /* enable */
                u8g_com_arduino_digital_write(u8g, U8G_PI_SCK, LOW);
                u8g_com_arduino_digital_write(u8g, U8G_PI_CS, LOW);
            }
            break;
            
        case U8G_COM_MSG_RESET:
            if ( u8g->pin_list[U8G_PI_RESET] != U8G_PIN_NONE )
                u8g_com_arduino_digital_write(u8g, U8G_PI_RESET, arg_val);
            break;
            
        case U8G_COM_MSG_WRITE_BYTE:
            u8g_spi_out(arg_val);
            break;
            
        case U8G_COM_MSG_WRITE_SEQ:
        {
            register uint8_t *ptr = arg_ptr;
            while( arg_val > 0 )
            {
                u8g_spi_out(*ptr++);
                arg_val--;
            }
        }
            break;
        case U8G_COM_MSG_WRITE_SEQ_P:
        {
            register uint8_t *ptr = arg_ptr;
            while( arg_val > 0 )
            {
                u8g_spi_out(u8g_pgm_read(ptr));
                ptr++;
                arg_val--;
            }
        }
            break;
    }
    return 1;
}

/* #elif defined(__18CXX) || defined(__PIC32MX) */

#elif defined(__SAM3X8E__)		// Arduino Due, maybe we should better check for __SAM3X8E__

#include <Arduino.h>

/* use Arduino pin definitions */
#define PIN_SCK SCK
#define PIN_MISO  MISO
#define PIN_MOSI MOSI
#define PIN_CS SS


static uint8_t u8g_spi_out(uint8_t data)
{
    /* wait until tx register is empty */
    while( (SPI0->SPI_SR & SPI_SR_TDRE) == 0 )
        ;
    /* send data */
    SPI0->SPI_TDR = (uint32_t)data;
    return  data;
}


uint8_t u8g_com_arduino_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{
    switch(msg)
    {
        case U8G_COM_MSG_STOP:
            break;
            
        case U8G_COM_MSG_INIT:
            u8g_com_arduino_assign_pin_output_high(u8g);
            u8g_com_arduino_digital_write(u8g, U8G_PI_CS, HIGH);
            
            /* Arduino Due specific code */
            
            /* enable PIOA and SPI0 */
            REG_PMC_PCER0 = (1UL << ID_PIOA) | (1UL << ID_SPI0);
            
            /* disable PIO on A26 and A27 */
            REG_PIOA_PDR = 0x0c000000;
            
            /* reset SPI0 (from sam lib) */
            SPI0->SPI_CR = SPI_CR_SPIDIS;
            SPI0->SPI_CR = SPI_CR_SWRST;
            SPI0->SPI_CR = SPI_CR_SWRST;
            SPI0->SPI_CR = SPI_CR_SPIEN;
            u8g_MicroDelay();
            
            /* master mode, no fault detection, chip select 0 */
            SPI0->SPI_MR = SPI_MR_MSTR | SPI_MR_PCSDEC | SPI_MR_MODFDIS;
            
            /* Polarity, Phase, 8 Bit data transfer, baud rate */
            /* x * 1000 / 84 --> clock cycle in ns
             5 * 1000 / 84 = 58 ns
             SCBR  > 50 *84 / 1000 --> SCBR=5
             SCBR  > 300*84 / 1000 --> SCBR=26
             SCBR  > 400*84 / 1000 --> SCBR=34
             */
            
            if ( arg_val <= U8G_SPI_CLK_CYCLE_50NS )
            {
                SPI0->SPI_CSR[0] = SPI_CSR_SCBR(5) | 1;
            }
            else if ( arg_val <= U8G_SPI_CLK_CYCLE_300NS )
            {
                SPI0->SPI_CSR[0] = SPI_CSR_SCBR(26) | 1;
            }
            else if ( arg_val <= U8G_SPI_CLK_CYCLE_400NS )
            {
                SPI0->SPI_CSR[0] = SPI_CSR_SCBR(34) | 1;
            }
            else
            {
                SPI0->SPI_CSR[0] = SPI_CSR_SCBR(84) | 1;
            }
            
            u8g_MicroDelay();
            break;
            
        case U8G_COM_MSG_ADDRESS:                     /* define cmd (arg_val = 0) or data mode (arg_val = 1) */
            u8g_com_arduino_digital_write(u8g, U8G_PI_A0, arg_val);
            u8g_MicroDelay();
            break;
            
        case U8G_COM_MSG_CHIP_SELECT:
            if ( arg_val == 0 )
            {
                /* disable */
                u8g_MicroDelay();		/* this delay is required to avoid that the display is switched off too early --> DOGS102 with DUE */
                u8g_com_arduino_digital_write(u8g, U8G_PI_CS, HIGH);
                u8g_MicroDelay();
            }
            else
            {
                /* enable */
                //u8g_com_arduino_digital_write(u8g, U8G_PI_SCK, LOW);
                u8g_com_arduino_digital_write(u8g, U8G_PI_CS, LOW);
                u8g_MicroDelay();
            }
            break;
            
        case U8G_COM_MSG_RESET:
            if ( u8g->pin_list[U8G_PI_RESET] != U8G_PIN_NONE )
                u8g_com_arduino_digital_write(u8g, U8G_PI_RESET, arg_val);
            break;
            
        case U8G_COM_MSG_WRITE_BYTE:
            u8g_spi_out(arg_val);
            u8g_MicroDelay();
            break;
            
        case U8G_COM_MSG_WRITE_SEQ:
        {
            register uint8_t *ptr = arg_ptr;
            while( arg_val > 0 )
            {
                u8g_spi_out(*ptr++);
                arg_val--;
            }
        }
            break;
        case U8G_COM_MSG_WRITE_SEQ_P:
        {
            register uint8_t *ptr = arg_ptr;
            while( arg_val > 0 )
            {
                u8g_spi_out(u8g_pgm_read(ptr));
                ptr++;
                arg_val--;
            }
        }
            break;
    }
    return 1;
}

#elif defined(__MK20DX128__) || defined(__MK20DX256__)

#include <Arduino.h>
#include <limits.h>
#include "pins_arduino.h"
#include "wiring_private.h"
#include <SPI.h>

// new Chris O.
// Teensy 3.1 can only generate 30 MHz SPI when running at 120 MHz (overclock)
// At all other speeds, SPI.beginTransaction() will use the fastest available clock
#define SPICLOCK 30000000

#define _rst (u8g->pin_list[U8G_PI_RESET])
#define _cs (u8g->pin_list[U8G_PI_CS])
#define _rs (u8g->pin_list[U8G_PI_A0])

uint8_t  _sid, _sclk;
uint8_t pcs_data, pcs_command;
uint32_t ctar;
volatile uint8_t *datapin, *clkpin, *cspin, *rspin;
bool hwSPI = false;
bool nextIsCommand = false;
//#endif

inline void writebegin()
{
}

inline void spiwrite(uint8_t c)
{
	for (uint8_t bit = 0x80; bit; bit >>= 1) {
		*datapin = ((c & bit) ? 1 : 0);
		*clkpin = 1;
		*clkpin = 0;
	}
}

void writecommand(uint8_t c)
{
	if (hwSPI) {
            // new Chris O.
            SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));

		KINETISK_SPI0.PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0);
		while (((KINETISK_SPI0.SR) & (15 << 12)) > (3 << 12)) ; // wait if FIFO full
            SPI.endTransaction();
	} else {
		*rspin = 0;
		*cspin = 0;
		spiwrite(c);
		*cspin = 1;
	}
}

void writedata(uint8_t c)
{
	if (hwSPI) {
            // new Chris O.
            SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));

		KINETISK_SPI0.PUSHR = c | (pcs_data << 16) | SPI_PUSHR_CTAS(0);
		while (((KINETISK_SPI0.SR) & (15 << 12)) > (3 << 12)) ; // wait if FIFO full
            SPI.endTransaction();
	} else {
		*rspin = 1;
		*cspin = 0;
		spiwrite(c);
		*cspin = 1;
	}
}

void writedata16(uint16_t d)
{
	if (hwSPI) {
            // new Chris O.
            SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));

		KINETISK_SPI0.PUSHR = d | (pcs_data << 16) | SPI_PUSHR_CTAS(1);
		while (((KINETISK_SPI0.SR) & (15 << 12)) > (3 << 12)) ; // wait if FIFO full
            SPI.endTransaction();
	} else {
		*rspin = 1;
		*cspin = 0;
		spiwrite(d >> 8);
		spiwrite(d);
		*cspin = 1;
	}
}

inline void write(uint8_t c)
{
    if(nextIsCommand)
        writecommand(c);
    else
        writedata(c);
}

static bool spi_pin_is_cs(uint8_t pin)
{
	if (pin == 2 || pin == 6 || pin == 9) return true;
	if (pin == 10 || pin == 15) return true;
	if (pin >= 20 && pin <= 23) return true;
	return false;
}

static uint8_t spi_configure_cs_pin(uint8_t pin)
{
    switch (pin) {
        case 10: CORE_PIN10_CONFIG = PORT_PCR_MUX(2); return 0x01; // PTC4
        case 2:  CORE_PIN2_CONFIG  = PORT_PCR_MUX(2); return 0x01; // PTD0
        case 9:  CORE_PIN9_CONFIG  = PORT_PCR_MUX(2); return 0x02; // PTC3
        case 6:  CORE_PIN6_CONFIG  = PORT_PCR_MUX(2); return 0x02; // PTD4
        case 20: CORE_PIN20_CONFIG = PORT_PCR_MUX(2); return 0x04; // PTD5
        case 23: CORE_PIN23_CONFIG = PORT_PCR_MUX(2); return 0x04; // PTC2
        case 21: CORE_PIN21_CONFIG = PORT_PCR_MUX(2); return 0x08; // PTD6
        case 22: CORE_PIN22_CONFIG = PORT_PCR_MUX(2); return 0x08; // PTC1
        case 15: CORE_PIN15_CONFIG = PORT_PCR_MUX(2); return 0x10; // PTC0
    }
    return 0;
}

// new Chris O.
// Teensy 3.1 can only generate 30 MHz SPI when running at 120 MHz (overclock)
// At all other speeds, SPI.beginTransaction() will use the fastest available clock
  //#define SPICLOCK 30000000 // I DID THIS ON THE BEGINING

#define CTAR_24MHz   (SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0) | SPI_CTAR_DBR)
#define CTAR_16MHz   (SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0) | SPI_CTAR_DBR)
#define CTAR_12MHz   (SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0))
#define CTAR_8MHz    (SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0))
#define CTAR_6MHz    (SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1))
#define CTAR_4MHz    (SPI_CTAR_PBR(1) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1))

void setBitrate(uint32_t n)
{
      // new Chris O.
    // ?  SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));

	if (n >= 24000000) {
		ctar = CTAR_24MHz;
	} else if (n >= 16000000) {
		ctar = CTAR_16MHz;
	} else if (n >= 12000000) {
		ctar = CTAR_12MHz;
	} else if (n >= 8000000) {
		ctar = CTAR_8MHz;
	} else if (n >= 6000000) {
		ctar = CTAR_6MHz;
	} else {
		ctar = CTAR_4MHz;
	}
	SIM_SCGC6 |= SIM_SCGC6_SPI0;
	KINETISK_SPI0.MCR = SPI_MCR_MDIS | SPI_MCR_HALT;
	KINETISK_SPI0.CTAR0 = ctar | SPI_CTAR_FMSZ(7);
	KINETISK_SPI0.CTAR1 = ctar | SPI_CTAR_FMSZ(15);
	KINETISK_SPI0.MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F) | SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF;
   //   SPI.endTransaction();
}

static uint8_t u8g_spi_out(uint8_t data)
{
    write(data);
    return 0;
}

uint8_t u8g_com_arduino_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{
    switch(msg)
    {
        case U8G_COM_MSG_STOP:
            break;
            
        case U8G_COM_MSG_INIT:


            //from https://github.com/PaulStoffregen/Adafruit_ST7735/blob/master/Adafruit_ST7735.cpp
            if (_sid == 0) _sid = 11;
            if (_sclk == 0) _sclk = 13;
            if ( spi_pin_is_cs(_cs) && spi_pin_is_cs(_rs)
                && (_sid == 7 || _sid == 11)
                && (_sclk == 13 || _sclk == 14)
                && !(_cs ==  2 && _rs == 10) && !(_rs ==  2 && _cs == 10)
                && !(_cs ==  6 && _rs ==  9) && !(_rs ==  6 && _cs ==  9)
                && !(_cs == 20 && _rs == 23) && !(_rs == 20 && _cs == 23)
                && !(_cs == 21 && _rs == 22) && !(_rs == 21 && _cs == 22) ) {
                hwSPI = true;
                //uncomment this line to test for hardware SPI
                //the program will take an extra 10 seconds to start up
                //delay(10000);

                // new Chris O.
               // SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
                if (_sclk == 13) {
                    CORE_PIN13_CONFIG = PORT_PCR_MUX(2) | PORT_PCR_DSE;
                    SPCR.setSCK(13);
                } else {
                    CORE_PIN14_CONFIG = PORT_PCR_MUX(2);
                    SPCR.setSCK(14);
                }
                if (_sid == 11) {
                    CORE_PIN11_CONFIG = PORT_PCR_MUX(2);
                    SPCR.setMOSI(11);
                } else {
                    CORE_PIN7_CONFIG = PORT_PCR_MUX(2);
                    SPCR.setMOSI(7);
                }
                ctar = CTAR_24MHz;
                pcs_data = spi_configure_cs_pin(_cs);
                pcs_command = pcs_data | spi_configure_cs_pin(_rs);
                SIM_SCGC6 |= SIM_SCGC6_SPI0;
                KINETISK_SPI0.MCR = SPI_MCR_MDIS | SPI_MCR_HALT;
                KINETISK_SPI0.CTAR0 = ctar | SPI_CTAR_FMSZ(7);
                KINETISK_SPI0.CTAR1 = ctar | SPI_CTAR_FMSZ(15);
                KINETISK_SPI0.MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F) | SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF;

                //SPI.endTransaction();
            } else {
                hwSPI = false;
                u8g_com_arduino_assign_pin_output_high(u8g);
                cspin = portOutputRegister(digitalPinToPort(_cs));
                rspin = portOutputRegister(digitalPinToPort(_rs));
                clkpin = portOutputRegister(digitalPinToPort(_sclk));
                datapin = portOutputRegister(digitalPinToPort(_sid));
                *cspin = 1;
                *rspin = 0;
                *clkpin = 0;
                *datapin = 0;
                pinMode(_cs, OUTPUT);
                pinMode(_rs, OUTPUT);
                pinMode(_sclk, OUTPUT);
                pinMode(_sid, OUTPUT);
            }
            if (_rst) {
                pinMode(_rst, OUTPUT);
                digitalWriteFast(_rst, HIGH); //digitalWriteFast
                delay(500);
                digitalWriteFast(_rst, LOW);
                delay(500);
                digitalWriteFast(_rst, HIGH);
                delay(500);
            }
            break;
            
        case U8G_COM_MSG_ADDRESS:                     /* define cmd (arg_val = 0) or data mode (arg_val = 1) */
            if(hwSPI)
            {
                nextIsCommand = (arg_val == 0);
            }
            else
                u8g_com_arduino_digital_write(u8g, U8G_PI_A0, arg_val);
            break;
            
        case U8G_COM_MSG_CHIP_SELECT:
            if(!hwSPI)
            {
                if ( arg_val == 0 )
                {
                    /* disable */
                    u8g_com_arduino_digital_write(u8g, U8G_PI_CS, HIGH);
                }
                else
                {
                    /* enable */
                    u8g_com_arduino_digital_write(u8g, U8G_PI_SCK, LOW);
                    u8g_com_arduino_digital_write(u8g, U8G_PI_CS, LOW);
                }
            }
            break;
            
        case U8G_COM_MSG_RESET:
            if ( u8g->pin_list[U8G_PI_RESET] != U8G_PIN_NONE )
                u8g_com_arduino_digital_write(u8g, U8G_PI_RESET, arg_val);
            break;
            
        case U8G_COM_MSG_WRITE_BYTE:
            u8g_spi_out(arg_val);
            break;
            
        case U8G_COM_MSG_WRITE_SEQ:
        {
            register uint8_t *ptr = (uint8_t*)arg_ptr;
            while( arg_val > 0 )
            {
                u8g_spi_out(*ptr++);
                arg_val--;
            }
        }
            break;
        case U8G_COM_MSG_WRITE_SEQ_P:
        {
            register uint8_t *ptr = (uint8_t*)arg_ptr;
            while( arg_val > 0 )
            {
                u8g_spi_out(u8g_pgm_read(ptr));
                ptr++;
                arg_val--;
            }
        }
            break;
    }
    return 1;
}

#else

#endif /* U8G_ARDUINO_ATMEGA_HW_SPI */

#else /* ARDUINO */

uint8_t u8g_com_arduino_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{
    return 1;
}

#endif /* ARDUINO */

Also I overclocked my chip to 144MHz to increase a bit the SPI clock. It wasn't running well at 120MHz, I had some noisy glitches on the screen due to the clock speed I think, or something else :D Anyway, at 144MHz the perf are quite enough !

Perf with the constructor : U8GLIB_NHD31OLED_2X_GR u8g(10, 9, 8); // Gray shade, so that means the slowest...
draw clip test : 58.2fps, draw clear : 16.7fps, draw @ : 23.1fps, draw pixel : 8.3fps

Perf with the constructor : U8GLIB_NHD31OLED_2X_BW u8g(10, 9, 8);
draw clip test : 78.4fps, draw clear : 26.1fps, draw @ : 34.4fps, draw pixel : 15.3fps

So if you don't need gray shade but a high frame rate, do not hesitate ;)

Thanks a lot for the Teensy community, after 2 days having this board I found a really helping community, I do love that !
All my best !
 
Last edited:
Also I overclocked my chip to 144MHz to increase a bit the SPI clock. It wasn't running well at 120MHz, I had some noisy glitches on the screen due to the clock speed I think, or something else

fwiw, the SPI clock is faster at 120MHz (30MHz), it's 24MHz at 144 MHz, so it might have been too fast ... or something else.
 
Yes, that's what we saw, and Chris O. told me the same. That's why I had decided to go at higher clock speed (144MHz) because it will lowered the SPI clock speed like you said.
Anyway, I encourage all the people to try at the fastest clock speed and see if some glitches appears or not on the display ;)

Now I'm trying to move the clock on PIN 14, instead of 13 and avoiding the led blinking...
 
Issues on SPI sharing between Autodriver board and U8Glib HW SPI

Dear all,

It's me again with a new problem on U8Glib and SPI sharing between two devices. I'm currently working with U8Glib on SSD1322 drives with SPI bus on Teensy 3.2 and it works perfectly.
I also run a L6470 Sparkfun Autodriver board, also in SPI, and it works perfectly alone, but there is some problem as soon as I'm trying to use the both at the same time.
I think that there is some problem on the SPI bus, but I don't know what.

Have you ever had this kind of issue between u8glib and autodriver board or maybe with u8glib and any other SPI device ?

Your support is really welcome ! ;)

Thanks a lot.
Christophe
 
Oh hello there!!! :)
Have you ever had this kind of issue between u8glib and autodriver board or maybe with u8glib and any other SPI device ?
YES.

And Here's my solution...

based on code by William Garrido
https://github.com/FriedCircuits/Ug8lib_Teensy

Transactional SPI setup by Chris O.

In U8glib Arduino library folder containing the U8G version 1.15 include the two files.
u8g_teensy.cpp and u8g_teensy.h
(Example on MS WIN) C:\....\arduino-1.6.5-r5\hardware\teensy\avr\libraries\U8glib <---put the files here.

The u8g_teensy.h file.
Code:
// u8g_teensy.h //

#ifndef _U8G_ARM_H
#define _U8G_ARM_H
 
//adjust this path:
#include "u8glib.h"

 
//main com function. read on...
uint8_t u8g_com_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr);
 
#endif

And the u8g_teensy.cpp file.
Code:
// u8g_teensy.cpp //

#include <Arduino.h>
#include "pins_arduino.h"

#include "u8glib.h"
#include "SPI.h"
// based on code by William Garrido
// FriedCircuits
// 07-09-2014
//
// Modified by Chris O.
// September 4 2015
// Transactional SPI setup

// Teensy 3.1 can only generate 30 MHz SPI when running at 120 MHz (overclock)
// At all other speeds, SPI.beginTransaction() will use the fastest available clock 24Mhz
//#define SPICLOCK 30000000
#define SPICLOCK 24000000  //24Mhz
//#define SPICLOCK 16000000  //16Mhz
//#define SPICLOCK  8000000  //8Mhz
//#define SPICLOCK  4000000  //4Mhz

/* use Arduino pin definitions */
//#define PIN_SCK SCK
//#define PIN_MISO  MISO
#define U8G_PI_A0 9 // A0  
//#define PIN_CS SS
#define U8G_PI_RESET 4 // oled reset pin, 

uint8_t u8g_com_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
{
  switch (msg)
  {
    case U8G_COM_MSG_STOP:
      //STOP THE DEVICE
      break;

    case U8G_COM_MSG_INIT:
      //INIT HARDWARE INTERFACES, TIMERS, GPIOS...
      pinMode(9, OUTPUT);
      //For hardware SPI
      pinMode(SS, OUTPUT);

      // 11/04/2015
      SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0);
      SPI.begin();
      // old / SPI.setClockDivider(SPI_CLOCK_DIV2);

      //if ( u8g->pin_list[U8G_PI_RESET] != U8G_PIN_NONE )
      pinMode(U8G_PI_RESET, OUTPUT); // oled reset pin set to output
      digitalWrite(U8G_PI_RESET, HIGH);
      //old / pinMode(14, OUTPUT);
      //old / digitalWrite(14, HIGH);

      // VDD (3.3V) goes high at start, lets just chill for a ms
      delay(1);
      // bring reset low
      digitalWrite(U8G_PI_RESET, LOW);
      // wait 10ms
      delay(10);
      // bring out of reset
      digitalWrite(U8G_PI_RESET, HIGH);

      //pinMode(MOSI, OUTPUT);
      //pinMode(SCK, OUTPUT);

      /*
      clkport     = portOutputRegister(digitalPinToPort(SCK));
      clkpinmask  = digitalPinToBitMask(SCK);
      mosiport    = portOutputRegister(digitalPinToPort(MOSI));
        mosipinmask = digitalPinToBitMask(NOSI);
        csport      = portOutputRegister(digitalPinToPort(SS));
        cspinmask   = digitalPinToBitMask(SS);
        dcport      = portOutputRegister(digitalPinToPort(9));
        dcpinmask   = digitalPinToBitMask(9);
      */
      break;

    case U8G_COM_MSG_ADDRESS:
      //SWITCH FROM DATA TO COMMAND MODE (arg_val == 0 for command mode)
      if (arg_val != 0)
      {
        // ORG / digitalWrite(9, HIGH);
        digitalWriteFast(U8G_PI_A0 , arg_val); //HIGH
      }
      else
      {
        digitalWriteFast(U8G_PI_A0 , arg_val); //LOW
      }
      break;

    case U8G_COM_MSG_CHIP_SELECT:
      if (arg_val == 0)
      {
        digitalWriteFast(SS, HIGH);
      }
      else {
        digitalWriteFast(SS, LOW);
      }
      break;

    case U8G_COM_MSG_RESET:
      //TOGGLE THE RESET PIN ON THE DISPLAY BY THE VALUE IN arg_val
      digitalWriteFast(U8G_PI_RESET, arg_val);
      // old / digitalWrite(14, arg_val);
      break;

    case U8G_COM_MSG_WRITE_BYTE:
      //WRITE BYTE TO DEVICE
      SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
      SPI.transfer(arg_val);
      SPI.endTransaction();
      break;

    case U8G_COM_MSG_WRITE_SEQ:
      {
        register uint8_t *ptr = static_cast<uint8_t *>(arg_ptr);
        while ( arg_val > 0 ) {
          SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
          SPI.transfer(*ptr++);
          SPI.endTransaction();
          arg_val--;
        }
      }
      break;

    case U8G_COM_MSG_WRITE_SEQ_P:
      {
        //WRITE A SEQUENCE OF BYTES TO THE DEVICE
        register uint8_t *ptr = static_cast<uint8_t *>(arg_ptr);
        while (arg_val > 0) {
          SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
          SPI.transfer(*ptr++);
          SPI.endTransaction();
          arg_val--;
        }
      }
      break;
  }
  return 1;
}

Now the constructor.
The old way:
Code:
//U8GLIB_NHD31OLED_2X_GR u8g(13, 11, 10, 9);	// SPI Com: SCK = 13, MOSI = 11, CS = 10, A0 = 9
The new way:
Code:
U8GLIB u8g(&u8g_dev_ssd1322_nhd31oled_2x_gr_hw_spi, u8g_com_hw_spi_fn); //  HW SPI Com: CS = 10, A0 = 9 (Hardware Pins are  SCK = 13 and MOSI = 11)

Code:
#include <SPI.h>
#include <u8g_teensy.h>
//Sets up instance and links to Teensy COM files.
//U8GLIB u8g(&u8g_dev_ssd1306_128x64_2x_hw_spi, u8g_com_hw_spi_fn);
U8GLIB u8g(&u8g_dev_ssd1322_nhd31oled_2x_gr_hw_spi, u8g_com_hw_spi_fn); // HW SPI Com: CS = 10, A0 = 9 (Hardware Pins are  SCK = 13 and MOSI = 11)

void setup() {
  // put your setup code here, to run once:
  u8g.setFont(u8g_font_6x12);
  u8g.setColorIndex(1);
}

void loop() {
  // put your main code here, to run repeatedly:
  u8g.firstPage();
  do {
    u8g.drawStr( 2, 10, "UGBLIB Teensy");
  } while ( u8g.nextPage());
  delay(1000);
}
;)
 
Last edited:
[SOLVED] SSD1322 in SPI on Teensy 3.2 with other SPI devices in transactional mode

Hi Chris !

Thanks a lot for the help, your solution is working like a charm, one more time you rock :cool: ! (and thanks for the OLED call with the right SSD model ;))

I made the changes on my U8Glib library with your SPI transactional code, and now the motor is correctly driven in the loop() and don't hang anymore, so cool.
I had an another question waiting after that one, about refreshing a position value coming from the motor board, but I found the solution. I didn't know that you can have multiple u8g.firstPage().... in the loop(), but if they are not overlapping, it seems that there is no issues.

Just for curiosity, why you did not implement it directly in U8Glib, I saw that some of your changes are in u8g_com_arduino_hw_spi.cpp?


Also, I would like to know if there a way to simplify the draw() function ?...

Here the testing loop() and draw() functions I'm talking about,

Code:
// graphic commands to redraw the complete screen should be placed here
void draw(long pos) {

  u8g.setFont(u8g_font_profont10r);
  if (redraw)
  {
    u8g.setColorIndex(1);
    if (ok_l6470)
      u8g.drawStr(1, 6, F("MBOARD - OK"));
    else
      u8g.drawStr(1, 6, F("MBOARD - ERROR"));

    u8g.drawStr(1, 14, F("BLE -"));
    
    u8g.drawStr(1, 32, F("SPEED"));
    u8g.drawStr(1, 58, F("DAMPING"));
    u8g.drawStr(1, 84, F("ANGLE"));
  }
  

  // Display dynamic values
  u8g.setColorIndex(3);
  u8g.setFont(u8g_font_profont11r);
  // Show Speed
  u8g.drawStr(1, 42, F("     ")); // clear line
  u8g.setPrintPos(1, 42);
  u8g.print(_mMAXSPD);

  // Show Damping
  u8g.drawStr(1, 68, F("     ")); // clear line
  u8g.setPrintPos(1, 68);
  u8g.print(_mACC);

  // Show Angle
  u8g.drawStr(1, 94, F("        ")); // clear line
  u8g.setPrintPos(1, 94);
  u8g.print(pos);
 
}

void loop()
{

  // Start motor move if everything is OK
  if (!ok_l6470)
  {
    digitalWrite(ledPin, HIGH);
  } else {
    if (nbLoop != 0)
    {
        _Motor.goToDir(FWD, _mEndPos);
        while (_Motor.getPos() != _mEndPos)
        {
          _mPos = _Motor.getPos();
           // OLED Display
          u8g.firstPage();
          do {
               draw(_mPos);
          } while( u8g.nextPage() );
        }

        _Motor.softStop();
        delay(500);
        
        _Motor.goToDir(REV, 0);
        while (_Motor.getPos() != 0)
        {
          _mPos = _Motor.getPos();
          // OLED Display
          u8g.firstPage();
          do {
               draw(_mPos);
          } while( u8g.nextPage() );
        }
         _Motor.softStop();
        
        nbLoop--;
        delay(500);
    } else {
      _Motor.hardHiZ();  
    }
  }


  // OLED Display
  u8g.firstPage();
  do {
       draw(_mPos);
  } while( u8g.nextPage() );

}

Can I have several draw() (named differently, drawPos(), etc.) functions to only refresh the moving index (and keep the rest displayed), or any other part of the display or do I need to refresh the entire display at every loop cycle (I think the answer is I need to refresh the entire display) ?

Thanks a lot !
 
Last edited:
I am trying to get a second SSD1306 display working with hardware SPI on a Teensy 3.2 but I can't figure out the correct constructor format. Looking through Chris O.'s and Hoss' mods, it appears that AO/DC is hardcoded to pin 9 and Reset to pin 4. Right now the first display is connected this way:
DO/SCK - 13
D1/MOSI - 11
RES - 4
DC/AO - 9
CS - 10

Works properly with the "new way" constructor in post #84.
My understanding is that the second display needs to have Reset, DC/AO and CS/SS on different pins? And a different object with those pins defined?
 
Last edited:
Hi gofaster,

I think that you have to modify the Chris O's mods to include the CS, RST and A0 pins values as parameters in the u8g constructor. That way you can choose the right pins for each display that you want to to control.

It will look likes that for the constructor :
Code:
U8GLIB u8g(&u8g_dev_ssd1322_nhd31oled_2x_gr_hw_spi, u8g_com_hw_spi_fn, CS_pin, RST_pin, A0_pin); // CS = 10, A0 = 9 default values

So you have to change the files u8g_teensy.h ans u8g_teensy.cpp accordingly.
Hope that it will help you.
 
New issue on U8Glib + Teensy and SSD1322 with SleepOn() and SleepOff()

Hi all,

I'm facing to a new issue with my display (SSD1322) and U8Glib running on Teensy 3.2. Does anybody had a problem using SleepOn() and SleepOff functionality ?
I create a new issue on U8Glib GitHub, here the address : https://github.com/olikraus/u8glib/issues/42.

The problem is that any call to SleepOn() or SleepOff() switch off the screen, except if after the call I place a Serial.print(); Don't ask me why, I just discover that makes the functions works because I would to test the call to the function. As soon as I'm removing the Serial.print() the screen remain blank.

Here the both code, without the U8Glib wrapper for Teensy. Please note that this is the only problem I have with this lib on Teensy, all the rest is working perfectly.

Here the code that makes that works.

Code:
void testSleep()
{
  (i) ? _U8G.sleepOn( ) : _U8G.sleepOff();
  (i) ? Serial.println("on") : Serial.println("off");
  i = !i;
}

void loop() {
  _U8G.firstPage();
  do {
    testSleep();
    delay(2000);
  } while( _U8G.nextPage() );
}

And here the code that makes them not working and turn the screen off :

Code:
void testSleep()
{
  (i) ? _U8G.sleepOn() : _U8G.sleepOff();
  i = !i;
}

void loop() {
  _U8G.firstPage();
  do {
    testSleep();
    delay(2000);
  } while( _U8G.nextPage() );
}

I thought about a problem of timing, but introduce delays doesn't change anything.

Any idea ?
Thanks.
 
Status
Not open for further replies.
Back
Top