Обсуждаем контроллеры компании Atmel.
Ответить

АЦП-Atmega 8

Чт авг 31, 2023 16:56:12

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

Вопрос такой. МК ( Atmega8 ) необходимо
измерять 2 напряжения одновременно,как переключатся
между каналами? Как грамотно построить программу?
Один канал делал как 2 не пойму. Подскажите пожалуйста.
Спасибо.

Re: АЦП-Atmega 8

Чт авг 31, 2023 17:11:50

// создаём переменную куда быдем записывать результат измерения
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;

Re: АЦП-Atmega 8

Чт авг 31, 2023 17:54:21

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

#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 тогда всё сыпется...

Re: АЦП-Atmega 8

Чт авг 31, 2023 18:27:09

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

переключение каналов осуществляется в регистре 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:

Re: АЦП-Atmega 8

Чт авг 31, 2023 18:53:56

Ну непрерывное потому что он у меня меряет потоянно и кождое прерывание по окончаню измерения складывается переменную uint_32, и так 250 раз. Потом результат высчитывается по формуле. Точность поражает просто.
С мультиметром один в один показания. Для такого МК я считаю это офигенно!
Пробовал другие способы ну они что-то не зашли...
То показания прыгаю то не точно...
Этот способ понравился поэтому его и использую.
Только мне нужно измерять разные напряжения. 2 канала нужны.
Хотя бы сделать сначала одно измерение потом другое...
Ну буду что-то придумывать.

Re: АЦП-Atmega 8

Чт авг 31, 2023 19:13:27

Переходи на stm32f103 хотя бы. Сам мастерю себе зу для акб, его парный режим АЦП меня больше радует чем АЦП меги.

Re: АЦП-Atmega 8

Чт авг 31, 2023 19:21:57

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

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

// создаём переменную куда быдем записывать результат измерения
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 в режиме непрерывного преобразования не будет ждать пока мы переключим каналы))
точность показаний может пойти на смарку))

хотя... если бы мы писали программу на Ассемблере... мы бы могли всё рассчитать с точность до такта процессора))
тогда другое дело.
но на Си гарантий нет что всё точно будет работать... это уже как компилятор Си решит))
:)

Re: АЦП-Atmega 8

Чт авг 31, 2023 19:29:55

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

Добавлено after 4 minutes 58 seconds:
Ну вот так и получается если я в прерывании начинаю чё-то там манипулировать каналами у меня протеус начинает подвисать! Он не может понять что от него хотят...
А Ассемблер это для людей "Индиго". Это мне недоступно!

Re: АЦП-Atmega 8

Чт авг 31, 2023 20:03:16

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

тут без Ассемблера никуда...
:roll:

это что... я человек "Индиго" ? ))
ничё се... каждый день узнаю про себя что-то новое))
:tea:

Re: АЦП-Atmega 8

Чт авг 31, 2023 20:05:10

я вот так считаю три канала меге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 раз.

Re: АЦП-Atmega 8

Чт авг 31, 2023 20:06:23

В шутку я попросил 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).

Изображение

Re: АЦП-Atmega 8

Пт сен 01, 2023 09:40:21

1) скачай из сундука и установи кодевиженавр
2) запусти мастер настроек
3) установи галочки в нужных местах
4) забери рабочий код

Re: АЦП-Atmega 8

Пт сен 01, 2023 11:31:20

Предделитель побольше поставь, меньше шумов будет и отпадёт необходимость в таком большом буфере. А ещё при переключении каналов есть такой прикол, что УВХ-то там одно и каналы могут друг на друга влиять. Если этот эффект будет заметен, то можно первое преобразование запускать вхолостую, а потом серию измерительных.

Re: АЦП-Atmega 8

Пт сен 01, 2023 12:09:19

не, он шумы во благо использует - для повышения разрядности оцифровки.
а вот про холостой первый замер - инфа стоящая!

Re: АЦП-Atmega 8

Пт сен 01, 2023 12:21:58

Предделитель побольше поставь, меньше шумов будет и отпадёт необходимость в таком большом буфере. А ещё при переключении каналов есть такой прикол, что УВХ-то там одно и каналы могут друг на друга влиять. Если этот эффект будет заметен, то можно первое преобразование запускать вхолостую, а потом серию измерительных.

Как это сделать если ты по очередно замеры делаешь, adc0 потом adc1 потом adc2... По два замера, и откидывать первый? Тогда в двое время увеличится на все замеры.

Re: АЦП-Atmega 8

Пт сен 01, 2023 12:30:01

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

Добавлено after 1 minute 1 second:
зато задержку между переключением входа и измерением можно будет убрать...

Re: АЦП-Atmega 8

Пт сен 01, 2023 12:43:07

задержку между переключением входа и измерением нет смысла делать, так как само переключение входа еще не означает, что вход подключился к УВХ.
к УВХ вход подключается непосредственно в начале измерения.

Re: АЦП-Atmega 8

Пт сен 01, 2023 12:46:59

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

Если так тож сделаю для эксперимента

Re: АЦП-Atmega 8

Пт сен 01, 2023 14:45:17

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;
};

Re: АЦП-Atmega 8

Вс сен 03, 2023 08:49:25

Вот как сделал я.
Возможно не по правилам и не так круто но работает 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;
}
}

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