Программирование Аналого-Цифрового Преобразователя
Для программирования АЦП необходимо произвести конфигурирование некоторых специальных регистров.
| | |
| $06 ($26) | ADCSRA | ADC Control and Status Register | |
| $07 ($27) | ADMUX | ADC Multiplexer Selection Register | |
Регистр ADCSRA (в ATMega163 - ADCSR):
| Бит | Флаг | Значение |
| 7 | ADEN | ADC Enable: Запись логической единицы в этот бит разрешает
аналого-цифровое преобразование. Если бит сброшен, то АЦП отключается. При сбросе этого флага во время
преобразования останавливает этот процесс. |
| 6 | ADSC | ADC Start Conversion: В режиме Single Conversion этот флаг
должен быть установлен в единицу для запуска процесса преобразования. В режиме Free Running этот флаг должен
быть установлен в единицу для запуска первого преобразования. Если установка флага ADSC происходит во время или
после разрешения АЦП (ADEN), то перед запрашиваемым преобразованием происходит дополнительное (extended) преобразование,
во время которого происходит инициализация АЦП.
Значение флага ADSC считывается в течение всего процесса преобразования. После того, как преобразование завершено,
флаг сбрасывается в 0. Если перед собственно преобразованием происходит дополнительное (extended) преобразование,
значение ADSC остается равным единице до тех пор, пока не завершится настоящее преобразование. Запись нуля во
флаг (во время преобразования) не оказывает никакого влияния на процесс.
|
| 5 | ADFR | ADC Free Running Select: |
| 4 | ADIF | ADC Interrupt Flag: |
| 3 | ADIE | ADC Interrupt Enable: |
| 2 | ADPS2 | ADC Prescaler Select Bits: эти биты определяют
коэффициент деления между частотой XTAL и входной частотой АЦП.
| ADSP2 | ADSP1 | ADSP0 | Коэффициент деления |
| 0 | 0 | 0 | 2 |
| 0 | 0 | 1 | 2 |
| 0 | 1 | 0 | 4 |
| 0 | 1 | 1 | 8 |
| 1 | 0 | 0 | 16 |
| 1 | 0 | 1 | 32 |
| 1 | 1 | 0 | 64 |
| 1 | 1 | 1 | 128 |
|
| 1 | ADPS1 |
| 0 | ADPS0 |
Регистр ADMUX:
| Бит | Флаг | Значение |
| 7 | RERFS1 |
| REFS1 | REFS0 | Выбор опорного напряжения |
| 0 | 0 | AREF, внутр. Vref отключено |
| 0 | 1 | AVCC с внешним конденсатором на выводе AREF |
| 1 | 0 | Зарезервировано |
| 1 | 1 | Внутреннее опорное напряжение 2,56В с внешним конденсатором на выводе AREF |
|
| 6 | RERFS0 | |
| 5 | ADLAR | |
| 4 | MUX4 | |
| 3 | MUX3 | |
| 2 | MUX2 | |
| 1 | MUX1 | |
| 0 | MUX0 | |
Код, найденный на форуме 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