Я хочу отобразить RMS (среднеквадратичное отклонение за период измерений) PkPk(среднее и максимальное значение пик ту пик), среднее напряжение на выходе и частоту пульсаций (буду определять по переходу ноля). Это нужно для мониторинга напряжения и пульсаций на выходе блока питания. В общем такой мини осцилограф.
Для этого я буду запихивать в массив результаты замеров и потом их анализировать.
Онид раз в секунду будет выполнена непрерывная серия измерений и результаты записаны в буфер, затем обработаны и выведены на экран.
При постоянной частоте замеров 1\40us:
Минимальная измеряемая частота будет определяться размером буфера, нужно что бы в буфер влезло хотя бы 2 периода, тесть если мне нужна минимальная измеряемая частота пульсаций 25гц то при периоде замеров 40us нужен буфер на 0.4 сек, а это 975 элементов uint16_t = 1951 байт, а памяти всего 2048, причем половину занимают библиотеки вывода на экран.
Максимальная частота ограничена периодом измерений и будет 1/0.00004 = всего 25Кгц. Используя работу с регистрами вместо analogRead() удалось получить период замеров около 2us а это уже 500Кгц… но тгда минимальная частота вырастет…
[details="Спойлер"]
//#include <GyverLBUF.h>
#include <TimerMs.h>
#include <GyverOLED.h>
uint32_t t1;
uint32_t t2;
uint16_t t3;
TimerMs tmr_v_capture(128, 1, 0);
TimerMs tmr_heartbeat_13(1000, 1, 0);
TimerMs tmr_display_v_now(100, 1, 0);
TimerMs tmr_display_v_rms(1000, 1, 0);
//GyverOLED<SSD1306_128x32> oled(0x3C);
uint16_t V_buf_length = 300;
uint16_t V_buf[300];
float analogReference_k = 4.096;
uint16_t analogReadResolution_k = 4095;
float v_divider_k = 7.657; // R1=1811 R2=12170 k = R1+R2/R1 = 7.720 (эксперементально = 7.652)
float v_divider_Vpkpk_k = 1.0; // Vpk-pk correction
float v_divider_Vrms_k= 1.0;
const uint8_t bitmap_32x32[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xC0, 0xE0, 0xF0, 0x70, 0x70, 0x30, 0x30, 0x30, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF0, 0x70, 0x30, 0x30, 0x20, 0x00, 0x00,
0x00, 0x30, 0x78, 0xFC, 0x7F, 0x3F, 0x0F, 0x0F, 0x1F, 0x3C, 0x78, 0xF0, 0xE0, 0xC0, 0x80, 0x80, 0x80, 0x40, 0xE0, 0xF0, 0xF8, 0xFC, 0xFF, 0x7F, 0x33, 0x13, 0x1E, 0x1C, 0x1C, 0x0E, 0x07, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF9, 0xF7, 0xEF, 0x5F, 0x3F, 0x7F, 0xFE, 0xFD, 0xFB, 0xF1, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x1E, 0x33, 0x33, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x1F, 0x0E, 0x04, 0x00, 0x00, 0x00, 0x00,
};
uint32_t V_buf_calibrate_value = 0;
uint16_t V_buf_calibrate_cicles = 10;
float V_buf_calibrate_desc_period = 0;
uint32_t V_buf_summ = 0;
int16_t V_buf_medium = 0;
int16_t V_buf_element_deviation = 0; // при int16_t V_buf_element_deviation_sq = V_buf_element_deviation * V_buf_element_deviation; дает переполнение
float V_buf_element_deviation_sq = 0;
uint16_t V_buf_element_deviation_max = 0;
uint32_t V_buf_element_deviation_max_sq = 0;
float V_buf_elements_deviation_sqr_sum = 0;
int16_t V_buf_element_deviation_pre = 0;
float V_buf_rms = 0.0;
uint16_t V_buf_zero_cross = 0;
uint16_t V_buf_half_period_start = 0;
uint16_t V_buf_half_period_end = 0;
uint16_t V_buf_period_i = 0; // период в количестве тактов дисткретизации
uint16_t V_buf_period_i_meas_count = 0; // кол-во замеров периода (сколько раз измерялся период в буфере)
float V_buf_period_1_meas = 0.0;
float V_buf_period_sum = 0.0;
float V_buf_freq_median = 0.0;
float V_buf_medium_volt = 0.0;
/*
// TYPE - указанный при инициализации тип данных
void write(TYPE newVal); // добавить в буфер
void write(int num, TYPE val); // запись в буфер по номеру num
TYPE read(int num); // чтение из буфера
int size(); // размер буфера
*/
void meas_start(){
t1 = micros();
}
void meas_stop(){
t2 = micros();
Serial.print(" / ");
Serial.print(t2-t1);
Serial.println(" / ");
}
void setup() {
analogReference(INTERNAL4V096); // внктренне опорное АЦП
analogReadResolution(12); // DAC resolution
DDRB = 0b00100000; // pinMode(PIN_A13, OUTPUT);
PORTB ^= (1<<PORTB5); // led bit inverter
ADCSRA = ADCSRA & 0b11111000; // установить делитель ADC. Очичтить младшие 3 бита в регистре
ADCSRA = ADCSRA | 0b00000100; // Установить младшие 3 бита = x8 (011) x16 (100) x32 (101) x64 (110)
pinMode(PIN_A0, INPUT);
tmr_v_capture.setPeriodMode();
//tmr_v_capture.setMicros(true);
tmr_display_v_now.setPeriodMode();
tmr_display_v_rms.setPeriodMode();
Serial.begin(9600);
/*
oled.init();
oled.setContrast(180);
oled.setScale(3);
oled.clear();
oled.update();
*/
//////////////////// Calibrate //////////////////////
Serial.println("Calibrate...");
for (int j=0; j < V_buf_calibrate_cicles; j++) {
t1 = micros();
for (int i=0; i < V_buf_length; i++){
V_buf[i] = analogRead(A0);
}
t2 = micros();
V_buf_calibrate_value += t2-t1;
Serial.print(" Время заполнени буфера нарастающим итогом по тестовым запускам (мкс)");
Serial.println(V_buf_calibrate_value);
}
V_buf_calibrate_value = V_buf_calibrate_value / V_buf_calibrate_cicles; // Ширина окна выборки (мкс) из V_buf_length выборок - это время заполнения массива данных
V_buf_calibrate_desc_period = (float)V_buf_calibrate_value / V_buf_length; // Время между измерениями АЦП в мкс это период дискретизации
Serial.print(" Среднее время заполнени буфера (мкс) ");
Serial.println(V_buf_calibrate_value);
Serial.print(" Период дискретизации (мкс) ");
Serial.println(V_buf_calibrate_desc_period);
Serial.print(" Длинна буфера (ед) ");
Serial.println(V_buf_length);
Serial.print(" Размер буфера (байт) ");
Serial.println(sizeof(V_buf));
Serial.print(" Минимальная частота измерения (Гц) ");
Serial.println((float)1 / V_buf_calibrate_value * 1000000 * 2);
Serial.print(" Максимальная частота измерения (Гц) ");
Serial.println((float)1 / V_buf_calibrate_desc_period * 1000000 / 2);
//////////////////////////////////////////////////////
Serial.println();
Serial.println("Starting...");
}
void loop() {
if (tmr_heartbeat_13.tick()) {
PORTB ^= (1<<PORTB5);
}
if (tmr_v_capture.tick()) {
noInterrupts();
for (int i=0; i < V_buf_length; i++){
V_buf[i] = analogRead(A0);
}
interrupts();
}
if (tmr_display_v_now.tick()) {
//noInterrupts();
V_buf_summ = 0;
for (int i=0; i < V_buf_length; i++) {
V_buf_summ = V_buf_summ + V_buf[i];
}
V_buf_medium = V_buf_summ / V_buf_length;
V_buf_medium_volt = (V_buf_medium * analogReference_k) / analogReadResolution_k * v_divider_k;
//interrupts();
/*
oled.setScale(1);
oled.home();
oled.print("V=");
oled.print(V_buf_medium_volt, 3);
oled.update();
*/
/*
Serial.print(V_buf_medium_volt, 3);
Serial.print(" === ");
Serial.println(V_buf_medium);
*/
}
if (tmr_display_v_rms.tick()) {
Serial.println("===========================");
V_buf_summ = 0;
V_buf_rms = 0;
V_buf_element_deviation = 0;
V_buf_element_deviation_pre = 0;
V_buf_element_deviation_sq = 0;
V_buf_element_deviation_max = 0;
V_buf_element_deviation_max_sq = 0;
V_buf_elements_deviation_sqr_sum = 0;
V_buf_period_i_meas_count = 0;
V_buf_period_sum= 0;
V_buf_half_period_start = 0;
V_buf_half_period_end = 0;
bool V_buf_start_is_found = 0; // флаг нахождения начала периода, что бы не найти первым окончание
//noInterrupts();
for (int i=0; i < V_buf_length; i++) {
//Serial.println(V_buf[i]);
V_buf_element_deviation = V_buf_medium - V_buf[i]; // отклонение от срднего текущей точки
if (V_buf_element_deviation < 0 && V_buf_element_deviation_pre > 0) {
//Serial.println("Period START");
V_buf_half_period_start = i; // пересечение ноля на подъеме из - в + по оси Х
V_buf_start_is_found = 1; // флаг нахождения начала периода, что бы не найти первым окончание
}
if (V_buf_element_deviation > 0 && V_buf_element_deviation_pre < 0 && V_buf_start_is_found) {
//Serial.println("Period END");
V_buf_half_period_end = i; // пересечение ноля на спаде из + в - по оси Х
V_buf_period_i = (V_buf_half_period_end - V_buf_half_period_start) * 2; // период в количестве тактов дисткретизации
V_buf_period_i_meas_count++; // кол-во событий пересечения (полуволна) это кол-во измерений периода которое получилось в буфере
V_buf_period_1_meas = V_buf_period_i * V_buf_calibrate_desc_period; // Период по результатам одного события пересечения (одна полуволна) в мкс = период в тактах дискретизации * время одного такта дискретизации
V_buf_period_sum += V_buf_period_1_meas; // Сумма значений замеров частоты (мкс)
V_buf_freq_median = 1.0 / ((V_buf_period_sum/V_buf_period_i_meas_count) / 1000000);
//V_buf_freq_median = V_buf_period_sum/V_buf_period_i_meas_count;
}
V_buf_element_deviation_pre = V_buf_element_deviation;
V_buf_element_deviation_sq = V_buf_element_deviation * V_buf_element_deviation; // квадрат отклонения элемента
V_buf_elements_deviation_sqr_sum = V_buf_elements_deviation_sqr_sum + V_buf_element_deviation_sq; // сумма квадратов отклонения элементов
/*
Serial.print(V_buf_element_deviation_sq);
Serial.print(" ");
Serial.print(V_buf_element_deviation);
Serial.print(" ");
Serial.print(V_buf_medium);
Serial.print(" ");
Serial.println(V_buf[i]);
*/
if (V_buf_element_deviation_sq > V_buf_element_deviation_max_sq){
V_buf_element_deviation_max_sq = V_buf_element_deviation_sq; // нахождение максимума квадрата отклонения
}
/*
Serial.print(i);
Serial.print(" = ");
Serial.print(V_buf[i]);
Serial.print(" = ");
Serial.println(V_buf_freq_median);
/*
Serial.print(" = ");
Serial.print(V_buf_period_i);
Serial.print(" = ");
Serial.print(V_buf_half_period_start);
Serial.print(" = ");
Serial.println(V_buf_half_period_end);
*/
}
//interrupts();
V_buf_element_deviation_max = sqrt(V_buf_element_deviation_max_sq);
V_buf_rms = sqrt(V_buf_elements_deviation_sqr_sum / (float)V_buf_length);
float V_buf_rms_volt = (V_buf_rms * analogReference_k) / analogReadResolution_k * v_divider_Vrms_k;
float V_buf_element_deviation_max_volt = ((float)V_buf_element_deviation_max * analogReference_k) * 2 / analogReadResolution_k * v_divider_Vpkpk_k;
Serial.print(V_buf_medium_volt, 3);
Serial.print(" ");
Serial.print(V_buf_rms_volt, 3);
Serial.print(" ");
Serial.print(V_buf_element_deviation_max_volt, 3);
Serial.print(" ");
Serial.println(V_buf_freq_median, 3);
/*
oled.setScale(1);
oled.setCursor(0, 1);
oled.print("Vrms=");
oled.print(V_buf_rms_volt, 3);
oled.setCursor(0, 2);
oled.print("Vpkpk=");
oled.print(V_buf_element_deviation_max_volt, 3);
oled.update();
*/
}
}
[/details]
В процессе я понял что пока это мне не по силам, код работает но плохо, думаю он ужасен. Сейчас хочу сделать просто вольт-ампер-ватметр на attiny85 что бы в процессе получить больше опыта работы с этими штуками.