PSoC. Глава 1. ADC

Разбор примера 1

Прежде чем рассуждать на тему, что происходило , нужно сначала разобраться с компонентами, которые использовались нами для создания прошивки. А использовался нами лишь один компонент — PWM8. PWM расшифровывается как Pulse Width Modulator, что значит Широтно-импульсный модулятор. Суть прибора в том, что он позволяет изменять широту импульсов, генерируемых микроконтроллером. Тем самым, меняя выходное напряжения для устройств, не чувствительных к частоте.
К примеру: рабочая частота микроконтроллера 1Гц (то есть, период генерации импульсов 1с), ширина импульса 0.5с, напряжение импульса 5В. 

Тогда среднее выходное напряжение за секунду равно 2.5 вольта. А получается эта величина просто: сложением подимпульсных площадей и делением их на общий промежуток времени. На всякий случай распишу подробней. Допустим, мы взяли промежуток времени в 1 секунду, из рисунка видно первый импульс длился 0.5. Умножаем 0.5с*5В (напряжение импульса) и все это делим на интервал времени. 0.5с*5В/1с = 2.5В. Если нам понадобится выходное напряжение в 3.33В, мы должны будем увеличить подимпульсную площадь до 75%. В литературе ещё часто фигурирует термин скважность. Так вот, скважность и есть отношений длительности импульса к длительности нулевого потенциала, например, для первого случая, она была 50%, для второго — 75%.

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

Clock — это рабочая частота ШИМа, любой цифровой или аналогово-цифровой блок должен работать на определённой тактовой частоте. Для этого и придуманы делители частоты и поле Clock. SysClk — это системная частота, выставленная в закладке Global Resources. Интересно заметить то, что в выпадающем списке Clock присутствуют такие поля, как Row_0_Input_0. Это значит, что тактовый генератор модуля может быть снаружи чипа и синхронизироваться они будут через шину Row_0_Input_0.

Enable — уровень логической единицы данного блока. Обычно используются два стандарта: High = 5В и Low = 3В. Сам микроконтроллер, кстати, можно так же перевести на один из режимов High или Low.

CompareOut — выход широтно-импульсного модулятора.

Может возникнуть вопрос: зачем была дана выше приведенная теория, если на практике мы это нигде не использовали? А ответ в том, что мы использовали дефолтные значения для длинны импульса и времени периода ( поля Period и PulseWidth) 0 0. При таких значениях на выходе PWM будет сплошной сигнал, равный по величине логической единице. Менять время периода и значения ширины импульса можно так же программно в режиме работы микроконтроллера, функциями PWM8_1_WritePeriod() и PWM8_1_WritePulseWidth().

ADC или АЦП

