Skip to main content
LibreTexts - Ukrayinska

27.1: Аналоговий запис ()

  • Page ID
    29504
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)

    Хоча деякі більш просунуті мікроконтролери містять цифровий аналоговий перетворювач (ЦАП) для отримання безперервно змінного аналогового вихідного сигналу, більшість цього не роблять. ATMega 328P на Uno є одним з тих, хто цього не робить.Як тоді ми можемо змусити контролер виробляти аналогові сигнали? Одним із методів є використання цілого банку портових штифтів, скажімо, усіх восьми бітів порту B, і подавати їх безпосередньо на зовнішній ЦАП з паралельним входом. Потім записуємо кожен байт в порт з потрібною швидкістю. ЦАП (разом з фільтром реконструкції низьких частот) реконструює ці дані в плавний сигнал. Другий метод спирається на широтно-імпульсну модуляцію. Ця схема застосовується на платах Uno і багатьох інших Arduino. Зверніть увагу, що не всі навантаження будуть справно працювати з простим ШІМ-сигналом. Деякі навантаження можуть вимагати подальшої обробки ШІМ-сигналу (наприклад, фільтрації).

    Ключем до розуміння широтно-імпульсної модуляції є врахування «області під кривою». Припустимо, у нас є імпульсний сигнал на один вольт, який триває протягом п'яти секунд, знижується протягом п'яти секунд, а потім повторюється. Така ж площа буде досягнута за допомогою імпульсу на п'ять вольт, який залишався високим лише одну секунду з десяти. Тобто середнє значення будь-якого імпульсу протягом десяти секунд становить піввольта. Аналогічно, якщо цей імпульс п'яти вольт залишався високим протягом двох секунд, то протягом десятисекундного періоду середнє значення становило б один вольт. Іншими словами; чим більше робочий цикл, тим вище середній рівень напруги. Якби ми прискорили це і імпульси були мілісекунди або мікросекунди в ширину і повторювалися знову і знову, ми могли б низькочастотний фільтр ланцюга імпульсів і досягти плавно змінюються результатів. Для деяких навантажень нам навіть не доведеться фільтрувати результат. Приклади можуть включати керування резистивним нагрівальним елементом для контролю температури або керування світлодіодом для регулювання яскравості.

    Uno досягає ШІМ за рахунок використання трьох внутрішніх лічильників. Кожен з них має A і B компонент, тому можна генерувати шість ШІМ-сигналів. Система Arduino попередньо налаштовує їх для вас. Ви можете сказати, які штифти здатні PWM, просто подивившись на плату. Штифти ШІМ матимуть тильду (~) поруч із номером штифта Arduino. Функція AnalogWrite () забирає велику роботу з ваших плечей. Ось он-лайн документація повторюється для вашої зручності:

    Малюнок\(\PageIndex{1}\): analogWrite docs

    Аналоговий запис (1)

    Опис

    Записує аналогове значення (ШІМ-хвиля) на штифт. Може використовуватися для освітлення світлодіода при різній яскравості або приводу двигуна на різних швидкостях. Після виклику AnalogWrite () контактний сигнал буде генерувати стійку квадратну хвилю зазначеного робочого циклу до наступного виклику AnalogWrite () (або виклику DigitalRead () або DigitalWrite () на тому ж контакті). Частота ШІМ-сигналу становить приблизно 490 Гц.

    На більшості плат Arduino (тих, у яких ATMega168 або ATMega328) ця функція працює на контактах 3, 5, 6, 9, 10 та 11. На Arduino Mega він працює на шпильках від 2 до 13. Старі плати Arduino з ATMega8 підтримують лише аналоговий запис () на шпильках 9, 10 та 11.

    Arduino Due підтримує AnalogWrite () на штифтах від 2 до 13, плюс контакти DAC0 і DAC1. На відміну від PWM контактів, DAC0 і DAC1 є цифровими аналоговими перетворювачами і діють як справжні аналогові виходи.

    Вам не потрібно викликати pinMode (), щоб встановити контакт як вихід перед викликом analogWrite ().

    Функція AnalogWrite не має нічого спільного з аналоговими штифтами або функцією AnalogRead.

    Синтаксис

    Аналоговий запис (штифт, значення)

    Параметри

    pin: шпилька для запису.

    значення: робочий цикл: від 0 (завжди вимкнено) до 255 (завжди увімкнено).

    Повертає

    нічого

    Примітки та відомі проблеми

    Виходи ШІМ, що генеруються на контактах 5 і 6, матимуть вищі, ніж очікувалося, робочі цикли. Це пов'язано з взаємодією з функціями millis () та delay (), які мають однаковий внутрішній таймер, який використовується для генерації цих виходів ШІМ. Це буде помічено здебільшого при налаштуваннях низького робочого циклу (наприклад, 0 - 10) і може призвести до того, що значення 0 не повністю вимкне вихід на штифтах 5 та 6.

    Суть полягає в тому, що ви викликаєте analogWrite () з лише контактним контактом і значенням робочого циклу, яке коливається від 0 (вимкнено) до 255 (повністю увімкнено). Ось і все, що є в ньому. Але чому діапазон від 0 до 255 замість більш очевидних від 0 до 100 відсотків? Давайте подивимося на код, як зазвичай трохи очищений для вашої зручності (оригінальний код можна знайти в файлі wiring_analog.c):

    void analogWrite(uint8_t pin, int val)
    {
        pinMode(pin, OUTPUT);
    
        if (val == 0)
        {
            digitalWrite(pin, LOW);
        }
        else
        {
            if (val == 255)
            {
                digitalWrite(pin, HIGH);
            }
            else
            {
                switch( digitalPinToTimer(pin) )
                {
                    case TIMER0A:
                        // connect pwm to pin on timer 0, channel A
                        sbi(TCCR0A, COM0A1);
                        OCR0A = val; // set pwm duty
                        break;
    
                    case TIMER0B:
                        // connect pwm to pin on timer 0, channel B
                        sbi(TCCR0A, COM0B1);
                        OCR0B = val; // set pwm duty
                        break;
    
                    // and so on for TIMER1A through TIMER2B
                    
                    case NOT_ON_TIMER:
                    default:
                        if (val < 128)
                            digitalWrite(pin, LOW);
                        else
                            digitalWrite(pin, HIGH);
                }
            }
        }
    }
    

    Перше, що ми бачимо, це виклик pinMode (), щоб гарантувати, що штифт налаштований для виведення. Хоча можна просто вимагати від програмістів, щоб ця функція викликала себе перед кожним використанням, безпечніше розмістити її всередині функції. Зверніть увагу, що якби ви використовували цей штифт лише в цьому режимі, ви могли б зробити цей виклик самостійно лише один раз як частина вашої процедури налаштування та створити власну версію AnalogWrite (), видаливши цю частину (і, можливо, деякі інші розділи), щоб голити час виконання.

    pinMode(pin, OUTPUT);
    

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

    if (val == 0)
    {
        digitalWrite(pin, LOW);
    }
    else
    {
        if (val == 255)
        {
            digitalWrite(pin, HIGH);
        }
    

    У цей момент має бути введено значення від 1 до 254. Штифт перекладається на таймер, і через оператор перемикача/випадку активується відповідний реєстр управління таймером, а значення робочого циклу завантажується у відповідний регістр лічильника. Ці таймери становлять вісім бітових одиниць. Тому вони можуть містити значення від 0 до 255. Ось чому значення «робочого циклу» працює від 0 до 255 замість 0 до 100 відсотків. (Технічно, таймер один - це 16-бітна одиниця, але використовується тут з вісьмома.)

    switch( digitalPinToTimer(pin) )
    {
        case TIMER0A:
            // connect pwm to pin on timer 0, channel A
            sbi(TCCR0A, COM0A1);
            OCR0A = val; // set pwm duty
            break;
    
        case TIMER0B:
            // connect pwm to pin on timer 0, channel B
            sbi(TCCR0A, COM0B1);
            OCR0B = val; // set pwm duty
            break;
    
        // and so on for TIMER1A through TIMER2B
    

    Нарешті, якщо AnalogWrite () використовується на контакті, який не налаштований за допомогою таймера, функція робить все можливе, а саме виклик DigitalWrite (). Будь-яке значення нижче половини (128) призведе до низького рівня, а будь-яке значення на половину або вище призведе до високого:

    case NOT_ON_TIMER:
    default:
        if (val < 128)
            digitalWrite(pin, LOW);
        else
            digitalWrite(pin, HIGH);
    

    Варто повторити, що на платах Arduino, які містять внутрішній ЦАП, такий як Due, ця функція буде виробляти справжні аналогові вихідні сигнали (код для цього не відображається).


    1. http://arduino.cc/en/Reference/AnalogWrite