Анализатор спектра до 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. Приводим в порядок код

 

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

/*
 * SPECT_168.c
 *
 * Created: 07.01.2016 22:37:03
 * Author : Владимир
*/

#include <avr/io.h>
#define F_CPU 16000000
#include <util/delay.h>
#define N 32
#include "lookup.h"
#define RS PC2
#define EN PC1
#define LCD_NIBBLE PORTD


void adc_init();
uint16_t adc_read();
void TRANSFORM();
void timer1_init();
void LCD_STROBE(void);
void lcd_data(unsigned char c);
void lcd_cmd(unsigned char c);
void lcd_clear(void);
void lcd_init();
void lcd_print(char *p, char l);
void lcd_fill_custom();
void lcd_com(unsigned char p);
void lcd_dat(unsigned char p);

uint8_t lcd_buf1[16];
uint8_t lcd_buf2[16];
int32_t fx[N];
int32_t Fu[N/2][2];

int main()
{
	uint8_t mag;
	int i;
//	uint8_t temp_index;
	adc_init();
	lcd_init();
	lcd_fill_custom();
	lcd_print("DFT SPECTROMETER",1);
	lcd_print("0Hz - 10KHz(16)",2);
	_delay_ms(100);
	lcd_clear();
	timer1_init();
	while(1) {
//		TCNT1 = 0;
//		TIFR |= 1<<OCF1A;
		for(i=0;i<N;i++) {
//			while((TIFR & (1<<OCF1A)) == 0);
			fx[i] = ((int16_t)adc_read());
//			TIFR |= 1<<OCF1A;
		}
		TRANSFORM();
		lcd_com(0xc0);
		for(i =1; i<N/2; i++) {
			if(Fu[i][0]<0)Fu[i][0]*=-1;
			if(Fu[i][1]<0)Fu[i][1]*=-1;
			mag = (uint8_t)(Fu[i][0] + Fu[i][1])/4;
			if((mag)>7) {
				lcd_buf1[i] = (mag) - 7 - 1;
				if(lcd_buf1[i] > 7)
				lcd_buf1[i] = 7;
				lcd_buf2[i] = 7;
			}
			else {
				lcd_buf1[i] = ' ';
				lcd_buf2[i] = mag;
			}
		}
		lcd_com(0x80);
		for(i=1;i<16;i++)
		lcd_dat(lcd_buf1[i]);
		lcd_com(0xc0);
		for(i=1;i<16;i++)
		lcd_dat(lcd_buf2[i]);
		
		
	}
}

void TRANSFORM()
{
	int16_t count,degree;
	uint8_t u,k;
	count = 0;
	for (u=0; u<N/2; u++) {
		for (k=0; k<N; k++) {
			degree = (uint16_t)pgm_read_byte_near(degree_lookup + count)*2;
			count++;
			Fu[u][0] +=  fx[k] * (int16_t)pgm_read_word_near(cos_lookup + degree);
			Fu[u][1] += -fx[k] * (int16_t)pgm_read_word_near(sin_lookup + degree);
		}
		Fu[u][0] /= N;
		Fu[u][0] /= 10000;
		Fu[u][1] /= N;
		Fu[u][1] /= 10000;
	}
}

void timer1_init()
{
	TCCR1B = (1<<WGM12)|(1<<CS10);
	OCR1A = 800;
}

void adc_init()
{
	ADMUX = 0b11000000;
	ADCSRA =0b10000010;
}

uint16_t adc_read()
{
	volatile uint16_t retl,reth;
	ADCSRA |= 1<<ADSC;
	while(!ADIF);
	ADCSRA |= 1<<ADIF;
	retl = ADCL;
	reth = ADCH;
	reth<<=8;
	reth|=retl;
	return reth;
}

void LCD_STROBE(void)
{
	
	PORTD |= (1 << EN);
	_delay_us(1);
	PORTD &= ~(1 << EN);
	
	/*
		PORTC |= (1 << EN);
		_delay_us(1);
		PORTC &= ~(1 << EN);
		*/
}

void lcd_data(unsigned char c)
{
	
	PORTD |= (1 << RS);
	_delay_us(50);
	LCD_NIBBLE = (c << 4)|(LCD_NIBBLE&0xf0);
	LCD_STROBE();
	LCD_NIBBLE = (c)|(LCD_NIBBLE&0XF0);
	LCD_STROBE();
	
/*		PORTC |= (1 << RS);
		_delay_us(50);
		LCD_NIBBLE = (c << 4)|(LCD_NIBBLE&0xf0);
		LCD_STROBE();
		LCD_NIBBLE = (c)|(LCD_NIBBLE&0XF0);
		LCD_STROBE();*/
}

