Не обновляются данные на вольтметре из семисегментника и atmega328

bibo
Offline
Зарегистрирован: 26.03.2018

Приветсвую, товарищи. Озадачился проникновением в самую глубь авр микроконтроллеров путем изучения азов и колупания даташита. Инфы в сети в виде видеоуроков или текстовых уроков достаточно, хотя они в основном по atmega8.

Так вот, попытка сообразить вольтметр на 328 меге пока не удается в полной мере.

Проблема заключается в том, что показация ацп, выводимые на трехразрядный(другого не было под рукой)семисегментник, обновляются только  в случае, если перезагрузить микроконтроллер.

Хотелось бы получить совета в решении проблемы. Код привожу ниже.

Не знаю, важно ли это, но укажу, что использую ардуино нано, которую шью с помощью уно в атмелстудио(ардуиновский загрузчик не давал работать с PD0 и PD1, а мне нужен был полный порт для подключения семисегментника на моей колхозной платке-обучалке)

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

unsigned int z=0;//переменая для икpементирования
unsigned char x=1;//переменная для функции ISR
unsigned int R1=0,R2=0, R3=0;
unsigned int y=0;//переменая для икpементирования
unsigned int zzz;
float Uvh;//переменная для записи напряжения
const int Koef=1;//коэффициент
int K1;

unsigned int chisla[10]=
{
	~0b00111111,//0
	~0b00000110,//1
	~0b01011011,//2
	~0b01001111,//3
	~0b01100110,//4
	~0b01101101,//5
	~0b01111101,//6
	~0b00000111,//7
	~0b01111111,//8
~0b01101111,};//9

/*настраиваем таймер-счетчик 0*/
void init_dynamic(void){
	TCCR0A|=(0<<WGM00)|(0<<WGM01);//режим normal(сброс по переполнению)
	TCCR0B |=(1<<CS02)|(0<<CS01)|(0<<CS00);//делитель 256
	TIMSK0|=(1<<TOIE0);//сброс по переполнению
	TIFR0|=(1<<TOV0);
	TCNT0=0;

}


/*настраиваем порты*/

void init_ports(void){

	DDRC|=(1<<PC0)|(1<<PC1)|(1<<PC2);//аноды семисегментника
	DDRC&=~(1<<PC4);//adc4
	//DDRB|=(1<<PB0)|(1<<PB2)|(1<<PB4);//leds
	//DDRB&=~(1<<PB5);//button
	DDRD=0xFF;//весь порт Д на выход(катоды семисегментника)
	//PORTB|=(0<<PB0)|(0<<PB2)|(0<<PB4)|(1<<PB5);//leds&&button
	PORTC|=(1<<PC0)|(1<<PC1)|(1<<PC2)|(1<<PC4);
	PORTD=0xFF;//логическая единица на порту Д,чтобы семисегментник не светился

}

/*настраиваем ацп*/
void init_adc(void){

	ADCSRA|=(1<<ADEN);//разрешаем работу ацп(7ой бит)
	ADCSRB|=(0<<ADTS2)|(0<<ADTS1)|(0<<ADTS0);//включаем режим постоянного измерения(5 бит)
	ADCSRA|=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);//делитель 128(частота дискретизации 16000000/128=125кГц)
	ADMUX|=(0<<REFS1)|(1<<REFS0);//источник опорного напряжения 5 вольт
	ADMUX|=(0<<MUX3)|(1<<MUX2)|(0<<MUX1)|(0<<MUX0);//включаем ADC4 0100
	ADMUX|=(0<<ADLAR);//выравнивание
	ADCSRA|=(1<<ADSC);//запускаем работу ацп

}

/*обрабатываем прерывания при переполнении счетчика*/
ISR(TIMER0_OVF_vect){
	if(x==1){PORTC=0b00000100;PORTD=chisla[R1];} //при первом прерывании на порту С включаем третий бит, в порт д выводим число, высчитанное в функции vse_chislo
	if(x==2){PORTC=0b00000010;PORTD=chisla[R2];}/*if(Uvh>=10){PORTD=(1<<PD7);}
	else {PORTD|=(0<<PD7);}};*/
	if(x==3){PORTC=0b00000001;PORTD=chisla[R3];}/*if(Uvh<10){PORTD=(1<<PD7);}
	else{PORTD|=(0<<PD7);}};*/
	x++;
	if(x>3){x=1;};
}


void vse_chislo(unsigned int razbivka_chisla){
	R1=razbivka_chisla%10; //единицы
	R2=razbivka_chisla%100/10;//десятки
R3=razbivka_chisla/100;}//сотни


int main(void){

	init_ports();
	init_adc();
	init_dynamic();
	sei();

	while(1){

		zzz=ADC;
		/*Uvh=(zzz*5.00)/1024;


		if(Uvh<10)
		{
			K1=100;
		}
		else
		{K1=10;
		}*/

		vse_chislo(zzz); //вывод в попугаях
		//vse_chislo(Uvh*K1); //вывод в вольтах
		ADCSRA|=(1<<ADIF);


	}
}

 

bibo
Offline
Зарегистрирован: 26.03.2018

не знаю, почему так происходит всегда, но после заданного вопроса еще разок залез в даташит и понял, что нужно установить бит ADATE в регистре ADCSRA( ADCSRA|=(1<<ADATE);) и все заработало))

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, для начала, Вы неправильно инициализируете АЦП. Вы хотите, чтобы он у Вас запускался от внешнего события, но при этом не установили бит "ADC Auto Trigger Enable" в регистре ADCSRA.

