ATmega + 12bit ADC

Обсуждаем контроллеры компании Atmel.
Аватара пользователя
slav0n
Опытный кот
Сообщения: 882
Зарегистрирован: Ср дек 01, 2010 00:38:15
Откуда: Харьков
Контактная информация:

Re: ATmega + 12bit ADC

Сообщение slav0n »

пересматривать надо не диапазон коррекции, а сам алгоритм.
нужно разделять режим установки уставки тока и рабочий режим.
а то получается фактически медленное ("единичка в секунду") накручивание тока.
нафига в таком случае вообще сдались эти всякие супер-пупер 15-бит, погрешности и тд...
крути ручку тока как попало да и все, эффект будет тот же
Последний раз редактировалось slav0n Пн сен 06, 2021 08:17:41, всего редактировалось 1 раз.
ohmycode!
primuss3.com
Реклама
Аватара пользователя
Starichok51
Модератор
Сообщения: 19054
Зарегистрирован: Сб авг 14, 2010 15:05:51
Откуда: г. Озерск, Челябинская обл.

Re: ATmega + 12bit ADC

Сообщение Starichok51 »

установка задания и рабочий режим у меня разделены.
устанавливать надо только в режиме "стоп". рабочем режиме крутить ручку бесполезно, так как после пуска запоминается установленное значение.
ты будешь делать, как ты посчитаешь нужным. а мне нравится так, как сделал я.
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Реклама
Аватара пользователя
slav0n
Опытный кот
Сообщения: 882
Зарегистрирован: Ср дек 01, 2010 00:38:15
Откуда: Харьков
Контактная информация:

Re: ATmega + 12bit ADC

Сообщение slav0n »

Starichok51 писал(а):а если автокоррекция успела набрать, допустим, код для 4,6А, а нагрузка захотела взять больше 4,6А (или сделали кз), то в первый момент ток будет 4,6А. и тут же коррекция начнет снижать ток к установленному значению 3А.
ну, если тебе нравится такая фигня, то зачем оно другим?

Добавлено after 7 minutes 8 seconds:
допустим, наивный юзер ставит ограничение 0.1 А в надежде уберечь свое детище от вони-огони.
ну, значит, он в нем ковыряется-настраивает... время идет, все норм...
тут чувак решает зафорсить режим своей приблуды надеясь на ограничение 0.1А... а твой супер-БП ему куяк, и десяточку ампер в "первый момент"
ohmycode!
primuss3.com
Аватара пользователя
Starichok51
Модератор
Сообщения: 19054
Зарегистрирован: Сб авг 14, 2010 15:05:51
Откуда: г. Озерск, Челябинская обл.

Re: ATmega + 12bit ADC

Сообщение Starichok51 »

не в начальный момент, а минут через 15 ...
и по поводу десяточки Ампер я уже согласился с Димой, что нужно ограничить коррекцию 10-15 единицами, чтобы далеко не убежало. или ты этого не читал?

Добавлено after 1 minute 28 seconds:
а по поводу других - еще никто не пожаловался из тех, кто собрал мой проект.
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Реклама
Эиком - электронные компоненты и радиодетали
Аватара пользователя
slav0n
Опытный кот
Сообщения: 882
Зарегистрирован: Ср дек 01, 2010 00:38:15
Откуда: Харьков
Контактная информация:

Re: ATmega + 12bit ADC

Сообщение slav0n »

Starichok51 писал(а): нужно ограничить коррекцию 10-15 единицами, чтобы далеко не убежало
это костыли для кривого алгоритма
правильный алгоритм никуда убегать не должен

Добавлено after 1 hour 11 minutes 10 seconds:
Starichok51 писал(а):установка задания и рабочий режим у меня разделены.
устанавливать надо только в режиме "стоп". рабочем режиме крутить ручку бесполезно, так как после пуска запоминается установленное значение.
а крутить ручку физически нет необходимости, "крутит ручку" твой чудо-алгоритм

Добавлено after 56 minutes 48 seconds:
вести с полей оверсемплинга

Код: Выделить всё

#define ROUNDS	(1024 * 2)
. . .
res += ((adc1 * 4550.0 / 102.4 / ROUNDS) - res) * 0.125;
вижу на дисплее разрешение 100 мкВ, но уже начинает возникать резонный вопрос - а нафига?
ohmycode!
primuss3.com
Реклама
Dimon456
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Re: ATmega + 12bit ADC

Сообщение Dimon456 »

slav0n писал(а):а нафига?
для тока самый раз, максимум 4,550А, разрешение 0,1миллиА.
Другой вопрос, а ты с чем сравниваешь показания, какой образцовый прибор используешь?
Реклама
Аватара пользователя
slav0n
Опытный кот
Сообщения: 882
Зарегистрирован: Ср дек 01, 2010 00:38:15
Откуда: Харьков
Контактная информация:

Re: ATmega + 12bit ADC

Сообщение slav0n »

я пишу не про абсолютное значение, а про разрешение
т.е. с каким шагом изменяются показания
ohmycode!
primuss3.com
Аватара пользователя
Starichok51
Модератор
Сообщения: 19054
Зарегистрирован: Сб авг 14, 2010 15:05:51
Откуда: г. Озерск, Челябинская обл.

Re: ATmega + 12bit ADC

Сообщение Starichok51 »

тогда расскажи, чему у тебя равен шаг и чему равно разрешение.
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Аватара пользователя
slav0n
Опытный кот
Сообщения: 882
Зарегистрирован: Ср дек 01, 2010 00:38:15
Откуда: Харьков
Контактная информация:

Re: ATmega + 12bit ADC

Сообщение slav0n »