void lcd_cmd(unsigned char c)
{
	
	PORTD &= ~(1 << RS);
	_delay_us(50);
	LCD_NIBBLE = (c << 4)|(LCD_NIBBLE&0xf0);
	LCD_STROBE();
	LCD_NIBBLE = (c)|(LCD_NIBBLE&0XF0);
	LCD_STROBE();
	
/*		PORTC &= ~(1 << RS);
		_delay_us(50);
		LCD_NIBBLE = (c << 4)|(LCD_NIBBLE&0xf0);
		LCD_STROBE();
		LCD_NIBBLE = (c)|(LCD_NIBBLE&0XF0);
		LCD_STROBE();*/
}

void lcd_clear(void)
{
	lcd_com(0x01);
	_delay_ms(5);
}

void lcd_init()
{
	/*
	DDRC = 0b00001111;
	DDRD |= (1 << RS)|(1 << EN);
	PORTC |= (1<<PC4);
	_delay_ms(15);
	lcd_cmd(0x30);
	_delay_ms(1);
	lcd_cmd(0x30);
	_delay_us(100);
	lcd_cmd(0x30);
	lcd_cmd(0x28);
	lcd_cmd(0x28);
	lcd_cmd(0x0c);
	lcd_clear();
	lcd_cmd(0x6);
	*/
			DDRC |= (1 << EN)|(1 << RS); // PC1, PC0 - выходы
			PORTC = 0x00;
			DDRD = 0xFF; // порт C - выход
			PORTD = 0x00;
			
			_delay_ms(50); // Ожидание готовности ЖК-модуля
			
			// Конфигурирование четырехразрядного режима
			PORTD |= (1 << PD5);
			PORTD &= ~(1 << PD4);
			
			// Активизация четырехразрядного режима
			PORTC |= (1 << EN);
			PORTC &= ~(1 << EN);
			_delay_ms(5);
			
			lcd_com(0x28); // Шина 4 бит, LCD - 2 строки
			lcd_com(0x08); // Полное выключение дисплея
			lcd_com(0x01); // Очистка дисплея
			_delay_us(100);
			lcd_com(0x06); // Сдвиг курсора вправо
			_delay_ms(10);
			lcd_com(0x0C); // Включение дисплея, курсор не видим




}

void lcd_print(char *p, char l)
{
	/*
	if(l==1)lcd_cmd(0x80);
	else lcd_cmd(0xc0);
	while(*p)
	lcd_data(*p++);
	*/
		if(l==1)lcd_com(0x80);
		else lcd_com(0xc0);
		while(*p)
		lcd_dat(*p++);
	
}

void lcd_fill_custom()
{
	uint8_t i,j;
	i=0;j=0;
	lcd_com(64);
	for(i=1;i<=8;i++) {
		for(j=8;j>i;j--)
		lcd_dat(0);
		for(j=i;j>0;j--)
		lcd_dat(0xff);
	}
}

void lcd_com(unsigned char p)
{
// NEW	
		PORTC &= ~(1 << RS); // RS = 0 (запись команд)
		PORTC |= (1 << EN);  // EN = 1 (начало записи команды в LCD)
		PORTD &= 0x0F; PORTD |= (p & 0xF0); // Выделяем старший нибл
		_delay_us(100);
		PORTC &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
		_delay_us(100);
		PORTC |= (1 << EN); // EN = 1 (начало записи команды в LCD)
		PORTD &= 0x0F; PORTD |= (p << 4); // Выделяем младший нибл
		_delay_us(100);
		PORTC &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
		_delay_us(100);

}
void lcd_dat(unsigned char p)
{
	
		PORTC |= (1 << RS)|(1 << EN); // RS = 1 (запись данных), EN - 1 (начало записи команды в LCD)
		PORTD &= 0x0F; PORTD |= (p & 0xF0); // Выделяем старший нибл
		_delay_us(100);
		PORTC &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
		_delay_us(100);
		PORTC |= (1 << EN); // EN = 1 (начало записи команды в LCD)
		PORTD &= 0x0F; PORTD |= (p << 4); // Выделяем младший нибл
		_delay_us(100);
		PORTC &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
		_delay_us(100);

}

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

//LOOKUP TABLE//
#include <avr/pgmspace.h>
 
