Artemis RedBoard vs Uno

I have code that compiles for the Uno but not the RedBoard.

Here is the error I receive:

C:\Users\KenMiller\Documents\Arduino\libraries\SH1106_SPI\SH1106_SPI.h:42:23: error: ‘PORTB’ was not declared in this scope

#define SH1106_PORT PORTB

I understand that PORTB is defined for the Uno but not the RedBoard, but I can’t find where it’s defined for the Uno.

Where is it defined?

Alternatively (or additionally), how do I define it for the RedBoard?

The underlying microprocessors in the original Arduino lineup are Atmel ones. While there are many versions and sizes of these ATMega chips out there, the base hardware is similar and they divide the pins into “PORT” in groups of 8. So PORTB will have 8 pins and the datasheet will tell you which pin is which. In order to hide this level of hardware detail and to make programs compatible across the whole platform, when you select the “UNO” or other device in the IDE the program will associate the correct hardware definitions. The ports are defined in the “iom328p.h” header file in the avr/include/avr subdirectory.

Thank you. Now I know how PORTB gets defined for the Uno.

I found iom328p.h in the following directory:

C:\Program Files (x86)\Arduino\hardware\tools\avr\avr\include\avr

Should this file be being used when I compile for the RedBoard?

If so, since it’s not, does that imply there is an issue with my environment?

Or, should there be a different equivalent file for the RedBoard?

If so, why might it not be?

Is this all documented somewhere?

From what the IDE shows when I compile for each of the platforms, the board and the core files for the RedBoard come from a different location.

The PORTB definition apparently doesn’t exist anywhere in the path for the Apollo3.

Should I just take #defines from the iom328p.h and paste them into the variant.h file in:

C:\Users\KenMiller\AppData\Local\Arduino15\packages\SparkFun\hardware\apollo3\2.0.5\variants\SFE_ARTEMIS

Are these just missing and nobody noticed?

Is this something everyone but me knows?

Is what I am proposing the correct way to resolve this?

Is there documentation on all of this stuff?

UNO

Using board ‘uno’ from platform in folder: C:\Program Files (x86)\Arduino\hardware\arduino\avr

Using core ‘arduino’ from platform in folder: C:\Program Files (x86)\Arduino\hardware\arduino\avr

REDBOARD ARTEMIS

Using board ‘sfe_artemis’ from platform in folder: C:\Users\KenMiller\AppData\Local\Arduino15\packages\SparkFun\hardware\apollo3\2.0.5

Using core ‘arduino’ from platform in folder: C:\Users\KenMiller\AppData\Local\Arduino15\packages\SparkFun\hardware\apollo3\2.0.5

iom328p.h is unique to the Atmel ATMEGA328p and shouldn’t be used for anything else. There will be a similar file for each processor that defines what hardware that processor actually has and what addresses in memory it lives at.

Arduino has added a layer of abstraction on top of that, defining things like A1 and D5. These take into account not only the processor but PCB layout as well.

SH1106_SPI.h should not be using the processor-specific defines (PORTB) or should have sections #ifdef’ed for each processor it works with.

/mike

OK, that all makes sense.

So how do I fix this?

There should be an equivalent file for the RedBoard Artemis - yes?

Apparently there isn’t.

I used the Boards Manager to install the SparkFun Apollo3 and I am able to successfully compile, download to the board, and run other code.

So it would appear that my tool chain is OK.

I did it on a second development system and the results are identical.

This is my first Arduino project, so I am trying to figure out if this is expected to work and I have either done something I shouldn’t or not done something I should; or if I am expected to figure out what’s missing and fix it myself?

Is there no support from SparkFun?

Hi Ken.

You’re trying to compile code written for a different system than the one you want to use. It’s a bit like trying to install Microsoft Office for Mac on a Windows PC. With enough work you could rewrite the Mac code to run on a PC but it’s a big job and you really need to know intricate details on both systems to do it. Some code and libraries are designed to be relatively hardware independent and will compile under lots of different systems but frequently someone will design their code specifically for one platform and the code depends on certain peripheral or hardware to be in a particular place. When you switch to a different platform things don’t line up and then compilation fails.

It sounds like this is what’s happening in your case and unfortunately SparkFun isn’t able to assist with the conversion, we just don’t have the resources to do that. You may end up needing to write your own code/libraries from scratch for the device you’re wanting to use.

Thank you for your response Chris.

Even though it is not what I expected to hear, or what I hoped to hear, I do appreciate you better setting my expectations.

Which SH1106 library are you using? This isn’t something you are doing wrong; it’s just that the library was written for Unos only (it may work with Megas but depending on where it connects to get the SPI signals, it may not either…) This was either done for speed (digital.write is painfully slow) or because the author of the library was only using Unos and was not aware of the differences for non-Atmel parts.

/mike

Hi Mike,

Arthur Liberman’s.