Кроме того, вызывает сомнения сама идея такого измерения. По мне так лучше сделать измерения равномерными (не по взведению флага, а по таймеру или free-running) - главное, чтобы измерения происходили строго через равные интервалы с прерыванием. Лучше по таймеру, чтобы можно было отправлять МК в сон на время измерения, как советует даташит в разделе "подавление шума". В обработчике прерывания расположить разумный фильтр с крутым срезом и нормальной фазовой характеристикой (чтобы не сильно запаздывали показания). Собственно равномерность измерений как раз и нужна для построения нормального фильтра. Результат фильтра нужно пихать в переменную. А основная программа просто тупо показывает эту переменную и ни о чём не думает. Получается. что процесс измерения и процесс показа идут параллельно и никак друг на друга не влияют.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Опа, пока я писал, Вы и сами разобрались! Отлично!

Но вторую часть моего поста почитайте всё равно.

Green
Offline
Зарегистрирован: 01.10.2015

Не хочу показаться умным, но мой вам совет. Выучите 10-к слов по английски - не пишите, типа, razbivka_chisla - это просто колхоз(. В чём плюс высокоуровневого языка от низкоуровневого - вы не зависите от железа! А у вас почти в каждом операторе эта зависимость. А у вас ведь только инициализация и чтение АЦП! Ну, ещё таймер для индикации. Оформите их отдельными макро или inline функциями в самом начале. С тем что бы любой другой мог изменив ТОЛЬКО их, использовать код для своего (ЛЮБОГО) контроллера.

b707
Offline
Зарегистрирован: 26.05.2017

Green пишет:

Не хочу показаться умным, но мой вам совет. Выучите 10-к слов по английски - не пишите, типа, razbivka_chisla - это просто колхоз

а как это будет по английски? :)

Вторую часть спича безусловно поддерживаю. В первую очередь надо учиться грамотно программировать на Си - а уже потом, даже не во вторую - а в пятую очередь - лезть в регистры конкретного МК. Грамотный программист, пользуясь только методами Ардуино - скорее всего напишет код лучше, чем человек, закопавшийся в регистры. но не умеющий писать хороший код на си

bibo
Offline
Зарегистрирован: 26.03.2018

Спасибо за советы, но я пока еще в самом начале пути и многое из того, что вы мне посоветовали, пока еще темный лес густой) Подавление шума и фильтры - дело будущего, думается. Пока же я просто пытаюсь понять, как работать с богатым внутренним миром микроконтроллера и что он, этот мир, вообще из себя представляет.

А вот сообразить  измерение   по таймеру уже ближе к моему уровню, который ушел очень недалеко от нулевого.

 

b707
Offline
Зарегистрирован: 26.05.2017

bibo пишет:

Спасибо за советы, но я пока еще в самом начале пути и многое из того, что вы мне посоветовали, пока еще темный лес густой)

 

мой вам совет - отложите регистры на полгодика, для начала напишите это же самое методами ардуино. будет раза в 3 короче и раз в 10 понятнее

bibo
Offline
Зарегистрирован: 26.03.2018

Колхоз как колхоз) Для новичка в СИ и в в микроконтроллерах вполне себе приемлемые наименования, даже просто для того, чтобы не особо путаться на первом этапе.

<<Оформите их отдельными макро или inline функциями в самом начале.>>

Научите, как это сделать)

Green
Offline
Зарегистрирован: 01.10.2015

bibo пишет:

Научите, как это сделать)

Гугль научит!)
Я предпочитаю так.

#define ADC_INIT()        (ADCSRA |= 1<<ADEN, ADCSRB |= 0<<ADTS2 | 0<<ADTS1 | 0<<ADTS0  | 1<<ADPS2 | 1<<ADPS1 | 1<<ADPS0)
причём, эти макро нужно вынести в хедер, что бы в коде ничего не менять
bibo
Offline
Зарегистрирован: 26.03.2018

Дело в том, что в ардуино, что в микроконтроллерах, в любом случае я буду писать на чистом листе своих мозгов. Две недели назад я истерил по поводу всяких непонятных галочек и крючочков вместо понятного pinMode, и  абсолютно не представлял, как работать с семисегментником без соответсвующей библиотеки для ардуино ИДЕ и так далее.

У меня есть некоторая уверенность в том, что надобно начинать, изучая азы, сам принцип работы.

Даже если не все сложится, то по крайней мере, в ардуино я смогу, применяя соответсвующие приемы , некоторым образом экономить память.

b707
Offline
Зарегистрирован: 26.05.2017

bibo пишет:

У меня есть некоторая уверенность в том, что надобно начинать, изучая азы, сам принцип работы.

не смешите.

"Азы" - это например как дефайны писать, а не регистрами жонглировать. И пока вы не знаете "азов" в языке - в регистрах вам делать нечего.

Green
Offline
Зарегистрирован: 01.10.2015

Основная беда многих - что то экономить. Вы часом не из блокадного Ленинграда?
Основная экономия - это время. Ибо время - это деньги. Хотя, приоритеты у каждого могут быть разными.)

bibo
Offline
Зарегистрирован: 26.03.2018

Я так понимаю, что хедер это файл с раширением .h, который потом просто подключаешь к основному файлу проекта.

То есть вместо горожения функции можно просто сделать такое объявление?

Спасибо, буду знать.

bibo
Offline
Зарегистрирован: 26.03.2018

Я этим не собираюсь зарабатывать(по крайней мере на данный момент) .Пока  это желание поупражнять закосневшие мозги, получить новые знания, собрать полезный девайс .

Green
Offline
Зарегистрирован: 01.10.2015

Тем более. Нужно делать по уму, а не выглядеть лохом (извините).

bibo
Offline
Зарегистрирован: 26.03.2018

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

nik182
Offline
Зарегистрирован: 04.05.2015

b707 пишет:

не смешите.

"Азы" - это например как дефайны писать, а не регистрами жонглировать. И пока вы не знаете "азов" в языке - в регистрах вам делать нечего.

Совершенно не согласен. Отделяйте мух от котлет. Это совершенно разные, хоть и связанные вещи. И то и то азы, но в разных областях. Програмирование и програмирование микроконтроллеров. Регистрами жонглировать можно по разному. Например в STM Cube просто галочками и обойтись вообще без дефайнов.