АЦП — аналого-цифровой преобразователь (или ADC Analog-to-digital converter) — это устройство, позволяющие преобразовать аналоговый сигнал в цифровой. Любые физические величины (давление, скорость, угол поворота, напряжение, ток, сила света) являются аналоговыми, и задача ADC — переводить их в цифровой сигнал. На практике же, для перевода в цифровой сигнал обычно используется величина напряжения.
Из многочисленных характеристик АЦП следует выделить три основных:

  • Разрядность — это наименьшая единица аналогового сигнала, изменение которой может зафиксировать ADC, обычно измеряется в битах.
  • Частота преобразования — количество измерений в секунду, измеряется в SPS (samples per second)
  • Рабочий диапазон — диапазон величин, в котором работает данный преобразователь.
  • Так как АЦП уже не такой простой прибор, как PWM. Придется разобрать ещё некоторые теоретические аспекты микроконтроллера и некоторые фичи самого АЦП.

    Общие характеристики микроконтроллера

    Перечень некоторых понятий и обозначений о которых нужно знать при работе c чипами PSoC( кстати и не лишь всё ниже сказанное будет верно и для микроконтроллеров AVR)

    В описаниях и на принципиальных схемах в даташитах часто фигурируют такие обозначения как Vcc, Vdd, Vss, AGND. И разница между ними порой не самая очевидная. Vcc — это напряжение питания микроконтроллера ( сс — collector-to-collector), то же самое что и Vdd, так уж исторически сложилось что одна и та же величина имеет 2 обозначения. Vss — минимальный потенциал на микроконтроллере, очень часто бывает, что эта величина эквивалентна AGND. Буква «A» в аббревиатуре AGND указывает на что, это artificial граунд или искусственная земля. Стоит упомянуть про такое напряжение на схеме, которое обычно называется как BandGap. BandGrap — это опорное напряжения. Опорное напряжение означает то что оно остаётся постоянным не зависимо от напряжения питания МК, температуры и других внешних показателей. Vref — опорное напряжения отдельно рассматриваемого модуля. Очень долго я не мог врубиться, что такое Rail-to-Rail. А попадалась мне эта фраза в контекстах типа: «Данный модуль может работать в режиме Rail-to-Rail». Так вот Rail-to-Rail означает то, что элемент может работать на всем размахе напряжений от Vcc до AGND.

    Пример 2. Voltage Measuring

    Задача: Измерить напряжение на потенциометре, зашитом в отладочную плату и вывести значение на экран.

    Вот тут уже будет по интересней. Как обычно запускаем дизайнер, создаем проект. Идем в User Modules -> Misc Data -> LCD и левой кнопкой перетягиваем его на микроконтроллер. LCD очень полезный и простой модуль, да к тому же ещё и не занимающий место на цифровых блока. Увидеть его можно в закладке Workspace Explorer. Из настоек ему нужно выбрать лишь LCDPort = Port_2. Теперь заходим в файл main.c, напомню, что он лежит в Workspace Explorer -> [Имя проекта] -> Source Files -> main.c. И добавляем в функцию main() следующий код.

    LCD_Start();
    LCD_Position(0,0);
    LCD_PrCString(«Measured Voltage»);

    Компилируем код, прошиваем микроконтроллер. Если всё было правильно сделано, получим на экране строку которая была прописана выше. Более простого управления экраном, и придумать не могли. И это радует. Теперь дело осталось за ADC. Выбираем User Modules -> Legacy -> ADCINC12 и выкидываем его на контроллер. Может возникнуть вопрос «Почему мы не выбрали просто ADCINC, а ADCINC12 да и ещё в придачу из папки Legacy? Причина кроется в сложности инициализации модулей. ADCINC более сложный и гибкий модуль, который требует более тщательной и доскональной настройки. В папке Legacy, что значит унаследованные, уже храниться модуль который менее гибкий, зато более простой в реализации. Далее не думая заходим в папку, amplifers и выкидываем на схему PGA(Programmable Gain Amplifier). Программируемый операционный усилитель. Он нужен не слишь для усиления, сколько для повышения входного сопротивления. Что бы величина текущего тока в цепи не влияла на точность измерения.
    Выставляем настройки PWM как на скриншоте.

  • Gain — коэффициент усиления.
  • Input — вход прибора.
  • Reference — опорное напряжение.
  • Настройки ADC выставляем так:

  • TMR Clock — тактовая частота таймера.
  • Input — вход прибора (подключен к PGA).
  • CNT Clock — частота счетчика.
  • Последнее, что понадобиться нам для нормальной работы, это глобальный ресурс называемый Ref Mux. Ref Mux — это рабочий диапазон напряжений аналоговых блоков. Если мы выставим значение (Vdd/2)+/-(Vdd/2) то получим полную разбежку напряжений от 0 до 5В. Но в этом есть один определённый минус. Так как за опорное напряжение принимается напряжение питания. Если будет плавать напряжение Vdd, это повлияет на правдивость результатов. В таком случае нам и пригодился бы BandGap. Но пока не будем париться, и выставляем (Vdd/2)+/-(Vdd/2).
    Для перепроверки скину скрин получившейся коммутации аналоговых блоков.

    Переходим в main.c и добавляем в функцию main() следующий код:

    PGA_Start(PGA_HIGHPOWER); //запуск PGA
    ADCINC12_Start(ADCINC12_HIGHPOWER); //запуск АЦП
    ADCINC12_GetSamples(0); //установка АЦП на песперерывную работу
    M8C_EnableGInt ; // Uncomment this line to enable Global Interrupts
    while(1) // главный цикл прошивки
    {
    if (ADCINC12_fIsDataAvailable() != 0) //проверка на данных в ADC
    {
    result = ADCINC12_iGetData() + 2048;
    ADCINC12_ClearFlag();
    LCD_Position(1,0); //установка позиции для вывода
    LCD_PrHexInt(result); //вывод результата в хексе
    }
    }

    К данным функции iGetData() прибавляется 2048 для того что перевести данные в беззнаковый эквиваленте (если интересно смотреть google «c++ знаковые и беззнаковые переменные»).

    Соединяем потенциометр с платой следующим образом.

    Прошиваем микроконтроллер и оцениваем результат работы.

    Осталось лишь перевести хексы в напряжение и проверить корректность данных тестером. Добиваться этой цели будем чисто опытным путём. Облегчим себе ситуацию, и примем нулевой потенциал за 0x0000 значение на выходе ADC. Затем запускаем нашу уже написанную программу, выкручиваем потенциометр на максимум и смотрим получившиеся значения. У меня, например, получилось 0х0FEC. Затем берём тестер и измеряем реальное напряжение на потенциометре. У меня получилось 4.78В. Теперь делим 4.78/0х0FEC (у кого проблемы с системами исчисления, советую подкачаться) и получаем шаг квантования, то есть единицу напряжения, которой соответствует одно значение выхода АЦП. У меня получилась 0.0011727183513248 вот такое вот число. Теперь просто результат с АЦП перемножаем на эту величину и выводим на экран. Для этого добавим переменные в глобальное поле видимости (это всё то что вне функции main()).

    int iResult;
    double dResult;
    const double scaleFactor = 0.0011727183513248;

    На каждом круге итерации будем перемножать наши данные.

    dResult = iResult * scaleFactor;

    Вроде бы всё шло хорошо. Но есть один нюанс. API нашего LCD поддерживает вывод на экран лишь HEX’ов или строк ASCII. Что бы решить эту проблему нам надо разобраться, как перевести Double в строку. Для этого подключим заголовочный файл stdlib.h.

    #include «stdlib.h»

    В котором определена такая функция как ftoa(). Входной параметр которой переменная double а выходной — указатель на строку с результатом. Что бы ей пользоваться понадобиться ещё и добавить указатель на строку к нашим переменным. Финальная версия кода получилась следующая.

    #include // part specific constants and macros
    #include «PSoCAPI.h» // PSoC API definitions for all User Modules
    #include «stdlib.h»

    int iResult;
    double dResult;
    const double scaleFactor = 0.0011727183513248;
    char* buf;
    void main(void)
    {
    LCD_Start();
    LCD_Position(0,0);
    LCD_PrCString(«Measured Voltage»);

    PGA_Start(PGA_HIGHPOWER); //запуск PGA
    ADCINC12_Start(ADCINC12_HIGHPOWER); //запуск АЦП
    ADCINC12_GetSamples(0); //установка АЦП на беспрерывную работу
    M8C_EnableGInt ; // Uncomment this line to enable Global Interrupts
    while(1) // главный цикл прошивки
    {
    if (ADCINC12_fIsDataAvailable() != 0) //проверка на данных в ADC
    {
    iResult = ADCINC12_iGetData() +2048;
    ADCINC12_ClearFlag();
    dResult = iResult * scaleFactor;
    LCD_Position(1,0); //установка позиции для вывода
    buf = ftoa(dResult,0);
    LCD_PrString(buf);
    }
    }
    // Insert your main routine code here.
    }

    Собираем проект. Прошиваем микроконтроллер и смотрим результат, балуясь с тестером и потенциометром на плате.

    Прикрепленные файлы:

    Добавить комментарий

    Ваш адрес email не будет опубликован.