ну, так я же кино показывал с шагом 1 мВ
100 мкВ для реального применения неинтересен, слишком инерционный фильтр получается. оно чисто для спорта
1 десятичный порядок по идее требует как минимум 3 двоичных
Последний раз редактировалось slav0n Пн сен 06, 2021 19:49:17, всего редактировалось 1 раз.
ohmycode!
primuss3.com
Dimon456
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Re: ATmega + 12bit ADC

Сообщение Dimon456 »

Меня интересует максимум и шаг:
максимум 5В шаг 1мВ
максимум 50В шаг 10мВ
максимум 500В шаг 100мВ
Аватара пользователя
slav0n
Опытный кот
Сообщения: 882
Зарегистрирован: Ср дек 01, 2010 00:38:15
Откуда: Харьков
Контактная информация:

Re: ATmega + 12bit ADC

Сообщение slav0n »

так все это уже давно есть
максимум 5В шаг 1мВ - напрямую с опорой 5в
максимум 50В шаг 10мВ - через делитель 1/10
максимум 500В шаг 100мВ - делитель 1/100
ohmycode!
primuss3.com
Аватара пользователя
slav0n
Опытный кот
Сообщения: 882
Зарегистрирован: Ср дек 01, 2010 00:38:15
Откуда: Харьков
Контактная информация:

Re: ATmega + 12bit ADC

Сообщение slav0n »

избавился от плавающей точки, а это минус 300Б кода.

Код: Выделить всё

#define ROUNDS   1024
. . .
res1 += (((adc1 >> 5) * 4550 / 1024 / (ROUNDS >> 5)) - res1) >> 2;
ohmycode!
primuss3.com
charchyard
Поставщик валерьянки для Кота
Сообщения: 2466
Зарегистрирован: Сб май 07, 2011 17:52:59

Re: ATmega + 12bit ADC

Сообщение charchyard »

желаю здравия :beer: churchyard in the air. спасибо всем неравнодушным, кто высказывал свои гипотезы.
вот такая кодировка использовалась для одной из двухканальных головок.
Спойлер

Код: Выделить всё

[code]//ATmega_8A + max7219 + mcp3201_0 + mcp3201_1
//va-meter +0...399.99vdc / +0...9.999adc
//16MHz

#define F_CPU 16000000UL                                     //тактовая частота мк (unsigned long)
#include <avr/io.h>                                          //подключение библиотеки "ввод/вывод" мк
#include <util/delay.h>                                      //подключение библиотеки "пауза" мк
#include <avr/interrupt.h>                                   //подключение библиотеки "прерывание" мк

#define Vref 4.096 //+Vref=4.096vdc
#define Kv 4.195 //коэффициент преобразования для вычислнения величины [V] Kv=4096*4.096/3999
#define Ki 1.678 //коэффициент преобразования для вычислнения величины [I] Ki=4096*4.096/9999
#define ADC_CS0_0 PORTB &= ~(1<<PORTB0) //ADC_CS0=0
#define ADC_CS0_1 PORTB |= (1<<PORTB0) //ADC_CS0=1
#define ADC_CS1_0 PORTB &= ~(1<<PORTB2) //ADC_CS1=0
#define ADC_CS1_1 PORTB |= (1<<PORTB2) //ADC_CS1=1
#define ADC_CLK_0 PORTB &= ~(1<<PORTB5) //ADC_CLK=0
#define ADC_CLK_1 PORTB |= (1<<PORTB5) //ADC_CLK=1

#define DRV_CLK_0 PORTC &= ~(1<<PORTC0) //DRV_CLK=0
#define DRV_CLK_1 PORTC |= (1<<PORTC0) //DRV_CLK=1
#define DRV_CS_0 PORTC &= ~(1<<PORTC1) //DRV_CS=0
#define DRV_CS_1 PORTC |= (1<<PORTC1) //DRV_CS=1
#define DRV_MOSI_0 PORTC &= ~(1<<PORTC2) //DRV_MOSI=0
#define DRV_MOSI_1 PORTC |= (1<<PORTC2) //DRV_MOSI=1
#define measure 0x01 // по результатам 1 измерений вычислить средне-арифметическое значение (VOLn=1+...+VOLn=1)/1

//-----------------
unsigned int Digit[8]; //массив беззнаковых целочисленных переменных Digit из 8 переменных (8 разрядов драйвера 7seg LED-дисплея)
unsigned char DRV_MOSI[8]; //массив беззнаковых однобайтных символьных переменных DRV_MOSI из 8 переменных (адреса и команды для конфигурирования драйвера)
unsigned int Display2_ADC; //беззнаковая целочисленная переменная Display2_ADC
unsigned char V; //беззнаковая однобайтная символьная переменная V (значение регистра ацп с результатом оцифровки напряжения)
unsigned char I; //беззнаковая однобайтная символьная переменная I (значение регистра ацп с результатом оцифровки тока)
unsigned int adc_conv count; // переменная для задержки обновления данных на индикаторах

unsigned int dt_v; //12-битный результат оцифровки величины [V]
float Av; //Av=dt_v
float Mv; //окончательный результат работы сглаживающего фильтра в канале [V]
float Mv1 = 0; //сброс промежуточного результата работы сглаживающего фильтра в канале [V]
float Ks_v = 0.05; //коэффициент сглаживания в канале [V]

unsigned int dt_i; //12-битный результат оцифровки величины [I]
float Ai; //Ai=dt_i
float Mi; //окончательный результат работы сглаживающего фильтра в канале [I]
float Mi1 = 0; //сброс промежуточного результата работы сглаживающего фильтра в канале [I]
float Ks_i = 0.05; //коэффициент сглаживания в канале [I]

