Анализатор спектра до 10 кГц с выводом результата на LCD

RN6LJK
Offline
Зарегистрирован: 24.03.2013

Доброго дня, коллеги.

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

известно ли мне решение для создания анализатора спектра на Ардуине с выводом результата на LCD. Покопались мы в инетах и... ничего толкового не нашли. Может плохо искали, но все найденные решения были с реадизацией на Atmega или других МП или ч выводом на видео. После недолгих раздумий было решено взять за основу решение http://meandr.org/archives/3544, но "перетащить" его на Ардуину, т..к. там предлагался готовый код на С для реализации на Atmega32 и применен алгоритм дискретно преобразовани Фурье ДПФ. Сложность задачи, по крайней мере для меня, заключалась в том, что исходное решение выполнено на Atmega32, а требуется на Ардуине Мини Про на Atmega328. Может для кого то это просто, но для меня нет. Начал я с повторения готового решеня с эмуляцией на Proteus, но не тут то было. Я так и не смог инициализировать

LCD. Пришлось искать выходы. Вот выход http://radioparty.ru/prog-avr/program-c/268-lcd-avr-lesson2. 

Да простят меня оба автора, но я скомпилировал из двух одно решение, которое и представляю уважаемой публике.

Для отладки программы микропроцессора в данном случае IDE Arduino не подходит. Пришлось исползовать AVR Studio 7, распространяемое бесплатно http://www.atmel.com/ru/ru/tools/ATMELSTUDIO.aspx. Мне так и не удалось напрямую заливать код в Ардуину как пишут в руководстве. но меня это не остановило. Есть очень простое решение это Xloader, который позволяет заливать Hex код в Ардуину.

Итак последоваельность действий такая.

1. Скачаваем AVR Studio 7

2. Приводим в порядок код

 

Вот код, который родился у меня:

001/*
002 * SPECT_168.c
003 *
004 * Created: 07.01.2016 22:37:03
005 * Author : Владимир
006*/
007 
008#include <avr/io.h>
009#define F_CPU 16000000
010#include <util/delay.h>
011#define N 32
012#include "lookup.h"
013#define RS PC2
014#define EN PC1
015#define LCD_NIBBLE PORTD
016 
017 
018void adc_init();
019uint16_t adc_read();
020void TRANSFORM();
021void timer1_init();
022void LCD_STROBE(void);
023void lcd_data(unsigned char c);
024void lcd_cmd(unsigned char c);
025void lcd_clear(void);
026void lcd_init();
027void lcd_print(char *p, char l);
028void lcd_fill_custom();
029void lcd_com(unsigned char p);
030void lcd_dat(unsigned char p);
031 
032uint8_t lcd_buf1[16];
033uint8_t lcd_buf2[16];
034int32_t fx[N];
035int32_t Fu[N/2][2];
036 
037int main()
038{
039    uint8_t mag;
040    int i;
041//  uint8_t temp_index;
042    adc_init();
043    lcd_init();
044    lcd_fill_custom();
045    lcd_print("DFT SPECTROMETER",1);
046    lcd_print("0Hz - 10KHz(16)",2);
047    _delay_ms(100);
048    lcd_clear();
049    timer1_init();
050    while(1) {
051//      TCNT1 = 0;
052//      TIFR |= 1<<OCF1A;
053        for(i=0;i<N;i++) {
054//          while((TIFR & (1<<OCF1A)) == 0);
055            fx[i] = ((int16_t)adc_read());
056//          TIFR |= 1<<OCF1A;
057        }
058        TRANSFORM();
059        lcd_com(0xc0);
060        for(i =1; i<N/2; i++) {
061            if(Fu[i][0]<0)Fu[i][0]*=-1;
062            if(Fu[i][1]<0)Fu[i][1]*=-1;
063            mag = (uint8_t)(Fu[i][0] + Fu[i][1])/4;
064            if((mag)>7) {
065                lcd_buf1[i] = (mag) - 7 - 1;
066                if(lcd_buf1[i] > 7)
067                lcd_buf1[i] = 7;
068                lcd_buf2[i] = 7;
069            }
070            else {
071                lcd_buf1[i] = ' ';
072                lcd_buf2[i] = mag;
073            }
074        }
075        lcd_com(0x80);
076        for(i=1;i<16;i++)
077        lcd_dat(lcd_buf1[i]);
078        lcd_com(0xc0);
079        for(i=1;i<16;i++)
080        lcd_dat(lcd_buf2[i]);
081         
082         
083    }
084}
085 
086void TRANSFORM()
087{
088    int16_t count,degree;
089    uint8_t u,k;
090    count = 0;
091    for (u=0; u<N/2; u++) {
092        for (k=0; k<N; k++) {
093            degree = (uint16_t)pgm_read_byte_near(degree_lookup + count)*2;
094            count++;
095            Fu[u][0] +=  fx[k] * (int16_t)pgm_read_word_near(cos_lookup + degree);
096            Fu[u][1] += -fx[k] * (int16_t)pgm_read_word_near(sin_lookup + degree);
097        }
098        Fu[u][0] /= N;
099        Fu[u][0] /= 10000;
100        Fu[u][1] /= N;
101        Fu[u][1] /= 10000;
102    }
103}
104 
105void timer1_init()
106{
107    TCCR1B = (1<<WGM12)|(1<<CS10);
108    OCR1A = 800;
109}
110 
111void adc_init()
112{
113    ADMUX = 0b11000000;
114    ADCSRA =0b10000010;
115}
116 
117uint16_t adc_read()
118{
119    volatile uint16_t retl,reth;
120    ADCSRA |= 1<<ADSC;
121    while(!ADIF);
122    ADCSRA |= 1<<ADIF;
123    retl = ADCL;
124    reth = ADCH;
125    reth<<=8;
126    reth|=retl;
127    return reth;
128}
129 
130void LCD_STROBE(void)
131{
132     
133    PORTD |= (1 << EN);
134    _delay_us(1);
135    PORTD &= ~(1 << EN);
136     
137    /*
138        PORTC |= (1 << EN);
139        _delay_us(1);
140        PORTC &= ~(1 << EN);
141        */
142}
143 
144void lcd_data(unsigned char c)
145{
146     
147    PORTD |= (1 << RS);
148    _delay_us(50);
149    LCD_NIBBLE = (c << 4)|(LCD_NIBBLE&0xf0);
150    LCD_STROBE();
151    LCD_NIBBLE = (c)|(LCD_NIBBLE&0XF0);
152    LCD_STROBE();
153     
154/*      PORTC |= (1 << RS);
155        _delay_us(50);
156        LCD_NIBBLE = (c << 4)|(LCD_NIBBLE&0xf0);
157        LCD_STROBE();
158        LCD_NIBBLE = (c)|(LCD_NIBBLE&0XF0);
159        LCD_STROBE();*/
160}
161 
162void lcd_cmd(unsigned char c)
163{
164     
165    PORTD &= ~(1 << RS);
166    _delay_us(50);
167    LCD_NIBBLE = (c << 4)|(LCD_NIBBLE&0xf0);
168    LCD_STROBE();
169    LCD_NIBBLE = (c)|(LCD_NIBBLE&0XF0);
170    LCD_STROBE();
171     
172/*      PORTC &= ~(1 << RS);
173        _delay_us(50);
174        LCD_NIBBLE = (c << 4)|(LCD_NIBBLE&0xf0);
175        LCD_STROBE();
176        LCD_NIBBLE = (c)|(LCD_NIBBLE&0XF0);
177        LCD_STROBE();*/
178}
179 
180void lcd_clear(void)
181{
182    lcd_com(0x01);
183    _delay_ms(5);
184}
185 
186void lcd_init()
187{
188    /*
189    DDRC = 0b00001111;
190    DDRD |= (1 << RS)|(1 << EN);
191    PORTC |= (1<<PC4);
192    _delay_ms(15);
193    lcd_cmd(0x30);
194    _delay_ms(1);
195    lcd_cmd(0x30);
196    _delay_us(100);
197    lcd_cmd(0x30);
198    lcd_cmd(0x28);
199    lcd_cmd(0x28);
200    lcd_cmd(0x0c);
201    lcd_clear();
202    lcd_cmd(0x6);
203    */
204            DDRC |= (1 << EN)|(1 << RS); // PC1, PC0 - выходы
205            PORTC = 0x00;
206            DDRD = 0xFF; // порт C - выход
207            PORTD = 0x00;
208             
209            _delay_ms(50); // Ожидание готовности ЖК-модуля
210             
211            // Конфигурирование четырехразрядного режима
212            PORTD |= (1 << PD5);
213            PORTD &= ~(1 << PD4);
214             
215            // Активизация четырехразрядного режима
216            PORTC |= (1 << EN);
217            PORTC &= ~(1 << EN);
218            _delay_ms(5);
219             
220            lcd_com(0x28); // Шина 4 бит, LCD - 2 строки
221            lcd_com(0x08); // Полное выключение дисплея
222            lcd_com(0x01); // Очистка дисплея
223            _delay_us(100);
224            lcd_com(0x06); // Сдвиг курсора вправо
225            _delay_ms(10);
226            lcd_com(0x0C); // Включение дисплея, курсор не видим
227 
228 
229 
230 
231}
232 
233void lcd_print(char *p, char l)
234{
235    /*
236    if(l==1)lcd_cmd(0x80);
237    else lcd_cmd(0xc0);
238    while(*p)
239    lcd_data(*p++);
240    */
241        if(l==1)lcd_com(0x80);
242        else lcd_com(0xc0);
243        while(*p)
244        lcd_dat(*p++);
245     
246}
247 
248void lcd_fill_custom()
249{
250    uint8_t i,j;
251    i=0;j=0;
252    lcd_com(64);
253    for(i=1;i<=8;i++) {
254        for(j=8;j>i;j--)
255        lcd_dat(0);
256        for(j=i;j>0;j--)
257        lcd_dat(0xff);
258    }
259}
260 
261void lcd_com(unsigned char p)
262{
263// NEW 
264        PORTC &= ~(1 << RS); // RS = 0 (запись команд)
265        PORTC |= (1 << EN);  // EN = 1 (начало записи команды в LCD)
266        PORTD &= 0x0F; PORTD |= (p & 0xF0); // Выделяем старший нибл
267        _delay_us(100);
268        PORTC &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
269        _delay_us(100);
270        PORTC |= (1 << EN); // EN = 1 (начало записи команды в LCD)
271        PORTD &= 0x0F; PORTD |= (p << 4); // Выделяем младший нибл
272        _delay_us(100);
273        PORTC &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
274        _delay_us(100);
275 
276}
277void lcd_dat(unsigned char p)
278{
279     
280        PORTC |= (1 << RS)|(1 << EN); // RS = 1 (запись данных), EN - 1 (начало записи команды в LCD)
281        PORTD &= 0x0F; PORTD |= (p & 0xF0); // Выделяем старший нибл
282        _delay_us(100);
283        PORTC &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
284        _delay_us(100);
285        PORTC |= (1 << EN); // EN = 1 (начало записи команды в LCD)
286        PORTD &= 0x0F; PORTD |= (p << 4); // Выделяем младший нибл
287        _delay_us(100);
288        PORTC &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
289        _delay_us(100);
290 
291}

