Программирование Аналого-Цифрового Преобразователя


Для программирования АЦП необходимо произвести конфигурирование некоторых специальных регистров.
$06 ($26)ADCSRAADC Control and Status Register
$07 ($27)ADMUXADC Multiplexer Selection Register
Регистр ADCSRA (в ATMega163 - ADCSR):
БитФлагЗначение
7ADENADC Enable: Запись логической единицы в этот бит разрешает аналого-цифровое преобразование. Если бит сброшен, то АЦП отключается. При сбросе этого флага во время преобразования останавливает этот процесс.
6ADSCADC Start Conversion: В режиме Single Conversion этот флаг должен быть установлен в единицу для запуска процесса преобразования. В режиме Free Running этот флаг должен быть установлен в единицу для запуска первого преобразования. Если установка флага ADSC происходит во время или после разрешения АЦП (ADEN), то перед запрашиваемым преобразованием происходит дополнительное (extended) преобразование, во время которого происходит инициализация АЦП.
Значение флага ADSC считывается в течение всего процесса преобразования. После того, как преобразование завершено, флаг сбрасывается в 0. Если перед собственно преобразованием происходит дополнительное (extended) преобразование, значение ADSC остается равным единице до тех пор, пока не завершится настоящее преобразование. Запись нуля во флаг (во время преобразования) не оказывает никакого влияния на процесс.
5ADFRADC Free Running Select:
4ADIFADC Interrupt Flag:
3ADIEADC Interrupt Enable:
2ADPS2ADC Prescaler Select Bits: эти биты определяют коэффициент деления между частотой XTAL и входной частотой АЦП.
ADSP2ADSP1ADSP0Коэффициент деления
0002
0012
0104
0118
10016
10132
11064
111128
1ADPS1
0ADPS0

Регистр ADMUX:
БитФлагЗначение
7RERFS1
REFS1REFS0Выбор опорного напряжения
00AREF, внутр. Vref отключено
01AVCC с внешним конденсатором на выводе AREF
10Зарезервировано
11Внутреннее опорное напряжение 2,56В с внешним конденсатором на выводе AREF
6RERFS0
5ADLAR
4MUX4
3MUX3
2MUX2
1MUX1
0MUX0

Код, найденный на форуме Roboternetz.de по ссылке http://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=8497&highlight=adc+atmega16:
hier ein beispiel aus dem forum, von mir zusammengewurfelt. 

#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#include <stdint.h>
#include <string.h>

#define ADCchannel_init   DDRA=0x00                 // ADC Port als Eingang deklarieren 
#define ADCinit           ADCSRA|=_BV(ADEN)         // Teilt dem Board mit das der jeweilige Port fur ADC verwendet wird 
#define ADCdisable        ADCSRA &=~_BV(ADEN)       // machs das vorherige wieder ruckganig 
#define ADCstart          ADCSRA|=_BV(ADSC)         // startet eine konvertierung auf dem gewunschten Kannal/Pin 
#define ADCfree           ADCSRA|=_BV(ADATE)        // schaltet den freilaufenden Modus ein 
#define ADCvintern        ADMUX|=_BV(REFS0)     // interne Spannungsversorgung 
#define ADCinterrupt_on   ADCSRA|=_BV(ADIE)                // ADC interrupt wird freigeschalten 
#define ADCprescaler_2    ADCSRA |=_BV(ADPS0)              // gewunschter Teilungsfaktor/Prescaler 
#define ADCprescaler_4    ADCSRA|=_BV(ADPS1) 
#define ADCprescaler_8    ADCSRA=_BV(ADPS1) | _BV(ADPS0) 
#define ADCprescaler_16   ADCSRA|=_BV(ADPS2) 
#define ADCprescaler_32   ADCSRA=_BV(ADPS2) | _BV(ADPS0) 
#define ADCprescaler_64   ADCSRA=_BV(ADPS2) | _BV(ADPS1) 
#define ADCprescaler_128  ADCSRA=_BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0) 
#define ADCprescaler_reset ADCSRA = ~_BV(ADPS2) & ~_BV(ADPS1) & ~_BV(ADPS0)        
#define ADCchannel_1                                      //gewunschter Kannal z.B bei ATmega32 PINA0 - PINA7 
#define ADCchannel_2      ADMUX|=_BV(MUX0)                // bei nicht freilaufen muss ADCchannel_x vor 
#define ADCchannel_3      ADMUX|=_BV(MUX1)                // ADCstart kommen dann kann man mit getadc() der 
#define ADCchannel_4      ADMUX= _BV(MUX1) | _BV(MUX0)    // Adcwert des gewahlten Kannals auslesen 
#define ADCchannel_5      ADMUX|=_BV(MUX2) 
#define ADCchannel_6      ADMUX= _BV(MUX2) | _BV(MUX0) 
#define ADCchannel_7      ADMUX= _BV(MUX2) | _BV(MUX1) 
#define ADCchannel_8      ADMUX= _BV(MUX2) | _BV(MUX1) | _BV(MUX0) 
#define ADCchannel_reset  ADMUX= ~_BV(MUX2) & ~_BV(MUX1) & ~_BV(MUX0) 
//==============
void uart0_init(void)
{
  UBRRL = 25;
  UBRRH = 0;
  UCSRB = 0xD8;		// RXCIE, RXCIE, RXEN, TXEN enabled
}
//==============
uint16_t getadc(void) 
{ 
   while (ADCSRA & _BV(ADSC)) {}	// Подождать, пока не будет сброшен флаг ADCSRA.ADSC
   return ADC;				// Теперь можно вернуть полученное значение
} 