//--- инициализация CS0 ---
void CS0_ini(void) //функция инициализации порта PB0
{
	DDRB |= (1<<PORTB0); //PB0 на вывод
	PORTB |= (1<<PORTB0); //PB0_hi
}

//--- инициализация CS1 ---
void CS1_ini(void) //функция инициализации порта PB2
{
	DDRB |= (1<<PORTB2); //PB2 на вывод
	PORTB |= (1<<PORTB2); //PB2_hi
}

//--- инициализация генератора ---
void PB1_ini(void) //функция инициализации порта PB1
{
	DDRB |= (1<<PORTB1); //PB1 на вывод (oc1a)
	PORTB &= ~(1<<PORTB1); //PB1 сброс
}

//--- инициализация PB2 ---
/*void PB2_ini(void) //функция инициализации порта PB2 (выход запуска генератора +Vpwr = +9...12v)
{
	DDRB |= (1<<PORTB2); //PB2 на вывод (oc1b)
	PORTB &= ~(1<<PORTB2); //PB2 сброс
}*/

//--- инициализация таймера oc1a ---
void oc1a(void) //функция таймера
{
	ASSR=0x00; //сбрасываем полностью регистр assr
	TCCR1A |= ((1<<COM1A1)|(1<<WGM10)); //Fast PWM oc1a, Clear oc1a on Compare Match, clkT2S/1 (no prescalling)
	TCCR1B |= ((1<<WGM12)|(1<<CS10));
	TCNT1H=0x00; // Timer Value = 0 сброс счётного регистра таймера 1
	TCNT1L=0x00;
	OCR1AH=0x00;
	OCR1AL=0x55; //Output Compare Register = dec85 - заполнение шим +DC~33%
	TIMSK=0x00; //сброс регистра timsk
}

//--- инициализация таймера oc1b ---
/*void oc1b(void) //функция таймера
{
	ASSR=0x00; //сбрасываем полностью регистр assr
	TCCR1A |= ((1<<COM1B1)|(1<<WGM10)); //Fast PWM oc1b, Clear oc1b on Compare Match, clkT2S/1 (no prescalling)
	TCCR1B |= ((1<<WGM12)|(1<<CS10));
	TCNT1H=0x00; // Timer Value = 0 сброс счётного регистра таймера 2
	TCNT1L=0x00;
	OCR1BH=0x00;
	OCR1BL=0x64; //Output Compare Register = dec100 - заполнение шим +DC~40%
	TIMSK=0x00; //сброс регистра timsk
}*/

//--- инициализация шины SPI_ADC ---
void ADC_SPI_ini(void)
{
	DDRB &= ~(1<<PORTB4); PORTB |= (1<<PORTB4); //MISO, pull-up=ON
	DDRB |= ((1<<PORTB5)|(1<<PORTB2)|(1<<PORTB0)); //выход ADC_CLK, выход ADC_CS1, выход ADC_CS0
	PORTB |= ((1<<PORTB2)|(1<<PORTB0)); 
	PORTB &= ~(1<<PORTB5); //сброс шины SPI_ADC
	SPCR = ((1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0)); //включим шину SPI, объ§вим ведущим, SCK=16e+06/128=125kHz
}

//--- функция передачи/приёма данных по шине SPI ---
void SPI_SendByte(char byte)
{
	SPDR = byte; //
	while(!(SPSR & (1<<SPIF))); //подождем пока данные передадутс¤
}
unsigned char SPI_ChangeByte(char byte)
{
	SPDR = byte;
	while(!(SPSR & (1<<SPIF))); //подождем пока данные передадутс¤ (обмен¤ютс¤)
	return SPDR;
}

//--- функция опроса внешнего АЦП mcp3201_0 ---
unsigned int Read_3201_0(unsigned char channel)
{
	unsigned int b1,b2;
	ADC_CS0_0; //CS0=0
	b1=SPI_ChangeByte(0); //первый байт
	b2=SPI_ChangeByte(0); //второй байт
	b1=(b1<<8)|b2; //собираем два байта в двухбайтовую величину
	b1<<=3;
	b1>>=4; //убираем ненужные биты (3 слева и 1 справа)
	ADC_CS0_1; //CS0=1
	return b1; //возвращаем 12-битный результат ацп-преобразования
}

//--- функция опроса внешнего АЦП mcp3201_1 ---
unsigned int Read_3201_1(unsigned char channel)
{
	unsigned int b1,b2;
	ADC_CS1_0; //CS1=0
	b1=SPI_ChangeByte(0); //первый байт
	b2=SPI_ChangeByte(0); //второй байт
	b1=(b1<<8)|b2; //собираем два байта в двухбайтовую величину
	b1<<=3;
	b1>>=4; //убираем ненужные биты (3 слева и 1 справа)
	ADC_CS1_1; //CS1=1
	return b1; //возвращаем 12-битный результат ацп-преобразования
}

//--- инициализация шины данных SPI_DRV драйвера max7219 ---
void DRV_SPI_ini(void)
{
	DDRC = 0x07; //биты PС0-PС2 порта PС на вывод
	PORTC &= ~((1<<PORTC2)|(1<<PORTC1)|(1<<PORTC0)); //PС0-PС2 сброс
}