Еще авторский файлик Lookup.h

001//LOOKUP TABLE//
002#include <avr/pgmspace.h>
003  
004PROGMEM const int16_t cos_lookup[]= {
005    10000,9998,9993,9986,9975,9961,9945,9925,9902,
006    9876,9848,9816,9781,9743,9702,9659,9612,9563,
007    9510,9455,9396,9335,9271,9205,9135,9063,8987,
008    8910,8829,8746,8660,8571,8480,8386,8290,8191,
009    8090,7986,7880,7771,7660,7547,7431,7313,7193,
010    7071,6946,6819,6691,6560,6427,6293,6156,6018,
011    5877,5735,5591,5446,5299,5150,5000,4848,4694,
012    4539,4383,4226,4067,3907,3746,3583,3420,3255,
013    3090,2923,2756,2588,2419,2249,2079,1908,1736,
014    1564,1391,1218,1045,871,697,523,348,174,
015    0,-174,-348,-523,-697,-871,-1045,-1218,-1391,
016    -1564,-1736,-1908,-2079,-2249,-2419,-2588,-2756,-2923,
017    -3090,-3255,-3420,-3583,-3746,-3907,-4067,-4226,-4383,
018    -4539,-4694,-4848,-4999,-5150,-5299,-5446,-5591,-5735,
019    -5877,-6018,-6156,-6293,-6427,-6560,-6691,-6819,-6946,
020    -7071,-7193,-7313,-7431,-7547,-7660,-7771,-7880,-7986,
021    -8090,-8191,-8290,-8386,-8480,-8571,-8660,-8746,-8829,
022    -8910,-8987,-9063,-9135,-9205,-9271,-9335,-9396,-9455,
023    -9510,-9563,-9612,-9659,-9702,-9743,-9781,-9816,-9848,
024    -9876,-9902,-9925,-9945,-9961,-9975,-9986,-9993,-9998,
025    -10000,-9998,-9993,-9986,-9975,-9961,-9945,-9925,-9902,
026    -9876,-9848,-9816,-9781,-9743,-9702,-9659,-9612,-9563,
027    -9510,-9455,-9396,-9335,-9271,-9205,-9135,-9063,-8987,
028    -8910,-8829,-8746,-8660,-8571,-8480,-8386,-8290,-8191,
029    -8090,-7986,-7880,-7771,-7660,-7547,-7431,-7313,-7193,
030    -7071,-6946,-6819,-6691,-6560,-6427,-6293,-6156,-6018,
031    -5877,-5735,-5591,-5446,-5299,-5150,-5000,-4848,-4694,
032    -4539,-4383,-4226,-4067,-3907,-3746,-3583,-3420,-3255,
033    -3090,-2923,-2756,-2588,-2419,-2249,-2079,-1908,-1736,
034    -1564,-1391,-1218,-1045,-871,-697,-523,-348,-174,
035    0,174,348,523,697,871,1045,1218,1391,
036    1564,1736,1908,2079,2249,2419,2588,2756,2923,
037    3090,3255,3420,3583,3746,3907,4067,4226,4383,
038    4539,4694,4848,5000,5150,5299,5446,5591,5735,
039    5877,6018,6156,6293,6427,6560,6691,6819,6946,
040    7071,7193,7313,7431,7547,7660,7771,7880,7986,
041    8090,8191,8290,8386,8480,8571,8660,8746,8829,
042    8910,8987,9063,9135,9205,9271,9335,9396,9455,
043    9510,9563,9612,9659,9702,9743,9781,9816,9848,
044    9876,9902,9925,9945,9961,9975,9986,9993,9998
045};
046  
047PROGMEM const int16_t sin_lookup[]= {
048    0,174,348,523,697,871,1045,1218,1391,
049    1564,1736,1908,2079,2249,2419,2588,2756,2923,
050    3090,3255,3420,3583,3746,3907,4067,4226,4383,
051    4539,4694,4848,4999,5150,5299,5446,5591,5735,
052    5877,6018,6156,6293,6427,6560,6691,6819,6946,
053    7071,7193,7313,7431,7547,7660,7771,7880,7986,
054    8090,8191,8290,8386,8480,8571,8660,8746,8829,
055    8910,8987,9063,9135,9205,9271,9335,9396,9455,
056    9510,9563,9612,9659,9702,9743,9781,9816,9848,
057    9876,9902,9925,9945,9961,9975,9986,9993,9998,
058    10000,9998,9993,9986,9975,9961,9945,9925,9902,
059    9876,9848,9816,9781,9743,9702,9659,9612,9563,
060    9510,9455,9396,9335,9271,9205,9135,9063,8987,
061    8910,8829,8746,8660,8571,8480,8386,8290,8191,
062    8090,7986,7880,7771,7660,7547,7431,7313,7193,
063    7071,6946,6819,6691,6560,6427,6293,6156,6018,
064    5877,5735,5591,5446,5299,5150,4999,4848,4694,
065    4539,4383,4226,4067,3907,3746,3583,3420,3255,
066    3090,2923,2756,2588,2419,2249,2079,1908,1736,
067    1564,1391,1218,1045,871,697,523,348,174,
068    0,-174,-348,-523,-697,-871,-1045,-1218,-1391,
069    -1564,-1736,-1908,-2079,-2249,-2419,-2588,-2756,-2923,
070    -3090,-3255,-3420,-3583,-3746,-3907,-4067,-4226,-4383,
071    -4539,-4694,-4848,-4999,-5150,-5299,-5446,-5591,-5735,
072    -5877,-6018,-6156,-6293,-6427,-6560,-6691,-6819,-6946,
073    -7071,-7193,-7313,-7431,-7547,-7660,-7771,-7880,-7986,
074    -8090,-8191,-8290,-8386,-8480,-8571,-8660,-8746,-8829,
075    -8910,-8987,-9063,-9135,-9205,-9271,-9335,-9396,-9455,
076    -9510,-9563,-9612,-9659,-9702,-9743,-9781,-9816,-9848,
077    -9876,-9902,-9925,-9945,-9961,-9975,-9986,-9993,-9998,
078    -10000,-9998,-9993,-9986,-9975,-9961,-9945,-9925,-9902,
079    -9876,-9848,-9816,-9781,-9743,-9702,-9659,-9612,-9563,
080    -9510,-9455,-9396,-9335,-9271,-9205,-9135,-9063,-8987,
081    -8910,-8829,-8746,-8660,-8571,-8480,-8386,-8290,-8191,
082    -8090,-7986,-7880,-7771,-7660,-7547,-7431,-7313,-7193,
083    -7071,-6946,-6819,-6691,-6560,-6427,-6293,-6156,-6018,
084    -5877,-5735,-5591,-5446,-5299,-5150,-5000,-4848,-4694,
085    -4539,-4383,-4226,-4067,-3907,-3746,-3583,-3420,-3255,
086    -3090,-2923,-2756,-2588,-2419,-2249,-2079,-1908,-1736,
087    -1564,-1391,-1218,-1045,-871,-697,-523,-348,-174
088};
089  
090PROGMEM const uint8_t degree_lookup[]= {
091    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
092    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
093    0,0,0,5,11,16,22,28,33,39,45,50,56,61,67,
094    73,78,84,90,95,101,106,112,118,123,129,135,140,146,151,
095    157,163,168,174,0,11,22,33,45,56,67,78,90,101,112,
096    123,135,146,157,168,0,11,22,33,44,56,67,78,90,101,
097    112,123,134,146,157,168,0,16,33,50,67,84,101,118,135,
098    151,168,5,22,39,56,73,90,106,123,140,157,174,11,28,
099    45,61,78,95,112,129,146,163,0,22,45,67,90,112,135,
100    157,0,22,44,67,90,112,134,157,0,22,45,67,89,112,
101    134,157,0,22,45,67,89,112,135,157,0,28,56,84,112,
102    140,168,16,44,73,101,129,157,5,33,61,89,118,146,174,
103    22,50,78,106,135,163,11,39,67,95,123,151,0,33,67,
104    101,135,168,22,56,90,123,157,11,45,78,112,146,0,33,
105    67,101,135,168,22,56,90,123,157,11,44,78,112,146,0,
106    39,78,118,157,16,56,95,134,174,33,73,112,151,11,50,
107    89,129,168,28,67,106,146,5,44,84,123,163,22,61,101,
108    140,0,45,90,135,0,44,90,134,0,45,89,134,0,45,
109    89,135,0,44,90,135,179,44,89,134,0,45,90,134,179,
110    44,90,135,0,50,101,151,22,73,123,174,45,95,146,16,
111    67,118,168,39,90,140,11,61,112,163,33,84,134,5,56,
112    106,157,28,78,129,0,56,112,168,44,101,157,33,89,146,
113    22,78,135,11,67,123,179,56,112,168,45,101,157,33,90,
114    146,22,78,134,11,67,123,0,61,123,5,67,129,11,73,
115    134,16,78,140,22,84,146,28,89,151,33,95,157,39,101,
116    163,44,106,168,50,112,174,56,118,0,67,135,22,90,157,
117    45,112,0,67,135,22,90,157,44,112,0,67,134,22,90,
118    157,44,112,0,67,134,22,89,157,45,112,0,73,146,39,
119    112,5,78,151,45,118,11,84,157,50,123,16,90,163,56,
120    129,22,95,168,61,134,28,101,174,67,140,33,106,0,78,
121    157,56,134,33,112,11,89,168,67,146,44,123,22,101,179,
122    78,157,56,134,33,112,11,89,168,67,146,44,123,22,101,
123    0,84,168,73,157,61,146,50,135,39,123,28,112,16,101,
124    5,90,174,78,163,67,151,56,140,45,129,33,118,22,106,
125    11,95
126};

