АЦП-Atmega 8

Обсуждаем контроллеры компании Atmel.
Ответить
Нашел транзистор. Понюхал.
Сообщения: 167
Зарегистрирован: Вс мар 27, 2022 09:38:17

Сообщение aleksey chilov »

Добрый день форумчане!

Вопрос такой. МК ( Atmega8 ) необходимо
измерять 2 напряжения одновременно,как переключатся
между каналами? Как грамотно построить программу?
Один канал делал как 2 не пойму. Подскажите пожалуйста.
Спасибо.
Реклама
Друг Кота
Сообщения: 9459
Зарегистрирован: Вт мар 13, 2012 12:16:13
Откуда: .ru

Сообщение roman.com »

// создаём переменную куда быдем записывать результат измерения
unsigned int u; // буфер АЦП-Atmega 8


// создаём функцию АЦП-Atmega 8
////////////////////////////////// АЦП: -0,000.070c
void adc(unsigned char adc_input)
{
ADMUX = 0b01000000 | adc_input; // опорное AVCC // канал ADC0...ADC7
delay_us(10);
ADCSRA|=0b01000000;
while (ADCSRA & 0b01000000);
}
//////////////////////////////////


// настраиваем АЦП-Atmega 8
ADCSRA=0x85; // вкл. ADC // 8 МГц/32=250.000 Hz


// вызываем функцию АЦП-Atmega 8 (из любого места программы)
// при вызове функции АЦП-Atmega 8 указываем номер канала на котором будем измерять напряжение (0...7)
adc(5); // канал ADC 5


//полученный результат сохраняем в переменную u (для последующей обработки)
u=ADCW;
Реклама
Нашел транзистор. Понюхал.
Сообщения: 167
Зарегистрирован: Вс мар 27, 2022 09:38:17

Сообщение aleksey chilov »

Вот моя конструкция:

#if 1
//______________Под аккумулятор________//
ADMUX = 5; // вход ADC5 (PORTC5)

ADMUX|=(1<<REFS0); // ИОН - AVCC с внешним конденсатором 5 вольт.

ADCSRA|=(1<<ADPS1)|(1<<ADPS2);// 125.000 Гц.

ADCSRA|=(1<<ADIE); // разрешаем прерывание от ацп

ADCSRA|=(1<<ADEN); // Разрешение АЦП

ADCSRA|=(1<<ADSC);// Начинаем преобразование

ADCSRA|=(1<<ADFR);// непрерывный режим
#endif

void ADC_battery(volatile uint8_t *adc_count, volatile uint32_t *adc_buff, volatile uint16_t *MO_adc) // Читаем МОТОР
{
if(*adc_count == 250)
{
*MO_adc = ((*adc_buff*5*4*100)/1024)/ *adc_count;
*adc_buff = 0;
*adc_count = 0;
}
}
Вопрос!
Как пререключать их чтобы 2 канала работали???
И где?
Пробовал в ISR тогда всё сыпется...
Друг Кота
Сообщения: 9459
Зарегистрирован: Вт мар 13, 2012 12:16:13
Откуда: .ru

Сообщение roman.com »

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

переключение каналов осуществляется в регистре ADMUX (он же мультиплексор).

а вот и сам регистр))

//ADMUX
//1... .... REFS1 - опорное напряжение - 0
//.1.. .... REFS0 - опорное напряжение - 1 - AVCC
//..1. .... ADLAR - Выравнивание результата
//...1 .... -
//.... 1... MUX3 - номер канала
//.... .1.. MUX2 - номер канала
//.... ..1. MUX1 - номер канала
//.... ...1 MUX0 - номер канала - ADC0...ADC7 (0000=ADC0)
//REFS1=0 REFS0=0 - ИОН=AREF
//REFS1=0 REFS0=1 - ИОН=AVCC
//REFS1=1 REFS0=0 - не используется (зарезервировано)
//REFS1=1 REFS0=1 - ИОН=2.56V (встроенный ИОН 2.56V)

значит надо в каком то месте программы прописывать регистр по новой...

ADMUX = 5; // вход ADC5 (PORTC5)
ADMUX|=(1<<REFS0); // ИОН - AVCC с внешним конденсатором 5 вольт.

...

ADMUX = 6; // вход ADC5 (PORTC5)
ADMUX|=(1<<REFS0); // ИОН - AVCC с внешним конденсатором 5 вольт.