PROGMEM const int16_t cos_lookup[]= {
    10000,9998,9993,9986,9975,9961,9945,9925,9902,
    9876,9848,9816,9781,9743,9702,9659,9612,9563,
    9510,9455,9396,9335,9271,9205,9135,9063,8987,
    8910,8829,8746,8660,8571,8480,8386,8290,8191,
    8090,7986,7880,7771,7660,7547,7431,7313,7193,
    7071,6946,6819,6691,6560,6427,6293,6156,6018,
    5877,5735,5591,5446,5299,5150,5000,4848,4694,
    4539,4383,4226,4067,3907,3746,3583,3420,3255,
    3090,2923,2756,2588,2419,2249,2079,1908,1736,
    1564,1391,1218,1045,871,697,523,348,174,
    0,-174,-348,-523,-697,-871,-1045,-1218,-1391,
    -1564,-1736,-1908,-2079,-2249,-2419,-2588,-2756,-2923,
    -3090,-3255,-3420,-3583,-3746,-3907,-4067,-4226,-4383,
    -4539,-4694,-4848,-4999,-5150,-5299,-5446,-5591,-5735,
    -5877,-6018,-6156,-6293,-6427,-6560,-6691,-6819,-6946,
    -7071,-7193,-7313,-7431,-7547,-7660,-7771,-7880,-7986,
    -8090,-8191,-8290,-8386,-8480,-8571,-8660,-8746,-8829,
    -8910,-8987,-9063,-9135,-9205,-9271,-9335,-9396,-9455,
    -9510,-9563,-9612,-9659,-9702,-9743,-9781,-9816,-9848,
    -9876,-9902,-9925,-9945,-9961,-9975,-9986,-9993,-9998,
    -10000,-9998,-9993,-9986,-9975,-9961,-9945,-9925,-9902,
    -9876,-9848,-9816,-9781,-9743,-9702,-9659,-9612,-9563,
    -9510,-9455,-9396,-9335,-9271,-9205,-9135,-9063,-8987,
    -8910,-8829,-8746,-8660,-8571,-8480,-8386,-8290,-8191,
    -8090,-7986,-7880,-7771,-7660,-7547,-7431,-7313,-7193,
    -7071,-6946,-6819,-6691,-6560,-6427,-6293,-6156,-6018,
    -5877,-5735,-5591,-5446,-5299,-5150,-5000,-4848,-4694,
    -4539,-4383,-4226,-4067,-3907,-3746,-3583,-3420,-3255,
    -3090,-2923,-2756,-2588,-2419,-2249,-2079,-1908,-1736,
    -1564,-1391,-1218,-1045,-871,-697,-523,-348,-174,
    0,174,348,523,697,871,1045,1218,1391,
    1564,1736,1908,2079,2249,2419,2588,2756,2923,
    3090,3255,3420,3583,3746,3907,4067,4226,4383,
    4539,4694,4848,5000,5150,5299,5446,5591,5735,
    5877,6018,6156,6293,6427,6560,6691,6819,6946,
    7071,7193,7313,7431,7547,7660,7771,7880,7986,
    8090,8191,8290,8386,8480,8571,8660,8746,8829,
    8910,8987,9063,9135,9205,9271,9335,9396,9455,
    9510,9563,9612,9659,9702,9743,9781,9816,9848,
    9876,9902,9925,9945,9961,9975,9986,9993,9998
};
 
