One issue which I had previously encountered on Nano was that I was having to use 2 separate software SPI buses to get SSD1306 & MAX31586 devices to work – this is because the 2 software implementations in the respective adafruit libraries would not coexist on the same software SPI bus.
For whatever reason I never thought to try them both on the same HW SPI bus (principally because I did not at that time appreciate that SPI could be done in HW on the device – I’m a n00b).
Now that I realise I can use HW SPI I want to refactor my code & hw to use a single HW SPI bus.
Questions:
1) I take it there’s no issues from a core perspective with me having multiple devices on a HW SPI bus on STM32, ie I just need a CS pin for each device?
2) The below reference for Maple Mini clones shows SPI1 as being exposed over 2 sets of pins, PB5, PB_4, PB_3, PA_15 and PA_4, PA_5, PA_6, PA_7 – the hardware doesn’t duplicate the SPI bus on the second set of pins does it? This would defeat the purpose of what I’m trying to achieve.

Thanks.
2) The below reference for Maple Mini clones shows SPI1 as being exposed over 2 sets of pins, PB5, PB_$, PB_3, PA_15 and PA_4, PA_5, PA_6, PA_7 – the hardware doesn’t duplicate the SPI bus on the second set of pins does it?
In relation to your first answer, why does the reference diagram show SPI1 exposed on 2 different sets of pins? Is this a config time option or just an error?
Fortunately the SSD’s and the MAX both use MSBFIRST and SPI_MODE0 – the two libraries are clocked significantly differently though, 8000000 & 500000 – I’m not even sure those speeds are achievable? clock divisor of 9 and 144 respectively? OR does it not work that way.
The other SPI device I’d like to add is an SD card reader – I’ll have a look at SDFAT to see how that would play.
spi2 is PB12-PB15
PA4 & PB12 being the CS line, naming sequence is the same and is cs, sck, miso and mosi
for multiple devices on SPI1, the PA5-PA7 lines are common to all, each device has its own CS, PA1,PA2,PA3 i tend to use as
CS, D/C and RES
PA4 needs to be set as an output and high, similar i suppose to the AVR SS line aka pin 10 ( not sure if still true ?? historical)
my system unit has an A4 sheet with SPI1, SPI2, I2C1 and I2C2 pins listed in very large font
stephen time crossed again
So I should ignore that first reference to SPI 1 being on PB5, PB_4, PB_3, PA_15.
So yes, just ignore it, or use it if you need PA4..7 for other purpose.
In the same manner USART1 pins can be remapped from PA9/10 to PB6/7.
So yes, just ignore it, or use it if you need PA4..7 for other purpose.
So yes, just ignore it, or use it if you need PA4..7 for other purpose.
In the same manner USART1 pins can be remapped from PA9/10 to PB6/7.
Actually, if you use SPI1 only in master mode, than you don’t need to “remap” PA4 by AFIO register, because PA4 (=chip select signal) is used anyway as normal GPIO in output mode. So you are free to select any other free GPIO as CS instead of PA4.
Well, they display garbage… I’ve paired the code right back.
Is there any pin preparation I need to do first?
if (hwSPI){
SPI.begin();
#ifdef SPI_HAS_TRANSACTION
SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
#else
SPI.setClockDivider (4);
#endif
}
With HW all the display is compressed into the bottom 8 lines of pixels, I can see it drawing some things correctly but something is up.
I’ve hacked the clocks about to no avail so far.
Specially the begin() function and the data writing functions.
Without checking the whole code is difficult to see where the problem is.
Which display library are you using?
EDIT
I already see a problem: you don’t need begin() if you use beginTransaction().
If you use beginTransaction(), you don’t need setClockDivider().
So I would re-write your code part this way:
if (hwSPI){
#ifdef SPI_HAS_TRANSACTION
SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
#else
SPI.begin();
SPI.setClockDivider (4);
#endif
}
I’ll play some more tomorrow, I’ve been tearing my hair out for 2 hours trying to figure out why 2 identically wired and programmed setups give me different results. If I swap the controllers between the 2 boards the issue stays on the same board, have tested all wires, swapped in different OLED’s etc.
Anyway, I’ve had enough and I’m going to the pub ![]()
Oddly when I now try to upload to the paired back test system the IDE hangs on:
maple_loader v0.1
Resetting to bootloader via DTR pulse
And I can see the LED pulsing on the board – is this due to the R10 issue? not sure what’s cuasing this but I can’t get a new sketch on now.
Anyhow, now of course the bloody thing is displaying garbage again.
I know I’m doing something really dumb here, but just what is evading me.
Anyhow, now of course the bloody thing is displaying garbage again.
I know I’m doing something really dumb here, but just what is evading me.
Scrapped the wiring and started again – must’ve got something wrong somehow, even though I checked the wires umpteen times.
So anyway, twiddled the suggestions into the 1306 library and HWSPI still not functional.
I’m going to test the library on a nano with HWSPI in case it’s an inherent fault.
Sigh.
For some reason I had it in my head that the DC pin on the screens was MISO, au contraire it’s simply a signal to tell the display to expect a command.
So HW SPI works on the Nano afterall.
Still can’t make it work on the Maple Mini – even after making edits to the library (disabling portreg code, and forcing spi mode and clock divider).
I also can’t get this code to work -> http://www.stm32duino.com/viewtopic.php?t=357
Software SPI works just fine over the same pins (ie no wiring issue)
Ironically, the reason that my 2 software spi devices probably didn’t work together was that I would’ve been stomping on MISO with the DC line for the SSD1306. If I can’t figure out why HW SPI does nothing then I’ll likely try that next.
Can you plot at least one pixel onto the screen? Or at least set background to a color (red)?
I would also recommend to use exclusively the SPI.beginTransaction(SPISetting(8000000, ...));
Zip file of libraries and code attached. This is just the ‘slimmed down’ test code. Will compile against Uno/Nano and STM32 – wire accordingly.
Comment/Uncomment lines 75 & 85 to switch between software/hardware SPI
The included Adafrui_SSD1306 library has been edited to remove portreg code & only use begin transaction.
PS. Nano/Uno HW SPI only works if the SPi.begin code is re-enabled in the lib
– I don’t see where “_VARIANT_ARDUINO_STM32_” is defined, to use the correct OLED_CS in line 32 of INO.
– I don’t see why do you need <Wire.h>. Even in the Adafruit_SSD1306 lib, it is not needed as long you only have SPI connection, so you could remove or #ifdef all of Wire instances.
– try to use “SPI.write(d);” in line 496 of Adafruit_SSD1306.cpp.
Otherwise everything else looks reasonable to me.
As alternative, you could try my version of SPI lib.
Hmm _VARIANT_ARDUINO_STM32_ worked for me previously, I thought this was defined as part of the core?
I’ll just comment out all the conditonals and try as is, to be sure.
I hereby authorise you to issue a remote punch to my head.
I just examined the breadboard and found that the power jumper had fallen out – just found it next to my desk.
Hardware SPI works without any library modifications.
</egg on face>
I truly am an idiot.