//--- функция побитовой отправки данных в драйвер max7219 ---
void Send_max7219(unsigned char rg, unsigned char dt) //
{
	unsigned char rg_copy; //копия значения переменной rg
	unsigned char i; //переменная для побитной отправки данных в драйвер индикаторов
	
	DRV_MOSI[rg] = dt; //
	rg_copy = rg; //создадим копию значения переменной rg
	
	DRV_CS_0; //отправим «0» на вывод CS микросхемы MAX7219, чтобы начать процесс передачи адреса и данных
	asm("nop"); //пауза в 1 такт
	
	for(i=0;i<8;i++) //цикл от 0 до 7 с шагом 1, для побитовой отправки байта адреса в микросхему MAX7219
	{
		if((rg & 0x80)==0x80) //пока rg * 0b1000 0000 > 0, ...
		{
			DRV_MOSI_1; //...отправим 1 на вывод Din микросхемы MAX7219
		}
		else //если же rg * 0b1000 0000 = 0, ...
		{
			DRV_MOSI_0; //...отправим 0 на вывод Din микросхемы MAX7219
		}
		
		//создадим тактовый импульс на выводе CLK микросхемы MAX7219
		asm("nop"); //пауза в 1 такт
		DRV_CLK_1; //отправим 1 на вывод Clk микросхемы MAX7219
		asm("nop"); //пауза в 1 такт
		DRV_CLK_0; //отправим 0 на вывод Clk микросхемы MAX7219
		rg <<= 1; //сдвинем значение переменной rg на 1 бит влево
	} //выйдем из цикла когда i станет равной 7, т.е. когда отправка байта адреса в микросхему MAX7219 будет окончена
	
	for(i=0;i<8;i++) //цикл от 0 до 7 с шагом 1, для побитовой отправки байта данных в микросхему MAX7219
	{
		if((DRV_MOSI[rg_copy] & 0x80)==0x80) //пока rg * 0b1000 0000 > 0, ...
		{
			DRV_MOSI_1; //...отправим 1 на вывод Din микросхемы MAX7219
		}
		else //если же rg * 0b1000 0000 = 0, ...
		{
			DRV_MOSI_0; //отправим 0 на вывод Din микросхемы MAX7219
		}
		
		//создадим тактовый импульс на выводе CLK микросхемы MAX7219
		asm("nop"); //пауза в 1 такт
		DRV_CLK_1; //отправим 1 на вывод Clk микросхемы MAX7219
		asm("nop"); //пауза в 1 такт
		DRV_CLK_0; //отправим 0 на вывод Clk микросхемы MAX7219
		DRV_MOSI[rg_copy] <<= 1; //сдвинем значение переменной DRV_MOSI на 1 бит влево
	}
	//выйдем из цикла когда i станет равной 7, т.е. когда отправка байта данных в микросхему MAX7219 будет окончена
	DRV_CS_1; //отправим «1» на вывод CS микросхемы MAX7219, чтобы завершить процесс передачи адреса и данных
}

//--- инициализация драйвера max7219 ---
void MAX7219_ini(void)
{
	Send_max7219(0x09,0xFF); //(номер регистра, данные) включаем режим BCD code B, для 0-7 разрядов
	Send_max7219(0x0A,0x0A); //DC = 21/32 яркость свечения
	Send_max7219(0x0B,0x07); //число используемых разрядов (0-7 разрядов)
	Send_max7219(0x0C,0x01); //отключаем режим энергосбережения (Shutdown)
	
	for(V=1;V<9;V++)
	{
	  Send_max7219(V,0x0F);
	}	
}

//--- Функция вывода значений на индикатор вольтметра ---
void ledprint_1(unsigned int number)
{
	Digit[8]=number/1000 ? number/1000: 0x0F; //гашение незначащего нуля в разряде "тысячи"
	Digit[7]=number/1000 || number%1000/100 ? number%1000/100: 0x0F; //гашение незначащего нуля в разряде "сотни"
	Digit[6]=number%100/10; //десятки
	Digit[6]=Digit[6]|128; //вкл децимальную точку в разряде Digit_6	
	Digit[5]=number%10; //единицы
	
	for(V=5;V<9;V++)
	{
		DRV_MOSI[V] = Digit[V];
		Send_max7219(V,DRV_MOSI[V]);
	}
}

//--- функция вывода значений на индикатор амперметра ---
void ledprint_2(unsigned int number)
{
	Digit[4]=number/1000; //тысячи
	Digit[4]=Digit[4]|128; //вкл децимальную точку в разряде Digit_4
	Digit[3]=number%1000/100; //сотни
	Digit[2]=number%100/10; //десятки
	Digit[1]=number%10; //единицы
	
	for(I=1;I<5;I++)
	{
		DRV_MOSI[I] = Digit[I];
		Send_max7219(I,DRV_MOSI[I]);
	}
}

//--- функция оцифровки напряжения ---
float ADCV_Conv(unsigned int dt_v)
{
	float dt_v1; //тип данных с плавающей точкой
	dt_v1=((float)dt_v*(Vref))/Kv; //преобразование 12-битного числа типа float в величину измеренного напряжения
	unsigned int adcv_tmp = 0; //сброс переменной для хранения промежуточных результатов оцифровки
	unsigned char adcv_counter = 0; //сброс переменной усреднения оцифровки measure
	if(adcv_counter < measure)
	{
		adcv_tmp += dt_v1; //adcv_tmp = adcv_tmp + dt_v1
		adcv_counter ++;
	}
	else
	{
		dt_v1 = adcv_tmp >> 0; //adcv_tmp / measure
		adcv_counter = 0; //сброс счётчика усреднения
		adcv_tmp = 0; //сброс регистра промежуточных результатов оцифровки
	}
	return dt_v1; //возвращаем величину измеренного напряжения в вольтах [V]
}

