简体   繁体   中英

Why does a global buffer variable produce different results than a local buffer variable when using SPI?

Ok, long-time dev but with higher-order languages like C# and Swift. Working in native C++ for a hw thing I'm working on and prototyping via Arduino, but this has me stumped.

As you know, variables defined outside of a function retain global scope. Those inside are scoped to the function. But... when I move a variable out of the function to global, I am getting different results!

This works as expected...

void writeRow(uint8_t row){

    uint8_t buffer[] = { B00111111 ^ 0xFF, B01111111 ^ 0xFF, B00111111 ^ 0xFF, B00011111 ^ 0xFF, B00001111 ^ 0xFF, B00000111 ^ 0xFF, B00000011 ^ 0xFF, B00000001 ^ 0xFF };

    uint16_t rowMask = 1 << row;

    digitalWrite(PIN_LATCH, LOW);

    SPI.transfer16(rowMask); SPI.transfer(buffer,     2);
    SPI.transfer16(rowMask); SPI.transfer(buffer + 2, 2);
    SPI.transfer16(rowMask); SPI.transfer(buffer + 4, 2);
    SPI.transfer16(rowMask); SPI.transfer(buffer + 6, 2);

    digitalWrite(PIN_LATCH, HIGH);
}

But this one does not. Moving the buffer to global level here now sends all zeroes to the SPI bus.

uint8_t buffer[] = { B00111111 ^ 0xFF, B01111111 ^ 0xFF, B00111111 ^ 0xFF, B00011111 ^ 0xFF, B00001111 ^ 0xFF, B00000111 ^ 0xFF, B00000011 ^ 0xFF, B00000001 ^ 0xFF };

void writeRow(uint8_t row){

    uint16_t rowMask = 1 << row;

    digitalWrite(PIN_LATCH, LOW);

    SPI.transfer16(rowMask); SPI.transfer(buffer,     2);
    SPI.transfer16(rowMask); SPI.transfer(buffer + 2, 2);
    SPI.transfer16(rowMask); SPI.transfer(buffer + 4, 2);
    SPI.transfer16(rowMask); SPI.transfer(buffer + 6, 2);

    digitalWrite(PIN_LATCH, HIGH);
}

So what am I missing? Is this a stack vs. heap thing? Not sure where globals are allocated.

That buffer has to be reachable/updateable by several functions, hence wanting to move it out globally.

If this is wrong, what's the correct way to do this?


Update

I misunderstood the Arduino API. The culprit was I made the incorrect assumption that this version of 'transfer' worked exactly like all the other versions, just this used a buffer vs sending uint8_t s individually.

However--and this was in the docs so this is technically on me-- unlike all the other overloads of the same function which send things out one or two bytes at a time, this version that takes a buffer overwrites that buffer with the incoming data, thus it was trashing my global buffer the first time it went out.

I think that was an incredibly poor decision on those who designed the SPI library for Arduino because other overloads of the same function do not modify your outgoing data, and nothing in the name indicates that this version would work any differently. To the contrary, it looks like if you already have your data in a buffer, it's a much simpler way to send it out.

Because it has this side-effect behavior, it should have been named something like bidirectionalTransfer or similar which would have given some indication that this variant behaves differently. That or at least adding an extra parameter saying you also wanted the incoming data.

Sending out buffers of data over SPI is a common thing. It makes no sense to me that if you have a buffer of data that you want to push out, you can't simply push it out without it also being overwritten. What's the alternative? You either have to a) copy your entire buffer first, then send it out, wasting all that memory, especially if it's large, or b) chunk it down and push it out manually one byte at a time. But why should I have to do the extra work of disassembling it just to have it reassembled by the SPI bus a second later? Why not leave transfer as just that... transferring the buffer only , then give a different function for when you want this overwriting behavior? I can see uses for it, but I see more uses for sending out large chunks of data without wanting it trashed.

Like I said, IMHO it's a poor API decision that wasted hours of my day. A simple name-change would have cleared all this up. Even an optional parameter saying you were also interested in the incoming data. But this? Different fundamental behaviors for essentially the same API? That's just a poor choice.

Anyway, sorry to waste everyone's time here, but hey... at least I now have all those down-votes to appreciate. $@#!#$!!!

I assume you are building this with the Arduino GUI/IDE/whatever-you-want-to-call-it. The thing to remember is that the the GUI does some preprocessing to what you feed it. [removed my (incorrect) suspicion, investigating further]

The difference here is that the global buffer is initialized once.

If you are overwriting it with another function to set the values to zero then they are going to stay at zero.

On the other hand the buffer in the function will be re-initialized to those specified values every time you enter the function writeRow() therefore you will always get the expected values written to the SPI bus.

It looks like to me that you have overwritten the values in the buffer at some point. Print out the values in the buffer before sending them to make sure you are getting the values in the buffer.

As discussed in various comments and the question update, this is not an issue with C++, but with a misunderstanding of the SPI API.

The issue is that SPI.transfer 's overload taking a buffer pointer and size as argument does still do bidirectional transfer as other overloads do, but instead of returning transferred data in the way other overloads do, it overwrites the out-going buffer with the incoming data.

Therefore, if buffer is global, the second and later calls to writeRow will send the previously received data rather than the data from the initialization. With a local buffer this doesn't happen because each call to writeRow initializes a new buffer with the content of the hard-coded initializer.

See the Arduino documentation for SPI.transfer .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM