【问题标题】:Very slow SPI writing STM32SPI写STM32很慢
【发布时间】:2019-03-12 20:47:40
【问题描述】:

我目前正在编写代码以逐个像素地写入 LCD 屏幕。代码工作正常,但是处理代码的速度非常慢。目标只是在 LCD 屏幕上写入数字,因此我使用带有“for 循环”的“开关”功能来读取我将激活的每个位。我想知道是否有人可以告诉我一种加快代码速度的方法...

int* switch_library_number_1(int num, int octet) {

switch(num)
{

case 0 : ;
    int number_0 [] = {0x80, 0x08,
              0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x88,
              0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, ...};

        int * pNumber_0 = &number_0[octet];

        return pNumber_0;
          break;

case 1 : ;
    int number_1 [] = {0x80, 0x08,
              0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, ...};

    int * pNumber_1 = &number_1[octet];

    return pNumber_1;
      break;
}

然后像这样上升到九个,我认为您不需要查看所有情况。另外,即使我删除了其中的大部分,我也有 522 个字节。其余代码闲置:

int main(void)
{
ADC_Initialization();
SPI_Initialization();
int nombre_octet = 522;
int premier_nombre;
int deuxieme_nombre;

while(1)
{
    GPIOA->BSRRL = CS;
    for(int i = 0; i < nombre_octet; i++)
    {
        write_spi(*switch_library_number_1(0, i));
    }
    GPIOA -> BSRRH = CS;

    for(int i = 0; i < 100; i++)
            {
            }

    GPIOA->BSRRL = CS;
    for(int i = 0; i < nombre_octet; i++)
    {
        write_spi(*switch_library_number_2(1, i));
    }
    GPIOA -> BSRRH = CS;

    }
}

最后,这里是 write_SPI 函数,但由于它很简单,我认为不是问题所在。

void write_spi(char data)
{
    SPI1->DR = data;

    while (!(SPI1->SR & SPI_I2S_FLAG_TXE));
    while (!(SPI1->SR & SPI_I2S_FLAG_RXNE));
    while (SPI1->SR & SPI_I2S_FLAG_BSY);
}

提前致谢!

【问题讨论】:

  • #1 这里是我们不在嵌入式系统中使用int,因为它是经过签名的,我们通常不想要,而且大小未知,这使得它不可移植。相反,我们使用stdint.h 类型。 char 更糟糕,因为我们甚至不知道它是否已签名。
  • #2 正在阅读有关如何在函数中使用局部变量的信息:Can a local variable's memory be accessed outside its scope?。 (不,它不能……)

标签: c performance stm32 stm32ldiscovery


【解决方案1】:

我非常喜欢您将代码分成三个 sn-ps 的方式。我可以对它们中的每一个提出改进建议:

switch_library_number_1():

  • 这可能只是一个二维数组number[][],或者如果number_0、number_1...的长度不同,它可能是指向这些的指针数组。需要检查有效的num 和offset。这可能是速度的小幅提升。
  • 您的number_0... 数组当前在堆栈上,并且可以读写。将它们设为const,这样它们就不会使用 RAM。
  • 目前您正在返回一个指向堆栈上内存位置的指针 - 这通常不起作用,如果它这样做是幸运和偶然的。当您超出定义的范围(函数)时,您不应该访问堆栈数据。 static const 会让这个安全,因为它不会再在堆栈上。

主循环:

  • 在每次循环迭代时调用switch_library_number_1/2 有点奇怪。你知道你的数据只是在数组中。如果正确设置了number 数组,这可能会被write_spi(number[0][i]); 替换。这应该可以提高您的速度,因为它大大简化了数据获取。
  • 您的循环似乎很忙。这是一个棘手的做法(我打赌100 是一个猜测,并注意编译器可以优化这个循环)。如果可能使用某些库提供的延迟函数或计时器来获得精确的延迟。这是 SPI 从机的实际要求吗?

write_spi(char 数据):

  • char 应该是 unsigned char 这里。 chars 可能有符号或无符号,因此当您将它们用作字节(不是实际的字符串字符)时,您应该指定有符号。
  • 您似乎在等待每个字节传输完成,这是安全的,但有点慢。通常,这可以重写为wait_for_SPI_ready_for_TX; SPI_TX 的更快替代方案,您只需在发送下一个字节之前等待。请注意,您还需要等待字节完全传输,然后再将 CS 拉回高电平。这可能会大大提高速度。

需要考虑的其他事项:

  • 实际的 SPI 时钟是多少?如果时钟增加,速度可能会大大提高。
  • 您如何将其衡量为“慢”?它是否指向代码的慢部分(那是什么?如果从 C 语言中看不出来,它们会被组装成什么?)
  • 您有示波器/逻辑分析仪来查看线上的实际信号吗?这可能会提供有用的数据。

【讨论】:

  • 根本不应该有任何繁忙的循环,既不是像 OP 中那样损坏的循环,也不是库中的循环。只需检查 SPI 状态标志。
  • 繁忙循环在两个 SPI 传输之间。可能需要它,因为一些奴隶有非常奇怪的要求。
  • 那应该用 cmets 澄清一下。
  • 我同意,我在答案中添加了一些内容。
  • 非常感谢您的这些建议!我已经执行了您的所有建议,并且确实将速度提高到了可以接受的水平。对每个提示也有很好的解释,它真的很有帮助。对于其他问题,我的 SPI 时钟设置为 2 MHz(系统约束)。我测量这是“慢”,因为在 LCD 屏幕上打印 4 个数字大约需要 15 秒。确实是我使用 switch 功能减慢数据的方式。
【解决方案2】:

我在 STM32F207 系列 Cortex-M3 控制器上遇到了类似的问题,当我通过振荡器观察 TX 线时,我发现 CHIP_SELECT 禁用在所有数据发送后需要太多时间才能设置。我想通了与标志控制有关所以我稍微玩一下控制标志,这对我来说效果很好;

static void SPI_Send(uint16_t len,uint8_t* data)
{
   uint16_t i;

   for(i = 0;i<len;i++)
   {        
     SPI_I2S_SendData(SPI1,*(data+i));
     while(!(SPI1->SR & SPI_SR_TXE));   
   }    
   while(SPI1->SR & SPI_SR_BSY);
   CHIP_SEL_DISABLE;
}

我认为这很慢,因为您还在不需要的地方检查“接收缓冲区非空”。

【讨论】:

  • 我们甚至不知道代码是使用手动还是自动/SS,所以你只是在这里猜测。此外,您描述的问题通常是接收器端硬件的问题,而不是 MCU 端的问题。 SPI 的标准化非常差,并且存在各种各样的混蛋硬件。
  • 我不明白你所说的手动/自动是什么意思,如果代码使用内核驱动程序等?无论如何,就我而言,当我观察到 TX 线从 MCU 的 SPI 外设引出时,我观察到在所有数据发送后将 CS 引脚设置为高电平花费了太多时间。上面的配置修复了它。虽然你可能是对的,但我不是这样的专家。但如果你能解释接收方如何影响传输方,我会很高兴。是不是 RX 电容影响了相应的标志寄存器,导致有更多的时间来设置标志?
  • 几乎所有的 SPI 硬件都有两个选项:硬件可以自动处理 /SS,或者您可以使用 GPIO 手动处理。接收端硬件有时序规范,不一定遵循任何标准。
  • 我确实在我的代码中手动控制了 ss。感谢您的解决方案!
猜你喜欢
  • 2019-10-19
  • 2017-04-14
  • 2019-04-06
  • 2019-06-14
  • 2022-01-02
  • 2021-08-27
  • 1970-01-01
  • 2021-12-31
  • 2018-01-09
相关资源
最近更新 更多