Вольт Ампер Ватт метр
- Войдите на сайт для отправки комментариев
Появилась идея сделать трехфазный измеритель электроэнергии. За основу был взят проект https://habr.com/ru/post/384597/ . Но я использовал вместо Atmega 1280 Blue Pill с процессором STM32F103C8T6 . Экран TFT 3.5' 320x480 с контролером ILI9488 . Тачскрин в данном проекте не использовал. Экран подключен на второй канал SPI . Частота 36 Мгц. Сигнал LED подключен к +3.3В. Измеряемое напряжение трех фаз 220 Вольт через делители 1-3 (680 кОм + 680 кОм + 1 Мом ) и 10 кОм подаются на входы PA0, PA2, PA4 относительно средней точки созданой делителем 4 из двух резисторов 200 Ом между корпусом и +3.3 В. (Сигнал снимается с резистора 10 кОм. Второй вывод резистора 10 кОм подключен к средней точке делителя 4. На входы PA1, PA3, PA5 будут подаваться напряжение с трансформаторов тока или датчиков Хола. Сейчас коэффициенты рассчитаны под датчик Хола 20 А. Устанавливаются в этих строках urms[j] = sqrt(utemp[j])*0.19; irms[j] = sqrt(itemp[j])/100; Про АЦП и таймеры читал статьи http://mypractic.ru/urok-26-acp-stm32-obshhie-svedeniya-rezhimy-ustanovka-konfiguracii-cherez-registry-cmsis.html
В программе подключены оба АЦП. Два регулярных канала и четыре инжектированных. Время выборки 41.5 цикл выбрано исходя из того чтобы можно было входной делитель сделать высокоомным. Итого шесть каналов измеряется за 14.5 мкс. в цикле 200 мкс . После накопления 4096 измерений (0.8 сек) таймер останавливается проводится вычисление результатов и вывод на экран. После паузы 2 сек включается таймер и цикл повторяется. Вид экрана
Сигнал подавал с генератора сделанного по мотивам генератора Dimaxa на AD9833 - 2 штуки синфазные и Si5351 с сенсорным управлением. Если кого то заинтересует код могу выложить
#include "SPI.h" #include <Adafruit_GFX.h> #include <ILI9488.h> #include <libmaple/dac.h> #define TFT_CS PB12 #define TFT_DC PB6 #define TFT_LED PA9 #define TFT_RST PB7 ILI9488 tft = ILI9488(TFT_CS, TFT_DC, TFT_RST); int i,j; int res[6]; unsigned long t_prev; unsigned long t_now; byte flag=0; //save current voltage data double urms[3], utemp[3] ; int umoment[3], umoment_old[3] ; //save current current data double irms[3], itemp[3]; int imoment[3] ,imoment_old[3]; //number of tick from 0 to 4095 int N = 0; //zero-cross dfetection for frequency calc long voltage_zerocross[3], voltage_zerocross_old[3], signalperiod[3]; //zero-cross detection for phase calc long current_zerocross[3], phaseperiod[3]; float S[3], P[3], Q[3]; float frequency[3]; float phase[3]; void setup() { Serial.begin(9600); pinMode(PB12, OUTPUT); pinMode(PA9, OUTPUT); pinMode(PC13, OUTPUT); pinMode(PB7, OUTPUT); SPI.setModule(2);// выбор SPI2 tft.begin(); tft.setRotation(0); tft.setTextColor(ILI9488_WHITE); tft.setTextSize(2); tft.fillScreen(ILI9488_BLACK); tft.println("Hello World!"); // установки таймера 2 на период 200 мкс RCC_BASE->APB1ENR|= (1<<0);//включить тактирование tim-2 |(1<<1)|(1<<2); //включить тактирование tim-3,4 RCC_BASE-> APB1ENR |= RCC_APB1ENR_TIM2EN; // разрешение тактирования таймера TIMER2_BASE->SMCR &= ~((1<<0) | (1<<1) | (1<<2)); // внутреннее тактирование, шина APB TIMER2_BASE->PSC = 719; // 10 мкс TIMER2_BASE->ARR = 19; // * 20 TIMER2_BASE->DIER |= (1<<0); timer_attach_interrupt(TIMER2, TIMER_UPDATE_INTERRUPT, my_int); TIMER2_BASE->CR1 |= (1<<0); // ADC1, ADC2 // пины PA0-PA7 аналог GPIOA_BASE->CRL=0x00000000; // разрешение тактирование АЦП RCC_BASE->APB2ENR |= RCC_APB2ENR_ADC1EN; //= E30 разрешение тактирования АЦП 1 RCC_BASE->APB2ENR |= RCC_APB2ENR_ADC2EN; // разрешение тактирования АЦП 2 // предделитель АЦП = 10 (/6) RCC_BASE->CFGR &= ~(1<<14); RCC_BASE->CFGR |= (1<<15); ADC1_BASE->SMPR2 = 0b00000000000000100100100100100100; // 7.5cycl=6 mks 0x00009249 13,5cycl=0x00012492 41,5cycl=14.5mks 0x00024924 ADC2_BASE->SMPR2 = 0b00000000000000100100100100100100; // 7.5cycl=0x00009249 13,5cycl=0x00012492 41,5cycl=0x00024924 ADC1_BASE->SQR1 = 0x00000000;//1канал 0b00000000000100000000000000000000; 2 каналов - 0x00100000 ADC1_BASE->SQR3 = 0x00000000; //0канал 0b00000000000000000000000000100000; последовательность канал 0-1 0x00000020 ADC2_BASE->SQR1 = 0x00000000;//1канал ADC2_BASE->SQR3 = 0x00000001;//1канал ADC1_BASE->JSQR = 0b00000000000100011000100000000000; // 2 инжектир. канала 2,3, =0x000118800 ADC2_BASE->JSQR = 0b00000000001100101001000000000000; // 4 инжектир. канала 4,5 =0x000329000 // включить ADC1 011:-Timer 2 CC2 event (19-17) 111:-SWSTART 20-ExtTrig 21-JswStart 22-SWSTART //CONT=1 непрерывный, jextsel=111(12-14), jexttrig =1(15) ADC1_BASE->CR2 = 0x00000000; ADC2_BASE->CR2 = 0x00000000; ADC1_BASE->CR2 |= (1<<22) | (1<<21) | (1<<20) | (1<<19) | (1<<18) | (1<<17); // | (1<<15) | (1<<14) | (1<<13) | (1<<12); ADC2_BASE->CR2 |= (1<<22) | (1<<21) | (1<<20) | (1<<19) | (1<<18) | (1<<17); ADC1_BASE->CR1 |= (1<<8) | (1<<10) | (1<<16);//=10500 //SCAN =8 JAUTO = 10 DUALMOD = 16 ADC2_BASE->CR1 |= (1<<8) | (1<<10) ; ADC1_BASE->CR2 |= (1<<0);// включение ADC1 ADC2_BASE->CR2 |= (1<<0);// включение ADC2 delay(1); //калибровка ADC1_BASE->CR2 |= (1<<2); ADC2_BASE->CR2 |= (1<<2); while ((ADC1_BASE->CR2 & 0x00000004) != 0) ; // ожидание окончания калибровки while ((ADC2_BASE->CR2 & 0x00000004) != 0) ; // ожидание окончания калибровки tft.fillScreen(ILI9488_BLACK); tft.println("Hello World!"); tft.println(RCC_BASE->CFGR,HEX); } void loop(void) { if(flag){ //has new data tft.fillScreen(ILI9488_BLACK); tft.setCursor(0, 0); tft.setTextSize(2); tft.setTextColor(ILI9488_WHITE); tft.println("VOLT AMPER WATT METER"); tft.println(); for(j=0;j<3;j++) { if(j == 0)tft.setTextColor(ILI9488_YELLOW); if(j == 1)tft.setTextColor(ILI9488_GREEN); if(j == 2)tft.setTextColor(ILI9488_ORANGE); frequency[j] = 5000 / ((float)(signalperiod[j])); phase[j] = (2*3.1416) * phaseperiod[j] / signalperiod[j]; S[j] = urms[j] * irms[j]; P[j] = S[j] * cos (phase[j]); Q[j] = S[j] * sin (phase[j]); flag = 0; tft.setTextSize(4); tft.print(" Pfaze "); tft.println(j+1); tft.setTextSize(2); tft.println(); tft.setTextSize(3); tft.print("U="); tft.print(urms[j]); tft.print(" I="); tft.println(irms[j]); tft.print("F="); tft.print(frequency[j],1); tft.print(" cos="); tft.println(cos(phase[j]),2); tft.print("S="); tft.print(S[j],0); tft.print(" P="); tft.print(P[j],0); tft.print(" Q="); tft.println(Q[j],0); // tft.setTextSize(2); tft.println(); } delay(2000); TIMER2_BASE->CR1 |= (1<<0); } } void my_int(){ ADC1_BASE->CR2 |= ADC_CR2_SWSTART; while(!(ADC1_BASE->SR & ADC_SR_EOC)) ; res[0] = ADC1_BASE->DR; res[1] = ADC2_BASE->DR; while(!(ADC1_BASE->SR & ADC_SR_JEOC)) ; res[2] = ADC1_BASE->JDR1; res[3] = ADC1_BASE->JDR2; res[4] = ADC2_BASE->JDR3; res[5] = ADC2_BASE->JDR4; ADC1_BASE->SR &= ~ADC_SR_JEOC; // сброс флага ADC1_BASE->CR2 &= ~ADC_CR2_SWSTART; for(j=0;j<3;j++){ umoment[j] = res[2*j] - 2020; utemp[j] = utemp[j] + pow((double)(umoment[j]),2)/4096; //zero-crossdetect if ((umoment_old[j] <= 0) && (umoment[j] > 0)){ voltage_zerocross[j] = N; signalperiod[j] = voltage_zerocross[j] - voltage_zerocross_old[j]; if (signalperiod[j] < 0){ signalperiod[j] = 4096 - signalperiod[j]; }//calc signal period in ticks voltage_zerocross_old[j] = voltage_zerocross[j]; } umoment_old[j] = umoment[j]; imoment[j] = res[1+2*j] - 2020; itemp[j] = itemp[j] + pow((double)(imoment[j]),2)/4096; if ((imoment_old[j] <= 0) && (imoment[j] > 0)){ phaseperiod[j] = N - voltage_zerocross[j]; if (phaseperiod[j] < 0){ phaseperiod[j] = 4096 - phaseperiod[j]; }//calc phase period in ticks } imoment_old[j] = imoment[j]; } N++; if (N == 4095){ TIMER2_BASE->CR1 &= ~(1<<0); //calc final result for(j=0;j<3;j++){ urms[j] = sqrt(utemp[j])*0.19; irms[j] = sqrt(itemp[j])/100; utemp[j] = 0; itemp[j] = 0; } N = 0; flag = 1; if(GPIOC_BASE->IDR&(1 << 13)) GPIOC_BASE->ODR &= ~(1<<13); else GPIOC_BASE->ODR |= (1 << 13); } }
Поправил код. Планирую в дальнейшем добавить запись на SD и считать мощность. Входы процессора РА0-РА5 нужно защитить диодами.
Продолжил изучать АЦП . Мне нужно два быстрых канала для оцифровки сигнала. В результате поиска в интернете попал на сайт https://www.gameinstance.com/post/79/STM32-Oscilloscope
Использовал идею установить тактирование АЦП частотой 36 Мгц . При этом все чудненько работает. Разрешение конечно не 12 бит, но сигнал 500 кГц выглядит так. Тут работают два АЦП со сдвигом 7 тактов. Вывод на экран растянут.
Фото с пленкой на экране хорошим не получается. Код для Блюпил с экраном ILI9341 3.2" под спойлером. Данные с двух АЦП через 1 канал DMA1 запмсываются в буфер. Код это просто заготовка. Учился управлять АЦП и DMA через регистры. Далее с этого можно делать двухканальный осциллограф .
Поскольку мы в той или иной мере метеозависимы сделал себе часы с барометром с трехдневной памятью и таймером для кухонных нужд. Управление энкодером. Внизу любимый осциллограф.
Возился с часами на Блюпил. В результате написал код для установки часов и преобразования в формат - (год,месяц,день, час, минута , секунда) без сторонней библиотеки. Начало 1 января 2021 года.