SPI Slave woes :-(


#1

Does anyone have experience using SPI? To get the following to even try to work, I had to set

#define DEVICE_SPISLAVE 1
in device.h

however, I get the following error -

..\Mine\ArduboyHat\Test.cpp|80|error: 'class mbed::SPI' has no member named 'read'|
#include "Pokitto.h"
#include "HWSound.h"
#include "SPISlave.h"

Pokitto::Core game;

void startup(){
    game.begin();
    game.display.persistence=1;
    game.setFrameRate(255); // max out the frame rate so that it doesn't get used
    game.display.setInvisibleColor(-1);

    //SPI spi(P1_22, P1_21, P1_20); // mosi, miso, sclk
    SPISlave device(P1_21, P1_22, P1_20, P1_5); // mosi, miso, sclk, ssel

    device.reply(0x00);              // Prime SPI with first reply
}


 int main(){

    startup();

    while (game.isRunning()) {

        int v = device.read();

        if(game.update()){

            //print v;

        } // update

    } // game running

return 1;
}

Am I missing something simple, or is something not set up correctly in Pokittolib somewhere?


#2

Well, given that the SD is read using SPI, we know SPI must be able to work


#3

@spinal, I think you are calling a method that is part of mbed OS5. Pokitto is OS2


#4

This code shouldn’t even compile, main can’t actually ‘see’ device,
because device is a local variable in another function.
device is being destroyed when startup ends,
which I suspect is the cause of whatever problem you’re having.

Also your main function shouldn’t return 1.
For a main function, 0 or EXIT_SUCCESS (from <cstdlib>) indicates success and EXIT_FAILURE (from <cstdlib>) indicates failure.


Edit

Ah, looking at the error message I’m guessing you’ve got two different variables in two different scopes both named device?

I’m guessing SPISlave.h contains a definition along the lines of SPI device,
which is why the compiler is giving 'class mbed::SPI' has no member named 'read' instead of telling you that device doesn’t exist.

This is an example of why picking good, descriptive names is important.


#5

Not because of anything I’ve done, that’s my entire source there.


#6

I thought SPISlave.h was a local file because you’re using quote includes.
You’re supposed to use angle-bracket includes for external libraries like Pokitto.h.

In which case there’s almost certainly a global SPI object called device somewhere.

Like I said, main can’t see the device being defined in startup because they’re two different scopes,
and the device in startup is destroyed after startup returns.


#7

As @Pharap said, this is a case where better naming would’ve made the problem clear:

../main.cpp: In function 'int main()':
../main.cpp:26:17: error: 'bacon' was not declared in this scope
         int v = bacon.read();
                 ^~~~~

#8

Got it, renamed device, moved it to be global. Seems ok.
Thanks @Pharap


#9

I found out what device is:

The moral of this story: dumping everything into the global namespace is a bad idea.
namespaces are your friends.

Also, use descriptive names.


#10

First hurdle == fail…

According to my logic analyser, the following code is only sending ‘0’ every time, rather than incrementing.
Can anyone see why?

// Simple SPI Master
// https://os.mbed.com/users/picnic/code/SSM/file/ec8b56c64406/main.cpp/


#include <Pokitto.h>
#include <HWSound.h>
#include <SPISlave.h>

Pokitto::Core game;

Pokitto::Display d;

//SPISlave spiSlave(P1_21, P1_22, P1_20, P1_5); // mosi, miso, sclk, ssel
SPI spi(P1_21, P1_22, P1_20); // mosi, miso, sclk
DigitalOut cs(P1_5);


//------------------------[ Button handling, very accurate ]------------------------
#define HELD 0
#define NEW 1
#define RELEASE 2
byte CompletePad, ExPad, TempPad, myPad;
bool _A[3], _B[3], _C[3], _Up[3], _Down[3], _Left[3], _Right[3];
int _AHeld, _BHeld, _CHeld = 0;

DigitalIn _aPin(P1_9);
DigitalIn _bPin(P1_4);
DigitalIn _cPin(P1_10);
DigitalIn _upPin(P1_13);
DigitalIn _downPin(P1_3);
DigitalIn _leftPin(P1_25);
DigitalIn _rightPin(P1_7);

void UPDATEPAD(int pad, int var) {
  _C[pad] = (var >> 1)&1;
  _B[pad] = (var >> 2)&1;
  _A[pad] = (var >> 3)&1;
  _Down[pad] = (var >> 4)&1;
  _Left[pad] = (var >> 5)&1;
  _Right[pad] = (var >> 6)&1;
  _Up[pad] = (var >> 7)&1;
}

void UpdatePad(int joy_code){
  ExPad = CompletePad;
  CompletePad = joy_code;
  UPDATEPAD(HELD, CompletePad); // held
  UPDATEPAD(RELEASE, (ExPad & (~CompletePad))); // released
  UPDATEPAD(NEW, (CompletePad & (~ExPad))); // newpress
}

byte updateButtons(byte var){
   var = 0;
   if (_cPin) var |= (1<<1);
   if (_bPin) var |= (1<<2);
   if (_aPin) var |= (1<<3); // P1_9 = A
   if (_downPin) var |= (1<<4);
   if (_leftPin) var |= (1<<5);
   if (_rightPin) var |= (1<<6);
   if (_upPin) var |= (1<<7);

   return var;
}

void waitNoKeys(){
    while(_A[HELD] || _B[HELD] || _C[HELD] || _Up[HELD] || _Down[HELD] || _Left[HELD] || _Right[HELD]){
        myPad = updateButtons(myPad);
        UpdatePad(myPad);
    }
}


// Golbals
long int frameNumber=0;
char tempText[32];
int lineNum = 0;


    long c = 0;
    unsigned char r = 0;
    unsigned char v = 0;
    int ch;

void sendbyte() {
    // Select the device by seting chip select low
    cs = 0;
    v = c & 0xff;
    r = spi.write( v );
    // Deselect the device
    cs = 1;

    d.printf("%ld v=%02x\r\n", c, v);
    if (( c>0 ) && ( r!=(unsigned char)(v-1)) ) {
        d.printf( "   Echo error %02x!=%02x\r\n", v-1, r );
    }
    c++;
}

void updateScreen(){

    game.update();
}

void startup(){
    game.begin();
    d.persistence=1;
    game.setFrameRate(255); // max out the frame rate so that it doesn't get used
    d.setInvisibleColor(-1);

    // Setup the spi
    // second edge capture, with a 1MHz clock rate
    spi.format(8,3);
    spi.frequency(400000);
}

int main(){

    startup();

    d.println("SPI Master");
    d.println("MOSI = P1_21");
    d.println("MISO = P1_22");
    d.println("SCLK = P1_21");
    d.println("  CS = P1_5");

    while (game.isRunning())
    {

        // update buttons
        myPad = updateButtons(myPad);
        UpdatePad(myPad);

        if(_A[NEW]){
            sendbyte();
        }

        updateScreen();
    }

return 1;
}


#11

according to datasheet P1_21 is SPI1 MISO and P1_22 is SP1 MOSI.


#12

This is true:

Where the hell is byte coming from?

Does the Pokitto library introduce a byte type as part of the fake Arduino code or something?


I’d like to offer some class-oriented code to make life easier:

class PadState
{
private:
	static constexpr std::uint8_t CShift = 1;
	static constexpr std::uint8_t BShift = 2;
	static constexpr std::uint8_t AShift = 3;
	static constexpr std::uint8_t DownShift = 4;
	static constexpr std::uint8_t LeftShift = 5;
	static constexpr std::uint8_t RightShift = 6;
	static constexpr std::uint8_t UpShift = 7;
	

private:
	std::uint8_t state;
	
public:
	explicit PadState(std::uint8_t state) :
		state{state}
	{
	}
	
	std::uint8_t getStateRaw() const
	{
		return this->state;
	}
	
	bool isAnyButtonPressed() const
	{
		return (this->state != 0);
	}
	
	bool isAPressed() const
	{
		return (((this->state >> AShift) & 0x1) != 0);
	}
	
	bool isBPressed() const
	{
		return (((this->state >> BShift) & 0x1) != 0);
	}
	
	bool isCPressed() const
	{
		return (((this->state >> CShift) & 0x1) != 0);
	}
	
	bool isUpPressed() const
	{
		return (((this->state >> UpShift) & 0x1) != 0);
	}
	
	bool isDownPressed() const
	{
		return (((this->state >> DownShift) & 0x1) != 0);
	}
	
	bool isRightPressed() const
	{
		return (((this->state >> RightShift) & 0x1) != 0);
	}
	
	bool isLeftPressed() const
	{
		return (((this->state >> LeftShift) & 0x1) != 0);
	}
};

PadState previousState;
PadState currentState;

PadState padCurrent;
PadState padReleased;
PadState padPressed;

void updatePad(PadState joyCode)
{
	previousState = currentState;
	currentState = joyCode;
	
	const std::uint8_t previous = previousState.getStateRaw();
	const std::uint8_t current = currentState.getStateRaw();
	
	padCurrent = currentState;
	padReleased = PadState(previous & ~current);
	padPressed = PadState(current & ~previous);
}

PadState getCurrentPadState()
{
   std::uint8_t state = 0;
   
   if (_cPin) var |= (1<<1);
   if (_bPin) var |= (1<<2);
   if (_aPin) var |= (1<<3); // P1_9 = A
   if (_downPin) var |= (1<<4);
   if (_leftPin) var |= (1<<5);
   if (_rightPin) var |= (1<<6);
   if (_upPin) var |= (1<<7);

   return { state };
}

void waitUntilNoKeysPressed()
{
	while(padCurrent.isAnyButtonPressed())
		updatePad(getCurrentPadState())
}

Uses less memory, is possibly faster for most operations.
Technically having both padCurrent and currentState is redundant, but that’s not a major problem.

It’s possible to override ~ and & if you don’t like having to use getStateRaw().


#13

Dunno, I’ve been copy+pasting this code for a while now, I think arduino has it, but I also think whatever C compiler was used got GBA homebrew had it also. I always just assumed it was uint8_t.


#14

It does, but Pokitto isn’t Arduino.

I probably is, but it’s non-standard,
so there’s no guarantee that an implementation will even have it.

C++ does introduce a std::byte in C++17,
but you can’t do arithmetic with it.


#15

OK, I have the master sending incrementing byte values and reading successfully on the logic analyser. Now just got to get the slave to read them, so far, nothing.
It is meant to be MOSI->MOSI, MISO->MISO isn’t it? not cross over?


#16


#17

Its not cross over. master and slave remain the same throughout the communication


#18

problem seems to be solved. It looks like you just can’t use a different pin for SS.
Setting this -

SPISlave spi(P1_22, P1_21, P1_20, P1_23); // mosi, miso, sclk, ssel

seems to work perfectly… Odd that you can define different pins, but they wont be used.


#19

OK, it seems to work, I hooked up my Arduboy to the spi pins, when it first boots, I get about 7 or 8 bytes, then nothing, not even noise. Even though the Arduboy screen is working fine.

// SPI Slave
// https://os.mbed.com/users/picnic/code/SSS/file/8a63b2e87af8/main.cpp/

#include <Pokitto.h>
#include <SPISlave.h>

Pokitto::Core game;
Pokitto::Display d;
SPISlave spi(P1_22, P1_21, P1_20, P1_23); // mosi, miso, sclk, ssel

void updateScreen(){
     game.update();
}

void startup(){
    game.begin();
    d.persistence=1;
    game.setFrameRate(255); // max out the frame rate so that it doesn't get used
    d.setInvisibleColor(-1);
}


 int main(){

    startup();

    d.println("Arduboy!");

    spi.format (8,3);
    spi.frequency (4000000);

    int counter = 0;
    int screenSize = 128*64;

    spi.reply(0x00);              // Prime SPI with first reply
    while (game.isRunning()){
        if(spi.receive()) {
            int val = spi.read();
            d.screenbuffer[counter] = val;
            if(counter++ >= screenSize){counter=0;}
        }
        updateScreen();
    }
    return 1;
}

The data I’m getting seems to be part of the screen init sequence, up until the command to switch the screen on. The first command sent changes the clock divider, I currently done have a clue what the number is meant to represent, but I’m guessing that change takes effect when the command for switching the screen on is sent, at which point the data being sent changed speed? Could that by why I can’t receive anything past that point maybe?


#20

No points for guessing what this is for.

Out of interest, I have actually had an Arduboy communicating with a Pokitto before, but it was over a Serial ‘bridge’, so I was using USBSerial/Serial.

You might want to move that increment out of the condition to make sure it’s doing what you think it’s doing.

At the moment it’s presumably incrementing after the test,
which seems to be incorrect behaviour.

(I don’t like making assumptions about what post increment does in expressions though, because the rules about what is and isn’t undefined behaviour for pre and post increment are ridiculously complicated.)

Not that it really matters for Pokitto because typically the Pokitto will be turned off before you reach the end of main,
but the only values that should be returned from main are 0, EXIT_FAILURE and EXIT_SUCCESS.
(EXIT_FAILURE and EXIT_SUCCESS are macros defined in <cstdlib>).
Any other values are implementation defined and may not be valid.


Check the datasheet:

Command descriptions start at page 34.

Also, check Arduboy2Core.cpp, it explains pretty much every command that it sends to the screen.

According to those two, Arduboy2 sends 0xD5, 0xF0,, which means 'set the oscillator frequency to 0xF'.

I’ve found nothing to suggest that’s the case.

So it stops after 0xAF is sent? And you don’t receive 0x20, 0x00,?