PROGMEM const int16_t sin_lookup[]= {
    0,174,348,523,697,871,1045,1218,1391,
    1564,1736,1908,2079,2249,2419,2588,2756,2923,
    3090,3255,3420,3583,3746,3907,4067,4226,4383,
    4539,4694,4848,4999,5150,5299,5446,5591,5735,
    5877,6018,6156,6293,6427,6560,6691,6819,6946,
    7071,7193,7313,7431,7547,7660,7771,7880,7986,
    8090,8191,8290,8386,8480,8571,8660,8746,8829,
    8910,8987,9063,9135,9205,9271,9335,9396,9455,
    9510,9563,9612,9659,9702,9743,9781,9816,9848,
    9876,9902,9925,9945,9961,9975,9986,9993,9998,
    10000,9998,9993,9986,9975,9961,9945,9925,9902,
    9876,9848,9816,9781,9743,9702,9659,9612,9563,
    9510,9455,9396,9335,9271,9205,9135,9063,8987,
    8910,8829,8746,8660,8571,8480,8386,8290,8191,
    8090,7986,7880,7771,7660,7547,7431,7313,7193,
    7071,6946,6819,6691,6560,6427,6293,6156,6018,
    5877,5735,5591,5446,5299,5150,4999,4848,4694,
    4539,4383,4226,4067,3907,3746,3583,3420,3255,
    3090,2923,2756,2588,2419,2249,2079,1908,1736,
    1564,1391,1218,1045,871,697,523,348,174,
    0,-174,-348,-523,-697,-871,-1045,-1218,-1391,
    -1564,-1736,-1908,-2079,-2249,-2419,-2588,-2756,-2923,
    -3090,-3255,-3420,-3583,-3746,-3907,-4067,-4226,-4383,
    -4539,-4694,-4848,-4999,-5150,-5299,-5446,-5591,-5735,
    -5877,-6018,-6156,-6293,-6427,-6560,-6691,-6819,-6946,
    -7071,-7193,-7313,-7431,-7547,-7660,-7771,-7880,-7986,
    -8090,-8191,-8290,-8386,-8480,-8571,-8660,-8746,-8829,
    -8910,-8987,-9063,-9135,-9205,-9271,-9335,-9396,-9455,
    -9510,-9563,-9612,-9659,-9702,-9743,-9781,-9816,-9848,
    -9876,-9902,-9925,-9945,-9961,-9975,-9986,-9993,-9998,
    -10000,-9998,-9993,-9986,-9975,-9961,-9945,-9925,-9902,
    -9876,-9848,-9816,-9781,-9743,-9702,-9659,-9612,-9563,
    -9510,-9455,-9396,-9335,-9271,-9205,-9135,-9063,-8987,
    -8910,-8829,-8746,-8660,-8571,-8480,-8386,-8290,-8191,
    -8090,-7986,-7880,-7771,-7660,-7547,-7431,-7313,-7193,
    -7071,-6946,-6819,-6691,-6560,-6427,-6293,-6156,-6018,
    -5877,-5735,-5591,-5446,-5299,-5150,-5000,-4848,-4694,
    -4539,-4383,-4226,-4067,-3907,-3746,-3583,-3420,-3255,
    -3090,-2923,-2756,-2588,-2419,-2249,-2079,-1908,-1736,
    -1564,-1391,-1218,-1045,-871,-697,-523,-348,-174
};
 
PROGMEM const uint8_t degree_lookup[]= {
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,5,11,16,22,28,33,39,45,50,56,61,67,
    73,78,84,90,95,101,106,112,118,123,129,135,140,146,151,
    157,163,168,174,0,11,22,33,45,56,67,78,90,101,112,
    123,135,146,157,168,0,11,22,33,44,56,67,78,90,101,
    112,123,134,146,157,168,0,16,33,50,67,84,101,118,135,
    151,168,5,22,39,56,73,90,106,123,140,157,174,11,28,
    45,61,78,95,112,129,146,163,0,22,45,67,90,112,135,
    157,0,22,44,67,90,112,134,157,0,22,45,67,89,112,
    134,157,0,22,45,67,89,112,135,157,0,28,56,84,112,
    140,168,16,44,73,101,129,157,5,33,61,89,118,146,174,
    22,50,78,106,135,163,11,39,67,95,123,151,0,33,67,
    101,135,168,22,56,90,123,157,11,45,78,112,146,0,33,
    67,101,135,168,22,56,90,123,157,11,44,78,112,146,0,
    39,78,118,157,16,56,95,134,174,33,73,112,151,11,50,
    89,129,168,28,67,106,146,5,44,84,123,163,22,61,101,
    140,0,45,90,135,0,44,90,134,0,45,89,134,0,45,
    89,135,0,44,90,135,179,44,89,134,0,45,90,134,179,
    44,90,135,0,50,101,151,22,73,123,174,45,95,146,16,
    67,118,168,39,90,140,11,61,112,163,33,84,134,5,56,
    106,157,28,78,129,0,56,112,168,44,101,157,33,89,146,
    22,78,135,11,67,123,179,56,112,168,45,101,157,33,90,
    146,22,78,134,11,67,123,0,61,123,5,67,129,11,73,
    134,16,78,140,22,84,146,28,89,151,33,95,157,39,101,
    163,44,106,168,50,112,174,56,118,0,67,135,22,90,157,
    45,112,0,67,135,22,90,157,44,112,0,67,134,22,90,
    157,44,112,0,67,134,22,89,157,45,112,0,73,146,39,
    112,5,78,151,45,118,11,84,157,50,123,16,90,163,56,
    129,22,95,168,61,134,28,101,174,67,140,33,106,0,78,
    157,56,134,33,112,11,89,168,67,146,44,123,22,101,179,
    78,157,56,134,33,112,11,89,168,67,146,44,123,22,101,
    0,84,168,73,157,61,146,50,135,39,123,28,112,16,101,
    5,90,174,78,163,67,151,56,140,45,129,33,118,22,106,
    11,95
};

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?

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