I see AdaFruit has one that supports that display over SPI so I’ll try that.

BUT, the reason I was trying this display is because the 4.3" display I purchased from AdaFruit based on the IL9341 didn’t work because of problems compiling the AdaFruit driver with the RedBoard Artemis and AdaFruit also doesn’t really support their stuff at this level either so I figured I’d try someone else’s driver for this display. Ha!

Assuming the library you are using is the one found at https://forum.arduino.cc/index.php?topic=242880.0 they are explicitly using register access to talk to some of the pins. This will work fine for the ATMega Arduinos but won’t work for any other processor…

Note that I don’t have either that display or an Artemis board, so I can’t test any of this, but try using this version of SH1106_SPI.cpp. I just replaced direct register access to PORTB with the appropriate digitalWrite() calls. It will be a bit slower but at least should get you further.

/mike

#include "SH1106_SPI.h"
#include <SPI.h>

SH1106_SPI::SH1106_SPI()
{
}

#define PIN_DC    D8 
#define   PIN_RESET D9 
#define   PIN_CS    D10

void SH1106_SPI::begin(bool invert, bool fastSpi, uint8_t contrast, uint8_t Vpp)
{
	pinMode(PIN_DC, OUTPUT); 
	pinMode(PIN_RESET, OUTPUT); 
	pinMode(PIN_DC, OUTPUT); 
	//SH1106_PORT |= (PIN_DC | PIN_RESET | PIN_CS);
	//SH1106_DDR |= (PIN_DC | PIN_RESET | PIN_CS);
	SPI.begin();
	if (fastSpi)
		SPI.setClockDivider(SPI_CLOCK_DIV2);
	// LCD init section:
	
	uint8_t invertSetting = invert ? 0xA7 : 0xA6;
	Vpp &= 0x03;

	// Must reset LCD first!
	digitalWrite(PIN_RESET, LOW);
	//SH1106_PORT &= ~PIN_RESET;
	delay(1);
	digitalWrite(PIN_RESET, HIGH);
	//SH1106_PORT |= PIN_RESET;
	
	
	this->writeLcd(SH1106_COMMAND, 0xAE);    /*display off*/
	this->writeLcd(SH1106_COMMAND, 0x02);    /*set lower column address*/
	this->writeLcd(SH1106_COMMAND, 0x10);    /*set higher column address*/
	this->writeLcd(SH1106_COMMAND, 0x40);    /*set display start line*/
	this->writeLcd(SH1106_COMMAND, 0xB0);    /*set page address*/
	this->writeLcd(SH1106_COMMAND, 0x81);    /*contract control*/
	this->writeLcd(SH1106_COMMAND, contrast);    /*128*/
	this->writeLcd(SH1106_COMMAND, 0xA1);    /*set segment remap*/
	this->writeLcd(SH1106_COMMAND, invertSetting);    /*normal / reverse*/
	this->writeLcd(SH1106_COMMAND, 0xA8);    /*multiplex ratio*/
	this->writeLcd(SH1106_COMMAND, 0x3F);    /*duty = 1/32*/
	this->writeLcd(SH1106_COMMAND, 0xAD);    /*set charge pump enable*/
	this->writeLcd(SH1106_COMMAND, 0x8B);     /*external VCC   */
	this->writeLcd(SH1106_COMMAND, 0x30 | Vpp);    /*0X30---0X33  set VPP   9V liangdu!!!!*/
	this->writeLcd(SH1106_COMMAND, 0xC8);    /*Com scan direction*/
	this->writeLcd(SH1106_COMMAND, 0xD3);    /*set display offset*/
	this->writeLcd(SH1106_COMMAND, 0x00);   /*   0x20  */
	this->writeLcd(SH1106_COMMAND, 0xD5);    /*set osc division*/
	this->writeLcd(SH1106_COMMAND, 0x80);
	this->writeLcd(SH1106_COMMAND, 0xD9);    /*set pre-charge period*/
	this->writeLcd(SH1106_COMMAND, 0x1F);    /*0x22*/
	this->writeLcd(SH1106_COMMAND, 0xDA);    /*set COM pins*/
	this->writeLcd(SH1106_COMMAND, 0x12);
	this->writeLcd(SH1106_COMMAND, 0xDB);    /*set vcomh*/
	this->writeLcd(SH1106_COMMAND, 0x40);
	this->writeLcd(SH1106_COMMAND, 0xAF);    /*display ON*/

	this->clear();
}

size_t SH1106_SPI::write(uint8_t data)
{
	// Non-ASCII characters are not supported.
	if (data < 0x20 || data > 0x7F) return 0;
	if (this->m_Column >= 123)
		this->advanceXY(SH1106_X_PIXELS - this->m_Column);

	uint8_t buf[6];
	memcpy_P(buf, ASCII[data - 0x20], 5);
	buf[5] = 0x00;
	this->writeLcd(SH1106_DATA, buf, 6);
	this->advanceXY(6);
	return 1;
}

