VU метр на ILI9481

Alexandrid
Offline
Зарегистрирован: 05.09.2015

Всем привет. Хочу выводить на ILI9481 (480*320) VU метр или анализатор. Пока написал простой скетч VU метра и есть подозрения, что с анализатором будут тормоза. Кто-то делал что-то подобное на этом экране. Поделитесь скетчем красивого VU метра и анализатора.

Волшебник
Offline
Зарегистрирован: 22.12.2016

Чего анализировать? А ардуино какая?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Чего бы он ни анализировал, от экрана это никак не зависит.

Alexandrid
Offline
Зарегистрирован: 05.09.2015

Arduino Mega. Хочу сделать интересный VU метр для стерео усилителя. На ютюбе видел интересные графические решения, но кодом там не делятся.

Волшебник
Offline
Зарегистрирован: 22.12.2016

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 На УНО как-то писал, и дисплей 240х320, могу выложить если интересно

Alexandrid
Offline
Зарегистрирован: 05.09.2015

Очень интересно. Мне еще интересен стандартный VU столбцом с пиковыми значениями. Я сделал свой, но очень резко изменения отрабатывает или меджу двух значений перемигивается при определенных уровнях.

Мой VU meter https://youtu.be/60dR3q-1IVw

Я хочу подобие этого https://youtu.be/vQxXD6dpaCo

Волшебник
Offline
Зарегистрирован: 22.12.2016
Украли чужую библиотеку, вот и не выкладывают код. Не думают о последствиях, лошары.
Как Вам эта презентация, музыка немного не в такт, одна дорожка с интернета, другая ФМ:

https://youtu.be/r7Mm5jKa0qc

Alexandrid
Offline
Зарегистрирован: 05.09.2015

Хорошо сделано, но мне хотелось анализатор по 10-15 полос на канал и интересные варианты VU метра, где пиковые значения красиво изменяются. Да и вообще понять как лучше писать код, что бы тормозов не словить.

Волшебник
Offline
Зарегистрирован: 22.12.2016

Из больше полос сделать меньше? Трудная задача. 

По поводу оптимизации графики, могу из собственного опыта подсказать, что не надо перерисовывать весь экран каждый раз.

Недавно делал блок питания, посмотрите здесь http://forum.cxem.net/index.php?/topic/174170-%D1%86%D0%B8%D1%84%D1%80%D0%BE%D0%B2%D0%BE%D0%B8-%D0%BD%D0%B0-%D0%B7%D0%B2%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%BC-%D1%83%D1%81%D0%B8%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D0%B5/

Видите там 8 цифровых полей? Я пробовал сначала все их перерисовать, смотрелось просто отвратительно, мыргает экран, потом появляются поля цифр, одно за другим,  а поскольку мега не 1ГГц, на это уходило чуть не пол секунды. Потом мне пришла идея не рисовать все цифры, а по одной в 0.1 секунды. Человек всё одно не способен заметить изменения 40 цифр каждую секунду. Визуально всё просто "летать" стало, очень быстро, и экран не мерцает.

Такая же идея применена в спекто-анализаторе из видео ролика, все полоски не рисуются одновреммено, а их там 250 или около того, можете посметреть адафрутовский бенчмарк и сколько времени бы заняло рисование 250 линий. При том , что ардуино Уно там ещё БПФ-512 делает, в реальном масштабе времени.

Рисуются поочереди "группами", на глаз всё сливается.  

Короче пишите код, мы посмотрим и может чего подскажем