//--- функция оцифровки тока ---
float ADCI_Conv(unsigned int dt_i)
{
	float dt_i1; //тип данных с плавающей точкой
	dt_i1=((float)dt_i*(Vref))/Ki; //преобразование 12-битного числа типа float в величину измеренного тока
	unsigned int adci_tmp = 0; //сброс переменной для хранения промежуточных результатов оцифровки
	unsigned char adci_counter = 0; //сброс переменной усреднения оцифровки measure
	if(adci_counter < measure) //пока число в счётчике измерений меньше предельного количества измерений  measure...
	{ //...суммируем результаты оцифровки в регистре промежуточных результатов
		adci_tmp += dt_i1; //adci_tmp = adci_tmp + dt_i1
		adci_counter ++; //...продолжаем инкрементировать счётчик измерений
	}
	else //если же счётчик накопил предельное кол-во измерений...
	{ //...то выходим из цикла накопления в регистре и находим среднее арифметическое...
		dt_i1 = adci_tmp >> 0; //...adci_tmp / measure (сдвигаем данные в регистре накопления на 5 разрядов вправо, что равнозначно /2^5=32 )
		adci_counter = 0; //сброс счётчика усреднения
		adci_tmp = 0; //сброс регистра промежуточных результатов оцифровки
	}
	return dt_i1; //возвращаем величину измеренного тока в амперах [A]
}

//--- основная функция с бесконечным циклом ---
int main(void)
{
	float dt_v=0; //сброс на ноль SPDR
	float dt_i=0; //сброс на ноль SPDR
	CS0_ini(); //инициализация порта PB0
	CS1_ini(); //инициализация порта PB2
	PB1_ini(); //инициализация порта PB1
	//PB2_ini(); //инициализация порта PB2
	oc1a(); //инициализация генератора
	OCR1AH = 0x00; //запись в регистр сравнения ocr1a...
	OCR1AL = 0x55; //...числа dec85
	//oc1b(); //инициализация инвертора +5v >> +9...12v
	//OCR1BH = 0x00; //запись в регистр сравнения ocr1a...
	//OCR1BL = 0x64; //...числа dec100
	ADC_SPI_ini(); //инициализация шины SPI
	DRV_SPI_ini(); //инициализация шины данных драйвера
	MAX7219_ini(); //инициализация MAX7219
	//ADCV_ini(); //инициализация внутреннего АЦП
	//-------------------------------------------
	while(1)
	{
		dt_v = ADCV_Conv(Read_3201_0(0)); //считаем значение SPDR АЦП напряжения
		Av=dt_v; //
		Mv = Ks_v * Av + Mv1 * (1-Ks_v); //фильтр Кальмана Mn=Ks*An + Mn1*(1-Ks)
		Mv1=Mv; //		
		dt_i = ADCI_Conv(Read_3201_1(1)); //считаем значение SPDR АЦП тока
		Ai=dt_i; //
		Mi = Ks_i * Ai + Mi1 * (1-Ks_i); //фильтр Кальмана Mn=Ks*An + Mn1*(1-Ks)
		Mi1=Mi; //
		
		adc_conv count++;  //инкрементируем счётчик ацп-преобразователя
		if(adc_conv count==1000) // выводим на дисплеи результат только каждого n=1000 преобразования
		{
          adc_conv count=0; //сброс счётчика ацп-преобразований
		  ledprint_1(Mv); //отправим значение в функцию вывода на индикатор вольтметра
		  ledprint_2(Mi); //отправим значение в функцию вывода на индикатор амперметра		
		}				
	}
}









Спойлер
Спойлер
Вложения
va_meter_400vdc_10adc_ver_1.pdf
(363.08 КБ) 48 скачиваний
chopper 48vdc_5vdc.pdf
(409.7 КБ) 38 скачиваний
chopper 38v_5v0.pdf
(202.06 КБ) 40 скачиваний
душа человеческая темна и с легкостью обращается ко злу
charchyard
Поставщик валерьянки для Кота
Сообщения: 2466
Зарегистрирован: Сб май 07, 2011 17:52:59

Re: ATmega + 12bit ADC

Сообщение charchyard »

Спойлер

Код: Выделить всё

//ATmega_8A + max7219 + mcp3201_0 + mcp3201_1
//va-meter +0...409.6vdc / +0...9.999adc
//16MHz

#define F_CPU 16000000UL                                     //тактовая частота мк (unsigned long)
#include <avr/io.h>                                          //подключение библиотеки "ввод/вывод" мк
#include <util/delay.h>                                      //подключение библиотеки "пауза" мк
#include <avr/interrupt.h>                                   //подключение библиотеки "прерывание" мк

#define Vref 4.096 //+Vref=4.096vdc
#define Kv 4.096 //коэффициент преобразования для вычислнения величины [V] Kv=4096*4.096/4096
#define Ki 1.678 //коэффициент преобразования для вычислнения величины [I] Ki=4096*4.096/10 000
#define ADC_CS0_0 PORTB &= ~(1<<PORTB0) //ADC_CS0=0
#define ADC_CS0_1 PORTB |= (1<<PORTB0) //ADC_CS0=1
#define ADC_CS1_0 PORTB &= ~(1<<PORTB2) //ADC_CS1=0
#define ADC_CS1_1 PORTB |= (1<<PORTB2) //ADC_CS1=1
#define ADC_CLK_0 PORTB &= ~(1<<PORTB5) //ADC_CLK=0
#define ADC_CLK_1 PORTB |= (1<<PORTB5) //ADC_CLK=1

#define DRV_CLK_0 PORTC &= ~(1<<PORTC0) //DRV_CLK=0
#define DRV_CLK_1 PORTC |= (1<<PORTC0) //DRV_CLK=1
#define DRV_CS_0 PORTC &= ~(1<<PORTC1) //DRV_CS=0
#define DRV_CS_1 PORTC |= (1<<PORTC1) //DRV_CS=1
#define DRV_MOSI_0 PORTC &= ~(1<<PORTC2) //DRV_MOSI=0
#define DRV_MOSI_1 PORTC |= (1<<PORTC2) //DRV_MOSI=1

