Hardware SPI

BennehBoy
Fri Feb 03, 2017 12:00 pm
So having upgraded from Arduino Nano to STM32 (Maple Mini clone), I find myself in the ridiculous position of having consumed all the available pins for the devices and analogue sensors that I want to connect.

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.

Image

Thanks.


stevestrong
Fri Feb 03, 2017 12:21 pm
BennehBoy wrote:
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?

BennehBoy
Fri Feb 03, 2017 12:28 pm
Thanks Steve.

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.


zmemw16
Fri Feb 03, 2017 12:33 pm
spi1 is PA4-PA7
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


BennehBoy
Fri Feb 03, 2017 12:49 pm
Thanks,

So I should ignore that first reference to SPI 1 being on PB5, PB_4, PB_3, PA_15.


stevestrong
Fri Feb 03, 2017 12:50 pm
PA15, PB3..5 are the alternate remapping pins of SPI1 from PA4..7, see RM0008, chapter 9.3.10, SPI1 alternate function remapping.
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.


BennehBoy
Fri Feb 03, 2017 1:05 pm
stevestrong wrote:PA15, PB3..5 are the alternate remapping pins of SPI1 from PA4..7, see RM0008, chapter 9.3.10, SPI1 alternate function remapping.
So yes, just ignore it, or use it if you need PA4..7 for other purpose.

zmemw16
Fri Feb 03, 2017 1:40 pm
stevestrong wrote:PA15, PB3..5 are the alternate remapping pins of SPI1 from PA4..7, see RM0008, chapter 9.3.10, SPI1 alternate function remapping.
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.


stevestrong
Fri Feb 03, 2017 2:02 pm
BennehBoy wrote:OK, I’m currently using PA_4 as an analogue input but I can shift that to PB_0 and move the piezo buzzer from that pin to one of the PWM’s that I free up.

BennehBoy
Fri Feb 03, 2017 3:17 pm
stevestrong wrote:
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.

BennehBoy
Fri Feb 03, 2017 4:45 pm
Epic fail, can’t get the screens to work with HW SPI.

Well, they display garbage… I’ve paired the code right back.

Is there any pin preparation I need to do first?


stevestrong
Fri Feb 03, 2017 5:02 pm
Any code available how did you initialize the SPI? Lower the clock frequency?

BennehBoy
Fri Feb 03, 2017 5:18 pm
The library initialises the SPI…

if (hwSPI){
SPI.begin();
#ifdef SPI_HAS_TRANSACTION
SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
#else
SPI.setClockDivider (4);
#endif
}


BennehBoy
Fri Feb 03, 2017 5:37 pm
Software SPI over the exact same pins works fine.

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.


stevestrong
Fri Feb 03, 2017 6:46 pm
Im afraid there is more to adapt in the display library than you expect.
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
}


BennehBoy
Fri Feb 03, 2017 7:39 pm
OK thanks, that’s all part of the adafruit_SSD1306 library.

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 :D


BennehBoy
Sat Feb 04, 2017 11:28 am
I know that hangovers are not conducive with productivity, but today both boards are behaving exactly as expected… I’ve changed nothing.

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.


BennehBoy
Sat Feb 04, 2017 3:36 pm
Flashed back to original bootloaders and can upload again – clearly some quirk of v2 that I’ve not read about – boot pins need to be manually configured?

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.


ahull
Sat Feb 04, 2017 3:38 pm
BennehBoy wrote:Flashed back to original bootloaders and can upload again – clearly some quirk of v2 that I’ve not read about – boot pins need to be manually configured?

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.


BennehBoy
Sat Feb 04, 2017 4:35 pm
Well I got it working,

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.


BennehBoy
Sat Feb 04, 2017 4:59 pm
So, the library doesn’t work properly on a Nano clone either, it rotates the entire display 1 byte right each time it’s drawn, as though it’s incorrectly xferring an extra byte of the screen buffer.

Sigh.


BennehBoy
Sat Feb 04, 2017 8:39 pm
Well, I’m an idiot.

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.


