SPI写STM32很慢
Very slow SPI writing STM32
我目前正在编写代码,逐个像素地在 LCD 屏幕上书写。代码工作正常,但是处理代码的速度非常慢。目标只是在 LCD 屏幕上写数字,所以我使用 "switch" 函数和 "for loop" 来读取我将激活的每个位。我想知道是否有人可以告诉我一种加速我的代码的方法...
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);
}
提前致谢!
我非常喜欢您将代码分成三个片段的方式。我可以为他们每个人提出改进建议:
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
这里应该是 unsigned char
。 char
s 可能是有符号的或无符号的,所以当您将它们用作字节(而不是实际的字符串字符)时,您应该指定有符号。
- 你似乎在等待每个字节传输完成,这是安全的,但有点慢。通常这可以重写为
wait_for_SPI_ready_for_TX; SPI_TX
的更快替代方案,您只需在发送下一个字节之前等待。请注意,您还需要等待字节完全传输后再将 CS 拉回高电平。这可能是一个很大的速度提升。
其他一些需要考虑的事情:
- 实际的 SPI 时钟是多少?如果增加时钟,速度可能会有很大的提高。
- 你是如何衡量这个 "slow" 的?它是否指向代码的缓慢部分(那是什么?如果从 C 中不明显,它们被组装成什么?)
- 您有 oscilloscope/logic 分析仪来查看线路上的实际信号吗?这可能会提供有用的数据。
我在 STM32F207 系列 Cortex-M3 控制器上遇到了类似的问题,当我通过振荡器观察 TX 线时,我看到 CHIP_SELECT disable 设置的时间太长了,毕竟所有数据都有sent.I 发现它与标志控制有关所以我稍微玩了一下控制标志,这对我来说效果很好;
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;
}
我认为它很慢,因为您还在检查不需要的 'Receive Buffer Not Empty'。
我目前正在编写代码,逐个像素地在 LCD 屏幕上书写。代码工作正常,但是处理代码的速度非常慢。目标只是在 LCD 屏幕上写数字,所以我使用 "switch" 函数和 "for loop" 来读取我将激活的每个位。我想知道是否有人可以告诉我一种加速我的代码的方法...
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);
}
提前致谢!
我非常喜欢您将代码分成三个片段的方式。我可以为他们每个人提出改进建议:
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
这里应该是unsigned char
。char
s 可能是有符号的或无符号的,所以当您将它们用作字节(而不是实际的字符串字符)时,您应该指定有符号。- 你似乎在等待每个字节传输完成,这是安全的,但有点慢。通常这可以重写为
wait_for_SPI_ready_for_TX; SPI_TX
的更快替代方案,您只需在发送下一个字节之前等待。请注意,您还需要等待字节完全传输后再将 CS 拉回高电平。这可能是一个很大的速度提升。
其他一些需要考虑的事情:
- 实际的 SPI 时钟是多少?如果增加时钟,速度可能会有很大的提高。
- 你是如何衡量这个 "slow" 的?它是否指向代码的缓慢部分(那是什么?如果从 C 中不明显,它们被组装成什么?)
- 您有 oscilloscope/logic 分析仪来查看线路上的实际信号吗?这可能会提供有用的数据。
我在 STM32F207 系列 Cortex-M3 控制器上遇到了类似的问题,当我通过振荡器观察 TX 线时,我看到 CHIP_SELECT disable 设置的时间太长了,毕竟所有数据都有sent.I 发现它与标志控制有关所以我稍微玩了一下控制标志,这对我来说效果很好;
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;
}
我认为它很慢,因为您还在检查不需要的 'Receive Buffer Not Empty'。