int main(void) 
{ 

uint16_t x; 

  DDRA = 0x00;		// чтение 
  DDRB = 0xFF;		// вывод на светодиоды (STK500)
  DDRD = 0x02;		// RXD на PD0 и TXD на PD1
  uart0_init();

ADCinit; 
ADCprescaler_16;	// Активирование АЦП, установка коэффициента деления = 16 
while (1)		// Выполнение АЦ-преобразований в бесконечном цикле
  { 
    ADCchannel_2;	// Выбор вывода A1 в качестве прослушиваемого ADC-канала
			// На него должно подаваться подлежащее измерению напряжение       
    ADCstart;		// Запуск процесса преобразования в режиме Single Conversion    
    x=getadc();		// Чтение результатов преобразования
    UDR = x>>2;		// Отправка измеренного значения через COM-канал на ПК...
    PORTB = x>>2;	// ... и отображение на светодиодах (STK500)
  } 
}
После ввода этого кода и коммутации простейшей схемы для тестирования АЦП (переменный резистор, крайними выводами подсоединенный к массе и линии питания STK500 и с выводом бегунка, соединенным с PA1) на линейке светодиодов можно было наблюдать их свечение в комбинации, изменяющейся при повороте ручки переменного резистора. В окне программы Hyperterminal также принимались символы, ASCII-код которых менялся пропорционально напряжению, снимаемому с бегунка переменного резистора. Итак, малыми средствами удалось получить работоспособную конфигурацию!
Воодушевленный первыми успехами, достал из своих запасов датчик влажности HIH-3610, идеально подходящий для вставки в вышеописанную схему вместо переменного резистора - три ножки, крайние подключаются к "+" и "-", а средняя - выход напряжения, изменяющегося пропорционаьлно относительной влажности воздуха.
char c[3]="123";
while (1)		// Выполнение АЦ-преобразований в бесконечном цикле
  { 
    ADCchannel_2;	// Выбор вывода A1 в качестве прослушиваемого ADC-канала
			// На него должно подаваться подлежащее измерению напряжение       
    ADCstart;		// Запуск процесса преобразования в режиме Single Conversion    
    x=getadc();		// Чтение результатов преобразования

    UDR = a[0];		// Тестовый вывод в последовательный канал.
    UDR = a[1];		// Вместо ожидаемых последовательностей "123" в Hyperterminal
    UDR = a[2];		// получил что-то подобное: "1111111111113111111111131111111111311111113"
    PORTB = x>>2;	// ... и отображение на светодиодах (STK500)
  } 
Подключил датчик, дополнил программу кодами для вывода трехзначного числа, представляющего значение, снимаемое с датчика (сначала сдвиг на 2 бита вправо, чтобы отбросить 2 шумящих младших бита и таким образом перейти в диапазон 0...255, затем дважды деление по модулю 10 и заполнение результатами массива из трех символов), запустил - на экране Hyperterminal какая-то странная смесь символов, не похожая на то, что ожидалось, в то время, как на светодиодной линейке - медленно изменяющаяся комбинация светящихся светодиодов. Что-то неправильно в последовательной передаче! Попробовал выводить тестовую последовательность '123' - на экране в некоторых попытках появляются длинные последовательности единиц (10-15) с редкими вкраплениями троек, в остальных - другие символы. Мелькнула мысль, что не в порядке один из контактов, но по зрелом размышлении понял, что предпринятый мной метод передачи (отправка трех байтов в пакете) неверен - контроллер не имеет буфера и моя информация просто теряется. Необходимо после посылки каждого байта в UDR дожидаться конца его передачи! Тут надо либо прерывание конца передачи байта обрабатывать, либо какой-либо флаг (какой?) проверять.
Ладно, отложим это на завтра, а пока посмотрим, что за данные высвечиваются светодиодами. Сняв последовательность "10001011" (1 - светодиод светится, 0 - нет), получил десятичное число 139, а после умножения на 4 (компенсация сдвига на два бита вправо!) - 556. При напряжении питания, подаваемом на датчик - естественно, 5 В - получаем величину сигнала 2,715. Для моего датчика в паспорте стоит формула пересчета в относительную влажность:
H = (V-0,827)*0,0318 = (2,715-0,827)/0,0318= 59,4
Полученный результат необходимо сравнить с показаниями другого прибора, благо такой оказался под рукой и показывал 60%. Совпало! Чтож, проблема измерения влажности практически (и неожиданно просто) решена. Осталось лишь данные на ПК правильно передать...
Штудирование документации показало, что передача символа в регистр UDR должна осуществляться в момент, когда завершена передача предыдущего символа и он готов к приему следующего. Индикатором такого состояния является бит UDRE регистра UCSRA: если он установлен, то регистр UDR готов к приему нового символа, если сброшен - обработка предыдущего символа еще не завершена.


На данной странице содержится лишь информация по регистрам, используемым при управлении аналого-цифровым преобразователем. Информация по программированию последовательного канала передачи данных находится на странице avr_uart.htm