...

ADMUX = 7; // вход ADC5 (PORTC5)
ADMUX|=(1<<REFS0); // ИОН - AVCC с внешним конденсатором 5 вольт.

...

и т.д.))

правда как это делать в случае непрерывное преобразования...
ну наверное это логичнее всего делать в обработчике прерывания...

каждый раз по окончанию преобразования срабатывает обработчик прерывания...
вот там то и надо менять канал АЦП-Atmega 8... чтоб пока не закончится преобразование канал не менялся... а то будут ошибки преобразования... и неверный результат преобразования...
:roll:

вообще то так никто не делает...
ну да ладно))
:tea:
Реклама
Эиком - электронные компоненты и радиодетали
Нашел транзистор. Понюхал.
Сообщения: 167
Зарегистрирован: Вс мар 27, 2022 09:38:17

Сообщение aleksey chilov »

Ну непрерывное потому что он у меня меряет потоянно и кождое прерывание по окончаню измерения складывается переменную uint_32, и так 250 раз. Потом результат высчитывается по формуле. Точность поражает просто.
С мультиметром один в один показания. Для такого МК я считаю это офигенно!
Пробовал другие способы ну они что-то не зашли...
То показания прыгаю то не точно...
Этот способ понравился поэтому его и использую.
Только мне нужно измерять разные напряжения. 2 канала нужны.
Хотя бы сделать сначала одно измерение потом другое...
Ну буду что-то придумывать.
Реклама
Встал на лапы
Сообщения: 121
Зарегистрирован: Пт мар 19, 2021 08:58:45

Сообщение kote52 »

Переходи на stm32f103 хотя бы. Сам мастерю себе зу для акб, его парный режим АЦП меня больше радует чем АЦП меги.
Реклама
Друг Кота
Сообщения: 9459
Зарегистрирован: Вт мар 13, 2012 12:16:13
Откуда: .ru

Сообщение roman.com »

так переключать каналы мы можем и главной программе...

делаем одиночное преобразование... и вызываем функцию преобразования столько сколько нужно))

// создаём переменную куда быдем записывать результат измерения
unsigned int u; // буфер АЦП-Atmega 8


// создаём функцию АЦП-Atmega 8
////////////////////////////////// АЦП: -0,000.070c
void adc(unsigned char adc_input)
{
ADMUX = 0b01000000 | adc_input; // опорное AVCC // канал ADC0...ADC7
delay_us(10);
ADCSRA|=0b01000000;
while (ADCSRA & 0b01000000);
}
//////////////////////////////////


// настраиваем АЦП-Atmega 8
ADCSRA=0x85; // вкл. ADC // 8 МГц/32=250.000 Hz



// главная программа.

while(1)
{

// вызываем функцию АЦП-Atmega 8
// при вызове функции АЦП-Atmega 8 указываем номер канала на котором будем измерять напряжение (0...7)
adc(5); // канал ADC 5
//полученный результат сохраняем в переменную u (для последующей обработки)
u=ADCW;

// вызываем функцию АЦП-Atmega 8
// при вызове функции АЦП-Atmega 8 указываем номер канала на котором будем измерять напряжение (0...7)
adc(6); // канал ADC 6
//полученный результат сохраняем в переменную u (для последующей обработки)
u=ADCW;

// вызываем функцию АЦП-Atmega 8
// при вызове функции АЦП-Atmega 8 указываем номер канала на котором будем измерять напряжение (0...7)
adc(7); // канал ADC 7
//полученный результат сохраняем в переменную u (для последующей обработки)
u=ADCW;

}// while(true)
:tea:

в этом случае работать будет чуть медленней... но зато мы получаем гарантированный результат))
:tea:

а если мы запустим непрерывное преобразование и будем переключать каналы в обработчике прерывания... то никто ничего не гарантирует)) потому что АЦП-Atmega 8 в режиме непрерывного преобразования не будет ждать пока мы переключим каналы))
точность показаний может пойти на смарку))

хотя... если бы мы писали программу на Ассемблере... мы бы могли всё рассчитать с точность до такта процессора))
тогда другое дело.
но на Си гарантий нет что всё точно будет работать... это уже как компилятор Си решит))
:)
Нашел транзистор. Понюхал.
Сообщения: 167
Зарегистрирован: Вс мар 27, 2022 09:38:17