3. Моделируем в Proteus с получением результата.

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

Вот примерно так. Кому интересно пишите.

R2PM
Offline
Зарегистрирован: 28.12.2015

Всем привет, с новогодними праздниками!!!

Собираю детали на изготовление макета и рабочего проекта

С уважением Виктор R2PM

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011
RN6LJK
Offline
Зарегистрирован: 24.03.2013

trembo пишет:

Бог в помощь:

Спасибо.

Подбные форумы не патентное бюро, а место где увлеченные люди делятся своим опытом

по вопросам конструирования любительских устройств. И очень желательно, чтобы кому это не интересно, проходили мимо не останавливаясь.

Yarik.Yar
Offline
Зарегистрирован: 07.09.2014

Вот-вот. У вас есть непревзойдённый шанс пройти мимо, RN6LJK.

Immortal
Offline
Зарегистрирован: 28.12.2013

Очень интересный проект получился.

Можете выложить куда то файл для Proteus?

RN6LJK
Offline
Зарегистрирован: 24.03.2013

Immortal пишет:

Очень интересный проект получился.

Можете выложить куда то файл для Proteus?

Укажите свое мыло, я вышлю.

Immortal
Offline
Зарегистрирован: 28.12.2013

Можете на дропбокс или яндекс диск выложить? Вдруг еще кому то будет интересно

RN6LJK
Offline
Зарегистрирован: 24.03.2013

Пишите на адрес sokolova_o@mail.ru, всем отвечу.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Ну вот. А я собирался делать анализатор для распознавания голосовых команд .. опять "все украдено до нас" .. :)

R2PM
Offline
Зарегистрирован: 28.12.2015

Всем доброе время суток!!!

"Проект" - рабочий. "Зашил" файл Hex  в про мини  и LCD  отобразило спектры.

Работает "класно". мне нравиться. Есть нюансы как увеличить усреднение например.

В общем "обкатываю" в эфире.

С уважением Виктор R2PM

MagicianT
Offline
Зарегистрирован: 03.10.2015
TiMurKaLikho
Offline
Зарегистрирован: 13.04.2020

Здравствуйте, уважаемые!

Кто-то решался сделать анализатор спектра на ардуино и адресной ленте WS2812b?

Так чтобы именно сделать из ленты матрицу