Но с тем что учить надо всё согласен на 100%.

bibo
Offline
Зарегистрирован: 26.03.2018

осталось только паровоза дождаться :-D

Green
Offline
Зарегистрирован: 01.10.2015

bibo пишет:

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

Нет, это вы скатились к своей личности, а мы продолжили.))
Но, обычно, это всё взаимосвязано. Ибо от человека всё зависит.)

bibo
Offline
Зарегистрирован: 26.03.2018

Новичок вполне себе может начать параллельно изучать и то, и другое.

Предполагаю, что впоследствии он, дойдя до некой точки, осознает, что ему для дальнейшего движения нужно "подтянуть" знание СИ.

Это как Ленинское "нельзя вполне понять «Капитал» Маркса, особенно его 1ой главы, не проштудировав и не поняв всей Логики Гегеля".

Однако данное изречение Ленина абсолютно не говорит о том, что вообще невозможно ничего понять в "Капитале" Маркса без понимания "Науки логики" Гегеля.  Часто бывало совсем наоборот: после прочтения "Капитала" начинали читать "Логику", а затем уже снова "Капитал" на новом качественном уровне.

 

Green
Offline
Зарегистрирован: 01.10.2015

Круто вы загнули!) Это ж надо. Вольтемтр, семисегментный индикатор и победа коммунизма!)

bibo
Offline
Зарегистрирован: 26.03.2018

Продвигаю "Капитал" Маркса)

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

теперь тебе вместо капитала надлежит прочитать библию от Кернигана и Ритчи. 

bibo
Offline
Зарегистрирован: 26.03.2018

Спасибо, скачал.

Только "Капитал" не библия.

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Мож лучше к вольтметру вернуться? К тому, что я написал в #2 ещё бы добавить замер питающего напряжения. И будет нормальный прибор. Чудес ждать не надо, до флюка не дотянет, но вполне так себе нормальный показометр. Работайте.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

bibo пишет:

Только "Капитал" не библия.

А никто и не утверждал. Это "K&R" - библия.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Я. Я в #23 утверждал.  Каюсь. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Перечитал. Про библию от K&R там есть, а про то, что капитал == библия так и не разглядел. "Мартышка к старости слаба глазами стала" :(

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Мда. Я спьяну прочитал:  <А никто и не утверждал  что "K&R" - библия.>

Прошу прощения.  Это я на голову слаб стал. 

bibo
Offline
Зарегистрирован: 26.03.2018

В общем, поправил код вот так:

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

unsigned int z=0;//переменая для икpементирования
unsigned char x=1;//переменная для функции ISR
unsigned int R1=0,R2=0, R3=0;
unsigned int y=0;//переменая для икpементирования
unsigned int zzz;
float Uvh;//переменная для записи напряжения
const int Koef=1;//коэффициент
int K1;
unsigned int sec;

unsigned int chisla[10]=
{
	~0b00111111,//0
	~0b00000110,//1
	~0b01011011,//2
	~0b01001111,//3
	~0b01100110,//4
	~0b01101101,//5
	~0b01111101,//6
	~0b00000111,//7
	~0b01111111,//8
~0b01101111,};//9

/*настраиваем таймер-счетчик 0*/
void init_dynamic(void){
	TCCR0A|=(0<<WGM00)|(0<<WGM01);//режим normal(сброс по переполнению)
	TCCR0B |=(1<<CS02)|(0<<CS01)|(0<<CS00);//делитель 256
	TIMSK0|=(1<<TOIE0);//сброс по переполнению
	TIFR0|=(1<<TOV0);
	TCNT0=0;

}


/*настраиваем порты*/

void init_ports(void){

	DDRC|=(1<<PC0)|(1<<PC1)|(1<<PC2);//аноды семисегментника
	DDRC&=~(1<<PC4);//adc4
	//DDRB|=(1<<PB0)|(1<<PB2)|(1<<PB4);//leds
	//DDRB&=~(1<<PB5);//button
	DDRD=0xFF;//весь порт Д на выход(катоды семисегментника)
	//PORTB|=(0<<PB0)|(0<<PB2)|(0<<PB4)|(1<<PB5);//leds&&button
	PORTC|=(1<<PC0)|(1<<PC1)|(1<<PC2)|(1<<PC4);
	PORTD =0xFF;//логическая единица на порту Д,чтобы семисегментник не светился

}

/*настраиваем ацп*/
void init_adc(void){

	ADCSRA|=(1<<ADEN);//разрешаем работу ацп(7ой бит)
	ADCSRA|=(1<<ADATE);//
	ADCSRB|=(0<<ADTS2)|(0<<ADTS1)|(0<<ADTS0);//включаем режим постоянного измерения
	ADCSRA|=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);//делитель 128(частота дискретизации 16000000/128=125кГц)
	ADMUX|=(0<<REFS1)|(1<<REFS0);//источник опорного напряжения 5 вольт
	ADMUX|=(0<<MUX3)|(1<<MUX2)|(0<<MUX1)|(0<<MUX0);//включаем ADC4 0100
	ADMUX|=(0<<ADLAR);//выравнивание
	ADCSRA|=(1<<ADSC);//запускаем работу ацп

}

/*обрабатываем прерывания при переполнении счетчика*/
ISR(TIMER0_OVF_vect){
	if(x==1){PORTC=0b00000100;PORTD=chisla[R1];} //при первом прерывании на порту С включаем третий бит, в порт д выводим число, высчитанное в функции vse_chislo
	if(x==2){PORTC=0b00000010;PORTD=chisla[R2];if(Uvh>=10){PORTD&=~(1<<PD7);}
	else {PORTD&=~(0<<PD7);}}
	if(x==3){PORTC=0b00000001;PORTD=chisla[R3];if(Uvh<10){PORTD&=~(1<<PD7);}
	else{PORTD&=~(0<<PD7);}}
	x++;
	if(x>3){x=1;};
		
		
}