Сообщение aleksey chilov »

Та под STM нужна среда.
CUBE IDE а меня его нет.
И программатор STM link комп перестал поддерживать.
Вернее не сам программатор а прошивальщик не открывается.
Пишет ошибку типа там не хватает фала какого-то там.
И не реально его поставить. Не ставится. Тоже запрещает.
Наверное Америкосы какую то западлянку намутили.

Добавлено after 4 minutes 58 seconds:
Ну вот так и получается если я в прерывании начинаю чё-то там манипулировать каналами у меня протеус начинает подвисать! Он не может понять что от него хотят...
А Ассемблер это для людей "Индиго". Это мне недоступно!
Друг Кота
Сообщения: 9459
Зарегистрирован: Вт мар 13, 2012 12:16:13
Откуда: .ru

Сообщение roman.com »

ну в данном случае переходить на Ассемблер не вижу смысла... и так всё будет нормально работать)) на Си.
:tea:
переходить на Ассемблер приходится... если нужно что-то необычное...)) например генератор нестандартных сигналов))
включаю несколько Atmega 8 в параллель... с тактированием от общего кварца... синхронизируем все инструкции во всех Atmega 8... причём надо не только синхронное выполнение всех инструкций с точностью до такта процессора... но и с точностью до фазы !
ATmega8-FIFO_4.jpg
(80.2 КБ) 78 скачиваний
тут без Ассемблера никуда...
:roll:

это что... я человек "Индиго" ? ))
ничё се... каждый день узнаю про себя что-то новое))
:tea:
Встал на лапы
Сообщения: 121
Зарегистрирован: Пт мар 19, 2021 08:58:45

Сообщение kote52 »

я вот так считаю три канала меге328 в меге8 аналогично

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

#define FIRST_ADC_INPUT 0
#define LAST_ADC_INPUT 2
volatile unsigned long adc[LAST_ADC_INPUT+1];

//********Voltage Reference: AVCC pin********************************************
unsigned int adc_data[LAST_ADC_INPUT-FIRST_ADC_INPUT+1];
// Voltage Reference: AVCC pin
#define ADC_VREF_TYPE ((0<<REFS1) | (1<<REFS0) | (0<<ADLAR))

ISR (ADC_vect)
{	
	static unsigned int cnt;
	static uint8_t input_index=0;
	static unsigned long adc_sum[LAST_ADC_INPUT+1]; //переменная для суммы значения ADC
	
	// Read the AD conversion result	
	cnt++;
	adc_data[input_index]=ADCW;
	adc_sum[input_index]+= (unsigned long)adc_data[input_index]; // суммируем

	// Select next ADC input
	if (++input_index > (LAST_ADC_INPUT-FIRST_ADC_INPUT))
		input_index=0;
	ADMUX=(FIRST_ADC_INPUT | ADC_VREF_TYPE)+input_index;
	// Delay needed for the stabilization of the ADC input voltage
	_delay_us(10);
	// Start the AD conversion
	ADCSRA|=(1<<ADSC);

    
	if (cnt >= (64*(LAST_ADC_INPUT+1))) {     //если сделали 64 замера на каждый канал
		for (uint8_t i=0; i<=LAST_ADC_INPUT; i++){
			adc[i]=adc_sum[i];    
			adc_sum[i]=0;      
		}
		cnt=0;          //сбросили счетчик
		adc_status=true;   //подняли флаг, что очередная сумма замеров ГОТОВА
	}
}

void ADC_Init(void){ 
        //в этих расчетах у меня сомнения
	//Время одного преобразования АЦП = 108,25uS 
	//T= ((11.5+2)/ADC Clk freq)+(4/F_CUP)=((11.5+2)/0,125)+(4/16)=0.00010825сек или 108,25uS 
	//где 11,5 для 10бит АЦП
	//Двойка(2) - минимальное время захвата измеряемого напряжения в УВХ – 2 такта частоты CLK_ADC
	 // ADC Clock frequency: 125,000 kHz
	 // ADC Voltage Reference: AREF pin
	 // ADC Auto Trigger Source: ADC Stopped
	 ADMUX= FIRST_ADC_INPUT | ADC_VREF_TYPE;
	 ADCSRA=(1<<ADEN) | (1<<ADSC) |(1<<ADIE) | (1<<ADPS2) | (1<<ADPS1)| (1<<ADPS0);
 }