//-----------------
unsigned int Digit[8]; //массив беззнаковых целочисленных переменных Digit из 8 переменных (8 разрядов драйвера 7seg LED-дисплея)
unsigned char DRV_MOSI[8]; //массив беззнаковых однобайтных символьных переменных DRV_MOSI из 8 переменных (адреса и команды для конфигурирования драйвера)
unsigned char n; //символьная переменная (максимальное количество разрядов led-драйвера n=8)
unsigned char V; //символьная переменная V (значение регистра ацп с результатом оцифровки напряжения)
unsigned char I; //символьная переменная I (значение регистра ацп с результатом оцифровки тока)
unsigned int count; //переменная для задержки обновления данных на индикаторах

unsigned short dt_v; //12-битный результат оцифровки величины [V]
float Av; //Av=dt_v
float Mv; //окончательный результат работы сглаживающего фильтра в канале [V]
float Mv1 = 0; //сброс промежуточного результата работы сглаживающего фильтра в канале [V]
float Ks_v = 0.05; //коэффициент сглаживания в канале [V]

unsigned short dt_i; //12-битный результат оцифровки величины [I]
float Ai; //Ai=dt_i
float Mi; //окончательный результат работы сглаживающего фильтра в канале [I]
float Mi1 = 0; //сброс промежуточного результата работы сглаживающего фильтра в канале [I]
float Ks_i = 0.05; //коэффициент сглаживания в канале [I]

//--- инициализация CS0 ---
void CS0_ini(void) //функция инициализации порта PB0
{
	DDRB |= (1<<PORTB0); //PB0 на вывод
	PORTB |= (1<<PORTB0); //PB0_hi
}

//--- инициализация CS1 ---
void CS1_ini(void) //функция инициализации порта PB2
{
	DDRB |= (1<<PORTB2); //PB2 на вывод
	PORTB |= (1<<PORTB2); //PB2_hi
}

//--- инициализация генератора ---
void PB1_ini(void) //функция инициализации порта PB1
{
	DDRB |= (1<<PORTB1); //PB1 на вывод (oc1a)
	PORTB &= ~(1<<PORTB1); //PB1 сброс
}

//--- инициализация PB2 ---
/*void PB2_ini(void) //функция инициализации порта PB2 (выход запуска генератора +Vpwr = +9...12v)
{
	DDRB |= (1<<PORTB2); //PB2 на вывод (oc1b)
	PORTB &= ~(1<<PORTB2); //PB2 сброс
}*/

//--- инициализация таймера oc1a ---
void oc1a(void) //функция таймера
{
	ASSR=0x00; //сбрасываем полностью регистр assr
	TCCR1A |= ((1<<COM1A1)|(1<<WGM10)); //Fast PWM oc1a, Clear oc1a on Compare Match, clkT2S/1 (no prescalling)
	TCCR1B |= ((1<<WGM12)|(1<<CS10));
	TCNT1H=0x00; // Timer Value = 0 сброс счётного регистра таймера 1
	TCNT1L=0x00;
	OCR1AH=0x00;
	OCR1AL=0x55; //Output Compare Register = dec85 - заполнение шим +DC~33%
	TIMSK=0x00; //сброс регистра timsk
}

//--- инициализация таймера oc1b ---
/*void oc1b(void) //функция таймера
{
	ASSR=0x00; //сбрасываем полностью регистр assr
	TCCR1A |= ((1<<COM1B1)|(1<<WGM10)); //Fast PWM oc1b, Clear oc1b on Compare Match, clkT2S/1 (no prescalling)
	TCCR1B |= ((1<<WGM12)|(1<<CS10));
	TCNT1H=0x00; // Timer Value = 0 сброс счётного регистра таймера 2
	TCNT1L=0x00;
	OCR1BH=0x00;
	OCR1BL=0x64; //Output Compare Register = dec100 - заполнение шим +DC~40%
	TIMSK=0x00; //сброс регистра timsk
}*/

//--- инициализация шины SPI_ADC ---
void ADC_SPI_ini(void)
{
	DDRB &= ~(1<<PORTB4); PORTB |= (1<<PORTB4); //MISO, pull-up=ON
	DDRB |= ((1<<PORTB5)|(1<<PORTB2)|(1<<PORTB0)); //выход ADC_CLK, выход ADC_CS1, выход ADC_CS0
	PORTB |= ((1<<PORTB2)|(1<<PORTB0)); 
	PORTB &= ~(1<<PORTB5); //сброс шины SPI_ADC
	SPCR = ((1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0)); //включим шину SPI, объ§вим ведущим, SCK=16e+06/128=125kHz
}

//--- функция передачи/приёма данных по шине SPI ---
void SPI_SendByte(char byte)
{
	SPDR = byte; //
	while(!(SPSR & (1<<SPIF))); //подождем пока данные передадутс¤
}
unsigned char SPI_ChangeByte(char byte)
{
	SPDR = byte;
	while(!(SPSR & (1<<SPIF))); //подождем пока данные передадутс¤ (обмен¤ютс¤)
	return SPDR;
}

//--- функция опроса внешнего АЦП mcp3201_0 ---
unsigned int Read_3201_0(unsigned char channel)
{
	unsigned int b1,b2;
	ADC_CS0_0; //CS0=0
	b1=SPI_ChangeByte(0); //первый байт
	b2=SPI_ChangeByte(0); //второй байт
	b1=(b1<<8)|b2; //собираем два байта в двухбайтовую величину
	b1<<=3;
	b1>>=4; //убираем ненужные биты (3 слева и 1 справа)
	ADC_CS0_1; //CS0=1
	return b1; //возвращаем 12-битный результат ацп-преобразования
}