void vse_chislo(unsigned int razbivka_chisla){
	R1=razbivka_chisla%10; //единицы
	R2=razbivka_chisla%100/10;//десятки
R3=razbivka_chisla/100;}//сотни


int main(void){

	init_ports();
	init_adc();
	init_dynamic();
	sei();

	while(1){
		
		if (TCNT0=255)sec++;
		if(sec==15625){zzz=ADC;sec=0;}//записываем в переменную значения ацп по таймеру
		

		
		Uvh=(zzz*5.00)/1024;


		if(Uvh<10)
		{
			K1=100;
		}
		else
		{K1=10;
		}

		
		vse_chislo(Uvh*K1); //вывод в вольтах
		


	}
}

По фильтру вопросов нет, так как не понимаю, как он может быть реализован.

Замер питающего напряжения в практическом смысле нужен для того, чтобы делать поправку  при пересчете показаний ацп в вольты?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

bibo пишет:
По фильтру вопросов нет, так как не понимаю, как он может быть реализован.

Для начала можно сгенерировать готовый программой WinFilter - она прямо готовый код выдаёт. Там же в ней можно посмотреть частотные и фазовые характеристики получившегося фильтра. Только для нормальной фильтрации важно, чтобы измерения проводились через равные промежутки времени (там один из главных параметров - частота измерений).

bibo пишет:
Замер питающего напряжения в практическом смысле нужен для того, чтобы делать поправку  при пересчете показаний ацп в вольты?
Ну, да. Как его замерять можно посмотреть вот здесь, начиная с заголовка "Detecting low voltage". Можно ещё здесь или здесь.

bibo
Offline
Зарегистрирован: 26.03.2018

Есть некоторая функция, приведенная по вашим ссылкам.

long readVcc() {
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
    ADMUX = _BV(MUX5) | _BV(MUX0);
  #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
    ADMUX = _BV(MUX3) | _BV(MUX2);
  #else
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #endif  

  delay(75); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring

  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
  uint8_t high = ADCH; // unlocks both

  long result = (high<<8) | low;

  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
  return result; // Vcc in millivolts
}

Я ее немного "адаптировал" в меру своего понимания

long readVcc() {
	
	ADMUX = (0<<REFS1)|(1<<REFS0)|(0<<MUX3)|(1<<MUX2)|(0<<MUX1)|(0<<MUX0);//устанавливаем опорное напряжение 5в 4 нога ацп
	_delay_ms(75); // подождем, пока все устаканится
	ADCSRA|=(1<<ADSC);//запускаем работу ацп

	while (ADCSRA&(1<<ADSC)); // измерение

	uint8_t low  = ADCL; // читаем сначала младший байт и записываем его в переменную low
	uint8_t high = ADCH; // читаем  старший байт и записываем его в переменную high

	long result = (high<<8) | low;//помещаем старший байт в правую часть, младший в левую

	result = 1125300 / result; // вычисляем Vcc в милливольтах; 1125300 = 1.1*1023*1000
	return result; // Vcc in millivolts
}

Некоторые моменты мне не понятны.

Как может измерять что-то строка while (ADCSRA&(1<<ADSC));, да и  еще именно внутренний источник опорного напряжения ?? О нем вообще ведь не идет никакой речи в этой функции!

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

Можете разъяснить, какова логика измерения?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, Вы текст Гэммона читали? Он же там всё подробно словами объясняет. 

Нужно сделать две вещи:

1. Как можно точнее измерить мультиметром внутреннее опорное напряжение. Так-то оно 1,1В, но по даташиту может быть от 1В до 1,2В - зависит от экземпляра, а это даст 10% погрешности. Но, для данного экземпляра оно довольно стабильно. Допустим измерили и забили константой или зашили в EEPROM.

2. Теперь уже программно измерить внутренне опорное напряжение, используя в качестве опорного напряжение пиатния. Сравнивая полученный результат с уже известным правильным внутренним опорным, можно определить точное напряжение питания.

Какого из этих пунктов Вы не понимаете? Вам дать пример второго, считая, что опорное Вы уже измерили? Или что?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

И да, кстати, очень не советую использовать конструкции типа Ваших строк №№ 9-12. Эта демонстрация "умности"  часто приводит к путанице и ошибкам. Регистры то читать и писать нужно в разном порядке. В даташите же написано, что компилятор с этим отлично справляется сам. Ну и пишите вместо этих строк просто:

long result = ADC;

Больше ничего не нужно

 

bibo
Offline
Зарегистрирован: 26.03.2018

Просто так получить требуемый код не особо интересно. Только если только как пример, в котором нужно разобраться.

<<Допустим измерили и забили константой>>

то есть я ввожу  константу, равную измеренному мультиметром опорному напряжению(мультиметр тоже, кстати, имеет погрешность) Vref =1,15

<<Теперь уже программно измерить внутренне опорное напряжение, используя в качестве опорного напряжение пиатния>> 

Не понятно, как это программно реализовывается. Есть строка, которая этим занимается(вроде как)while (ADCSRA&(1<<ADSC));

Мне бы яснее стало если была бы примерно вот такая запись:

while (ADCSRA&(1<<ADSC)){long result=ADC;}

Эквивалетна ли такая запись оригинальной записи у Гэммона?