int main(void){
     ADC_Init();
while(1){
     if(adc_status ){	//ждем окончания измерений ацп 
         for (uint8_t i=0; i<=LAST_ADC_INPUT; i++) {
	     adc[i]=adc[i]>>6;//сумму АЦП делим на 64
         }
     }
}//end while
}//end main
Последний раз редактировалось kote52 Чт авг 31, 2023 20:06:39, всего редактировалось 1 раз.
Поставщик валерьянки для Кота
Сообщения: 1948
Зарегистрирован: Сб май 05, 2012 20:24:52
Откуда: KN34PC, Болгария

Сообщение veso74 »

В шутку я попросил ChatGPT o решение :).

Ответ:

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

#include <avr/io.h>

void ADC_Init() {
  ADMUX |= (1 << REFS0);                  // Set reference voltage to AVCC
  ADCSRA |= (1 << ADPS2) | (1 << ADPS1);  // Set ADC clock prescaler to 64 (125 kHz)
  ADCSRA |= (1 << ADEN);                  // Enable ADC
}

uint16_t ADC_ReadChannel(uint8_t channel) {
  ADMUX = (ADMUX & 0xF0) | (channel & 0x0F);  // Set ADC channel
  ADCSRA |= (1 << ADSC);                      // Start ADC conversion
  while (ADCSRA & (1 << ADSC));               // Wait for conversion to complete
  return ADC;                                 // Return ADC value
}

int main(void) {
  ADC_Init();

  uint16_t adc_value1, adc_value2;

  while (1)  {
    adc_value1 = ADC_ReadChannel(0);  // Read ADC value from channel 0
    adc_value2 = ADC_ReadChannel(1);  // Read ADC value from channel 1

    float voltage1 = (adc_value1 * 5000.0) / 1023.0;  // Calculate voltage in millivolts for channel 0
    float voltage2 = (adc_value2 * 5000.0) / 1023.0;  // Calculate voltage in millivolts for channel 1

    // Print ADC values and voltages to serial monitor or display
    // You can use UART or any other communication method to send the values
  }
}
Код даже работает :lol: .
(Добавил вывод на ЖК-дисплей (Arduino/MiniCore).

Изображение
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 7080
Зарегистрирован: Пт ноя 11, 2016 05:48:09
Откуда: Сердце Пармы

Сообщение Ivanoff-iv »

1) скачай из сундука и установи кодевиженавр
2) запусти мастер настроек
3) установи галочки в нужных местах
4) забери рабочий код
Для тех, кто не учил магию мир полон физики :)
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
Друг Кота
Аватара пользователя
Сообщения: 5861
Зарегистрирован: Ср ноя 11, 2009 17:19:30
Откуда: Воронеж

Сообщение >TEHb< »

Предделитель побольше поставь, меньше шумов будет и отпадёт необходимость в таком большом буфере. А ещё при переключении каналов есть такой прикол, что УВХ-то там одно и каналы могут друг на друга влиять. Если этот эффект будет заметен, то можно первое преобразование запускать вхолостую, а потом серию измерительных.
Усложнять просто. Упрощать сложно.
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 7080
Зарегистрирован: Пт ноя 11, 2016 05:48:09
Откуда: Сердце Пармы

Сообщение Ivanoff-iv »

не, он шумы во благо использует - для повышения разрядности оцифровки.
а вот про холостой первый замер - инфа стоящая!
Для тех, кто не учил магию мир полон физики :)
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
Встал на лапы
Сообщения: 121
Зарегистрирован: Пт мар 19, 2021 08:58:45

Сообщение kote52 »

[uquote=">TEHb<",url="/forum/viewtopic.php?p=4471202#p4471202"]Предделитель побольше поставь, меньше шумов будет и отпадёт необходимость в таком большом буфере. А ещё при переключении каналов есть такой прикол, что УВХ-то там одно и каналы могут друг на друга влиять. Если этот эффект будет заметен, то можно первое преобразование запускать вхолостую, а потом серию измерительных.[/uquote]
Как это сделать если ты по очередно замеры делаешь, adc0 потом adc1 потом adc2... По два замера, и откидывать первый? Тогда в двое время увеличится на все замеры.
Друг Кота
Аватара пользователя
Сообщения: 7080
Зарегистрирован: Пт ноя 11, 2016 05:48:09
Откуда: Сердце Пармы

