Переключение каналов АЦП для одновременного получения информации с датчиков

Тема в разделе "Arduino & Shields", создана пользователем Борис144, 14 сен 2016.

  1. Борис144

    Борис144 Нуб

    Доброго времени. Друзья, возник вопрос по одновременному считыванию сигнала с аналоговых портов при управлении регистрами. Есть код для опроса одного канала:
    Код (C++):
    int analogValue = 0; // значение аналогового сигнала
    void setup()
    {
    Serial.begin(9600);
    DDRC = 0b00000000; // режиме порта С "INTPUT"
    DIDR0=0b00111101; //выключаем разряды порта С, кроме ADC1
    PORTC=0b00000010; на ADC1 поключаем подтягивающий резистор
    ADMUX =0b01000001; //  канал ADC1, используем внутреннее опорное напр.= 5В
    ADCSRA = 0xAF; // включаем АЦП, разрешаем прерывания, делитель = 128
    ADCSRB = 0x40; // включаем АЦП, каналы MUX, режим скользящей выборки
    bitWrite(ADCSRA, 6, 1); // Запускаем преобразование установкой бита 6 (=ADSC) в ADCSRA
    sei(); // устанавливаем флаг прерывания
    }
    void loop()
    {

    }
    /*** Процедура обработки прерывания АЦП ***/
    ISR(ADC_vect)
    {
      analogValue = ADCL; // сохраняем младший байт результата АЦП
    analogValue += ADCH << 8; // сохраняем старший байт АЦП
    Serial.println(analogValue);// выводим значение в порт
    }
    Проблема в том, что затрудняюсь добавить опрос еще одного канала. Вопрос: как прописать:
    - после окончания преобразования на первом канале АЦП, вывести значение преобразования в сериал-порт (это в коде прописано);
    - начать преобразование на втором канале;
    - после окончания преобразования на втором канале АЦП, вывести значение преобразования в сериал-порт.
    - начать цикл заново.

    Т.е. заставить мультиплексор переключаться между разрядами порта.
     
  2. Airbus

    Airbus Радиохулиган Модератор

    Вопрос: для чего DDRC = 0b00000000; // режиме порта С "INTPUT"-он и так там по умолчанию
    Зачем DIDR0=0b00111101; //выключаем разряды порта С, кроме ADC1 если вы хотите заюзать 2 канала АЦП и вообще зачем выключать?Пусть будут-вдруг вам захочется 3-4 канала сделать.
    Зачем опрос сделан из прерывания?Не проще ли в void loop неспеша и методично опрашивать оба датчика по-очереди и данные выводить в USART или куда там Вам ещё надо?Да и зачем вообще вся эта Канитель
    Код (C++):
    DDRC = 0b00000000; // режиме порта С "INTPUT"
    DIDR0=0b00111101; //выключаем разряды порта С, кроме ADC1
    PORTC=0b00000010; на ADC1 поключаем подтягивающий резистор
    ADMUX =0b01000001; //  канал ADC1, используем внутреннее опорное напр.= 5В
    ADCSRA = 0xAF; // включаем АЦП, разрешаем прерывания, делитель = 128
    ADCSRB = 0x40; // включаем АЦП, каналы MUX, режим скользящей выборки
    bitWrite(ADCSRA, 6, 1); // Запускаем преобразование установкой бита 6 (=ADSC) в ADCSRA
    sei(); // устанавливаем флаг прерывания
    }
    void loop()
    {

    }
    /*** Процедура обработки прерывания АЦП ***/
    ISR(ADC_vect)
    {
      analogValue = ADCL; // сохраняем младший байт результата АЦП
    analogValue += ADCH << 8; // сохраняем старший байт АЦП
    Serial.println(analogValue);// выводим значение в порт
    }
    если можно использовать так
    Val1 = analogRead(A2); // Читаем со второй ноги аналогово входа напряжение падающей волны
    Val2 = analogRead(A1); // Читаем с первой ноги аналогово входа напряжение отраженной волны
    Цэж Ардуино!
     
    Последнее редактирование: 14 сен 2016
  3. Борис144

    Борис144 Нуб

    Пока всех знаний о всех тонкостях управления выводами регистрами я не имею, поэтому такие вот ошибки возникают. Но вопрос остался: как грамотно прописать управление мультиплексором при считывании информации с нескольких каналов? Тот скетч привел в качестве примера считывания с одного канала.
     
  4. Onkel

    Onkel Гуру

    а у вас ваш код работает? У меня сомнения, поскольку прерывание ISR(ADC_vect) уже задействовано в ардуиновских функциях. А по сути вопроса - меняете в теле в регистре ADMUX младшие биты, они и отвечают за номер мультиплексируемого входа.
     
  5. Борис144

    Борис144 Нуб

    да, код работает. а смена входов мультиплексора, как я понимаю, должна в происходить в void loop?
     
  6. Onkel

    Onkel Гуру

    В лупе или может /если используете/ в прерываниях по таймерам.
     
  7. ostrov

    ostrov Гуру

    Посмотрите в сторону аппаратного решения. Такой АЦП снимает показания с ноги наааамного быстрее, можно сказать что со всех ног одновременно.
     
  8. Alex19

    Alex19 Гуру

    Вот один из вариантов реализации ADC для Arduino на AVR (поддерживает Atmega328, Atmega32u4, Atmega2560).
    Сам код - https://github.com/SimpleRov/AVR/tree/master/ADC.

    Принцип работы.
    1. Инициализация ADC - void AdcInit(), тут все понятно из кода.
    2. Смена входов мультиплексора - void SetAdcPin(uint8_t adcInput).
    Параметр adcInput номер входа Arduino, чтобы переключиться на входа Arduino - A0, передаем 0 и т.д.
    В начале происходит проверка, выбран новый вход Arduino или аналогичный предыдущему чтению порта.
    Если да, смена входа мультиплексора с небольшой задержкой для стабилизации значений, если нет, переходим к следующему шагу - запуск преобразования ADC - StartConversionAdc().
    2.а. В настройках инициализация ADC, мы настроили прерывание по завершению преобразования.
    3. Проверяем закончено преобразование и готовы ли данные - uint8_t AdcReadyToRead().
    4. Получаем значение - uint16_t GetAdcValue().

    И так по кругу, пункт 1, первый порт 2, 2a, 3, 4, другой порт 2, 2a, 3, 4 и т.д. Вот пример работы. Функция аналогичная analogRead - AnalogPinRead(uint8_t pin, uint16_t* adcValue).
    Код (C++):
    static uint8_t AnalogPinRead(uint8_t pin, uint16_t* adcValue)
    {
      SetAdcPin(pin);

      if (AdcReadyToRead())
      {
        *adcValue = GetAdcValue();

        return 1;
      }
      return 0;
    }
    2 канала по очереди, просто вызываем loop.
    Код (C++):
    void AdcSensorsRead()
    {
      static uint8_t stepAdcSelect = 0;
      static uint16_t analogPinValue = 0;
      if (!stepAdcSelect)
      {
        if (AnalogPinRead(0, &analogPinValue))
        {
          DEBUG_PRINT(F("Value A0 - "));
          DEBUG_PRINTLN(analogPinValue);
     
          stepAdcSelect = 1;
        }
      }

      if (stepAdcSelect == 1)
      {
        if (AnalogPinRead(1, &analogPinValue))
        {
          DEBUG_PRINT(F("Value A1 - "));
          DEBUG_PRINTLN(analogPinValue);

          stepAdcSelect = 0;
        }
      }
    }
    UPD.
    Он же без библиотеки. - http://forum.amperka.ru/threads/Простые-примеры-adc-1-wire.6438/.
     
    Последнее редактирование: 15 сен 2016
    InterestedStudent и Борис144 нравится это.
  9. Борис144

    Борис144 Нуб

    Благодарю вас друзья, за помощь. Alex19, отдельное спасибо, ваша информация будет полезна.
     
  10. Alex19

    Alex19 Гуру

    Не за что, тут все помогают по мере сил (времени, знаний).
    Удачи в изучении!
     
  11. Интересный код, спасибо, позаимствовал его и обернул в класс
    Код (C++):
    // Библиотека работы с ADC
    #include "ADC.h"

    // Переменные для работы с ADC

    // Переменная класса внутреннего АЦП
    InternalAdc internalAdc;

    // /Переменные для работы с ADC

    void setup()
    {
      // put your setup code here, to run once:
      Serial.begin(115200);

      // 1. Инициализация ADC - void AdcInit(), тут все понятно из кода.
      internalAdc.AdcInit();
    }

    void loop() {
      // put your main code here, to run repeatedly:
      AdcSensorsRead();
    }

    // И так по кругу, пункт 1, первый порт 2, 2a, 3, 4, другой порт 2, 2a, 3, 4 и т.д. Вот пример работы. Функция аналогичная analogRead - AnalogPinRead(uint8_t pin, uint16_t* adcValue).
    uint8_t AnalogPinRead(uint8_t pin, uint16_t* adcValue)
    {
      // 2. Смена входов мультиплексора - void SetAdcPin(uint8_t adcInput).
      // Параметр adcInput номер входа Arduino, чтобы переключиться на входа Arduino - A0, передаем 0 и т.д.
      // В начале происходит проверка, выбран новый вход Arduino или аналогичный предыдущему чтению порта.
      // Если да, смена входа мультиплексора с небольшой задержкой для стабилизации значений, если нет, переходим к следующему шагу - запуск преобразования ADC - StartConversionAdc().
      // 2.а. В настройках инициализация ADC, мы настроили прерывание по завершению преобразования.
      internalAdc.SetAdcPin(pin);

      // 3. Проверяем закончено преобразование и готовы ли данные - uint8_t AdcReadyToRead().
      if (internalAdc.AdcReadyToRead())
      {
        // 4. Получаем значение - uint16_t GetAdcValue().
        *adcValue = internalAdc.GetAdcValue();

        return 1;
      }
      return 0;
    }

    // 2 канала по очереди
    void AdcSensorsRead()
    {
      static uint8_t stepAdcSelect = 0;
      static uint16_t analogPinValue = 0;
      if (!stepAdcSelect)
      {
        if (AnalogPinRead(2, &analogPinValue))
        {
          Serial.print(F("Value A2 - "));
          Serial.println(analogPinValue);
          stepAdcSelect = 1;
        }
      }

      if (stepAdcSelect == 1)
      {
        if (AnalogPinRead(4, &analogPinValue))
        {
          Serial.print(F("Value A4 - "));
          Serial.println(analogPinValue);

          stepAdcSelect = 0;
        }
      }
    }
     

    Вложения:

    • ADC.cpp
      Размер файла:
      8 КБ
      Просмотров:
      131
    • ADC.h
      Размер файла:
      2 КБ
      Просмотров:
      91
    • Timer.cpp
      Размер файла:
      2 КБ
      Просмотров:
      115
    • Timer.h
      Размер файла:
      1 КБ
      Просмотров:
      95