So I rejigged all the wiring on my main test board to use the hardware SPI and the 8 screens work just fine…. except, after a while 2 of them just fail to display anything.
The MAX31856 won’t work sharing on the same bus, but unlike with s/w spi at least it doesn’t cause the OLED’s just fill with white pixels – I’ll see if it works with the OLED’s disabled.
Can’t imagine I’m going to get SD working too lol.
The MAX31856 won’t work sharing on the same bus, but unlike with s/w spi at least it doesn’t cause the OLED’s just fill with white pixels – I’ll see if it works with the OLED’s disabled.
I figured out why some of the screens were going blank, software spi code in the library initialises the CS pins and writes them high.
Adding CS pin initialisation and writing them all high fixed that for HW SPI.
Still working on the MAX31856 problem.
Will be moved onto custom strip board with interconnects as short as possible, SPI bus speed will be adjusted for the fact that the head unit needs to be dangled off the end of an ~ 60cm ribbon cable (works fine at 8mhz on the Arduni Nano that’s in vehicle presently I might add).
the STM32F1 SPI.h includes <libmaple/spi.h> – this header file is not in the libmaple core folder??
I’m probably missing something obvious
the STM32F1 SPI.h includes <libmaple/spi.h> – this header file is not in the libmaple core folder??
I’m probably missing something obvious
In slave mode it is needed and configured automatically by the SPI hw to detect external SS, but not tried what happens if you re-configure it after SPI slave initialisation.
In slave mode it is needed and configured automatically by the SPI hw to detect external SS, but not tried what happens if you re-configure it after SPI slave initialisation.
But if it works for you as analogue, then I’d carry on using it.
I might give it a try with begintransaction, still not had any luck with the adafruit library, I don’t need all the bloat that library brings to the table so I’m quite happy to go with a minimalist approach.
I hacked this together and it works… swapped it to use beginTransation also.
Some things to note… the adafruit library incorrectly uses SPI_MODE0, the device expects SPI_MODE3 – correcting the library did not result in success.
Double checking wiring always helps, I had SDO & SDI swapped! Correcting this did not result in success.
Learning is great fun, even when it includes public humiliation
![]()
Just need to hack this into my project and make sure it coexists nicely with the OLED’s now.
#include "SPI.h"
#define MAX_CS 29
#define NumRegisters 10
byte RegisterValues[] = {0x90, 0x03, 0xFC, 0x7F, 0xC0, 0x07, 0xFF, 0x80, 0x00, 0x00 };
String RegisterNames[] = {"CR0", "CR1", "MASK", "CJHF", "CHLF", "LTHFTH", "LTHFTL", "LTLFTH", "LTLFTL", "CJTO"};
byte RegisterAddresses[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x04, 0x06, 0x07, 0x08, 0x09 };
void setup() {
// put your setup code here, to run once:
Serial.begin(250000);
pinMode(MAX_CS, OUTPUT); //init CS
digitalWrite(MAX_CS, HIGH);
InitializeChannel(MAX_CS);
delay(500);
}
void InitializeChannel(int Pin) {
Serial.print("Initializing channel on pin ");
Serial.println(Pin);
for (int i = 0; i < NumRegisters; i++) {
WriteRegister(Pin, i, RegisterValues[i]);
}
}
void loop() {
Serial.println("=================================================");
double TC_temp = ReadTemperature(MAX_CS);
Serial.print("\tThermocouple temp is ");
Serial.println(TC_temp);
delay(1000);
}
byte ReadSingleRegister(int Pin, byte Register) {
SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE3));
digitalWrite(Pin, LOW);
delayMicroseconds(1);
SPI.transfer(Register & 0x7F); //set bit 7 to 0 to ensure a read command
delayMicroseconds(1);
byte data = SPI.transfer(0);
digitalWrite(Pin, HIGH);
SPI.endTransaction();
return data;
}
unsigned long ReadMultipleRegisters(int Pin, byte StartRegister, int count) {
//reads up to 4 sequential registers
SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE3));
digitalWrite(Pin, LOW);
unsigned long data = 0;
SPI.transfer(StartRegister & 0x7F); //force bit 7 to 0 to ensure a read command
delayMicroseconds(1);
for (int i = 0; i < count; i++) {
data = (data << 8) | SPI.transfer(0); //bitshift left 8 bits, then add the next register
}
digitalWrite(Pin, HIGH);
SPI.endTransaction();
return data;
}
void WriteRegister(int Pin, byte Register, byte Value) {
byte Address = Register | 0x80; //Set bit 7 high for a write command
SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE3));
digitalWrite(Pin, LOW);
delayMicroseconds(1);
SPI.transfer(Address);
delayMicroseconds(1);
SPI.transfer(Value);
digitalWrite(Pin, HIGH);
SPI.endTransaction();
}
double ReadTemperature(int Pin) {
double temperature;
long data;
data = ReadMultipleRegisters(Pin, 0x0C, 4);
// Strip the unused bits and the Fault Status Register
data = data >> 13;
// Negative temperatures have been automagically handled by the shift above :-)
// Convert to Celsius
temperature = (double) data * 0.0078125;
// Return the temperature
return (temperature);
}
I added some extra code in which checks the state of the status register also, so my fault checking still works.
Job jobbed. Code will be on github later.
I added some extra code in which checks the state of the status register also, so my fault checking still works.
Job jobbed. Code will be on github later.

The all important part is the top third from left screen, reading just over ambient (well within %err for the k-type).