Сообщение Ivanoff-iv »

так ТС 250 делает... ну будет делать 251...

Добавлено after 1 minute 1 second:
зато задержку между переключением входа и измерением можно будет убрать...
Для тех, кто не учил магию мир полон физики :)
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
Модератор
Аватара пользователя
Сообщения: 19091
Зарегистрирован: Сб авг 14, 2010 15:05:51
Откуда: г. Озерск, Челябинская обл.

Сообщение Starichok51 »

задержку между переключением входа и измерением нет смысла делать, так как само переключение входа еще не означает, что вход подключился к УВХ.
к УВХ вход подключается непосредственно в начале измерения.
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Встал на лапы
Сообщения: 121
Зарегистрирован: Пт мар 19, 2021 08:58:45

Сообщение kote52 »

[uquote="Ivanoff-iv",url="/forum/viewtopic.php?p=4471243#p4471243"]так ТС 250 делает... ну будет делать 251...[/uquote]
Если так тож сделаю для эксперимента
Друг Кота
Аватара пользователя
Сообщения: 5861
Зарегистрирован: Ср ноя 11, 2009 17:19:30
Откуда: Воронеж

Сообщение >TEHb< »

kote52 писал(а):если ты по очередно замеры делаешь, adc0 потом adc1 потом adc2
101 замер ацп0, первый выкинуть, потом 101 замер ацп1 и так далее. Разве есть необходимость обязательно перемежать каналы? Я так на аттини25 повышающий преобразователь сделал, где один канал измерял резистор для управления, а второй как раз и отвечал за напряжение. 1 холостой и 4 рабочих замера на каждый канал. Вполне себе отрабатывало всё.

Добавлено after 1 minute 54 seconds:
Точнее, количество рабочих замеров задаётся программно, но для моих целей оказалось оптимально именно 4. Но их может быть сколько нужно.

Добавлено after 1 hour 52 minutes 47 seconds:
Ага, вот нашёл кусочек кода. Это для тини25, так что регистры и значения в них могут быть другими. Но принцип тот же. Функция получает количество измерений, а возвращает сумму всех всех измерений.

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

uint16_t getsup(uint8_t count)           //измерение напряжения питания
{
uint16_t result=0;
ADMUX=0b00001100;       //питание как опорное, выравнивание вправо, измеряется 1,1 Вольт

for(uint8_t i=0; i<ADCFLUSHCOUNT; i++) {
    ADCSRA|=0b01000000;         //запуск для обновления УВХ
    while(ADCSRA&0b01000000){};
    };

for(uint8_t i=0; i<count;i++){
    ADCSRA|=0b01000000;         //запуск перобразования
    while(ADCSRA&0b01000000){}; //ожидание окончания преобразования
    result+=ADCW;
    };
return result;
};
Усложнять просто. Упрощать сложно.
Контактная информация:
Нашел транзистор. Понюхал.
Сообщения: 167
Зарегистрирован: Вс мар 27, 2022 09:38:17

Сообщение aleksey chilov »

Вот как сделал я.
Возможно не по правилам и не так круто но работает 2 канала одновременно.
Толь почемуто на делителе ( 128 ) это гдето 65 кГц.
При других настрорйках не работает. И мне кажется если так делать то больше 2 каналов
не опросить.


ISR(ADC_vect) // прерывание от АЦП
{
adc_counter++;
adc_buffer+=ADC;

if(adc_counter == 250)
{
ADCSRA&=~(1<<ADSC);// запрет adc
adc_shanel++;
if(adc_shanel > 1)adc_shanel = 0;

if(adc_shanel == 0)
{
ADMUX = 0x5;
ADMUX|=(1<<REFS0);
}
if(adc_shanel == 1)
{
ADMUX = 0x4;
ADMUX|=(1<<REFS0);
}
ADCSRA|=(1<<ADSC);// запуск adc
}
switch(adc_shanel)
{
case 0:
ADC_motor_PID(&adc_counter, &adc_buffer, &MOT_adc);// мотор
ADCSRA|=(1<<ADSC);
break;

case 1:
ADC_battery(&adc_counter, &adc_buffer, &BATT_adc); // аккум
ADCSRA|=(1<<ADSC);
break;
}
}

Изображение
Ответить

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