//--- функция опроса внешнего АЦП mcp3201_1 ---
unsigned int Read_3201_1(unsigned char channel)
{
	unsigned int b1,b2;
	ADC_CS1_0; //CS1=0
	b1=SPI_ChangeByte(0); //первый байт
	b2=SPI_ChangeByte(0); //второй байт
	b1=(b1<<8)|b2; //собираем два байта в двухбайтовую величину
	b1<<=3;
	b1>>=4; //убираем ненужные биты (3 слева и 1 справа)
	ADC_CS1_1; //CS1=1
	return b1; //возвращаем 12-битный результат ацп-преобразования
}

//--- инициализация шины данных SPI_DRV драйвера max7219 ---
void DRV_SPI_ini(void)
{
	DDRC = 0x07; //биты PС0-PС2 порта PС на вывод
	PORTC &= ~((1<<PORTC2)|(1<<PORTC1)|(1<<PORTC0)); //PС0-PС2 сброс
}

//--- функция побитовой отправки данных в драйвер max7219 ---
void Send_max7219(unsigned char rg, unsigned char dt) //
{
	unsigned char rg_copy; //копия значения переменной rg
	unsigned char i; //переменная для побитной отправки данных в драйвер индикаторов
	
	DRV_MOSI[rg] = dt; //
	rg_copy = rg; //создадим копию значения переменной rg
	
	DRV_CS_0; //отправим «0» на вывод CS микросхемы MAX7219, чтобы начать процесс передачи адреса и данных
	asm("nop"); //пауза в 1 такт
	
	for(i=0;i<8;i++) //цикл от 0 до 7 с шагом 1, для побитовой отправки байта адреса в микросхему MAX7219
	{
		if((rg & 0x80)==0x80) //пока rg * 0b1000 0000 > 0, ...
		{
			DRV_MOSI_1; //...отправим 1 на вывод Din микросхемы MAX7219
		}
		else //если же rg * 0b1000 0000 = 0, ...
		{
			DRV_MOSI_0; //...отправим 0 на вывод Din микросхемы MAX7219
		}
		
		//создадим тактовый импульс на выводе CLK микросхемы MAX7219
		asm("nop"); //пауза в 1 такт
		DRV_CLK_1; //отправим 1 на вывод Clk микросхемы MAX7219
		asm("nop"); //пауза в 1 такт
		DRV_CLK_0; //отправим 0 на вывод Clk микросхемы MAX7219
		rg <<= 1; //сдвинем значение переменной rg на 1 бит влево
	} //выйдем из цикла когда i станет равной 7, т.е. когда отправка байта адреса в микросхему MAX7219 будет окончена
	
	for(i=0;i<8;i++) //цикл от 0 до 7 с шагом 1, для побитовой отправки байта данных в микросхему MAX7219
	{
		if((DRV_MOSI[rg_copy] & 0x80)==0x80) //пока rg * 0b1000 0000 > 0, ...
		{
			DRV_MOSI_1; //...отправим 1 на вывод Din микросхемы MAX7219
		}
		else //если же rg * 0b1000 0000 = 0, ...
		{
			DRV_MOSI_0; //отправим 0 на вывод Din микросхемы MAX7219
		}
		
		//создадим тактовый импульс на выводе CLK микросхемы MAX7219
		asm("nop"); //пауза в 1 такт
		DRV_CLK_1; //отправим 1 на вывод Clk микросхемы MAX7219
		asm("nop"); //пауза в 1 такт
		DRV_CLK_0; //отправим 0 на вывод Clk микросхемы MAX7219
		DRV_MOSI[rg_copy] <<= 1; //сдвинем значение переменной DRV_MOSI на 1 бит влево
	}
	//выйдем из цикла когда i станет равной 7, т.е. когда отправка байта данных в микросхему MAX7219 будет окончена
	DRV_CS_1; //отправим «1» на вывод CS микросхемы MAX7219, чтобы завершить процесс передачи адреса и данных
}

//--- инициализация драйвера max7219 ---
void MAX7219_ini(void)
{
	Send_max7219(0x09,0xFF); //(номер регистра, данные) включаем режим BCD code B, для 0-7 разрядов
	Send_max7219(0x0A,0x0A); //DC = 21/32 яркость свечения
	Send_max7219(0x0B,0x07); //число используемых разрядов (0-7 разрядов)
	Send_max7219(0x0C,0x01); //отключаем режим энергосбережения (Shutdown)
	
	for(n=1;n<9;n++)
	{
	  Send_max7219(n,0x0F); //гасим все разряды драйвера дисплея V/A до момента вывода информации
	}	
}

//--- функция оцифровки напряжения ---
float ADCV_Conv(unsigned short dt_v)
{
	float dt_v1; //величина измеренного напряжения [V]
	dt_v1=((float)dt_v*(Vref))/Kv; //преобразование 12-битного числа типа u_int в величину измеренного напряжения (float)
	return dt_v1; //возвращаем величину измеренного напряжения в вольтах [V]
}

//--- функция оцифровки тока ---
float ADCI_Conv(unsigned short dt_i)
{
	float dt_i1; //величина измеренного тока [I]
	dt_i1=((float)dt_i*(Vref))/Ki; //преобразование 12-битного числа типа u_int в величину измеренного тока (float) 
	return dt_i1; //возвращаем величину измеренного тока в амперах [A]
}

