VU метр на ILI9481
- Войдите на сайт для отправки комментариев
Чт, 12/01/2017 - 05:33
Всем привет. Хочу выводить на ILI9481 (480*320) VU метр или анализатор. Пока написал простой скетч VU метра и есть подозрения, что с анализатором будут тормоза. Кто-то делал что-то подобное на этом экране. Поделитесь скетчем красивого VU метра и анализатора.
Чего анализировать? А ардуино какая?
Чего бы он ни анализировал, от экрана это никак не зависит.
Arduino Mega. Хочу сделать интересный VU метр для стерео усилителя. На ютюбе видел интересные графические решения, но кодом там не делятся.
На УНО как-то писал, и дисплей 240х320, могу выложить если интересно
Очень интересно. Мне еще интересен стандартный VU столбцом с пиковыми значениями. Я сделал свой, но очень резко изменения отрабатывает или меджу двух значений перемигивается при определенных уровнях.
Мой VU meter https://youtu.be/60dR3q-1IVw
Я хочу подобие этого https://youtu.be/vQxXD6dpaCo
https://youtu.be/r7Mm5jKa0qc
Хорошо сделано, но мне хотелось анализатор по 10-15 полос на канал и интересные варианты VU метра, где пиковые значения красиво изменяются. Да и вообще понять как лучше писать код, что бы тормозов не словить.
Из больше полос сделать меньше? Трудная задача.
По поводу оптимизации графики, могу из собственного опыта подсказать, что не надо перерисовывать весь экран каждый раз.
Недавно делал блок питания, посмотрите здесь 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 делает, в реальном масштабе времени.
Рисуются поочереди "группами", на глаз всё сливается.
Короче пишите код, мы посмотрим и может чего подскажем
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
007
extern
uint8_t SmallFont[];
// подключаем маленький шрифт
008
extern
uint8_t BigFont[];
// подключаем большой шрифт
009
extern
uint8_t SevenSegNumFont[];
// подключаем шрифт имитирующий семисегментный индикатор
010
011
UTFT myGLCD(TFT32MEGA, 38, 39, 40, 41);
// задаем тип дисплея
012
013
014
//LED
015
int
led = 9;
// the pin that the LED is attached to
016
int
brightness = 0;
// how bright the LED is
017
int
fadeAmount = 5;
// how many points to fade the LED by
018
// Init LCD
019
int
s;
020
int
l;
021
int
lp;
022
int
r;
023
int
A0val;
024
int
A1val;
025
int
barl;
026
int
barr;
027
028
029
void
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
}
042
void
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
//-----------------------------------------------------------------
Это просто пробный набросок, очень много лишнего и буду переписывать по другому.
Выводит только изменения, без прорисовки всей графики.
Первое, что бросается в глаза - вы не правильно меряете. Индикатор 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
011
defined as the ANSI specification C165. Some of the most important specifications for an AC meter
012
are its dynamic characteristics. These define how the meter responds to transients and how fast the reading
013
decays. The VU meter is a relatively slow full-wave averaging type, specified to reach 99% deflection in
014
300 ms and overshoot by 1 to 1.5%. In engineering terms this means a slightly underdamped second order
015
response with a resonant frequency of 2.1 Hz and a Q of 0.62.
016
While several European organizations have specifications for peak program meters, the German DIN specification
017
45406 is becoming a de facto standard. Rather than respond instantaneously to peak, however, PPM specifications re-
018
quire a finite “integration time” so that only peaks wide enough to be audible are displayed. DIN 45406
019
calls 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.
020
These requirements are consistent with the other frequently encountered spec of 2 dB down for a 5 ms burst and
021
are met by an attack time constant of 1.7 ms. The specified return time of 1.5s to −20 dB requires a 650 ms
022
decay 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;
028
volatile int32_t ppm_Level = 0;
029
float
rms_Level = 0.0;
030
// int16_t x10_coeff = 10;
031
032
ISR(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
042
void
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
055
void
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
077
void
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
131
void
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
159
void
countdown(
int
count){
160
while
(count--){
// do countdown
161
countdownArea.ClearArea();
162
countdownArea.print(count);
163
delay(1000);
164
}
165
}
166
167
void
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
}
Идея в том, что на АЦП вход подается смещение - половина напряжение, и переменка расчитывается как корень квадратный из суммы квадратов. Смотрите в учебниках подробней. Потом добавлюется логарифмическая шкала.
Второе, задайте тактовую частоту, без неё я вообще не представляю как мерять или рисовать . В примере таймер инициирует измерения, и поскольку частота известна, к ней легко привязать обновлвние экрана введя простой счётчик.
Читаю и не пойму с 94 по 128. Что там происходит?
Там настраивается таймер на частоту 40 кГц, по теореме Котел'никова это минимальная частота для сэмплинга входного аудио сигнала до 20 кГц.
Потом настраивается АЦП работать по таймеру, быстро, конверсия аналог-цифра должна быть быстрее чем 25 микросек. И мерять на А5 - остальные заняты жадным до пинов экраном,
Все эти регистры расписаны в даташите на микропроцессор АТмега328. Погуглите , наверняка есть лекции на русском по настройке , или книжки. И меня тут с десяток книжек по ардуино завалялось, но все на английском.
Переписал скетч на мой индикатор, но если я вывожу на экран текст, то все начинает тормозить... И не очень пойму, для чего нужен rms_Level, если он к индикатору вообще не касается. Значит происходит вывод не среднеквадратичного значения?
rms_Level у меня логарифмическое и читается через сериал монитор, сейчас и не помню почему на экран я выводил среднеквадратичное -линейное а на его. Короче, простое RMS оно среднеквадратичное, то которое надо для измерения переменки, после sqrt оно и вычисляется.
Но проблема со шкалами, если для вольтметра скажем шкала нужна линейная, 10, 20, 30 В и так далее, то для VU она обычно логарифмическая, в дБ меряется. Я там и вычислял в дБ, но на экран вывел Вольты, наверно для красоты.
Я делаю именно VU в db.
Любое мелкое изменение парализует всю работу, добавил знак умножения или еще мелочи и все сразу глючит.
Это всё происки вражеских нейро-сетей.
Любое мелкое изменение парализует всю работу, добавил знак умножения или еще мелочи и все сразу глючит.
Это свидетельствует, что была выбрана изначально неверная структура программы.
И, в общем-то, я на это намекал уже в посте №2.