stevestrong
Sun Feb 05, 2017 4:30 pm
@BennehBoy, could you be a bit more specific, what does it mean “HW SPI does nothing”?
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, ...));


BennehBoy
Sun Feb 05, 2017 5:13 pm
No pixels whatsoever, I’ll try using the begintransaction change in the library, thanks.

BennehBoy
Sun Feb 05, 2017 5:37 pm
Nope Nothing.

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


stevestrong
Sun Feb 05, 2017 6:34 pm
My comments:
– 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.


BennehBoy
Sun Feb 05, 2017 7:42 pm
I’ll make the edit and try.

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.


BennehBoy
Sun Feb 05, 2017 7:47 pm
No change. :(

stevestrong
Sun Feb 05, 2017 8:00 pm
– try to use “SPI.write(d);” in line 496 of Adafruit_SSD1306.cpp.

BennehBoy
Sun Feb 05, 2017 8:11 pm
<egg on face>
I hereby authorise you to issue a remote punch to my head. :oops: :oops:

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.


stevestrong
Sun Feb 05, 2017 8:48 pm
Image
:D

BennehBoy
Sun Feb 05, 2017 8:54 pm
:lol: :lol:

BennehBoy
Sun Feb 05, 2017 10:01 pm
Well, this is just very very frustrating.

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.


RogerClark
Sun Feb 05, 2017 10:38 pm
BennehBoy wrote:

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.

BennehBoy
Sun Feb 05, 2017 10:44 pm
They’re daisy chained.

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).


BennehBoy
Sun Feb 05, 2017 10:53 pm
Is it possible that I need to initialise MISO? I see commented out SS intialisations in SPI.cpp but I guess this it to allow those SS pins to be re-used for other purposes – for example I use SPI 1 SS as an analogue input…

the STM32F1 SPI.h includes <libmaple/spi.h> – this header file is not in the libmaple core folder??

I’m probably missing something obvious


RogerClark
Mon Feb 06, 2017 4:03 am
BennehBoy wrote:Is it possible that I need to initialise MISO? I see commented out SS intialisations in SPI.cpp but I guess this it to allow those SS pins to be re-used for other purposes – for example I use SPI 1 SS as an analogue input…

the STM32F1 SPI.h includes <libmaple/spi.h> – this header file is not in the libmaple core folder??

I’m probably missing something obvious


stevestrong
Mon Feb 06, 2017 8:25 am
In SPI master mode the SS pin is left up to the user to configure (to output mode) and to use (set low before transfer and set high after that). But any GPIO can thus be used.
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.

BennehBoy
Mon Feb 06, 2017 8:29 am
stevestrong wrote:In SPI master mode the SS pin is left up to the user to configure (to output mode) and to use (set low before transfer and set high after that). But any GPIO can thus be used.
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.

BennehBoy
Mon Feb 06, 2017 8:31 am
RogerClark wrote:
But if it works for you as analogue, then I’d carry on using it.

stevestrong
Mon Feb 06, 2017 10:45 am
BennehBoy wrote:Regarding the libmaple spi.h should I worry that I can’t find it?

BennehBoy
Mon Feb 06, 2017 10:53 am
Thanks, just me looking in the wrong location. I have a lot to learn.

BennehBoy
Tue Feb 07, 2017 2:24 pm
Roger, I don’t suppose you have your version of this code to hand still? -> http://forum.arduino.cc/index.php?topic … msg2945098

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.


BennehBoy
Tue Feb 07, 2017 7:39 pm
Never mind,

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 :lol: :lol:

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);
}


BennehBoy
Tue Feb 07, 2017 8:51 pm
OK, have merged the code into my LRDuino project and all is working well. YAY :D :D

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.


ahull
Tue Feb 07, 2017 9:35 pm
BennehBoy wrote:OK, have merged the code into my LRDuino project and all is working well. YAY :D :D

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.


BennehBoy
Tue Feb 07, 2017 9:56 pm
As if by magic…

Image

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


Leave a Reply

Your email address will not be published. Required fields are marked *