Ваша программа работает медленнее, чем ожидалось от STM32. Возможно, вам придется посмотреть, какая сборка производится, настройки оптимизации компилятора, если частота MCU верна, если память слишком медленная, и т. Д. c. У нас недостаточно информации, чтобы дать определенный ответ, почему. Кажется, ваш код тратит 8 мс * 200 м / (8 * 8 * 8 * 40) = 78 циклов на каждую внутреннюю итерацию l oop. Для справки, stm32f723 требуется всего около 15 циклов, а stm32f103 - около 28 циклов (в последнем случае код был настроен для доступа к меньшим массивам).
Таблица LUT не нужна так как его содержание очень регулярное. Чтение значений LUT добавляет больше операций чтения из памяти, что может быть значительным вкладом. Если я правильно получил ваш код генерации LUT, он выдает следующие числа во внутреннем l oop:
Y1 Cb Cr Y2
0 128 192 1
2 129 193 3
4 130 194 5
6 131 195 7
64 132 196 65
66 133 197 67
68 134 198 69
70 135 199 71
8 136 200 9
etc
Второй и третий столбцы просто последовательные номера. Четвертый столбец равен первому плюс один. И первое число нужно немного перевернуть. Вы можете попробовать следующий код (пожалуйста, убедитесь, что он правильный):
uint32_t lutOffset = 0;
for(uint8_t i = 0; i < ROWS_PER_MCU; i++)
{
for(uint8_t j = 0; j < WORDS_PER_MCU; j++)
{
uint32_t rawBufferAddress = (inputOffset+j) /* % 2048 */;
#if 0
unsigned y_lut1 = jpegInputLUT.JPEG_Y_MCU_LUT[lutOffset];
unsigned Cb_lut = jpegInputLUT.JPEG_Cb_MCU_422_LUT[lutOffset];
unsigned Cr_lut = jpegInputLUT.JPEG_Cr_MCU_422_LUT[lutOffset];
unsigned y_lut2 = jpegInputLUT.JPEG_Y_MCU_LUT[lutOffset+1];
#else
unsigned y_lut1 = lutOffset | (j / 4) << 6 | (j % 4) << 1;
unsigned Cb_lut = 128 + lutOffset + j;
unsigned Cr_lut = 192 + lutOffset + j;
unsigned y_lut2 = y_lut1 + 1;
#endif
jpegInBuffer[y_lut1 + currentMCU] = (rawBuffer[rawBufferAddress] & 0x7F);
jpegInBuffer[Cb_lut + currentMCU] = ((rawBuffer[rawBufferAddress] >> 7) & 0x7F);
jpegInBuffer[Cr_lut + currentMCU] = ((rawBuffer[rawBufferAddress] >> 23) & 0x7F);
jpegInBuffer[y_lut2 + currentMCU] = ((rawBuffer[rawBufferAddress] >> 16) & 0x7F);
}
lutOffset += 8;
inputOffset += 320;
}
Эта версия занимает около 20 циклов на итерацию на моем stm32f103, что составляет менее 6 мс даже при 72 МГц.
UPD. Другой вариант - использовать одну небольшую справочную таблицу вместо битовых вычислений:
static const unsigned x[8] = { 0, 2, 4, 6, 64, 66, 68, 70 };
// unsigned y_lut1 = lutOffset | (j / 4) << 6 | (j % 4) << 1;
unsigned y_lut1 = lutOffset + x[j];
Это улучшает внутреннюю синхронизацию l oop до 18 (f103) / 7.5 (f723) циклов. По какой-то причине оптимизация этого выражения для F723 не работает должным образом. Я ожидал бы, что эти опции дадут идентичный результат, так как внутренний l oop развернут, но кто знает.
В качестве дополнительной оптимизации, которая, вероятно, не требуется, выходные значения могут быть объединены в 32-разрядные слова и записаны по одному слову за раз. Это кажется возможным, потому что значения LUT входят в блоки из четырех последовательных. Для этого внутренний l oop может быть преобразован во вложенный l oop из 2 на 4 итерации. Каждые 4 итерации самой внутренней l oop будут давать один uint32_t для Cb, один uint32_t для Cr и два uint32_t для Y.
Но делать это не стоит. Я измеряю время выполнения с SysTick:
SysTick->LOAD = SysTick_LOAD_RELOAD_Msk;
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
volatile unsigned t0 = SysTick->VAL;
f();
volatile unsigned t1 = t0 - SysTick->VAL;
Я тоже иногда использовал выходные выводы, когда подключение отладчика нецелесообразно. Строго говоря, оба метода не гарантированно работают, потому что компилятор может перемещать код между точками измерения, но он работал так, как мне предназначалось (с g cc). Проверка сборки необходима, чтобы убедиться, что ничего подозрительного не происходит.