av-meter

Ответить
GND
Сообщения: 39
Зарегистрирован: 22 июн 2015, 19:21

av-meter

Сообщение GND »

данный девайс разрабатывался для вторичного регулируемого источника постоянного тока dc0...63v 0...150a как индикатор выходных параметров. в нём применён бесконтактный датчик съёма показаний выходного тока источника на базе американской микросхемы линейного датчика Холла ss495. мне удалось приобрести не самые точные типы данной микры, поэтому это как-бэ не совсем meter, а скорее показометр. он так и планировался, с точностью, не хуже приблизительно +/-10%. я выложил схематоз на извесном сайте любителей радио. и начался, буквально кромешный ад :o мгновенно налетели какие-то "праграмисты", "електрики", "преподаватели по технике электробезопасности", "учителя русского языка", "филологи", "философы" и всякие другие любители радио и начали меня интенсивно учить во все места :( ... вообщем, я написал модератору просьбу стереть тему под корень :!: потом я вспомнил басню про работника Балду, крепкую длинную пеньковую верёвку и синее-синее море :D это был филосовско-лирический экскурс в недалекое прошлое, а теперь я опишу этот корень Рандрагорры...
девайс представляет собой 3-х или 4-х разрядный измеритель постоянного тока и напряжения на mcu atmega 8a и led-драйвере дисплея max7219. он может оцифровывать параметры, как внутренним 10-битным ацп атмеги, так и внешними 12-битными одноканальными ацп американский фирмы микрочип mcp3201. предусмотрена возможность монтажа внешнего источника опорного напряжения mcp1526/1541, как для внутренних, так и для внешних ацп. Датчик тока расположен прямо на плате, поэтому для измерения выходного тока необходимо пропустить сквозь плату в определённом направлении плюсовую или минусовую шину с максимальным диаметром D8mm, идущую к выходному терминалу устройства. никаких шутов, клоунов или шунтов :D питается головка от источника постоянного тока напряжением +9...35v.
Изображение
Вложения
65v_150a_2.jpg
65v_150a_1.jpg
65v_150a_10bit_12bit_ver.3.JPG
GND
Сообщения: 39
Зарегистрирован: 22 июн 2015, 19:21

Re: av-meter

Сообщение GND »

бэдлокод

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

//кодировка для 3/4-х разрядного va-метра //ATmega_8A + max7219 + mcp3201 для 4-разрядных индикаторов 
//comm cath с разделительным двоеточием 0 0. : 0.0.
//va-meter +0...64.9v / +0...149.9a (10bit/12bit)
//16MHz

//Other Linker Flafs: -Wl,-u,vfprintf -lprintf_flt -lm (активизация режима вычислений с плавающей точкой)

#define F_CPU 16000000UL //тактовая частота мк (unsigned long)
#include <avr/io.h> //подключение библиотеки "ввод/вывод" мк
#include <util/delay.h> //подключение библиотеки "пауза" мк
#include <avr/interrupt.h> //подключение библиотеки "прерывание" мк
                                   
#define ADC_CS_0 PORTB &= ~(1<<PORTB2) //ADC_CS=0
#define ADC_CS_1 PORTB |= (1<<PORTB2) //ADC_CS=1
#define ADC_CLK_0 PORTB &= ~(1<<PORTB5) //ADC_CLK=0
#define ADC_CLK_1 PORTB |= (1<<PORTB5) //ADC_CLK=1

#define Vref 4.096 //+Vref=4.096vdc
#define Kv 6.456 //коэффициент преобразования для вычисления величины [V] Kv=1023*4.096/649
#define Ki 11.19 //коэффициент преобразования для вычисления величины [I] Ki=4095*4.096/1 499

#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 voltage Mv
#define current Mi

//-----------------
unsigned int Digit[8]; //массив переменных Digit[8] из 8 переменных (разряды драйвера digit_0...digit_7)
unsigned char DRV_MOSI[8]; //массив переменных DRV_MOSI из 8 переменных (адреса rg и данные dt для led-драйвера)

unsigned char V; //переменная для передачи разложенной величины измеренного напряжения в led-драйвер
unsigned char I; //переменная для передачи разложенной величины измеренного тока в led-драйвер

unsigned char n; //символьная переменная (количество разрядов led-драйвера n=8)
unsigned int count; //переменная (счётчик циклов основной функции)

unsigned int adcv_value; //10-битный результат преобразования внутреннего ацп mux_n (adc_mux_n code)
unsigned int adci_value; //12-битный результат преобразования внешнего ацп  (ext_adc code)

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

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

//--- инициализация выхода таймера oc1a ---
void PB1_ini(void) //инициализация порта PB1 (pwm_out)
{
	DDRB |= (1<<PORTB1); //PB1_out (timer_oc1a)
	PORTB &= ~(1<<PORTB1); //PB1_lo
}

//--- инициализация ADC_CS ---
void CS_ini(void) //функция инициализации порта PB2
{
	DDRB |= (1<<PORTB2); //PB2_out
	PORTB |= (1<<PORTB2); //PB2_hi
}

//--- инициализация таймера oc1a ---
void timer_ini(void) //pwm_out: Tosc=16us; Fosc=62.5kHz; +tp=8us; +DC=-DC=50%
{
	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;
	TCNT1L=0x00; // Timer Value = 0 сброс счётного регистра таймера oc1a
	OCR1AH=0x00;
	OCR1AL=0x55; //Output Compare Register = dec85: заполнение шим +DC~33%, -DC~67%
	TIMSK=0x00; //сброс регистра timsk
}

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

//--- функция передачи/приёма данных по шине ADC_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 ---
unsigned int Read_3201(void)
{
	unsigned int b1,b2;
	ADC_CS_0; //CS=0
	b1=SPI_ChangeByte(0); //первый байт
	b2=SPI_ChangeByte(0); //второй байт
	b1=(b1<<8)|b2; //собираем два байта в двухбайтовую величину
	b1<<=3;
	b1>>=4; //убираем ненужные биты (3 слева и 1 справа)
	ADC_CS_1; //CS=1
	return b1; //возвращаем 12-битный результат ацп-преобразования
}

//--- инициализация шины данных LED-драйвера max7219 ---
void LED_SPI_ini(void)
{
	DDRC |= ((1<<PORTC2)|(1<<PORTC1)|(1<<PORTC0)); //PС0_out; PC1_out; PС2_out
	PORTC &= ~((1<<PORTC2)|(1<<PORTC1)|(1<<PORTC0)); //PС0-PС2_lo
}

//--- функция побитовой отправки данных в драйвер 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,0x06); //число используемых разрядов (0-6 разрядов)
	Send_max7219(0x0C,0x01); //отключаем режим энергосбережения (Shutdown)
	
	for(n=1;n<8;n++)
	{
		Send_max7219(n,0x0F); //гасим все разряды драйвера дисплея V/A до момента вывода информации
	}
}

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

