Чтобы получить более быстрый доступ к значениям математических функций, вы часто предпочитаете хранить заранее рассчитанные значения в ОЗУ или ПЗУ. Можно ли это ускорить в Arduino Uno R4 по сравнению с версией R3?

Вы можете часто видеть скетчи, которые начинаются с таких строк:
const PROGMEM char sine256[] = {128,130,133,136,139,143,146,149,152,
Они могут включать длинную таблицу значений синуса. Это очень полезно, поскольку вычисления во время выполнения возможны, но при использовании 8-битных контроллеров они выполняются очень медленно. Быстрый доступ к синусоидальным значениям может иметь решающее значение в аудиопроектах, когда вы не хотите выводить раздражающие прямоугольные звуки. Чтобы сэкономить драгоценное пространство в SRAM, лучше хранить эти константы во флэш-памяти с помощью PROGMEM.
Давайте сравним, что может предложить новый Arduino Uno R4 с контроллером RA4M1. Для этого теста мы реализовали две таблицы синусоидальных значений: одна рассчитывается во время компиляции (компилятором внутри ПК), загружается и сохраняется во флэш-памяти, а другая рассчитывается во время установки и сохраняется в SRAM. Оба сравнивались со значениями, рассчитанными во время выполнения.
Следующее изображение получено с помощью Arduino Uno R3.

А следующий график получили уже с помощью Arduino Uno R4.

Слева вы видите график синусоидальной функции от 0 до 360 градусов (от 0 до 2*PI). На графике отображено только 180 значений. Они были немного сдвинуты друг относительно друга, иначе вы бы увидели только один график.
Справа вы видите гистограмму времени, необходимого для доступа к значениям во FLASH, SRAM и их расчета. Как и ожидалось, вычисления требуют гораздо больше времени, чем чтение значений из памяти, но это соотношение намного ниже при использовании нового R4, поскольку внутри него имеется встроенный модуль вычислений с плавающей запятой. Можно увидеть еще одно отличие: при использовании R3 доступ к данным, хранящимся во флэш-памяти, происходит немного медленнее, чем доступ к данным в SRAM, тогда как при использовании R4 доступ к данным во флэш-памяти происходит значительно быстрее, чем чтение данных из SRAM.
Для проверки был использован следующий код:
#define M 360
#define RAD TWO_PI / M
#ifdef __AVR__
PROGMEM const
#endif
double sinCt[] = {
#include "sin.h"
};
const int N = M / 2;
const float rad2 = RAD * 2;
double sinSu[N];
void setup() {
Serial.begin(9600);
while (!Serial);
Serial.println(__FILE__);
for (int i = 0; i < N; i++)
sinSu[i] = sin(i * rad2);
Serial.println("compile-time setup run-time");
#ifdef __AVR__
int F = 20000;
int G = 500;
#else
int F = 1000;
int G = 20;
#endif
for (int i = 0; i < N; i++) {
/*
Serial.print(i * RAD);
Serial.print(" ");
*/
#ifdef __AVR__
Serial.print(F * pgm_read_float(&sinCt[i]));
#else
Serial.print(F * sinCt[i]);
#endif
Serial.print(" ");
Serial.print(F * sinSu[i] + G);
Serial.print(" ");
Serial.print(F * sin(i * rad2) - G);
Serial.println(" 0");
}
int t0 = getValue(0, 0) * N;
for (byte i = 1; i <= 3; i++) {
int t = getTime(i) - t0;
for (int j = 0; j < 105; j++) {
for (byte k = 1; k <= 3; k++)
if (k == i) Serial.print(t);
else Serial.print(" 0 ");
Serial.println(" 0");
}
}
Serial.println("0 0 0 0\n0 0 0 0");
}
void loop() {}
int getTime(byte s) {
long t1, t2;
float dummy1 = 0;
float dummy2;
t1 = micros();
for (int i = 0; i < N; i++)
dummy1 = dummy1 + getValue(s, i);
t2 = micros();
if (dummy1 > 1) Serial.println("error");
return t2 - t1;
}
float getValue(byte s, int i) {
switch (s) {
case 0: return 0;
case 1: return pgm_read_float(&sinCt[i]);
case 2: return sinSu[i];
case 3: return sin(i * rad2);
}
}
Файл sin.h:
sin(__COUNTER__ * RAD),
#if __COUNTER__ < M
#include __FILE__
#endif
© digitrode.ru