long readVcc() {
	
	ADMUX = (0<<REFS1)|(1<<REFS0)|(0<<MUX3)|(1<<MUX2)|(0<<MUX1)|(0<<MUX0);//устанавливаем опорное напряжение 5в 4 нога ацп
	_delay_ms(75); // подождем, пока все устаканится
	ADCSRA|=(1<<ADSC);//запускаем работу ацп

	while (ADCSRA&(1<<ADSC)){long result = ADC}; // измерение


	result = Vref*1023*1000 / result; // вычисляем Vcc в милливольтах; 
	return result; 
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

bibo пишет:

Не понятно, как это программно реализовывается. Есть строка, которая этим занимается(вроде как)while (ADCSRA&(1<<ADSC));

Мне бы яснее стало если была бы примерно вот такая запись:

while (ADCSRA&(1<<ADSC)){long result=ADC;}

Не знаю, что именно Вам яснее. В даташите явно написано, что бит ADSC остаётеся 1 до тех пор, пока идет преобразование. Когда преобразование заканчивается, в этом бите появляется 0. Поэтому цикл

while (ADCSRA&(1<<ADSC));

просто ожидание пока преобразование не закончится. Как только оно закончится, там появится 0 и этот цикл завершится. Тогда (когда преоброзование закончилось) пора читать результат из ADC.

А какой смысл в том, что Вы дергаетесь читать ADC в процессе преобразования, когда оно еще не закончено, я не понимаю.

b707
Offline
Зарегистрирован: 26.05.2017

bibo пишет:

Эквивалетна ли такая запись оригинальной записи у Гэммона?

а просто сравнить код Гаммона из вашего сообщения №32 и этот - не пробовали? инициализация АЦП (ваша строка 3) разве совпадает с оригинальной? - посмотрите, какие биты устанавливает в ADMUX Гаммон - и какие вы.

Измерение внутреннего опрного напряжения - элементарная конструкция. по следам Гаммона ею пользуются чуть ли не первоклашки. А вы тут что-то жуете который день...

bibo
Offline
Зарегистрирован: 26.03.2018

Просто даже если тупо скопировать код Гаммона, то все по нулям. То есть не работает оно у меня.

Может первоклашки и справляются, а я еще детсадовец, получается, коль не справляюсь. 

UPD.

Запоказывало. Только результат странный. 0.83 вольта.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, я не знаю, что оно у Вас там запоказывало. Вы же свой скетч секретите. Я вот прямо сейчас по двум своим пунктам из #33 за три минуты написал скетч. Ардуина питается от лабораторного БП (на пин 5V). запускаю, показывает правильное напряжение питания, кручу ручку на БП (понижаю питание до 4 вольт) адекватно показывает 4 вольта, не знаю в чём у Вас заморочка.

Вот мой скетч:

//
//	ПРИМЕР ИЗМЕРЕНИЯ НАПРЯЖЕНИЯ ПИТАНИЯ
//
//	Константа internalRefference представляет собой
//	более или менее точное значение внутреннего напряжения Vbg
//	Для того, чтобы его получить, необходимо запустить скетч
//
//		void setup (void) { ADMUX = bit (REFS0) | bit (REFS1); }
//		void loop (void) {}
//
//	и во время его работы замерить напряжение на пине AREF
//	это лучше сделать несколько раз, перевключая устройство 
//	и результат осреднить
//
static const float internalRefference = 1.1;

// Делитель частоты для ADC
static const uint8_t adcPrescaler = 
	#if F_CPU >= 16000000 // 16 MHz / 128 = 125 KHz
		bit(ADPS2) | bit(ADPS1) | bit(ADPS0);
	#elif F_CPU >= 8000000 // 8 MHz / 64 = 125 KHz
		bit(ADPS2) | bit(ADPS1);
	#elif F_CPU >= 4000000 // 4 MHz / 32 = 125 KHz
		bit(ADPS2) | bit(ADPS0);
	#elif F_CPU >= 2000000 // 2 MHz / 16 = 125 KHz
		bit(ADPS2);
	#elif F_CPU >= 1000000 // 1 MHz / 8 = 125 KHz
		bit(ADPS1) | bit(ADPS0);
	#else // 128 kHz / 2 = 64 KHz
		bit(ADPS0);
	#endif


static const uint8_t refAVcc = bit(REFS0);	//	AVcc как опорное
static const uint8_t inpVbg = bit(MUX3) | bit(MUX2) | bit(MUX1); // измеряем Vbg (1.1В)

//
//	Включение ADC
//
static inline void adcEnable(void) { 
	PRR &= ~bit(PRADC); 
	ADCSRA = bit(ADEN); 
	ADMUX = 0; 
	ADCSRB = 0; 
}

//
//	Замер напряжения питания
//
float getAVccVoltage(void) {
	ADMUX = refAVcc | inpVbg;	// Измеряем Vbg, используя AVcc как опорное
	ADCSRA |= adcPrescaler | bit(ADSC); //	Запускаем преобразование
	while(ADCSRA & bit(ADSC)); // Ждём завершения преобразования
	return internalRefference * 1023 / ADC; // Считаем AVcc
}

//
// Применим всё это
//
void setup(void) {
	Serial.begin(57600);
	adcEnable();	// Включаем ADC
	getAVccVoltage(); // Игнорируем первый результат
}

//
//	Раз в секунду выдаём текущее напряжение питания на печать
//
void loop(void) {	
	Serial.print("AVcc voltage is: ");
	Serial.println(getAVccVoltage());
	delay(1000);
}
bibo
Offline
Зарегистрирован: 26.03.2018

Не секречу ничего.

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

unsigned int z=0;//переменая для икpементирования
unsigned char x=1;//переменная для функции ISR
unsigned int R1=0,R2=0, R3=0;
unsigned int y=0;//переменая для икpементирования
unsigned int zzz;
float Uvh;//переменная для записи напряжения
const int Koef=1;//коэффициент
int K1;
unsigned int sec;
unsigned int sec1;
static const float Vref=1.1;
float Vcc;
unsigned int chisla[10]=
{
	~0b00111111,//0
	~0b00000110,//1
	~0b01011011,//2
	~0b01001111,//3
	~0b01100110,//4
	~0b01101101,//5
	~0b01111101,//6
	~0b00000111,//7
	~0b01111111,//8
~0b01101111,};//9

/*настраиваем таймер-счетчик 0*/
void init_dynamic(void){
	TCCR0A|=(0<<WGM00)|(0<<WGM01);//режим normal(сброс по переполнению)
	TCCR0B |=(1<<CS02)|(0<<CS01)|(0<<CS00);//делитель 256
	TIMSK0|=(1<<TOIE0);//сброс по переполнению
	TIFR0|=(1<<TOV0);
	TCNT0=0;

}


/*настраиваем порты*/

void init_ports(void){

	DDRC|=(1<<PC0)|(1<<PC1)|(1<<PC2);//аноды семисегментника
	DDRC&=~(1<<PC4);//adc4
	//DDRB|=(1<<PB0)|(1<<PB2)|(1<<PB4);//leds
	//DDRB&=~(1<<PB5);//button
	DDRD=0xFF;//весь порт Д на выход(катоды семисегментника)
	//PORTB|=(0<<PB0)|(0<<PB2)|(0<<PB4)|(1<<PB5);//leds&&button
	PORTC|=(1<<PC0)|(1<<PC1)|(1<<PC2)|(1<<PC4);
	PORTD =0xFF;//логическая единица на порту Д,чтобы семисегментник не светился

}

/*настраиваем ацп*/
void init_adc(void){

	ADCSRA|=(1<<ADEN);//разрешаем работу ацп(7ой бит)
	ADCSRA|=(1<<ADATE);//
	ADCSRB|=(0<<ADTS2)|(0<<ADTS1)|(0<<ADTS0);//включаем режим постоянного измерения
	ADCSRA|=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);//делитель 128(частота дискретизации 16000000/128=125кГц)
	ADMUX|=(0<<REFS1)|(1<<REFS0);//источник опорного напряжения 5 вольт
	ADMUX|=(0<<MUX3)|(1<<MUX2)|(0<<MUX1)|(0<<MUX0);//включаем ADC4 0100
	ADMUX|=(0<<ADLAR);//выравнивание
	ADCSRA|=(1<<ADSC);//запускаем работу ацп

}