//--- функция вывода значений на индикатор амперметра 000.0a (display_2) ---
void ledprint_2(unsigned int number) //number - величина измеренного тока Mi
{
	if((float)Mi < 1498) //если Mi < 149.8, то выводим на дисплей результат...
	{
		Digit[4]=number/1000 ? number/1000 : 0x0F; //тысячи /гасим старший незначащий нуль
		Digit[3]=number/1000 || number%1000/100 ? number%1000/100 : 0x0F; //сотни /гасим младший незначащий нуль
		Digit[2]=number%100/10; //десятки
		Digit[2]=Digit[2]|0x80; //вкл децимальную точку в разряде Digit_2
		Digit[1]=number%10; //единицы
	}
	else //...в противном случае, выводим на дисплей 0L (overload)
	{
		Digit[4]=0x0F; //blanking digit_4
		Digit[3]=0x0F; //blanking digit_3
		Digit[2]=0x00; //<<O>>
		Digit[1]=0x0D; //<<L>>
	}
	
	for(I=1;I<5;I++)
	{
		DRV_MOSI[I] = Digit[I];
		Send_max7219(I,DRV_MOSI[I]);
	}
}

//--- инициализация внутреннего ацп ---
void ADC_ini(void)
{
	ADCSRA |= ((1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)); //разрешение использования АЦП, предделитель 16M/128 = 125 кГц
	ADMUX &= ~((1<<REFS1)|(1<<REFS0)|(1<<MUX3)|(1<<MUX2)|(1<<MUX1)|(1<<MUX0)); //внешний источник опорного напряжения Aref, сброс mux
}