Alexandrid
Offline
Зарегистрирован: 05.09.2015
001//#include <Wire.h>
002//#include <PinChangeInt.h>
003#include <LCD.h>
004#include <LiquidCrystal.h>
005#include <EEPROM.h>
006#include <UTFT.h>                          // подключаем библиотеку UTFT
007extern uint8_t SmallFont[];                // подключаем маленький шрифт
008extern uint8_t BigFont[];                  // подключаем большой шрифт
009extern uint8_t SevenSegNumFont[];          // подключаем шрифт имитирующий семисегментный индикатор
010 
011UTFT myGLCD(TFT32MEGA, 38, 39, 40, 41);    // задаем тип дисплея
012 
013 
014//LED
015int led = 9;           // the pin that the LED is attached to
016int brightness = 0;    // how bright the LED is
017int fadeAmount = 5;    // how many points to fade the LED by
018// Init LCD
019int s;
020int l;
021int lp;
022int r;
023int A0val;
024int A1val;
025int barl;
026int barr;
027 
028 
029void setup()
030{ myGLCD.InitLCD();                        // инициируем дисплей
031  // Serial.begin (9600);
032 
033  pinMode(A0, INPUT);
034  pinMode(A4, INPUT);
035  myGLCD.clrScr();
036 
037 
038 
039  myGLCD.setColor(VGA_WHITE);
040  myGLCD.setFont(BigFont);
041}
042void loop()
043{
044 
045  // Считывание значения напряжения
046  A0val = analogRead(A0);
047  A1val = analogRead(A5);
048  barl = 20.0 / 1024.0 * int(A0val);
049  barr = 20.0 / 1024.0 * int(A1val);
050 
051 
052  // Вывод L
053 
054  // myGLCD.setColor(l*10,0,150);
055  // myGLCD.fillRect(l*20,0,l*20+15,50);
056  // myGLCD.setColor(VGA_BLACK);
057 
058 
059 
060  if ((l > barl))
061 
062  {
063    myGLCD.setColor(VGA_BLACK);
064    myGLCD.fillRect(l * 20 + 20, 0, l * 20 + 40, 50);
065 
066    l = l - 1;
067    // myGLCD.setColor(VGA_BLACK);
068    //myGLCD.fillRect(lp+5,0,lp+15,50);
069    //myGLCD.fillRect(s*20+32,0,s*20+34,20);
070    //delay(50);
071  }
072 
073  if ((l < barl))
074  { l = l + 1;
075 
076    //lp = l*20;
077 
078  }
079 
080 
081  //Пик--------------------------------
082  //s = barl * 10;
083 
084  if ((lp > l))
085  {
086 
087    lp = (s / 10);
088    s = s - 1;
089  }
090 
091  if ((lp < l))
092  {
093    s = s + 10;
094    lp = lp + 1 ;
095 
096  }
097 
098 
099  if ((lp == l))
100  {
101    // myGLCD.setColor(255,255,150);
102    ////  myGLCD.fillRect(lp*20+20,0,lp*20+35,50);
103    // lp = lp - 1;
104 
105  }
106  //пик
107  myGLCD.setColor(255, 255, 150);
108  myGLCD.fillRect(lp * 20 + 40, 0, lp * 20 + 55, 50);
109  //черный
110  myGLCD.setColor(VGA_BLACK);
111  myGLCD.fillRect(lp * 20 + 60, 0, lp * 20 + 80, 50);
112 
113  //уровень
114  myGLCD.setColor(l * 12, 0, 120);
115  //myGLCD.setColor(l*10,0,150);
116  myGLCD.fillRect(l * 20, 0, l * 20 + 15, 50);
117 
118  // myGLCD.print("  ", 14, 100);
119  delay(30);
120 
121 
122 
123  // Вывод R------------------------------
124 
125  myGLCD.setColor(0, 100, 100);
126  myGLCD.fillRect(r * 20, 200, r * 20 + 15, 250);
127  //
128 
129 
130 
131  if ((r > barr))
132  { myGLCD.setColor(VGA_BLACK);
133    myGLCD.fillRect(r * 20 + 20, 200, r * 20 + 40, 250);
134    // myGLCD.setColor(VGA_WHITE);
135    r = r - 1;
136    //myGLCD.fillRect(s*20+32,0,s*20+34,20);
137    //delay(50);
138  }
139 
140  if ((r < barr))
141  { r = r + 1;
142 
143 
144 
145 
146  }
147 
148  //   myGLCD.printNumI(s, 50, 130);
149 
150 
151  //  myGLCD.printNumI(barl, 0, 100);
152  //  myGLCD.printNumI(lp, 0, 130);
153  //  myGLCD.printNumI(barr, 0, 150);
154  // myGLCD.print("  ", 14, 100);
155 
156 
157}
158 
159 
160 
161//-----------------------------------------------------------------

 

Alexandrid
Offline
Зарегистрирован: 05.09.2015

Это просто пробный набросок, очень много лишнего и буду переписывать по другому.

Выводит только изменения, без прорисовки всей графики.

Волшебник
Offline
Зарегистрирован: 22.12.2016

Первое, что бросается в глаза - вы не правильно меряете. Индикатор VU должен показывать среднеквадратичное значение (RMS), аудио сигнал - переменка а не постоянный ток, и просто аналогРеад тут не работает.  Тут как пример из скетча (Уно), дисплей 12864:

001#include <glcd.h>                     // <a href="http://playground.arduino.cc/Code/GLCDks0108" title="http://playground.arduino.cc/Code/GLCDks0108" rel="nofollow">http://playground.arduino.cc/Code/GLCDks0108</a>
002#include <avr/pgmspace.h>
003 
004#include "fonts/allFonts.h"           // system and arial14 fonts are used
005#include "bitmaps/allBitmaps.h"       // all images in the bitmap dir
006 
007#define  SMP_RATE                   40       // Sampling Rate, in kHz
008#define  SMP_TMR1 ((16000/SMP_RATE) -1)      // Sampling Period of Timer1
009 
010/* VU Meter / The audio level meter most frequently encountered is the VU meter. Its characteristics are
011defined as the ANSI specification C165. Some of the most important specifications for an AC meter
012are its dynamic characteristics. These define how the meter responds to transients and how fast the reading
013decays. The VU meter is a relatively slow full-wave averaging type, specified to reach 99% deflection in
014300 ms and overshoot by 1 to 1.5%. In engineering terms this means a slightly underdamped second order
015response with a resonant frequency of 2.1 Hz and a Q of 0.62.
016While several European organizations have specifications for peak program meters, the German DIN specification
01745406 is becoming a de facto standard. Rather than respond instantaneously to peak, however, PPM specifications re-
018quire a finite “integration time” so that only peaks wide enough to be audible are displayed. DIN 45406
019calls for a response of 1 dB down from steady-state for a 10 ms tone burst and 4 dB down for a 3 ms tone burst.
020These requirements are consistent with the other frequently encountered spec of 2 dB down for a 5 ms burst and
021are met by an attack time constant of 1.7 ms. The specified return time of 1.5s to −20 dB requires a 650 ms
022decay time constant.*/
023 
024            Image_t      icon;
025            gText        countdownArea =  gText(GLCD.CenterX, GLCD.CenterY,1,1,Arial_14);
026             
027             int16_t     adc_Offst =   512;
028volatile     int32_t     ppm_Level =     0;
029             float       rms_Level =   0.0;
030//             int16_t     x10_coeff =    10;
031            
032ISR(TIMER1_COMPB_vect)
033{
034  int32_t temp = ADC - adc_Offst;
035 
036          temp = temp * temp;
037           
038  if ( temp > ppm_Level ) ppm_Level = ((ppm_Level * 255) + temp) >> 8;
039  else ppm_Level = (ppm_Level * 16383) >> 14;  
040}
041 
042void   Draw_Table()
043{
044  GLCD.CursorToXY( 3,  3);
045  GLCD.Puts("-20 10 5 3 1 0 1 2 3");
046  GLCD.CursorToXY( 5, 52);
047  GLCD.Puts("VU meter");
048  GLCD.CursorToXY(75, 52);
049  GLCD.Puts("Magician");
050  GLCD.DrawRoundRect( 0, 0, 126, 63, 5); 
051  GLCD.DrawLine( 64, 62, 5, 10, BLACK ) ;
052  GLCD.DrawLine( 63, 62, 4, 10, BLACK ) ;
053}
054 
055void   Draw_Arrow( int32_t scale )
056{
057  static int st1 = 5;
058  static int st2 = 5;
059 
060  st2 = map( scale, 20, 300, 5, 122);     //  23.5 dB 
061 
062  if ( st2 > 122 ) st2 = 122;
063  if ( st2 <   5 ) st2 =   5;
064  
065  if ( abs(st1 - st2) > 3 )               // 1/3 dB
066  {
067  GLCD.DrawLine( 64, 62, st1, 10, WHITE ) ;
068  GLCD.DrawLine( 63, 62, st1 -1, 10, WHITE ) ;
069 
070  GLCD.DrawLine( 64, 62, st2, 10, BLACK ) ;
071  GLCD.DrawLine( 63, 62, st2 -1, 10, BLACK ) ;
072 
073  st1 = st2;
074  }  
075}
076 
077void setup()
078{
079  Serial.begin(115200); 
080  GLCD.Init();  
081  if(GLCD.Height >= 64)  
082    icon = ArduinoIcon64x64;  // the 64 pixel high icon
083  else
084    icon = ArduinoIcon64x32;  // the 32 pixel high icon
085 
086  introScreen();
087  GLCD.ClearScreen();
088  GLCD.SelectFont(System5x7, BLACK);
089 
090  adc_Offst = analogRead(A5);       
091 
092  Draw_Table();
093   
094    /* Setup ADC */
095        ADMUX    = 0x45;        // PIN 5 Analog.
096 
097    ADCSRA = ((1<< ADEN)| // 1 = ADC Enable
098          (0<< ADSC)| // ADC Start Conversion
099          (1<<ADATE)| // 1 = ADC Auto Trigger Enable
100          (0<< ADIF)| // ADC Interrupt Flag
101          (0<< ADIE)| // ADC Interrupt Enable
102          (1<<ADPS2)|
103          (0<<ADPS1)| // ADC Prescaler : 1 MHz.
104          (0<<ADPS0)); 
105 
106    ADCSRB = ((1<<ADTS2)|   // Sets Auto Trigger source - Timer/Counter1 Compare Match B
107          (0<<ADTS1)|
108          (1<<ADTS0));
109 
110        /* Set up TIMER 1 - ADC sampler */
111        TIMSK0 = 0x00;
112        TIMSK1 = 0x00;
113        TIMSK2 = 0x00;
114 
115        TCCR1A = 0;
116        TCCR1B = 0;
117        TCCR1C = 0;
118 
119        TCCR1A =  ((1<<WGM11) | (1<<WGM10));       // Mode 15, Fast PWM
120        TCCR1B =  ((1<<WGM13) | (1<<WGM12));       // Mode 15, Fast PWM
121 
122        TCCR1B |=  (1<<CS10);                      // clk/1 prescaling.
123        OCR1A  = SMP_TMR1;
124        OCR1B  = SMP_TMR1;
125 
126        TCNT1  = 0;
127        TIFR1   |= (1<<OCF1B);
128        TIMSK1  |= (1<<OCIE1B);
129}
130 
131void  loop()
132
133  char incomingByte;
134  int32_t temp;
135    
136  temp = ppm_Level;                   // Take a copy, so Real Value not affected by calculation.
137  temp = sqrt(temp);
138 
139  rms_Level = 20.0 * log10(temp +1);  // Calculated, available over Serial
140 
141  Draw_Arrow( temp );
142 
143   if (Serial.available() > 0) {  
144    incomingByte = Serial.read();
145    // "x" command - DEBUG
146    if (incomingByte == 'x') {
147       Serial.println("\n\t");
148       Serial.println(adc_Offst, DEC);
149       Serial.println(ppm_Level, DEC);
150       Serial.println(rms_Level, 2);
151      }
152    if (incomingByte == 'c') {
153      GLCD.ClearScreen();
154      Draw_Table();
155      }
156   }
157}
158 
159void countdown(int count){
160  while(count--){  // do countdown 
161    countdownArea.ClearArea();
162    countdownArea.print(count);
163    delay(1000); 
164  
165}
166 
167void introScreen(){ 
168  GLCD.DrawBitmap(icon, 32,0);
169  countdown(3);
170  GLCD.ClearScreen();
171  GLCD.SelectFont(Arial_14);   
172  GLCD.CursorToXY(GLCD.Width/2 - 44, 3);
173  GLCD.print("*** VU Meter ***");
174  GLCD.DrawRoundRect(8,0,GLCD.Width-19,17, 5);  
175  countdown(3); 
176  GLCD.ClearScreen();
177}

Идея в том, что на АЦП вход подается смещение - половина напряжение, и переменка расчитывается как корень квадратный из суммы квадратов. Смотрите в учебниках подробней. Потом добавлюется логарифмическая шкала.

Второе, задайте тактовую частоту, без неё я вообще не представляю как мерять или рисовать . В примере таймер инициирует измерения, и поскольку частота известна, к ней легко привязать обновлвние экрана введя простой счётчик. 

Alexandrid
Offline
Зарегистрирован: 05.09.2015

Читаю и не пойму с 94 по 128. Что там происходит?

Волшебник
Offline
Зарегистрирован: 22.12.2016

Там настраивается таймер на частоту 40 кГц, по теореме Котел'никова это минимальная частота для сэмплинга входного аудио сигнала до 20 кГц.

Потом настраивается АЦП работать по таймеру, быстро, конверсия аналог-цифра должна быть быстрее чем 25 микросек. И мерять на А5 - остальные заняты жадным до пинов экраном,

Все эти регистры расписаны в даташите на микропроцессор АТмега328. Погуглите , наверняка есть лекции на русском по настройке , или книжки. И меня тут с десяток книжек по ардуино завалялось, но все на английском.

Alexandrid
Offline
Зарегистрирован: 05.09.2015

Переписал скетч на мой индикатор, но если я вывожу на экран текст, то все начинает тормозить... И не очень пойму, для чего нужен rms_Level, если он к индикатору вообще не касается. Значит происходит вывод не среднеквадратичного значения?

Волшебник
Offline
Зарегистрирован: 22.12.2016

rms_Level у меня логарифмическое и читается через сериал монитор, сейчас и не помню почему на экран я выводил среднеквадратичное -линейное а на его. Короче, простое RMS оно среднеквадратичное, то которое надо для измерения переменки, после sqrt оно и вычисляется.

Но проблема со шкалами, если для вольтметра скажем шкала нужна линейная, 10, 20, 30 В и так далее, то для VU она обычно логарифмическая, в дБ меряется. Я там и вычислял в дБ, но на экран вывел Вольты, наверно для красоты.

Alexandrid
Offline
Зарегистрирован: 05.09.2015

Я делаю именно VU в db.

Любое мелкое изменение парализует всю работу, добавил знак умножения или еще мелочи и все сразу глючит.

Волшебник
Offline
Зарегистрирован: 22.12.2016

Это всё происки вражеских нейро-сетей.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Alexandrid пишет:

Любое мелкое изменение парализует всю работу, добавил знак умножения или еще мелочи и все сразу глючит.

Это свидетельствует, что была выбрана изначально неверная структура программы.

И, в общем-то, я на это намекал уже в посте №2.