Программирование AVR Пример подключения АЦП MCP3202 к микроконтроллеру AVR Sun, October 06 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

Пример подключения АЦП MCP3202 к микроконтроллеру AVR Печать
Добавил(а) microsin   

Рассматривается код программного SPI микроконтроллера AVR и подключение через него внешнего 12-битного ADC (АЦП) MCP3202.

MCP3202 CIP_DIP8 MCP3202-CISN-SOIC8

Аппаратно подключение довольно простое - тип интерфейса SPI, используется 4 сигнала CS, DI, DO и CLK (см. таблицу).

MCP3202.png

 Цоколевка АЦП MCP3202 (аналог ADC0832)

Сигнал MCP3202
Порт AVR
Описание
CS, вход PB5, выход Выборка для чипа MCP3202, активный уровень - лог. 0
DI, вход PB7, выход Последовательные данные команды для MCP3202
DO, выход PC7, вход Последовательные данные результата АЦП MCP3202
CLK, вход PB6, выход Такты для бит сигналов DI и DO MCP3202

У АЦП два мультиплексируемых входа CH0 и CH1, которые могут быть выбраны программно. В качестве эталонного напряжения (reference voltage) для АЦП MCP3202 служит напряжение питания VDD/VREF.

Используемый протокол обмена данными максимально прост. Сначала по шине DI идут 4 бита команды, потом по шине DO выдвигаются из АЦП 12 бит данных (каждый бит команды DI и каждый бит данных DO тактируется фронтом сигнала CLK). Данные передаются в порядке, когда самый старший бит идет первым (MSBF, Most Significant Bit First).

MCP3202-SPI-protocol.png

Так как сигналы DI и DO активны в разное время, то для экономии портов они теоретически могут быть запараллелены. Однако в этом случае необходимо очень аккуратно манипулировать сигналами CS и CLK - чтобы не было паразитного зацикливания с выхода DO на вход DI. Биты данных на DO и DI считываются по нарастанию (фронту) сигнала CLK.

tCYC - полное время цикла аналого-цифрового преобразования.
tCSH - минимальное время нахождения сигнала выборки в неактивном (лог. 1) состоянии (500 нс).
tSUCS - минимальное время между спадом сигнала выборки и первым фронтом нарастания сигнала тактов (100 нс).
tSAMPLE - время, за которое делается выборка и запоминание уровня напряжения на аналоговом входе АЦП (CH0 или CH1).
tCONV - время аналого-цифрового преобразования.
tDATA - в течение этого времени выключаются внутренние узлы АЦП.
START - стартовый бит команды, всегда лог. 1
SGL/DIFF - бит, задающий режим работы входов CH0 и CH1. Если лог. 1, то входы одиночные (недифференциальные), если лог. 0, то входы дифференциальные.
ODD/SIGN - бит, задающий номер выбранного входа CH0 или CH1 (когда вход недифференциальный), либо полярность дифференциальных входом (когда вход дифференциальный). Если в недифференциальном режиме значение этого бита равно 0, то выбран как аналоговый вход CH0, если 1, то выбран CH1.
MSBF - бит, задающий порядок передачи бит данных. В самом простейшем случае этот бит должен быть равен 1, что означает - самый старший бит будет идти первым, а самый младший - последним.
HI-Z - высокоимпедансное состояние выхода данных DO (выход чипа MCP3202 отключен).
Don't Care - значение входных данных DI не имеют значения.
Null Bit - пустой нулевой, ничего не значащий бит данных.
B11..B0 - биты данных АЦП (результат аналого-цифрового преобразования).

Для реализации обмена данными использовался программно формируемый интерфейс SPI. За основу взят код для подключения MCP3202 к микроконтроллеру MCS51 (см. [2]). Код испытывался на чипе AVR USB AT90USB162, но он будет работать на любом микроконтроллере семейства AVR компании Atmel, и может быть портирован на другие микроконтроллеры.

[MCP3202.h]

/* Подпрограмма чтения ADC MCP3202 фирмы Microchip. 
Идея взята тут: http://www.sixca.com/micro/mcs51/adc12bit/ */ 
#include "MCP3202.h"
 
