为什么使用 SPI 时,全局缓冲区变量产生的结果与局部缓冲区变量不同



好的,长期开发,但使用C#和Swift等高阶语言。 在原生C++中工作,我正在做一件硬件工作并通过 Arduino 进行原型设计,但这让我感到困惑。

如您所知,在函数外部定义的变量保留全局范围。 里面的那些作用域为函数。 但。。。当我将变量从函数中移出到全局时,我得到了不同的结果!

这按预期工作...

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

但这个没有。 现在,将缓冲区移动到全局级别会将所有零发送到SPI总线。

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

那么我错过了什么? 这是堆栈与堆的东西吗? 不确定全局变量的分配位置。

该缓冲区必须可由多个函数访问/更新,因此希望将其全局移出。

如果这是错误的,正确的方法是什么?


更新

我误解了Arduino API。 罪魁祸首是我做出了错误的假设,即此版本的"传输"的工作方式与所有其他版本完全相同,只是这使用了缓冲区而不是单独发送uint8_t

然而 - 这是在文档中,所以这在技术上是关于我的 - 与一次发送一个或两个字节的同一函数的所有其他重载不同,这个采用缓冲区的版本用传入数据覆盖该缓冲区,因此它在第一次发出时破坏了我的全局缓冲区。

我认为对于那些为 Arduino 设计 SPI 库的人来说,这是一个非常糟糕的决定,因为同一函数的其他重载不会修改您的传出数据,并且名称中没有任何内容表明此版本会有所不同。相反,看起来如果您已经在缓冲区中拥有数据,那么发送数据的方法要简单得多。

因为它有这种副作用行为,所以它应该被命名为类似bidirectionalTransfer或类似的东西,这将给出一些迹象,表明这种变体的行为不同。或者至少添加一个额外的参数,说你也想要传入的数据。

通过 SPI 发送数据缓冲区是很常见的事情。对我来说,如果你有一个要推送的数据缓冲区,你不能简单地推出它而不被覆盖,这对我来说是没有意义的。 还有什么选择? 你要么 a) 首先复制整个缓冲区,然后将其发送出去,浪费所有内存,特别是如果它很大,或者 b) 将其分块并手动推送一个字节。 但是,为什么我必须做额外的拆卸工作,只是为了让SPI总线在一秒钟后重新组装它呢?为什么不把transfer留下来...传输缓冲区,然后为何时需要此overwriting行为提供不同的函数?我可以看到它的用途,但我看到了发送大量数据而不希望它被丢弃的更多用途。

就像我说的,恕我直言,这是一个糟糕的 API 决定,浪费了我一天中的几个小时。 一个简单的名称更改就可以清除所有这些。甚至是一个可选参数,表示您对传入的数据也感兴趣。可是这个?本质上相同的 API 的不同基本行为? 这只是一个糟糕的选择。

无论如何,很抱歉在这里浪费大家的时间,但是嘿...至少我现在有所有这些反对票可以欣赏。$@#!#$!!!

正如各种评论和问题更新中所讨论的那样,这不是C++的问题,而是对SPIAPI的误解。

问题是SPI.transfer的重载将缓冲区指针和大小作为参数仍然像其他重载一样进行双向传输,但它不是像其他重载那样返回传输的数据,而是用传入数据覆盖传出缓冲区。

因此,如果buffer是全局的,则对writeRow的第二次和以后调用将发送以前接收的数据,而不是初始化中的数据。使用本地buffer不会发生这种情况,因为每次调用writeRow都会使用硬编码初始值设定项的内容初始化新buffer

有关SPI.transfer,请参阅 Arduino 文档

我假设你正在使用Arduino GUI/IDE/任何你想要调用它的东西来构建它。要记住的是,GUI 会对您提供给它的内容进行一些预处理。[消除我的(不正确的)怀疑,进一步调查]

这里的区别在于全局缓冲区初始化一次。

如果您用另一个函数覆盖它以将值设置为零,那么它们将保持为零。

另一方面,每次输入函数时,函数中的缓冲区都会重新初始化为这些指定值writeRow()因此,您将始终获得写入SPI总线的预期值。

在我看来,您已经在某个时候覆盖了缓冲区中的值。在发送之前打印出缓冲区中的值,以确保在缓冲区中获取值。

相关内容

  • 没有找到相关文章

最新更新