§ 48. 10-ти разрядный АЦП на основе PIC16F877
|
Иванов Дмитрий, Апрель 2007
|
Статья обновлена 26 Мая 2014
|
|
Эта статья я думаю, будет немного поинтереснее. Здесь мы сделаем АЦП на основе МК PIC16F877. АЦП этот будет 10-ти разрыдным, что очень неплохо ибо позволяет получать хорошую точность преобразования. При диапозоне входных напряжений АЦП 0-5 В получаем шаг квантования 5/(210) = 0.00488 В
|
Начнем, пожалуй со схемы, чтоб был более понятным код. В МК PIC16F877 есть аж целых 8 линий, на котроые, при соответствующих программных настройках, можно подавать аналоговое напряжение на вход АЦП. В этом примере я использую линию RA0/AN0 (вывод 2). Диапозон рабочих АЦП этого МК составляет 0 - 5 В . Не превышайте этот порог - можно МК сжечь. Еще одним схемным изменением стало появление еще двух светодиодов - нам же надо как-то показать результат работы АЦП. Т.к. 10-ти разрыдный, то нам и нужны 10 светодиодов для отображения результата преобразования.

Для упрощения этого примера, я решил не использовать еще один источник напряжения для входа АЦП, а запитал его через переменый резистор от общей шины питания схемы. Теперь изменяя положение движка резистора можно будет изменять подаваемое на вход АЦП напряжение - от 0 до 5 В.

Теперь рассотрим код для МК, который позволяет использовать АЦП и отображает результат его работы на светодиодах. Готовый проект приложения MPLAB можно найти в файлах к этой статье. Здесь я решил показать помимо работы с модулем АЦП еще и обработку прерываний в МК. Схема функционирования программы следующая: сначала проводим инициализацию АЦП и настраиваем т.н. таймер. После этого входим в бесконечный цикл. Таймер срабатывает с определенной частотой и при каждом его срабатывании управление в МК переходит на нашу специализированную функцию, в которой мы проводим обработку результатов работы АЦП. После того как функция-обработчик прерывания от таймера завершит свою работу, управление передается обратно на тот участок, откуда мы перешли на функцию обработчик. Потом снова сработает таймер и т.д.
Рассмотрим что здесь происходит. Начнем с функции main, ведь имено с нее начнется выполнение программы. Сначала мы присваиваем значение счетчику таймера равным 0 (TMR0=0). Это нужно для того, чтобы таймер не успел сработать, пока мы проводим начальную инициализацию (до входа в бесконечный цикл). Тут необходимо сказать пару слов об этом самом таймере. Это обычный 8-ми разрядный регистр, значение которго при каждом машинном такте увеличивается на еденицу. Как только значение в нем будет равно 256, он сбрасывается и в определенном регистре устанавливается флаг, говорящий о том что произошло переполнение таймера. (вот тут то управление и перейдет на нашу функцию обработчик прерывания). Перодом срабатывания таймера можно управлять, указав через сколько прошедших машинных тактов значение регистра таймера увеличивается на 1. Ругилировать можно в пределах 1:2, 1:4, 1:8, 1:16, 1:32, ..., 1:256. Т.е. в последнем случае, чтобы регистр увеличился на еденицу должно пройти 256 машинных тактов. Тогда для срабатывания таймера потребуется 256*256 = 65536 машинных тактов. Зная длительность машинного такта можно однозначно опрделеить период срабатывания таймера в секудах. Для МК PIC один машинный такт = 4 тактам кварца. Тогда, если у нас стоит кварц на 20 МГц, длительность одного машинного такта будет равна 1/(5 МГц) = 0.2 * 10-6 сек = 0.2 мкс.
Слующей строчкой в коде мы как раз и настраиваем работу таймера. Для этого служит спецальный регистр, каждый бит в котором предназначен для определенной настройки какого-либо свойства. Полное описание этого регистра советую прочитать в описании к этому МК, которое можно было найти в 3-тьей статье этого раздела.

То числовое значение которое загружается в него (регистр) в это примере означает что таймер будет работать с предделителем 1:2 (увеличение счетчика таймера происходит после двух машинных тактов). Далее устанавливая спец. бит настройки T0IE в еденицу мы разрешаем прерывания от таймера. GIE=1 - разрешает обработку прерываний в МК глобально. Потом настраиваем порт B и D для работы на выход и помещаем в них 0 (светодиоды подключенные к ним будут погашены).
Далее проводим настройку работы АЦП. Для его настроек предназначенны два регистра ADCON1 и ADCON0. Более подробно про их содержимое расписано в описаниии МК.


На что хотел обратить Ваше внимание, так это на т.н. выравнивание результата преобразования. Результат то 10-ти разрядный, а регистры в МК PIC 8-ми разрядные. Значит нужны два регистра. Размещением в них этого результата (с какой сторны они выровнены, т.е. откуда начинаются) можно устанавливать самомтоянтельно. В этом примере я указал правое выравнивание. Это значит что 8 младших бит результата будут находиться в одном регистре а оставшиеся 2 будут находится в двух младших битах другогог регистра.
После того как все настройки сделаны, запускаем бесконечный цикл и ждем кагда сработает таймер. Как только это происходит, управление переходит на специальнцю функцию с ключевым словом interrupt. В ней мы проверяем бит T0IF, в которм указывается что произошло прерывание таймера. Если это так, то мы его сбрасываем в ноль (чтоб таймер дальше мог работать) и запускаем процесс преобразования входного аналогового напряжения в цифровой код установкой бита управления АЦП ADGO. Как только преобразование АЦП будет сделано, этот бит станет равным 0. Поэтому с помощью while мы ждем этого события. Теперь нам нужно прочесть результат преобразования. Он храниться в двух регистрах с именами ADRESL и ADRESH. В первом из них храняться 8 младших бит результата и мы сразу же их отсылаем в порт B (красные светодиоды). В ADRESH находятся 2 старших бита результата. Т.к. на схеме я хотел чтобы эти два бита отображались на выводах 2,3 порта D (чтоб все светодиды были по одну сторону от МК) то нужно содержимое этого регистра сдвинуть на два разряда влево. После этого то что сдвинули записываем в порт D.
#include <pic.h>
__CONFIG(0x03F72);
unsigned char b=0;
void interrupt IntFun(void)
{
if(T0IF)
{
T0IF=0;
ADGO=1;
while(ADGO==1)
{
}
PORTB=ADRESL;
b=ADRESH;
PORTD=b<<2;
GIE=1;
}
}
void main(void)
{
TMR0=0;
OPTION= 0b11010000; //таймер 1:2
T0IE=1; //разрешаем прерывания от таймера
GIE=1;
TRISB=0;
PORTB=0;
TRISD=0;
PORTD=0;
/*
правое выравнивание
RA0 - аналог, все остальные из PORTA - цифра
*/
ADCON1=0b10001110;
/*
тактовый сигнал Fosc/32
канал 0 (RA0)
GO-/DONE = 0
АЦП включен
*/
ADCON0=0b10000001;
while(1==1)
{
}
}
Внешне полученная схема может выглядеть например вот так.

© Иванов Дмитрий
Апрель 2007
http://www.kernelchip.ru