void SH1106_SPI::clear()
{
	for (uint8_t j = SH1106_ROWS; j > 0; j--)
	{
		this->gotoXY(0, j-1);
		for (uint8_t i = SH1106_X_PIXELS; i > 0; i--)
			this->writeLcd(SH1106_DATA, 0x00);
	}
	this->gotoXY(0, 0);
}

uint8_t SH1106_SPI::gotoXY(uint8_t x, uint8_t y) 
{
	if (x >= SH1106_X_PIXELS || y >= SH1106_ROWS) return SH1106_ERROR;
	this->m_Column = x;
	this->m_Line = y;
    x = x + 2;										// Panel is 128 pixels wide, controller RAM has space for 132,
													// it's centered so add an offset to ram address.

    this->writeLcd(SH1106_COMMAND, 0xB0 + y);		// Set row
    this->writeLcd(SH1106_COMMAND, x & 0xF);		// Set lower column address
    this->writeLcd(SH1106_COMMAND, 0x10 | (x >> 4));// Set higher column address
	return SH1106_SUCCESS;
}

uint8_t SH1106_SPI::writeBitmap(const uint8_t *bitmap, uint8_t x, uint8_t y, uint8_t width, uint8_t height)
{	
	if (this->gotoXY(x, y) == SH1106_ERROR) return SH1106_ERROR;	
	const uint8_t *maxY = bitmap + height * width;	

	for (const uint8_t *y = bitmap; y < maxY; y += width)
	{	
		this->writeLcd(SH1106_DATA, y , width);
		this->gotoXY(this->m_Column, this->m_Line + 1);
	}

	this->advanceXY(width);
	return SH1106_SUCCESS;
}

void SH1106_SPI::advanceXY(uint8_t columns)
{
	this->m_Column += columns;
	if (this->m_Column >= SH1106_X_PIXELS)
	{
		this->m_Column %= SH1106_X_PIXELS;
		this->m_Line++;
		this->m_Line %= SH1106_ROWS;
		this->gotoXY(this->m_Column, this->m_Line);
	}
}

void SH1106_SPI::writeLcd(uint8_t dataOrCommand, const uint8_t *data, uint16_t count)
{
	//SH1106_PORT = (SH1106_PORT & ~PINS_CS_DC) | dataOrCommand;
	digitalWrite(PIN_CS, LOW);
     digitalWrite(PIN_DC, dataOrCommand);

    for (uint16_t i = count; i > 0; i--)
	{
		SPI.transfer(data[count-i]);
		//SPDR = *data++;
		//while (!(SPSR & _BV(SPIF)));
	}
//	SH1106_PORT |= PIN_CS;
	digitalWrite(PIN_CS, HIGH);
}

void SH1106_SPI::writeLcd(uint8_t dataOrCommand, uint8_t data)
{
	digitalWrite(PIN_CS, LOW);
     digitalWrite(PIN_DC, dataOrCommand);

	//SH1106_PORT = (SH1106_PORT & ~PINS_CS_DC) | dataOrCommand;
	SPI.transfer(data);
	//SPDR = data;
	//while (!(SPSR & _BV(SPIF)));
	digitalWrite(PIN_CS, HIGH);
	//SH1106_PORT |= PIN_CS;
}

Thanks Mike for taking the time to take a stab at this.

Unfortunately, your code yields the following error when compiled and it’s obviously a similar thing in that the RedBoard SPI code lacks a method setClockDivider :frowning:

ForumModded:21:9: error: ‘class arduino::MbedSPI’ has no member named ‘setClockDivider’

SPI.setClockDivider(SPI_CLOCK_DIV2);

^~~~~~~~~~~~~~~

The #include <SPI.h> resolves to this:

“C:\Users\KenMiller\AppData\Local\Arduino15\packages\SparkFun\hardware\apollo3\2.0.5\libraries\SPI\src\SPI.cpp”

I did try the AdaFruit driver and it compiled without error. Plus, the software SPI version at least lit up the display, although it was badly garbled. The hardware SPI version did nothing.

However, this isn’t even the display I want to use. I was just trying to make this display work because I couldn’t get the one I want to use working and didn’t understand why.

At this point, I am going to completely restart my project. I went down this path under the erroneous assumption that if two boards of the Arduino persuasion physically plugged together that they would work - doh!

I selected the RedBoard Artemis because it has onboard BLE. But it appears it will be a LOT easier to start with a more compatible board and find a way to add BLE to that since the alternative appears to be to write/re-write a bunch of the board and peripheral driver code.

If anyone sees any errors in my conclusions I’d like to know.

Also, I’ve ordered a copy of Arduino A Technical Reference. Are there additional equally or better-detailed references avaialble?