//----------------------------------------------------------
// Чтение аналоговых данных из ADC MCP3202 через программный
// SPI. Подпрограмма не требует предварительной 
// инициализации портов для работы с ножками SPI.
// Режим MCP3202: недифференциальный (Single end, 2 канала),
// старший бит идет первым (MSB first).
// Входной параметр channel задает канал: 0 канал с ножки 2,
// 1 канал с ножки 3.
//----------------------------------------------------------
unsigned int ReadMCP3202ADC (unsigned char channel)
{
   unsigned char i,k;
   unsigned int AdcResult;  // 12 бит
 
   //настройка DO как входа без pullup
   DDRC  &= ~(1<<ADC_DO);
   PORTC &= ~(1<<ADC_DO);
   //остальные ножки как выходы, у CLK начальное состояние 0,
   // у CS и DI начальное состояние 1.
   DDRB  |= ((1<<ADC_CS)|(1<<ADC_CLK)|(1<<ADC_DI));
   PORTB |= ((1<<ADC_CS)|(0<<ADC_CLK)|(1<<ADC_DI));
 
   //ADC_CS=0 активируем выборку чипа
   PORTB &= ~(1<<ADC_CS);
   k++;k++;                       // задержка около 2 мкс
 
   #define CMD_BIT_START 0x08
   #define CMD_BIT_SGL 0x04
   #define CMD_BIT_MSBF 0x01
 
   channel <<= 1;
   channel |= (CMD_BIT_START | CMD_BIT_SGL | CMD_BIT_MSBF);
   ////////////////////////////////////////////////////
   // передаем 4 бита команды, старший бит идет первым
   for(i=0; i< 4;i++) 
   {
      PORTB = ((channel & 0x08) != 0) ?
              PORTB |  (1<<ADC_DI) : 
              PORTB & ~(1<<ADC_DI);
      channel <<= 1;
      PORTB |= (1 << ADC_CLK);    //ADC_CLK=1
      k++;k++;                    // задержка около 2 мкс
      PORTB &= ~(1 << ADC_CLK);   //ADC_CLK=0
   }
 
   k++;k++;                       // задержка около 2 мкс
   PORTB |= (1 << ADC_CLK);       //ADC_CLK=1
   k++;k++;                       // задержка около 2 мкс
   PORTB &= ~(1 << ADC_CLK);      //ADC_CLK=0
   k++;k++;                       // задержка около 2 мкс
 
   ////////////////////////////////////////////////////
   // чтение 12 бит результата ADC, старший бит первый
   AdcResult=0;
   for(i=0;i<12;i++)
   {
      PORTB |= (1 << ADC_CLK);    //ADC_CLK=1
      k++;k++;                    // задержка около 2 мкс
      AdcResult<<=1;
      if (PINC & (1 << ADC_DO))
         AdcResult |= 1;
      PORTB &= ~(1 << ADC_CLK);   //ADC_CLK=0
      k++;k++;                    // задержка около 2 мкс
   }
   PORTB |= ((1 << ADC_CS)|(0 << ADC_CLK)|(1 << ADC_DI));
   return(AdcResult);
}

При подключении АЦП к измеряемым цепям нужно иметь в виду, что входы CH0 и CH1 у MCP3202 имеют низкое и нелинейное сопротивление (которое меняется в зависимости от состояния процесса аналого-цифрового преобразования). Поэтому напрямую подключать MCP3202 в большинстве случаев нельзя, нужен буферный усилитель, иначе результат измерения напряжения будет неверным. На рисунке показан пример простой схемы такого буферного усилителя вместе с антиалиазинговым фильтром низкой частоты на операционном усилителе MCP601.

MCP3202-buffer.PNG

[Пример использования АЦП MCP3202 - апгрейд программатора AVRISP-MkII]

После изготовления клона AVRISP-MkII (см. [1]) выяснилось, что желательно было бы иметь возможность измерять напряжение питания программируемого микроконтроллера. Так как в микроконтроллере AT90USB162 внутреннего АЦП нету, то возникла идея подключить внешний АЦП.

