Очень медленная запись SPI в STM32

В настоящее время я пишу код для записи на ЖК-экране пиксель за пикселем. Код работает нормально, однако скорость обработки кода невероятно медленная. Цель состоит в том, чтобы просто написать число на ЖК-экране, поэтому я использую функцию «переключение» с «циклом for», чтобы прочитать каждый бит, который я активирую. Мне интересно, может ли кто-нибудь сказать мне способ ускорить мой код ...

int * switch_library_number_1 (int num, int октет) {

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

Заранее спасибо!


person Nathaniel Brochu    schedule 12.03.2019    source источник
comment
№1 здесь заключается в том, что мы не используем int во встроенных системах, потому что он подписан, чего мы обычно не хотим, и неизвестного размера, что делает его непереносимым. Вместо этого мы используем stdint.h типов. char еще хуже, так как мы даже не знаем, подписано оно или нет.   -  person Lundin    schedule 13.03.2019
comment
№2 - это чтение о том, как использовать локальные переменные в функциях: Можно ли получить доступ к памяти локальной переменной вне ее области?. (Нет, не может...)   -  person Lundin    schedule 13.03.2019


Ответы (2)


Мне очень нравится, как вы разбиваете свой код на три фрагмента. Могу предложить улучшения для каждого из них:

switch_library_number_1 ():

  • Это может быть просто 2D-массив, number[][], или, если number_0, _3 _... имеют разную длину, это может быть массив указателей на них. Потребуются проверки на действительные num и offset. Это может быть незначительное улучшение скорости.
  • Ваши массивы _6 _... в настоящее время находятся в стеке и доступны для чтения и записи. Сделайте их const, чтобы они не использовали оперативную память.
  • В настоящее время вы возвращаете указатель на ячейку памяти в стеке - обычно это не работает, если это происходит случайно или случайно. Вам не следует обращаться к данным стека, когда вы находитесь вне области действия (функции), в которой они были определены. static const сделает это безопасным, поскольку его больше не будет в стеке.

основной цикл:

  • Немного странно вызывать switch_library_number_1/2 на каждой итерации цикла. Вы знаете, что ваши данные будут просто в массиве. Его, вероятно, можно было бы заменить на write_spi(number[0][i]);, если number массив правильно настроен. Это должно дать вам некоторое улучшение скорости, так как это очень упрощает выборку данных.
  • Кажется, у вас есть цикл занятости. Это непростая практика (держу пари, 100 - предположение, и обратите внимание, что компилятор может оптимизировать этот цикл). Если возможно, используйте функцию задержки, предоставленную библиотекой, или таймер, чтобы получить точные задержки. Это реальное требование подчиненного устройства SPI?

write_spi (символьные данные):

  • char должно быть unsigned char здесь. chars могут быть подписаны или беззнаковые, поэтому, когда вы используете их как байты (а не фактические строковые символы), вы должны указать подпись.
  • Кажется, вы ждете завершения передачи каждого байта, что безопасно, но немного медленно. Обычно это можно переписать в более быструю альтернативу wait_for_SPI_ready_for_TX; SPI_TX, где вы только ждете перед отправкой следующего байта. Обратите внимание, что вам также нужно будет дождаться полной передачи байта, прежде чем снова вернуть CS на высокий уровень. Это могло бы значительно улучшить скорость.

Еще кое-что, что следует учитывать:

  • Каковы фактические часы SPI? Если увеличить тактовую частоту, может произойти огромное улучшение скорости.
  • Как вы измерили это как "медленное"? Указывает ли он на медленные части кода (что это тогда? Если это не очевидно из C, то для чего они собраны?)
  • У вас есть осциллограф / логический анализатор для просмотра реальных сигналов по проводам? Это может предоставить полезные данные.
person domen    schedule 13.03.2019
comment
Никаких занятых циклов быть вообще не должно, ни сломанных, как в ОП, ни библиотечных. Просто проверьте флаги состояния SPI. - person Lundin; 13.03.2019
comment
Цикл занятости находится между двумя передачами SPI. Это может быть необходимо, так как у некоторых рабов довольно странные требования. - person domen; 13.03.2019
comment
Тогда это следует пояснить в комментариях. - person Lundin; 13.03.2019
comment
Согласен, немного добавил к ответу. - person domen; 13.03.2019
comment
Большое спасибо за эти советы! Я выполнил все ваши рекомендации, и действительно, скорость увеличилась до очень приемлемого уровня. Также отличное объяснение по каждому совету, это действительно помогло. Что касается других вопросов, мои часы SPI установлены на 2 МГц (системное ограничение). Я измерил, что это происходит медленно, потому что для печати 4 чисел на ЖК-экране требовалось около 15 секунд. Это действительно был способ, которым я использовал функцию переключения, которая замедляла передачу данных. - person Nathaniel Brochu; 14.03.2019

У меня была аналогичная проблема с контроллером Cortex-M3 серии STM32F207, когда я наблюдал за линией TX через Oscillator, я увидел, что отключение 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;
}

Я считаю, что это медленно, потому что вы также проверяете, что буфер приема не пуст, где вам не нужно.

person İlkerK    schedule 13.03.2019
comment
Мы даже не знаем, использует ли код ручной или автоматический / SS, так что вы здесь только догадываетесь. Кроме того, проблема, которую вы описываете, обычно связана с оборудованием на стороне приемника, а не на стороне MCU. SPI очень плохо стандартизирован, и существует всевозможное ублюдочное оборудование. - person Lundin; 13.03.2019
comment
Я не понимаю, что вы имеете в виду под ручным / автоматическим режимом, вы имели в виду, если в коде используются драйверы ядра и т. Д.? В любом случае, в моем случае, когда я наблюдаю, как линия TX выходит из периферийного устройства SPI MCU, я заметил, что установка вывода CS на высокий уровень после отправки всех данных занимала слишком много времени. И приведенная выше конфигурация исправила это. Хотя вы все равно можете быть правы, я не такой уж эксперт. Но я был бы рад, если бы вы могли объяснить, как сторона приемника может влиять на сторону передачи. Это похоже на то, что емкость RX влияет на соответствующий регистр флагов, что дает больше времени для установки флага? - person İlkerK; 14.03.2019
comment
Практически все оборудование SPI имеет два варианта: оборудование может обрабатывать / SS автоматически, или вы можете сделать это вручную с помощью GPIO. Аппаратное обеспечение на стороне приемника имеет временные характеристики, не обязательно соответствующие каким-либо стандартам. - person Lundin; 14.03.2019
comment
Я контролирую ss вручную в своем коде. Спасибо за решение! - person Nathaniel Brochu; 14.03.2019