[pic12f675] Этап 7. Работа с АЦП. Цифровой вольтметр

Дмитрий Филатов
Рассмотрим принцип работы с аналого-цифровым преобразователем (АЦП) микроконтроллера pic12f675. Предложенную схему вольтметра не рекомендуется использовать в боевых условиях. Её следует расценивать как учебную модель для изучения принципов работы АЦП этого МК.

Информация
Компиляция исходного кода прошивки и настройки программатора аналогичны первому этапу.
Даташит на микроконтроллер pic12f675 на русском языке

Радиокомпоненты и приборы
Аналогичны четвёртому этапу.

Электрическая принципиальная схема
От предыдущей отличается только наличием щупа напряжения на GP4. Измеряемый элемент своим положительным полюсом должен подключаться к GP4, а отрицательным - к общей земле схемы.
Исходный код прошивки на языке C
Файл pic.c, компилятор SDCC

#include <pic12f675.h>

#define RS_SHCP GPIO0 //Сдвоенные сигналы: тактовый и тип данных
#define DS GPIO1
#define E_STCP GPIO5 //Сдвоенные сигналы: стробирующий и защёлка

//Функция задержки, в мкс. Исходит из того, что один такт этого МК при 4 МГц выполняется за 200 нс, согласно даташиту.
void __delay_us(unsigned char t){
while(t--){
//Ассемблерная вставка: 5 холостых тактов
__asm
NOP
NOP
NOP
NOP
NOP
__endasm;
}
}

//Функция задержки в мс
void __delay_ms(unsigned char m){
while(m--){
__delay_us(1000);
}
}

//Функция отправки байта в ЖКИ через сдвиговый регистр
void lcd_write(unsigned char c, unsigned char sig_rs){
int i;
E_STCP = 0; //Здесь Е нас не интересует, может быть любым
for(i=7;i>=0;i--){
DS = (c >> i) & 1;
RS_SHCP = 0; //Здесь важна SHCP, RS может быть любым
RS_SHCP = 1; //-//-
}
E_STCP = 1; //Защёлкнули данные и выставили высокий уровень стробирующего сигнала. Интересуют оба.
RS_SHCP = sig_rs; //Теперь можем пренебрегать SH. Выставили нужный уровень сигнала RS
__delay_us(200); //Теперь имеем сигналы DS (DB7-DB0), для создания которых отработали SH и ST, и сигнал RS
E_STCP = 0; //Начинаем выполнять операцию записи в ЖКИ, STCP не интересует
}

//Инициализация ЖКИ
void lcd_init(){
__delay_ms(15);

lcd_write(0b00111100, 0); //Шина 8 бит, 2 строки, 5х8 точек - Function Set
__delay_ms(40);

lcd_write(0b00000001, 0); //Очистка экрана - Clear Display
__delay_ms(2);

lcd_write(0b00000110, 0); //Инкремент - Entry Mode Set
__delay_ms(40);

lcd_write(0b00001100, 0); //Включили дисплей и курсор, мигание курсора отключили - Display ON/OFF Control
__delay_ms(40);

lcd_write(0b10000000, 0); //Очистили дисплей - Set DDRAM Address
__delay_ms(2);
}

main(){
OSCCAL = 0x80; //Установить внутренний осциллятор на среднюю частоту
TRISIO = 0b11011100; //Установить GPIO0, GPIO1, GPIO5 на выход, остальные - на вход. GPIO4/AN3/#3 - вход АЦП
GPIO = 0b00000000; //На всех GPIO установить логический ноль
CMCON = 0b00000111; //Отключить компаратор
ADCON0 = 0b10001100; //Правое выр., опорн. Vdd, канал 3, ожидание, модуль выключен
ANSEL = 0b00011000; //Предделитель /8, AN3

lcd_init();

while(1){
unsigned int x;
__delay_ms(2);
ADON = 1; //Включили модуль
__delay_ms(15); //Заряжается Chold
GO_DONE = 1; //Начало преобразования
while(GO_DONE); //Конец преобразования
//Читаем результат. Аналогично функции read_ADC в некоторых других средах, вроде MPLAB
//Для правого выравнивания получим 10-битный результат, для левого выравнивания - 16-битный
x = (ADRESH<<8) + ADRESL;
ADON = 0;
x = (x * 49)/10;
lcd_write(0b00000001, 0); //Очистили дисплей
lcd_write((x/1000)+'0', 1);
//lcd_write(((x/100)%10)+'0', 1);
//lcd_write(((x/10)%10)+'0', 1);
//lcd_write((x%10)+'0', 1);
__delay_ms(10);
}
}

Необходимые пояснения
10-битный результат аналого-цифрового преобразования содержится в двух 8-битных регистрах: ADRESH (старшие биты результата) и ADRESL (младшие биты результата). Подробнее см. п. 7.1.6 даташита. 210 = 1024 уровня квантования, в качестве опорного выбрано напряжение питания, которое в нашем случае (при использовании собранного ранее источника стабильного напряжения) составляет +5.0 В. Следовательно, 10-битный диапазон 0...1023 (при правом выравнивании) соответствует диапазону напряжений 0...5 В. Для пересчёта 10-битного значения в десятичный результат необходимо выполнить следующие вычисления: V = (x / 1023) * 5, где x - результат аналого-цифрового преобразования. Однако, такая математическая операция требует иных мощностей процессора, чем у данного МК. Проводим несложные математические преобразования: V = x * (5 / 1023), то есть V = x * 4.88 * 10-3. Однако, и такое уравнение не под силу нашему МК, поэтому считать будем не в вольтах, а в милливольтах (умножение на 4.88). Кроме того, чтобы избавиться от ресурсоёмких расчётов чисел с плавающей точкой, вместо умножения на 4.88 будем умножать на 49 и делить на 10.

Для отображения чисел на дисплее воспользуемся созданной ранее функцией. Чтобы конвертировать число unsigned int в символ unsigned char, необходимо прибавить к числу символ нуля (x + '0') или цифру 48 (x + 48). Это связано с тем, что коды цифр в таблице ASCII численно равны коду нуля (48) плюс значение цифры (код 49 - единица, код 50 - двойка etc).

Чтобы выводить по одному символу, используем операцию взятия остатка от деления на 10 (%10). Впрочем, в приведённом примере размер прошивки при выводе на экран всех четырёх цифр значения напряжения, в мВ, превышает объём программной памяти МК, поэтому я закомментировал три строки, и на дисплей выводится только первая цифра (фактически, мантисса, выражающая значение в вольтах).

В приведённой схеме нет никаких делителей напряжения, не стоит никаких фильтрующих стабилитронов, поэтому измерять напряжение выше опорного (выше 5 вольт) этим вольтметром нельзя.
2013-12-02