Я пытаюсь прочитать буфер из системы шин с помощью stm32F4 при падающем прерывании. Проблема в том, что мне нужно обработать данные в основном цикле, и мне не нужна задержка. Падающее прерывание происходит через 500 нс, и я не хочу пропустить какие-либо данные. Поэтому отключение разрешения прерывания здесь не принесет пользы.
Я ищу способ уменьшить задержку и не пропустить ни одного прерывания. Вот мой подход:
В основном цикле:
while (1)
{
if (iowr_set)
{
iowr_set = 0;
for (int i = 0; i < BUFFER_SIZE; i++)
{
processData(aRxBuffer[i]);
}
}
}
Прерывать:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == GPIO_PIN_6) { // IORD
HAL_SRAM_Read_16b(&hsram1, (uint32_t *)(SRAM_BANK_ADDR + WRITE_READ_ADDR), aRxBuffer, BUFFER_SIZE);
iowr_set = 1;
}
}
Декларации:
#define BUFFER_SIZE ((uint32_t)0x0100)
#define WRITE_READ_ADDR ((uint32_t)0x0800)
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
SRAM_HandleTypeDef hsram1;
FMC_NORSRAM_TimingTypeDef SRAM_Timing;
/* Read/Write Buffers */
volatile uint16_t aRxBuffer[BUFFER_SIZE];
Если мои расчеты верны, то если вы запустите процессор на частоте 180 МГц (максимальная частота, поддерживаемая этим чипом), каждый такт составит ~5,5 нс. Это означает, что вы можете выполнить ~90 инструкций за 500 нс, если каждая инструкция занимает один такт. И за это время вам нужно прочитать и обработать 256 байт (или 64 uint32). Вы уверены, что это вообще возможно?
@pmacfarlane Вы правы с анализом. Что мне следует сделать, чтобы решить эту проблему, пожалуйста? Я имел в виду обрабатывающую часть
@WeatherVane Что, если я обрабатываю только одну переменную, а не буфер, из-за ограничений обработки
@pmacfarlane Интересно, с учетом вашего анализа, что образец FMC_SRAM имеет 256 байт, и они выполняют одинаковую обработку, хотя без двойной буферизации...
То же самое: используйте альтернативные переменные, обычно стоит буферизовать данные между ISR и считывателем. Но как бы вы это ни делали, как пишет pmacfarlane, нужно уметь справляться с пропускной способностью. Я предполагаю, что чем больше данных вы читаете за одно прерывание, тем эффективнее оно будет: меньше прерываний (компенсируется типичной необходимостью свести к минимуму время отклика на прерывание).
@WeatherVane, а как насчет его анализа? поэтому мне следует увеличить buffer_size или уменьшить его
Это компромисс с другими частями программы. Как долго программа может терпеть отключенные прерывания? Могут быть и другие прерывания, которые должны работать. Но посмотрите на время, выбранное pmac. Как бы то ни было, вам нужно справиться с общей пропускной способностью данных. Если не получается, то нужно уменьшить скорость ввода данных.
@WeatherVane Я не могу уменьшить скорость входных данных, извините, но я не понял, что такое общая пропускная способность данных. Как я могу с этим справиться?
Вам необходимо прочитать и обработать 64 32-битных значения в 90 инструкциях. Я не знаю, что для вас означает «процесс», но даже прочитать значения за это время будет сложно. Вы могли бы развернуть цикл и, возможно, просто продолжать, предполагая, что вы вообще не обрабатывали. В принципе кажется, что это будет невозможно. Если вам нужна такая скорость передачи данных, вам нужен (намного) более быстрый MCU.
Забудьте об обработке значений... Предполагая, что шина данных SRAM 16-битная (из HAL_SRAM_Read_16b
), в лучших возможных обстоятельствах вы физически не можете передать 128 16-битных значений за 90 циклов HCLK. Таким образом, единственный способ, которым это вообще возможно, - это 32-битная шина, двойной буфер DMA от шины (без прерываний для ее перезапуска - не уверен, что это вообще возможно), каким-то образом избежать всех/почти всех внутренних задержек арбитража шины (отдельный код, процессор буфер и буфер DMA в SRAM1/2/3 и надейтесь на лучшее), а затем у вас осталось 90 циклов на обработку данных - так что, возможно, вы сможете выполнить xor их все, но не более того...
Нам нужно знать, как и чем заполняется SRAM. (например) В момент T1 SRAM начинает заполняться. В момент T2 SRAM заполнена и генерируется прерывание. Все это время между T1 и T2 тратится процессором впустую, когда он может получать данные. После Т2 заполнитель сразу же снова начинает заполнять байт 0? Если да, то это состояние гонки. Как наполнитель/отправитель и процессор руки синхронизируют процессы? Вы используете функцию *_16b
HAL. *_32b
может быть в два раза быстрее.
AFAICT, есть версия DMA: *_DMA
. При его использовании циклы процессора, затраченные на передачу из SRAM в основную RAM, доступны для обработки данных, а не просто для их передачи. То есть обработка может перекрывать передачу. Но в лучшем случае скорость процессора находится на грани того, чтобы успевать обрабатывать данные, даже с учетом этих уловок/настроек.
Ваш STM32F429 может работать на максимальной тактовой частоте 180 МГц. Это означает, что, если предположить, что каждая инструкция занимает один такт, вы можете выполнить 180 инструкций за 1 мкс или 90 инструкций за 500 нс.
Ваша цель — обработать 256 байт за 500 нс или 64 32-битных слова. Значит, вам нужно уметь обрабатывать 64 слова в 90 инструкциях.
Давайте представим, что вы просто хотели за это время прочитать эти слова по памяти. Итак, давайте упростим ваш код до этого:
#include <stdint.h>
void read_data(uint32_t * data)
{
for (int i = 0; i < 256 / sizeof(uint32_t); ++i)
{
volatile uint32_t datum = data[i];
// No processing
}
}
Может ли этот код прочитать 64 слова в 90 инструкциях? Возможно нет. Кажется, что такой наивный подход потребует около 4 инструкций на слово. https://godbolt.org/z/MarW37qvq
Вы могли бы развернуть цикл и, возможно, просто возможно, сможете читать данные из оперативной памяти с необходимой скоростью. Но это без какой-либо обработки.
Вдобавок ко всему, у вас есть вызов HAL_SRAM_Read_16b()
, который сам по себе, вероятно, займет сотни, если не тысячи тактов. И вы используете прерывания, которые добавляют еще больше накладных расходов.
То, что вы пытаетесь сделать, в принципе невозможно. Вам придется снизить скорость передачи данных или смириться с отсутствием некоторых данных.
Вы можете использовать два чередующихся буфера. Один считывается при обработке содержимого другого. Итак
volatile uint16_t aRxBuffer[2][BUFFER_SIZE];