//--- функция вывода значений на индикатор вольтметра 000.0v ---
void ledprint_1(unsigned int number) //number - величина измеренного напряжения Mv
{
	if((float)Mv < 4090) //если Mv <= 409.0, то выводим на дисплей результат...
	{
		Digit[8]=number/1000 ? number/1000 : 0x0F; //тысячи /гасим старший незначащий нуль
		Digit[7]=number/1000 || number%1000/100 ? number%1000/100 : 0x0F; //сотни /гасим младший незначащий нуль
		Digit[6]=number%100/10; //десятки
		Digit[6]=Digit[6]|0x80; //вкл децимальную точку в разряде Digit_6
		Digit[5]=number%10; //единицы
	}
	else //...в противном случае, выводим на дисплей -0L- (overload)
	{
		Digit[8]=0x0A; //<<->>
		Digit[7]=0x00; //<<0>>
		Digit[6]=0x0D; //<<L>>
		Digit[5]=0x0A; //<<->>
	}
	
	for(V=5;V<9;V++) //заполняем разряды вольтметра цифрами разложенного Mv
	{
		DRV_MOSI[V] = Digit[V]; //
		Send_max7219(V,DRV_MOSI[V]); //
	}
}

//--- функция вывода значений на индикатор амперметра ---
void ledprint_2(unsigned int number)
{
	if((float)Mi < 9980) //если Mi < 9.980, то выводим на дисплей результат...
	{
		Digit[4]=number/1000; //тысячи
		Digit[4]=Digit[4]|128; //вкл децимальную точку в разряде Digit_4
		Digit[3]=number%1000/100; //сотни
		Digit[2]=number%100/10; //десятки
		Digit[1]=number%10; //единицы
	}
	else //...в противном случае, выводим на дисплей -0L- (overload)
	{
		Digit[4]=0x0A; //<<->>
		Digit[3]=0x00; //<<0>>
		Digit[2]=0x0D; //<<L>>
		Digit[1]=0x0A; //<<->>
	}
	
	for(I=1;I<5;I++)
	{
		DRV_MOSI[I] = Digit[I];
		Send_max7219(I,DRV_MOSI[I]);
	}
}

//--- основная функция с бесконечным циклом ---
int main(void)
{
	float dt_v=0; //сброс на ноль SPDR
	float dt_i=0; //сброс на ноль SPDR
	CS0_ini(); //инициализация порта PB0
	CS1_ini(); //инициализация порта PB2
	PB1_ini(); //инициализация порта PB1
	//PB2_ini(); //инициализация порта PB2
	oc1a(); //инициализация генератора
	OCR1AH = 0x00; //запись в регистр сравнения ocr1a...
	OCR1AL = 0x55; //...числа dec85
	//oc1b(); //инициализация инвертора +5v >> +9...12v
	//OCR1BH = 0x00; //запись в регистр сравнения ocr1a...
	//OCR1BL = 0x64; //...числа dec100
	ADC_SPI_ini(); //инициализация шины SPI
	DRV_SPI_ini(); //инициализация шины данных драйвера
	MAX7219_ini(); //инициализация MAX7219
	//ADCV_ini(); //инициализация внутреннего АЦП
	//-------------------------------------------
	while(1)
	{
		dt_v = ADCV_Conv(Read_3201_0(0)); //считаем значение SPDR АЦП напряжения
		Av=dt_v; //
		Mv = Ks_v * Av + Mv1 * (1-Ks_v); //фильтр Кальмана Mn=Ks*An + Mn1*(1-Ks)
		Mv1=Mv; //		
		dt_i = ADCI_Conv(Read_3201_1(1)); //считаем значение SPDR АЦП тока
		Ai=dt_i; //
		Mi = Ks_i * Ai + Mi1 * (1-Ks_i); //фильтр Кальмана Mn=Ks*An + Mn1*(1-Ks)
		Mi1=Mi; //
		
		count++;  //инкрементируем счётчик ацп-преобразователя
		if(count==750) // выводим на дисплеи результат только каждого n=750 преобразования
		{
          count=0; //сброс счётчика ацп-преобразований
		  ledprint_1(Mv); //отправим значение в функцию вывода на индикатор вольтметра
		  ledprint_2(Mi); //отправим значение в функцию вывода на индикатор амперметра		
		}				
	}
}









Вложения
copper_24_5_12_12.pdf
(256.34 КБ) 51 скачивание
душа человеческая темна и с легкостью обращается ко злу
charchyard
Поставщик валерьянки для Кота
Сообщения: 2466
Зарегистрирован: Сб май 07, 2011 17:52:59

Re: ATmega + 12bit ADC

Сообщение charchyard »

вау-вау-метер
Вложения
display_board_max7219.pdf
(141.35 КБ) 64 скачивания
душа человеческая темна и с легкостью обращается ко злу
charchyard
Поставщик валерьянки для Кота
Сообщения: 2466
Зарегистрирован: Сб май 07, 2011 17:52:59

Re: ATmega + 12bit ADC

Сообщение charchyard »

Спойлерновейшее кино про лёд-головку цифровую 100vdc 200adc. для колибровки было задействовано несколько источников тока постояного напряженья. но един член для выстаки верха диапазона энерхетики не хватилло. старую убогую кэтайску узкоглазую с шунтом я выколупал. она на сотку ампериев была. разбежка в покозаниях вольтмера связана с конечной проводимосью верёвок в симинсах и неудачной точкой подключенья внутри. новую голову обернул просто в пакетон от свища и хомутками прищипил. в соседнем дурдоме один пациэнт дюже сильно пережывал за ленейность показомера и в итоге сказал что оно это ему задарром не нада. я првда ему ниччего и не навязывал и посоветовал ему если он в следущий рааз будет пороходить мимо проходить мимо. даальним леском.
душа человеческая темна и с легкостью обращается ко злу
Ответить

Вернуться в «AVR»