/*обрабатываем прерывания при переполнении счетчика*/
ISR(TIMER0_OVF_vect){
	if(x==1){PORTC=0b00000100;PORTD=chisla[R1];} //при первом прерывании на порту С включаем третий бит, в порт д выводим число, высчитанное в функции vse_chislo
	if(x==2){PORTC=0b00000010;PORTD=chisla[R2];if(Uvh>=10){PORTD&=~(1<<PD7);}
	else {PORTD&=~(0<<PD7);}}
	if(x==3){PORTC=0b00000001;PORTD=chisla[R3];if(Uvh<10){PORTD&=~(1<<PD7);}
	else{PORTD&=~(0<<PD7);}}
	x++;
	if(x>3){x=1;};
	
	
}


void vse_chislo(unsigned int razbivka_chisla){
	R1=razbivka_chisla%10; //единицы
	R2=razbivka_chisla%100/10;//десятки
R3=razbivka_chisla/100;}//сотни



float getVcc ()
{
	// REFS0 : Selects AVcc external reference
	// MUX3 MUX2 MUX1 : Selects 1.1V (VBG) //каким таким макаром этими битами задается внутренний ион?? это же биты выбора вывода ацп
	ADMUX =  (1<<REFS0) |  (1<<MUX3) | (1<<MUX2) | (1<<MUX1);
	ADCSRA|=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
	ADCSRA |= (1<< ADSC ); 
	while (ADCSRA & (1<<ADSC)){ }  
	float results = Vref * 1024 *1000/ ADC;//в вольтах
	return results;
}



int main(void){

	init_ports();
	init_adc();
	init_dynamic();
	sei();

	while(1){
		
		//if (TCNT0==255)sec++;
		//if(sec==100){zzz=ADC;sec=0;}//записываем в переменную значения ацп по таймеру
		/*Uvh=(zzz*5.00)/1024;
		if(Uvh<10)
		{
			K1=100;
		}
		else
		{K1=10;
		}

		
		vse_chislo(Uvh*K1); //вывод в вольтах*/
		
		
if (TCNT0==128)sec1++;
if(sec1==100){getVcc();}
		Vcc=getVcc();
		
vse_chislo(Vcc); //вывод в вольтах

		
		


	}
}

в общем, пойду дальше читать\изучать.

терять время на том, что пока еще недоступно, не имеет смысла.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Мой-то код измеряет питание у Вас? 

bibo
Offline
Зарегистрирован: 26.03.2018

Кажет 5.2 вольта.

Ну и дела, понимаешь.

Кстати, атмелстудио ругается на конструкцию bit().

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

bibo пишет:

Кажет 5.2 вольта.

Покрутите питание - будет реагировать. Всё нормально работает.

bibo пишет:

Кстати, атмелстудио ругается на конструкцию bit().

Ну, нету у неё такого макроса. Вы ж не сказали, что студией пользуетесь. Я пример под ИДЕ писал.

Добавьте макрос - не будет ругаться

#define bit(b) (1UL << (b))
bibo
Offline
Зарегистрирован: 26.03.2018

Да, все работает. Осталось только осмыслить ваш код в полной мере и переработать всю свою программку. Получается, однажды создав static inline void adcEnable(void), его можно использовать в каждом новом задействовании ацп без лишней писанины. Круто.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

bibo пишет:

Осталось только осмыслить

Словами всё написано в посте #33 - больше в коде ничего нет.

bibo
Offline
Зарегистрирован: 26.03.2018

По идее, полученное значение напряжения питания микроконтроллера я должен использовать для вычисления измеряемого напряжения.

В чем моя ошибка?

В таком виде показывает внутреннее опорное напряжение

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

unsigned int z=0;//переменная для икpементирования
unsigned char x=1;//переменная для функции ISR
unsigned int R1=0,R2=0, R3=0;
unsigned int y=0;//переменная для икpементирования
//unsigned int zzz;
float Uvh;//переменная для записи напряжения
const int Koef=1;//коэффициент
int K1;
unsigned int sec;
//unsigned int sec1;
float Vcc;
float U;

static const float internalRefference = 1.1;//измеренное вольтметром внутреннее опорное напряжение

