На рисунке показана схема простого осциллографа (пробник) основанного на микроконтроллере STM32F103C. Осциллограф имеет минимальный набор внешних компонентов, прост в сборке. Максимальное входное напряжение (только положительной полярности) 6,6 В и может быть увеличено применением внешнего делителя напряжения. Так АЦП STM32F103C 12 бит, это позволило сделать программный умножитель входного сигнала, без существенный потери качества осциллограммы.
При измерении напряжения более 3,3 В, включается делитель напряжения (вручную или автоматически). Управление осциллографом осуществляется при помощи 4-х кнопок. На экран выводятся показания амплитуды входного сигнала, множитель сигнала, длительность одного деления сетки и курсор синхронизации.
1 кГц
Кнопками UP и WOWN можно поменять длительность развертки, множитель входного сигнала и уровень синхронизации. Режимы настроек меняются кнопкой SET.
При нажатии на кнопку HOLD включается режим остановки осциллограммы, кнопками UP и WOWN можно перемещать по горизонтали осциллограмму. Нажимая на кнопку SET поочередно активируются курсоры измерения напряжения и длительности.
Тест полосы пропускания
Максимальная длительность развертки 100 Гц |
Максимальная длительность развертки 100 Гц |
1000 Гц |
10.0 кГц |
50,0 кГц |
100,0 кГц |
100,0 кГц (меандр) |
200,0 кГц (меандр) минимальная длительность развертки |
400,0 кГц (меандр) минимальная длительность развертки |
500,0 кГц (меандр) минимальная длительность развертки |
100,0 кГц (синус) минимальная длительность развертки |
200,0 кГц (синус) минимальная длительность развертки |
#include "SPI.h" #include <EEPROM.h> #include <Adafruit_GFX_AS.h> // http://rcl-radio.ru/wp-content/uploads/2020/06/Adafruit_GFX.zip #include <Adafruit_ILI9341_STM.h> #include <STM32ADC.h> #include <HardwareTimer.h> STM32ADC myADC(ADC1); uint8 pin = PA0; volatile static bool dma1_ch1_Active; #define maxSamples 1000 uint16_t buffer[maxSamples]; uint16 dataPoints[maxSamples]; #define TFT_CS PB1 #define TFT_DC PB10 #define TFT_RST PB11 Adafruit_ILI9341_STM tft = Adafruit_ILI9341_STM(TFT_CS, TFT_DC, TFT_RST); // Mosi - PA7, SCK - PA5 byte data[1000],data_old[1000]; int setting,hold_set,i,x,y,i2,u_max,u_min,minn,u_sinh,mn=2,raz,per,razv,sinhro; int u1,u2,t1,t2,zap,ux=1,uxx=1,fun; long h0,h1; long times,times1,hhh,times2,times3,tim; byte hold,www,w=1,w1=1,w2=1,w3=1,w4=1,link,w5=1,b1=1,b2=1; String raz_x; float k1,k2,del=1; int data1[1000]; void setup() { Serial.begin(115200); EEPROM.init(0x801F000,0x801F800,0x400);// 1024 byte pinMode(PA0, INPUT_ANALOG); pinMode(PB12,INPUT_PULLUP);// HOLD pinMode(PB13,INPUT_PULLUP);//+ pinMode(PB14,INPUT_PULLUP);//- pinMode(PB15,INPUT_PULLUP);// SET tft.begin();tft.setRotation(1); tft.fillScreen(ILI9341_BLACK); razv=EEPROM.read(0); sinhro=EEPROM.read(1); ADC1->regs->CR1 = 0; // Обнулить регистр управления ADC1->regs->SQR1 = 0; // Обнулить регистр SQR1 ADC1->regs->CR2 |= ADC_CR2_CAL; // Пуск калибровки while (!(ADC1->regs->CR2 & ADC_CR2_CAL)){}; } void loop() { if(digitalRead(PB15)==LOW&&hold==0){w=1;w1=1;b1=1;b2=0;setting++;if(setting>2){setting=0;}delay(300);} if(digitalRead(PB15)==LOW&&hold==1){w=1;w2=1;w4=1;hold_set++;if(hold_set>4){hold_set=0;}delay(300); } ////// TFT ////////////////////////////////////////// tft.setCursor(295, 0); tft.setTextColor(ILI9341_WHITE); tft.setTextSize(1); //////////////////// HOLD ON OFF /////////////////////////////////////////// if(digitalRead(PB12)==LOW&&hold==0){hold=1;w2=1;w4=1;link=1;setting=0;delay(300);} if(digitalRead(PB12)==LOW&&hold==1){hold=0;www=1;b2=0;hold_set=0;delay(300);} if(link==1){link=0;tft.fillRect(170,0,100,30,ILI9341_BLACK);} if(hold==0){tft.setCursor(170, 0);tft.print("OSCILLOSCOPE");tft.setCursor(170, 10);tft.print("VERSION 0.1");tft.setCursor(170, 20);tft.print("RCL-RADIO.RU");} razmer(); if(hold==0){ if(setting==0){ if(digitalRead(PB14)==LOW&&hold==0){razv++;if(razv>12){razv=12;}EEPROM.update(0,razv);delay(300);b2=1;w3=1;w=1;w1=1;u1=0;u2=0;t1=0;t2=0;} if(digitalRead(PB13)==LOW&&hold==0){razv--;if(razv<0){razv=0;}EEPROM.update(0,razv);delay(300);b2=1;w3=1;w=1;w1=1;w5=1;} razmer(); if(b2<5){b2++;tft.fillRect(80,0,65,8,ILI9341_RED);tft.setCursor(90, 0);tft.print((float)times3/10/mn,1);tft.print(" uS");} } if(setting==1){ if(digitalRead(PB14)==LOW&&hold==0){uxx++;if(uxx>5){uxx=5;}del=1;ux=uxx;delay(300);w=1;w1=1;u1=0;u2=0;t1=0;t2=0;} if(digitalRead(PB13)==LOW&&hold==0){uxx--;ux=uxx;if(uxx<=0){del=2;uxx=0;ux=1;}delay(300);w=1;w1=1;} if(w1==1){w1=0; tft.fillRect(85,10,50,8,ILI9341_RED);tft.setCursor(90, 10);tft.print("U x ");if(uxx==0){tft.print(0.5,1);}else{tft.print(uxx);}} } if(setting==2){ if(digitalRead(PB13)==LOW&&hold==0){sinhro++;if(sinhro>200){sinhro=200;}EEPROM.update(1,sinhro);delay(10);w=1;w1=1;} if(digitalRead(PB14)==LOW&&hold==0){sinhro--;if(sinhro<0){sinhro=0;}EEPROM.update(1,sinhro);delay(10);w=1;w1=1;} tft.fillRect(0, 230-sinhro+3, 3, 3, ILI9341_BLACK);tft.fillRect(0, 230-sinhro, 3, 3, 0xFFFFDD);tft.fillRect(0, 230-sinhro-3, 3, 3, ILI9341_BLACK); if(w1==1){w1=0;tft.fillRect(85,20,50,8,ILI9341_RED);tft.setCursor(90, 20);tft.print("SINH");} } } if(hold==0&&w==1){w=0; if(setting!=0){tft.fillRect(80,0,65,8,ILI9341_BLACK);tft.setCursor(90, 0);tft.print((float)times3/10/mn,1);tft.print(" uS");} if(setting!=1){tft.fillRect(70,10,65,8,ILI9341_BLACK);tft.setCursor(90, 10);tft.print("U x ");if(uxx==0){tft.print(0.5,1);}else{tft.print(uxx);}} if(setting!=2){tft.fillRect(70,20,65,8,ILI9341_BLACK);tft.setCursor(90, 20);tft.print("SINH"); tft.fillRect(0, 230-sinhro+3, 3, 3, ILI9341_BLACK);tft.fillRect(0, 230-sinhro, 3, 3, 0x333333);tft.fillRect(0, 230-sinhro-3, 3, 3, ILI9341_BLACK); }} if(uxx==0){pinMode(PA2,OUTPUT);digitalWrite(PA2,LOW);} if(uxx>0){pinMode(PA2,INPUT);analogRead(PA2);} //////////////// HOLD SET //////////////////////////////////////////////////////////////////////////////// if(hold==1){ if(w4==1){w4=0; tft.fillRect(80,0,65,8,ILI9341_BLUE);tft.setCursor(90, 0);tft.print((float)times3/10/mn,1);;tft.print(" uS");tft.fillRect(70,10,65,8,ILI9341_BLACK); tft.fillRect(0, 230-sinhro, 3, 3, 0x000000);tft.setCursor(90, 10);tft.print("U x ");tft.fillRect(70,20,65,8,ILI9341_BLACK);if(uxx==0){tft.print(0.5,1);}else{tft.print(uxx);}} tft.setCursor(295, 0); if(digitalRead(PB14)==LOW&&hold_set==0){i2+=2;if(i2>290){i2=290;}delay(50); tft.drawLine(0, u1+30,320, u1+30, 0x000000); tft.drawLine(0, 230-u2,320, 230-u2, 0x000000); tft.drawLine(t1, 230, t1, 30, 0x000000); tft.drawLine(318-t2, 230, 318-t2, 30, 0x000000); u1=0;u2=0;t1=0;t2=0;} if(digitalRead(PB13)==LOW&&hold_set==0){i2-=2;if(i2<0){i2=0;}delay(50); tft.drawLine(0, u1+30,320, u1+30, 0x000000); tft.drawLine(0, 230-u2,320, 230-u2, 0x000000); tft.drawLine(t1, 230, t1, 30, 0x000000); tft.drawLine(318-t2, 230, 318-t2, 30, 0x000000); u1=0;u2=0;t1=0;t2=0;} if(hold==1){tft.fillRect(i2,235,4,3,ILI9341_BLACK);tft.fillRect(i2+2,235,30,3,ILI9341_GREEN);tft.fillRect(i2+32,235,4,3,ILI9341_BLACK);} if(hold==0){tft.fillRect(295,0,25,8,ILI9341_RED);tft.fillRect(0,235,240,3,ILI9341_BLACK);}else{tft.print("HOLD");} if(digitalRead(PB14)==LOW&&hold==1&&hold_set==1){if(u1<199-u2){u1++;}w2=1;if(u1>200){u1=200;}delay(30);w=1;w1=1;} if(digitalRead(PB13)==LOW&&hold==1&&hold_set==1){if(u1<201-u2){u1--;}w2=1;if(u1<0){u1=0;}delay(30);w=1;w1=1;} if(digitalRead(PB13)==LOW&&hold==1&&hold_set==2){if(u2<199-u1){u2++;}w2=1;if(u2>200){u2=200;}delay(30);w=1;w1=1;} if(digitalRead(PB14)==LOW&&hold==1&&hold_set==2){if(u2<201-u1){u2--;}w2=1;if(u2<0){u2=0;}delay(30);w=1;w1=1;} if(digitalRead(PB14)==LOW&&hold==1&&hold_set==3){if(t1<319-t2){t1++;};w2=1;if(t1>320){t1=320;}delay(30);w=1;w1=1;} if(digitalRead(PB13)==LOW&&hold==1&&hold_set==3){if(t1<321-t2){t1--;}w2=1;if(t1<0){t1=0;}delay(30);w=1;w1=1;} if(digitalRead(PB13)==LOW&&hold==1&&hold_set==4){if(t2<319-t1){t2++;}w2=1;if(t2>320){t2=320;}delay(30);w=1;w1=1;} if(digitalRead(PB14)==LOW&&hold==1&&hold_set==4){if(t2<321-t1){t2--;}w2=1;if(t2<0){t2=0;}delay(30);w=1;w1=1;} if(w2==1){w2=0; tft.drawLine(0, u1-1+30,320, u1-1+30, ILI9341_BLACK);if(hold_set==1){tft.drawLine(0, u1+30,320, u1+30, 0xFFDAB9);}else{tft.drawLine(0, u1+30,320, u1+30, 0x222222);} tft.drawLine(0, u1+1+30,320, u1+1+30, ILI9341_BLACK); tft.drawLine(0, 230-u2-1,320, 230-u2-1, ILI9341_BLACK);if(hold_set==2){tft.drawLine(0, 230-u2,320, 230-u2, 0xFFDAB9);}else{tft.drawLine(0, 230-u2,320, 230-u2, 0x222222);} tft.drawLine(0, 230-u2+1,320, 230-u2+1, ILI9341_BLACK); tft.drawLine(t1-1, 230, t1-1, 30, ILI9341_BLACK);if(hold_set==3){tft.drawLine(t1, 230, t1, 30, 0xFFDAB9);}else{tft.drawLine(t1, 230, t1, 30, 0x222222);}tft.drawLine(t1+1, 230, t1+1, 30, ILI9341_BLACK); tft.drawLine(318-t2-1, 230, 318-t2-1, 30, ILI9341_BLACK);if(hold_set==4){tft.drawLine(318-t2, 230, 318-t2, 30, 0xFFDAB9);}else{{tft.drawLine(318-t2, 230, 318-t2, 30, 0x222222);}}tft.drawLine(318-t2+1, 230, 318-t2+1, 30, ILI9341_BLACK); if(hold_set==1){tft.fillRect(150,0,55,8,ILI9341_RED);}else{tft.fillRect(150,0,55,8,ILI9341_BLACK);} if(hold_set==2){tft.fillRect(150,10,55,8,ILI9341_RED);}else{tft.fillRect(150,10,55,8,ILI9341_BLACK);} if(hold_set==3){tft.fillRect(220,0,60,8,ILI9341_RED);}else{tft.fillRect(220,0,60,8,ILI9341_BLACK);} if(hold_set==4){tft.fillRect(220,10,60,8,ILI9341_RED);}else{tft.fillRect(220,10,60,8,ILI9341_BLACK);} tft.fillRect(150,20,55,8,ILI9341_BLACK); tft.setCursor(150, 0);tft.print("U1 = ");tft.print((3.3-u1*0.0165)/ux*del,2); tft.setCursor(150, 10);tft.print("U2 = ");tft.print((abs(u2*0.0165))/ux*del,2); tft.setCursor(150, 20);tft.print("U = ");tft.print(((3.3-u1*0.0165)-abs(u2*0.0165))/ux*del,2); tft.fillRect(220,20,65,8,ILI9341_BLACK); if(razv<6){zap=1;}else{zap=0;} tft.setCursor(220, 0);tft.print("T1 = ");tft.print(t1*(float)times3/1000/mn,zap); tft.setCursor(220, 10);tft.print("T2 = ");tft.print(((float)times3/3.125-t2*(float)times3/1000)/mn,zap); tft.setCursor(220, 20);tft.print("T = ");tft.print(((((float)times3/3.125-t2*(float)times3/1000))/mn-((t1*(float)times3/1000))/mn),zap); tft.fillRect(280,20,30,8,ILI9341_BLACK);tft.print(" uS"); }} ////////// END HOLD SET /////////////////////////////////////////////////////////////////////////////////// setka(); if(hold==0){DMA();if(dma1_ch1_Active == 0){for(int x=0; x<maxSamples; x++){data[x]=map(buffer[x],0,4095/ux,0,200);}}u1=0;u2=0;t1=0;t2=0;} arr(); if(hold==0){i2=0;for(i=1;i<1000;i++){if(data[i+5]>sinhro&&data[i+3]<sinhro){fun=i;if(fun>680){fun=0;}i=2000;}} for(i=0;i<1000-fun;i++){data1[i]=data[fun+i];}i=0;} while(i<320){ tft.drawLine(i*mn, 230-data_old[i],i*mn+mn-1, 230-data_old[i+1], ILI9341_BLACK); tft.drawLine(i*mn, 230-data1[i+i2],i*mn+mn-1, 230-data1[i+1+i2], ILI9341_RED);i++;}i=0; while(i<639){data_old[i]=data1[i+i2];i++;}i=0; if((mn>1&&w1==1&&hold==0&&setting==0)||www==1){www=0;w=1;w1=0;tft.fillScreen(ILI9341_BLACK);} if(razv==2&&w5==1){w5=0;w=1;w1=0;tft.fillScreen(ILI9341_BLACK);} } void DMA(){ switch(per){ case 0: rcc_set_prescaler(RCC_PRESCALER_ADC, RCC_ADCPRE_PCLK_DIV_2); break; case 1: rcc_set_prescaler(RCC_PRESCALER_ADC, RCC_ADCPRE_PCLK_DIV_4); break; case 2: rcc_set_prescaler(RCC_PRESCALER_ADC, RCC_ADCPRE_PCLK_DIV_6); break; case 3: rcc_set_prescaler(RCC_PRESCALER_ADC, RCC_ADCPRE_PCLK_DIV_8); break; } switch (raz) { case 0: adc_set_sample_rate(ADC1, ADC_SMPR_1_5); break; case 1: adc_set_sample_rate(ADC1, ADC_SMPR_7_5); break; case 2: adc_set_sample_rate(ADC1, ADC_SMPR_13_5); break; case 3: adc_set_sample_rate(ADC1, ADC_SMPR_28_5); break; case 4: adc_set_sample_rate(ADC1, ADC_SMPR_41_5); break; case 5: adc_set_sample_rate(ADC1, ADC_SMPR_55_5); break; case 6: adc_set_sample_rate(ADC1, ADC_SMPR_71_5); break; case 7: adc_set_sample_rate(ADC1, ADC_SMPR_239_5); break; } adc_set_reg_seqlen(ADC1, 1); ADC1->regs->SQR3 = PIN_MAP[pin].adc_channel; ADC1->regs->CR2 |= ADC_CR2_CONT; ADC1->regs->CR2 |= ADC_CR2_EXTSEL; ADC1->regs->CR2 |= ADC_CR2_SWSTART; dma_init(DMA1); dma_attach_interrupt(DMA1, DMA_CH1, DMA1_CH1_Event); myADC.setDMA(buffer, maxSamples, (DMA_MINC_MODE | DMA_TRNS_CMPLT), DMA1_CH1_Event); dma1_ch1_Active = 1; times3 = micros(); dma_enable(DMA1, DMA_CH1); while (dma1_ch1_Active == 1); dma_disable(DMA1, DMA_CH1); times3 = micros() - times3; } void setka(){ for(y=30;y<240;y=y+50){for(x=10;x<320;x=x+5){tft.drawPixel(x, y, ILI9341_DARKGREY);}} for(x=0;x<320;x=x+64){for(y=40;y<240;y=y+10){tft.drawPixel(x, y, ILI9341_DARKGREY);}}} static void DMA1_CH1_Event(){dma1_ch1_Active = 0;} void arr(){ ///// U max max if(millis()-times>500){u_max=0;u_min=4100;w3=1; tft.fillRect(40,0,30,28,ILI9341_BLACK); for(int mmm=0;mmm<640;mmm++){u_min=min(u_min,buffer[mmm]);u_max=max(u_max,buffer[mmm]);} tft.setCursor(0, 0);tft.print("Vmax = ");tft.print(u_max*3.3/4095*del,2); tft.setCursor(0, 10);tft.print("Vmin = ");tft.print(u_min*3.3/4095*del,2); u_sinh = u_max-u_min; tft.setCursor(0, 20);tft.print("Vpp = ");tft.print(u_sinh*3.3/4095*del,2); times=millis(); if(u_max*3.3/4095>=3.3){uxx=0;ux=1;del=2;w3=1;tft.fillRect(70,10,65,8,ILI9341_BLACK);tft.setCursor(90, 10);tft.print("U x ");tft.print(0.5,1);}} } void razmer(){ switch(razv){ case 0: mn=4; per=0;raz=0;;break; case 1: mn=2; per=0;raz=0;;break; case 2: mn=1; per=0;raz=0;break; case 3: mn=1; per=0;raz=1;break; case 4: mn=1; per=1;raz=1;break; case 5: mn=1; per=2;raz=1;break; case 6: mn=1; per=3;raz=1;break; case 7: mn=1; per=3;raz=2;break; case 8: mn=1; per=3;raz=3;break; case 9: mn=1; per=3;raz=4;break; case 10: mn=1; per=3;raz=5;break; case 11: mn=1; per=3;raz=6;break; case 12: mn=1; per=3;raz=7;break; } }
Поддержка платы STM32 в Arduino IDE — http://rcl-radio.ru/?p=68376
Привет. exit status 1
no matching function for call to ‘min(int&, uint16_t&)’
Ошибка при компиляции. Строка 226
У меня компиляция проходит нормально Arduino IDE 1.8.9
Последняя 1.8.13
Замените строку
for(int mmm=0;mmm<640;mmm++){u_min=min(u_min,buffer[mmm]);u_max=max(u_max,buffer[mmm]);}
на
for(int mmm=0;mmm<640;mmm++){u_min=min(u_min,(int)buffer[mmm]);u_max=max(u_max,(int)buffer[mmm]);}
Все заработало. Не бала обновлена библиотека stm32. Жду обновления часов под tm1637 на attyny85.
Добрый день.скажите а по 8 битной шине с другими дисплеями не пробовали?допустим ili9327 ili9341
Нет, только по ISP
Хочу попробовать ваш скетч редактировать под 8 бит ili9327
Здравствуйте! При компиляции вылезли такие ошибки
«]C:\Users\Home\Documents\Arduino\libraries\EEPROM\EEPROM.cpp:24:24: fatal error: avr/eeprom.h: No such file or directory
#include
^
compilation terminated.
Несколько библиотек найдено для «SPI.h»
Используется: C:\Users\Home\AppData\Local\Arduino15\packages\stm32duino\hardware\STM32F1\2020.12.17\libraries\SPI
Не используется: C:\Users\Home\Documents\Arduino\libraries\SPI
Несколько библиотек найдено для «EEPROM.h»
Используется: C:\Users\Home\Documents\Arduino\libraries\EEPROM
Не используется: C:\Users\Home\AppData\Local\Arduino15\packages\stm32duino\hardware\STM32F1\2020.12.17\libraries\EEPROM
»
Компилирую в ардуине иде 1.8.13 ,ядро обновил stm32 до последнего релиза
Конфликт библиотек, удалите (перед удалением заархивируйте ее) папку C:\Users\Home\Documents\Arduino\libraries\EEPROM
Спасибо,была мысль об этом,еще забыл библиотеку GFX поменять…Скомпилилось )
было бы интересно прилепить все это к планшету
Я пока не знаю как это программно сделать.
Приветствую. Собрал на макетке, работает. Но в моем дисплее не видно верхнюю стоку пикселей — что делать ?
Какое разрешение Вашего дисплея, он точно совместим с контроллером ILI9341?
дисплей — «3,2 дюймов 320*240 SPI серийный TFT ЖК-модуль, экран дисплея с драйвером сенсорной панели IC ILI9341 для MCU» — https://aliexpress.ru/item/32960934541.html?spm=a2g0s.9042311.0.0.360633edLobPTU&_ga=2.54547819.1597985063.1615637415-1336414455.1612635644. Вариант без сенсора.
Это конечно поправимо, но придется внести много правок в код, например первый столбец данных
tft.setCursor(0, 0);tft.print(«Vmax = «);tft.print(u_max*3.3/4095*del,2);
tft.setCursor(0, 10);tft.print(«Vmin = «);tft.print(u_min*3.3/4095*del,2);
u_sinh = u_max-u_min;
tft.setCursor(0, 20);tft.print(«Vpp = «);tft.print(u_sinh*3.3/4095*del,2);
Везде придется немного сдвинуть строки вниз
tft.setCursor(0, 2);tft.print(«Vmax = «);tft.print(u_max*3.3/4095*del,2);
tft.setCursor(0, 12);tft.print(«Vmin = «);tft.print(u_min*3.3/4095*del,2);
u_sinh = u_max-u_min;
tft.setCursor(0, 22);tft.print(«Vpp = «);tft.print(u_sinh*3.3/4095*del,2);
Спасибо за подсказку, будем пробовать править.
Приветствую. Было бы неплохо дополнить: STM32-осциллограф + генератор сигналов!
В инете есть варианты генераторов на Arduino, но скрестить их с STM32 мне не под силу.