AVRISP-MKII-full-AVR-USB162MU-MCP3202.PNG

Резисторы R7 и R8 - защитные. R7 уравнивает разность потенциалов между выходом DOUT АЦП и входным портом микроконтроллера, когда у них различается напряжение питания - у АЦП всегда напряжение питания +5 вольт, а у портов ввода/вывода микроконтроллера напряжение может переключаться в зависимости от положения перемычки SJ1 (либо 3.3 вольт, либо 5 вольт). Буфера на операционном усилителе перед входом CH0 нет, так как выходное сопротивление источника питания VTARGET достаточно мало, и не требуется высокая точность измерения напряжения.

MCP3202_in_AVRISP-MkII_IMG_1287.JPG

Использование внешнего АЦП MCP3202 в firmware программатора включается на этапе компиляции, если в makefile задан макрос USE_MCP3202. Если этот макрос задан, то в подпрограмме V2ProtocolParams.c -> V2Params_UpdateParamValues напряжение VTarget считывается с АЦП MCP3202 и записывается в V2Params_GetParamFromTable(PARAM_VTARGET)->ParamValue как число в единицах десятых долей вольта.

После такой доработки в утилите программатора AVR Studio на закладке HW Settings стало правильно измеряться и отображаться напряжение питания программируемого чипа VTarget.

AVRISP-MkII-AVRStudio07.PNG

[Ссылки]

1. AVR-USB162MU: макетирование и изготовление программатора AVRISP-MKII в домашних условиях.
2. Connecting 12 bit ADC 2 channel - 8052 Code Library site:8052.com.
3. MCP3202 site:futureelectronics.com - страница краткого описания АЦП MCP3202.
4110626AVRISP-MKII-fixed-MCP3202.ZIP - исходный код и скомпилированные прошивки программатора AVRISP-MkII, доработанного подключенным АЦП MCP3202.

 

Комментарии  

 
0 #2 viktor 30.11.2015 00:34
Спасибо за статью! Не подскажите, какие номиналы резисторов R1-R4, C1,C2 на схеме с ОУ MCP601?

microsin: в этом примере схемы ФНЧ реализован фильтр второго порядка. Номиналы резисторов и конденсаторов здесь не указаны потому, что они рассчитываются по заданным исходным параметрам ФНЧ. Т. е. Вы изначально должны определиться, какая должна быть частота среза ФНЧ и крутизна и форма кривой его характеристики. Методика расчета стандартная, прогуглите ключевые слова: расчет ФНЧ на операционном усилителе.
Цитировать
 
 
+1 #1 Виталий СПб 27.06.2011 19:08
А почему вы не пользуетесь стандартом С99 для обозначения переменных? Ведь uint16_t выглядит понятней, чем unsignet int для 8-ми битной архитектуры.

В чем прикол k++; ??? Компилятор их вообще соптимизирует. Почему не использовать _NOP(); ? А еще лучше _delay_us(); ?

Почему бы PORTB тоже не продефайнить скажем как PORT_ADC?

microsin: Вы во всем правы, Виталий. Однако замечания некритичны. Вы ведь поняли, что такое unsigned int? Поняли. Компилятор понял? Наверное тоже понял. Поэтому тут чистое ИМХО - кому что больше нравится. Я например больше люблю обозначения типов еще короче: s8, s16, s32 (это типы со знаком, signed) и u8, u16, u32 (это для типов unsigned). То же самое можно сказать про PORTx - переименовать его было бы конечно красивее, однако код все равно тупо не переносится с порта B на порт C или на какой-то еще (если нужно поменять используемые ножки портов), поэтому такое скрытие деталей может сыграть злую шутку. Так что делайте как Вам самому больше нравится.

Насчет k++ : Вы опять правы, наверное лучше было применить что-то типа NOP(). Я не проверял, какой именно ассемблерный код генерит у меня компилятор, однако проверил реальную работоспособнос ть кода, и мне этого показалось достаточным.
Цитировать
 

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


Защитный код
Обновить

Top of Page