// Делитель частоты для ADC
static const uint8_t adcPrescaler =(1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);//устанавливаем частоту дискретизации делитель 128(частота дискретизации 16000000/128=125кГц)
static const uint8_t refAVcc = (1<<REFS0);	//	AVcc как опорное
static const uint8_t inpVbg = (1<<MUX3) | (1<<MUX2) | (1<<MUX1); // измеряем Vbg (1.1В)


//	Включение ADC
static inline void adcEnable(void) {
	PRR &= ~(1<<PRADC);//пишем нуль в нулевой бит регистра, отвечающего за обесточивание компонентов МК, чтобы исключить выключение ацп
	ADCSRA |= (1<<ADEN);//включаем ацп
	ADCSRB|=(0<<ADTS2)|(0<<ADTS1)|(0<<ADTS0);//включаем режим постоянного измерения
}

//
//	Замер напряжения питания
//
float getAVccVoltage(void) {
	adcEnable();
	ADMUX = refAVcc | inpVbg;	// Измеряем Vbg, используя AVcc как опорное
	ADCSRA |= adcPrescaler | (1<<ADSC); //	Запускаем преобразование
	while(ADCSRA & (1<<ADSC)); // Ждём завершения преобразования
	return internalRefference * 1023 / ADC; // Считаем AVcc
}


/*замер внешнего напряжения*/
float voltage_ext(void){
	adcEnable();
	ADCSRA|=adcPrescaler;//делитель 128(частота дискретизации 16000000/128=125кГц)
	ADMUX|=refAVcc;//источник опорного напряжения 5 вольт
	ADMUX|=(0<<MUX3)|(1<<MUX2)|(0<<MUX1)|(0<<MUX0);//включаем ADC4 0100
	ADMUX|=(0<<ADLAR);//выравнивание
	ADCSRA|=(1<<ADSC);//запускаем работу ацп
while(ADCSRA & (1<<ADSC)); // Ждём завершения преобразования
return ADC;// напряжение в "попугаях"
}



unsigned int digits[10]=
{
	~0b00111111,//0
	~0b00000110,//1
	~0b01011011,//2
	~0b01001111,//3
	~0b01100110,//4
	~0b01101101,//5
	~0b01111101,//6
	~0b00000111,//7
	~0b01111111,//8
~0b01101111,};//9

/*настраиваем таймер-счетчик 0*/
void init_TC0(void){
	TCCR0A|=(0<<WGM00)|(0<<WGM01);//режим normal(сброс по переполнению)
	TCCR0B |=(1<<CS02)|(0<<CS01)|(0<<CS00);//делитель 256
	TIMSK0|=(1<<TOIE0);//сброс по переполнению
	TIFR0|=(1<<TOV0);
	TCNT0=0;

}


/*настраиваем порты*/

void init_ports(void){

	DDRC|=(1<<PC0)|(1<<PC1)|(1<<PC2);//аноды семисегментника
	DDRC&=~(1<<PC4);//adc4
	//DDRB|=(1<<PB0)|(1<<PB2)|(1<<PB4);//leds
	//DDRB&=~(1<<PB5);//button
	DDRD=0xFF;//весь порт Д на выход(катоды семисегментника)
	//PORTB|=(0<<PB0)|(0<<PB2)|(0<<PB4)|(1<<PB5);//leds&&button
	PORTC|=(1<<PC0)|(1<<PC1)|(1<<PC2)|(1<<PC4);
	PORTD =0xFF;//логическая единица на порту Д,чтобы семисегментник не светился

}



/*обрабатываем прерывания при переполнении счетчика*/
ISR(TIMER0_OVF_vect){
	if(x==1){PORTC=0b00000100;PORTD=digits[R1];} //при первом прерывании на порту С включаем третий бит, в порт д выводим число, высчитанное в функции vse_chislo
	if(x==2){PORTC=0b00000010;PORTD=digits[R2];if(Uvh>=10){PORTD&=~(1<<PD7);}
	else {PORTD&=~(0<<PD7);}}
	if(x==3){PORTC=0b00000001;PORTD=digits[R3];if(Uvh<10){PORTD&=~(1<<PD7);}
	else{PORTD&=~(0<<PD7);}}
	x++;
	if(x>3){x=1;};
	
	
}


void number(unsigned int part_of_number){
	R1=part_of_number%10; //единицы
	R2=part_of_number%100/10;//десятки
R3=part_of_number/100;}//сотни