///--- функция ацп-преобразователя с переменным каналом ch_ADC ---
unsigned int ADC_Conv(unsigned char ch_ADC) //(переменная ch_ADC - канал ацп)
{
	unsigned int adc_value = 0; //сброс регистра хранения результата ацп-преобразования
	unsigned char set_ADMUX = ADMUX; //переменная для выбора канала АЦП
	set_ADMUX &= ((1 << REFS1) | (1 << REFS0)); //оставить неизменным только Vref
	set_ADMUX |= ch_ADC; //выбор канала АЦП ch_ADC
	ADMUX = set_ADMUX;
	_delay_us(10); //пауза 10us
	ADCSRA |= (1 << ADSC); //запуск АЦП
	while ((ADCSRA & (1 << ADSC))); //ожидание окончания ацп-преобразования
	adc_value = ADCW; //10-битный результат ацп-преобразования (из регистров результата ADCL и ADCH)
	return (unsigned int)adc_value; //выйдем из функции и вернём значение (из регистров результата ADCL и ADCH АЦП)
}

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

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

	//--- основная функция с бесконечным циклом ---
	int main(void)
	{
		float dt_v=0; //
		float dt_i=0; //
		PB1_ini(); //инициализация порта PB1
		CS_ini(); //инициализация порта PB2
		timer_ini(); //инициализация pwm_out
		OCR1AH = 0x00; //запись в регистр сравнения...
		OCR1AL = 0x55; //...числа dec85
		ADC_SPI_ini(); //инициализация шины данных ADC_SPI
		LED_SPI_ini(); //инициализация шины данных LED-драйвера
		MAX7219_ini(); //инициализация MAX7219
		ADC_ini(); //инициализация внутреннего АЦП
		
		//-------------------------------------------
		while(1)
		{
			adcv_value = ADC_Conv(5); //считаем результат ацп-преобразования канала mux_5
			dt_v = ADCV_Conv(); //преобразуем результат в величину измеренного напряжения [V]
			
			Av=dt_v; //входной параметр сглаживающего фильра
			//фильтр Кальмана Mn=Ks*An + Mn1*(1-Ks)
			Mv = Ks_v * Av + Mv1 * (1-Ks_v); //вычисляем среднее Vavr_out=Mv
			Mv1=Mv; //усредним значение Vavr = Vavr_out
			
			adci_value = Read_3201(); //считаем значение с внешнего ацп 
			dt_i = ADCI_Conv(); //преобразуем результат в величину измеренного тока [A]
			
			Ai=dt_i; //входной параметр сглаживающего фильра
			//фильтр Кальмана Mn=Ks*An + Mn1*(1-Ks)
			Mi = Ks_i * Ai + Mi1 * (1-Ks_i); //вычисляем среднее Iavr_out=Mi
			Mi1=Mi; //усредним значение Iavr = Iavr_out
			
			count++;  //инкрементируем счётчик основного цикла
			if(count==3000) //обновляем показания каждый 3 000 цикл
			{
				count=0; //сброс счётчика основного цикла
				ledprint_1(voltage); //отправим значение в функцию вывода на индикатор вольтметра
				ledprint_2(current); //отправим значение в функцию вывода на индикатор амперметра
			}		
		}
	}









ссылки на видео работы
https://youtu.be/bBg0EzCNJYI
https://youtu.be/C6bc4MEWk28
Ответить