int main(void){

	init_ports();
	init_TC0();
	sei();
	

	while(1){
	//выводим на семисегментник напряжение, измеряемое на 4ом пине ацп
		if (TCNT0==255)sec++;
		if(sec==100){U=voltage_ext();Vcc=getAVccVoltage();sec=0;}//записываем в переменную значения ацп по таймеру
	Uvh=U*Vcc/1024;
		if(Uvh<10)
		{
			K1=100;
		}
		else
		{K1=10;
		}

		number(Uvh*K1); //вывод в вольтах
		
		/*//выводим на семисегментник питающее напряжение
		if (TCNT0==255)sec++;
		if(sec==100){Vcc=getAVccVoltage();sec=0;}//записываем в переменную значения ацп по таймеру
		
		if(Vcc<10)
		{
			K1=100;
		}
		else
		{K1=10;
		}

		number(Vcc*K1); //вывод в вольтах*/
		
		

	}
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Две проблемы

1.
нигде не чистите регистры. Везде испольуете |, а старые значения не чистите (значит, старые единицы там остаются).

2.
Вы уж определитесь, что Вам нужно режим постоянного измерения или измерения по очереди напряжения из разных источников. Режим постоянного измерения работает и одним источником.  А Вы и его включаете, и с питания на 4-ый пин постоянно скачете.

Нафига Вам это "постоянное измерение" вообще нужно?

bibo
Offline
Зарегистрирован: 26.03.2018

Вообще, я просто хотел использовать измеренное в фуннкции getAVccVoltage напряжение питания при расчете замеряемого напряжения на четвертом пине ацп.

Чистить регистр это значит после преобразования в ацп выключить(сбросить) его?

bibo
Offline
Зарегистрирован: 26.03.2018

вообще, я не понимаю, почему вся программа ломается, если поэкспериментировать в каком-либо месте, а затем вернуть все на свои места?

это ппц как напрягает.

рпосто все перестает работать напрочь.

bibo
Offline
Зарегистрирован: 26.03.2018

вот почему у меня навернулся замер внешнего напряжения?? что изменилось??

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

unsigned int z=0;//переменная для икpементирования
unsigned char x=1;//переменная для функции ISR
unsigned int R1=0,R2=0, R3=0;
unsigned int y=0;//переменная для икpементирования
//unsigned int zzz;
float Uvh;//переменная для записи напряжения
const int Koef=1;//коэффициент
int K1;
unsigned int sec;
//unsigned int sec1;
float Vcc;
float U;

static const float internalRefference = 1.1;//измеренное вольтметром внутреннее опорное напряжение

// Делитель частоты для ADC
static const uint8_t adcPrescaler =(1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);//устанавливаем частоту дискретизации делитель 128(частота дискретизации 16000000/128=125кГц)
static const uint8_t refAVcc = (1<<REFS0);	//	AVcc как опорное
static const uint8_t inpVbg = (1<<MUX3) | (1<<MUX2) | (1<<MUX1); // измеряем Vbg (1.1В)


//	Включение ADC
static inline void adcEnable(void) {
	PRR &= ~(1<<PRADC);//пишем нуль в нулевой бит регистра, отвечающего за обесточивание компонентов МК, чтобы исключить выключение ацп
	ADCSRA |= (1<<ADEN);//включаем ацп
	//ADCSRB|=(0<<ADTS2)|(0<<ADTS1)|(0<<ADTS0);//включаем режим постоянного измерения
}

//
//	Замер напряжения питания
//
float getAVccVoltage(void) {
	adcEnable();
	ADMUX = refAVcc | inpVbg;	// Измеряем Vbg, используя AVcc как опорное
	ADCSRA |= adcPrescaler | (1<<ADSC); //	Запускаем преобразование
	while(ADCSRA & (1<<ADSC)); // Ждём завершения преобразования
	return internalRefference * 1023 / ADC; // Считаем AVcc
}


/*замер внешнего напряжения*/
float voltage_ext(void){
	adcEnable();
	ADCSRA|=adcPrescaler;//делитель 128(частота дискретизации 16000000/128=125кГц)
	ADMUX|=refAVcc;//источник опорного напряжения 5 вольт
	ADMUX|=(0<<MUX3)|(1<<MUX2)|(0<<MUX1)|(0<<MUX0);//включаем ADC4 0100
	ADMUX|=(0<<ADLAR);//выравнивание
	ADCSRA|=(1<<ADSC);//запускаем работу ацп
while(ADCSRA & (1<<ADSC)); // Ждём завершения преобразования
return ADC*5.00/1024;// напряжение в  вольтах
}



unsigned int digits[10]=
{
	~0b00111111,//0
	~0b00000110,//1
	~0b01011011,//2
	~0b01001111,//3
	~0b01100110,//4
	~0b01101101,//5
	~0b01111101,//6
	~0b00000111,//7
	~0b01111111,//8
~0b01101111,};//9

/*настраиваем таймер-счетчик 0*/
void init_TC0(void){
	TCCR0A|=(0<<WGM00)|(0<<WGM01);//режим normal(сброс по переполнению)
	TCCR0B |=(1<<CS02)|(0<<CS01)|(0<<CS00);//делитель 256
	TIMSK0|=(1<<TOIE0);//сброс по переполнению
	TIFR0|=(1<<TOV0);
	TCNT0=0;

}


/*настраиваем порты*/

void init_ports(void){

	DDRC|=(1<<PC0)|(1<<PC1)|(1<<PC2);//аноды семисегментника
	DDRC&=~(1<<PC4);//adc4
	//DDRB|=(1<<PB0)|(1<<PB2)|(1<<PB4);//leds
	//DDRB&=~(1<<PB5);//button
	DDRD=0xFF;//весь порт Д на выход(катоды семисегментника)
	//PORTB|=(0<<PB0)|(0<<PB2)|(0<<PB4)|(1<<PB5);//leds&&button
	PORTC|=(1<<PC0)|(1<<PC1)|(1<<PC2)|(1<<PC4);
	PORTD =0xFF;//логическая единица на порту Д,чтобы семисегментник не светился

}



/*обрабатываем прерывания при переполнении счетчика*/
ISR(TIMER0_OVF_vect){
	if(x==1){PORTC=0b00000100;PORTD=digits[R1];} //при первом прерывании на порту С включаем третий бит, в порт д выводим число, высчитанное в функции vse_chislo
	if(x==2){PORTC=0b00000010;PORTD=digits[R2];if(Uvh>=10){PORTD&=~(1<<PD7);}
	else {PORTD&=~(0<<PD7);}}
	if(x==3){PORTC=0b00000001;PORTD=digits[R3];if(Uvh<10){PORTD&=~(1<<PD7);}
	else{PORTD&=~(0<<PD7);}}
	x++;
	if(x>3){x=1;};
	
	
}


void number(unsigned int part_of_number){
	R1=part_of_number%10; //единицы
	R2=part_of_number%100/10;//десятки
R3=part_of_number/100;}//сотни





int main(void){

	init_ports();
	init_TC0();
	sei();
	

	while(1){
	//выводим на семисегментник напряжение, измеряемое на 4ом пине ацп
		if (TCNT0==255)sec++;
		if(sec==100){Uvh=voltage_ext();sec=0;}//записываем в переменную значения ацп по таймеру
	
		if(Uvh<10)
		{
			K1=100;
		}
		else
		{K1=10;
		}

		number(Uvh*K1); //вывод в вольтах
		
		
		
		

	}
}