Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Вывод на ЖКИ значения напряжения
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Все остальные микроконтроллеры
andrei89
АЦП встроенный в контроллер (ADuC812) оцифровывает аналоговый сигнал, в результате имеем два регистра с результатом оцифровки - ADCDATAH и ADCDATAL. Каким образом можно преобразовать значения в этих регистрах в напряжение и выдать на дисплей? Функция вывода на дисплей строки работает. Если можно то примером кода, пожалуйста. Заранее спасибо.
Savrik
(знач_АЦП*опорное_напр)\разрядность_АЦП
Что за дисплей, контроллер? Телепаты в отпуске...
rezident
Код
unsigned int ADCresult;
ADCresult=ADCDATAH<<8;
ADCresult+=ADCDATAL;

Ну или наоборот в зависимости от требуемого порядка чтения регистров
Код
unsigned int ADCresult;
ADCresult=ADCDATAL;
ADCresult+=ADCDATAH<<8;


Затем вычисление напряжения
Код
unsigned int Voltage;
Voltage=(ADCresult*2500U)>>12;

Результат получается в миллиВольтах.
andrei89
Контроллер ADuC812, дисплей МТ16s2d (на контроллере HD77480). Собственно я так и делаю, но только почему то на дисплее значения постоянно прыгают. АЦП выполняет однократное преобразование, опорное напряжение 2.5 В, разрядность АЦП - 12.

Вот функция обработки прерывания от АЦП:



void display_voltage(void) interrupt 6 {

unsigned char lcd_buffer[6];
float ADCValue;
ADCValue = ((ADCDATAL + (ADCDATAH&0x0F))<<8);
ADCValue=ADCValue*2500/4096;
ADCValue=ADCValue/1000;
sprintf (lcd_buffer,"U=%1.3f V", ADCValue);
send_lcd(set_first_line, lcd_buffer);
}
rezident
А float-то зачем тут? blink.gif
Какой сигнал измеряете? Паузы >=300мс между выводом значений на индикатор делаете?
andrei89
Ну значение с плавающей точкой же должно быть? Паузы не делаю так как значение выводится только один раз. Измеряю постоянный сигнал с батарейки ~1.5В.
Herz
Цитата(andrei89 @ Oct 2 2010, 09:18) *
Ну значение с плавающей точкой же должно быть? Паузы не делаю так как значение выводится только один раз. Измеряю постоянный сигнал с батарейки ~1.5В.

Подсказка: точку поставите на индикаторе, не в переменной.
rezident
Цитата(andrei89 @ Oct 2 2010, 13:18) *
Ну значение с плавающей точкой же должно быть?
Это ко мне вопрос? Откуда я знаю, в каком формате вам нужно выводить laughing.gif Я бы на вашем месте избавился от вычислений во float по двум причинам. 1) Для вычислений float линкуется отдельная библиотека, которая занимает доп. объем Flash. 2) Для работы printf с числами float требуется до 1,5кБайт стековой памяти (ОЗУ). Следует это учитывать, чтобы не напороться на ошибки, связанные с наползанием стека на сегмент статических данных, - ошибки частые и очень трудно вылавливаемые. Для вывода значения напряжения в Вольтах, вам достаточно разделить число на целую и дробную часть, между которыми выводить символ точки. То бишь, например, для вывода числа в виде 1,798В, которое у вас имеется как 1798мВ, выводите
- значение целочисленного деления 1798/1000,
- символ ","
- остаток 1798-1798/1000*1000.
на экране получите то же самое, как и при выводе float-овского числа. Только все это будет быстрее и экономнее по размеру памяти.
Цитата(andrei89 @ Oct 2 2010, 13:18) *
Паузы не делаю так как значение выводится только один раз. Измеряю постоянный сигнал с батарейки ~1.5В.
Если напряжение на входе постоянное и выводите один раз, то что за "прыгающие значения"?
andrei89
Ну если смотреть после ресета, то напряжение постоянно скачет. Из - за чего не могу понять. Если выводить вот такую строку sprintf (lcd_buffer,"U=%u V", ADCDATAL), то значение этого регистра постоянно меняется, выводятся числа типа -16896, 18176, 12800. Абракадабра короче говоря.

Переделал в int, дробную часть пока не вывожу. Он сейчас должен хотя бы 1В показывать, а показывает 0. Отчего это может быть? Где тут ошибка?

void display_voltage(void) interrupt 6 {

unsigned char lcd_buffer[6];
int ADCValue;
ADCValue = ((ADCDATAL + (ADCDATAH&0x0F))<<8);
ADCValue=ADCValue*2500/4096;
ADCValue=(ADCValue/1000);
sprintf (lcd_buffer,"U=%i V", ADCValue);
send_lcd(set_first_line, lcd_buffer);
}
Палыч
Цитата(andrei89 @ Oct 4 2010, 13:55) *
Где тут ошибка?
Вот тут
Цитата(andrei89 @ Oct 4 2010, 13:55) *
ADCValue=ADCValue*2500/4096;
После умножения 16-ти разрядов не всегда достаточно для представления результата. Нужно записать, например, так:
ADCValue= (long)ADCValue*2500/4096;
rezident
Цитата(Палыч @ Oct 4 2010, 20:14) *
После умножения 16-ти разрядов не всегда достаточно для представления результата. Нужно записать, например, так:
ADCValue= (long)ADCValue*2500/4096;


Кстати, да. Я вот тоже недописал тип константы в примере, надо было хотя бы вот так Voltage=(ADCresult*2500UL)>>12; Или явно к типу long привести.
andrei89
Все равно выводит только 0. Может еще можно каким нибудь другим способом преобразовать значения регистров в напряжение?

А почему 16 разрядов? МК 8ми разрядный же.
Палыч
Цитата(andrei89 @ Oct 4 2010, 18:48) *
Все равно выводит только 0. Может еще можно каким нибудь другим способом преобразовать значения регистров в напряжение?
Попробуйте выводить значение регистра ADCDATA (ADCValue до его преобразования в милливольты). Какое значение выводиться?
Цитата(andrei89 @ Oct 4 2010, 18:48) *
А почему 16 разрядов? МК 8ми разрядный же.
Для int отводится два байта - 16 бит.
andrei89
Сейчас начал цифры показывать вместо нуля, но неверные - то 25, то 15, то 10, то 5.

Сейчас код такой:

void display_voltage(void) interrupt 6 {

unsigned int ADCValue;
unsigned char lcd_buffer[6];
ADCValue = (ADCDATAL + (ADCDATAH&0x0F))<<12;
ADCValue=(long)(ADCValue*2500UL/4096);
ADCValue=(ADCValue/1000);
sprintf (lcd_buffer,"U=%i V", ADCValue);
send_lcd(set_first_line, lcd_buffer);
}
rezident
Цитата(andrei89 @ Oct 4 2010, 20:56) *
ADCValue = (ADCDATAL + (ADCDATAH&0x0F))<<12;
Почему <<12? Должно быть <<8 . Причем скобки не там стоят.
Код
ADCValue = ADCDATAL + (ADCDATAH&0x0F)<<8;

Цитата(andrei89 @ Oct 4 2010, 20:56) *
ADCValue=(long)(ADCValue*2500UL/4096);

Скобки не там расставлены.
Должно быть
Код
ADCValue=((long)ADCValue*2500UL)>>12;
andrei89
Вывожу значение регистра ADCDATAL, в шестнадцатеричном виде, в результате показывает постоянно разные значения: 3400, 2а00, 2800, 2600 и тп.
Значение ADCDATAH не прыгает, 3а00. Хотя после первого ресета изменилось.

Если вот такой код загрузить, то получается абракадабра, то 12288, то -2457, 16384 и тп.

void display_voltage(void) interrupt 6 {

unsigned int ADCValue;
unsigned char lcd_buffer[6];
ADCValue = (ADCDATAL + (ADCDATAH&0x0F))<<12;
//ADCValue=(long)(ADCValue*2500UL/4096);
//ADCValue=(ADCValue/1000);
sprintf (lcd_buffer,"U=%i V", ADCValue);
send_lcd(set_first_line, lcd_buffer);
}

Цитата(rezident @ Oct 4 2010, 19:01) *
Почему <<12? Должно быть <<8 . Причем скобки не там стоят.
Код
ADCValue = ADCDATAL + (ADCDATAH&0x0F)<<8;

Ну ADCDATAH это ведь старший байт, а в старшей его тетраде хранится номер канала, поэтому как я понимаю ее нужно обнулить и передвинуть в самый конец. Или я неправильно понимаю?

Заметил что значения все эти прыгают независимо от того подключена ли батарейка к АЦП или нет smile.gif
Палыч
Наверное, вызов процедуры send_lcd(set_first_line, lcd_buffer) необходимо вынести из прерывания...



Цитата(andrei89 @ Oct 4 2010, 19:12) *
Ну ADCDATAH это ведь старший байт, а в старшей его тетраде хранится номер канала, поэтому как я понимаю ее нужно обнулить и передвинуть в самый конец. Или я неправильно понимаю?
Младшую тетраду этого регистра нужно подставить впереди 8-ми бит взятых из ADCDATAL
andrei89
Все равно не реагирует. Странные дела sad.gif

Может поможет полный код программы:

#include <ADuC812.h>
#include <stdio.h>


#define set_first_line 0x80
#define set_second_line 0xC0
unsigned char lcd_buffer[6];

sbit RS=P2^2;
sbit RW=P2^1;
sbit En=P2^0;
unsigned char i,j;

void delay(void) {
for(i=0; i<5; i++) {
for(j=0;j<50;j++) {}
}
}

void send_lcd_4bit(unsigned char send_data) {
P2=(P2 | 0xF0);
send_data=(send_data | 0x0F);
P2=(P2 & send_data);
En=1;
delay();
En=0;
}

void send_lcd_cmd(unsigned char send_data) {
RS=0;
send_lcd_4bit(send_data);
send_data=(send_data << 4);
send_lcd_4bit(send_data);
delay();
}

void send_lcd_data(unsigned char send_data) {
RS=1;
send_lcd_4bit(send_data);
send_data=(send_data << 4);
send_lcd_4bit(send_data);
delay();
}

void send_lcd_symbol(unsigned char adress, unsigned char send_data) {
send_lcd_cmd(adress);
send_lcd_data(send_data);
}

void LCD_Init(void) {
delay();

send_lcd_4bit(0x30);
delay();
send_lcd_4bit(0x30);
delay();
send_lcd_4bit(0x30);
delay();
send_lcd_4bit(0x20);
delay();
send_lcd_cmd(0x28);
delay();
send_lcd_cmd(0x0C);
delay();
send_lcd_cmd(0x06);
delay();
send_lcd_cmd(0x01);
}

void send_lcd(unsigned char line,unsigned char *txt) {
unsigned int i=0;
while(txt[i]!=NULL) {
send_lcd_symbol(line+i, txt[i]);
i++;
}
}

void display_voltage(void) interrupt 6 {

unsigned int ADCValue;
ADCValue = (ADCDATAL + (ADCDATAH&0x0F))<<8;
ADCValue=((long)ADCValue*2500UL)>>12;
ADCValue=(ADCValue/1000);
sprintf (lcd_buffer,"U=%i V", ADCValue);

}

void ADC_Init(void) {
EA=0;
EADC=0;
SCONV=0;
ADCCON1=0x7C;
ADCCON2=0x03;
EA=0;
EADC=0;
}


void main(void) {

unsigned char *txt = "Freq. :";
P2=0x00;
delay();
En=0;
RW=0;
LCD_Init();
delay();
send_lcd(set_second_line, txt);
delay();
ADC_Init();
EA=1;
EADC=1;
SCONV=1;
while(1) {
send_lcd(set_first_line, lcd_buffer);
delay();
}

}
Палыч
Цитата(andrei89 @ Oct 4 2010, 19:27) *
ADCValue = (ADCDATAL + (ADCDATAH&0x0F))<<8;
Спешите куда-то... Вы внимательно скобочки расставляйте...
andrei89
Исправил, но толку это не принесло(
ADCValue = ADCDATAL + (ADCDATAH&0x0F)<<8;
ut1wpr
Цитата(andrei89 @ Oct 4 2010, 18:12) *
Вывожу значение регистра ADCDATAL, в шестнадцатеричном виде, в результате показывает постоянно разные значения: 3400, 2а00, 2800, 2600 и тп.
Байтовые значения выводите пожалуйста в однобайтовом формате. Не путайте сами себя. Добейтесь правильной работы АЦП, а потом переходите к нормализации (арифметике).
Цитата
Ну ADCDATAH это ведь старший байт, а в старшей его тетраде хранится номер канала
Откуда сведения? Цитату, пожалуйста. Я не знаю АЦП, которые в регистрах данных возвращают номер канала. Сообразите, наконец, как из двух отдельных байтовых величин получить целое двухбайтовое. Вес старшего байта - 256. Младшего - 1. Не путайтесь в сдвигах и масках. Это от лукавого. Старший байт умножьте на 256 и прибавьте младший. Получите двухбайтовое целое.
Может так понятнее будет:
Код
unsigned int measure = ADCDATAL + ADCDATAH*256;

Цитата
Заметил что значения все эти прыгают независимо от того подключена ли батарейка к АЦП или нет smile.gif
Это отдельная тема. Сначала с получением результат и его отображением в hex-форматах разберитесь.
rezident
Цитата(ut1wpr @ Oct 4 2010, 23:06) *
Откуда сведения? Цитату, пожалуйста. Я не знаю АЦП, которые в регистрах данных возвращают номер канала.
Из datasheet. Вопрос-то про АЦП, встроенное в МК (ADuC812). Как же вы советуете, если не в теме? cranky.gif
andrei89
Так, процесс кажется пошел. Вывожу значения ADCDATAH и ADCDATAL в 16-м виде, показал он мне А38, что соответствует напряжению 1.59. Ровно столько же показывает мультиметр. Интересно что если сменить канал, и произвести преобразование то все разряды этих регистров установлены в 1. (FFF)
rezident
Цитата(andrei89 @ Oct 5 2010, 00:31) *
Вывожу значения ADCDATAH и ADCDATAL в 16-м виде, показал он мне А38
Так может порядок считывания регистров тоже имеет значение?
Код
ADCValue = (ADCDATAH&0x0F)<<8;
ADCValue |= ADCDATAL;
andrei89
Цитата(rezident @ Oct 4 2010, 23:03) *
Так может порядок считывания регистров тоже имеет значение?
Код
ADCValue = (ADCDATAH&0x0F)<<8;
ADCValue |= ADCDATAL;

Работает smile.gif

Сделал пока тип флоат, выводит в десятичном виде smile.gif Заработало все smile.gif Потом переделаю в инт с точкой и дробной частью.
Вот только странно что если отключить батарейку от входа АЦП, оно показывает опорное напряжение 2.5В, и то не всегда. После очередного ресета показывало и 2.12В. С чем это может быть связано?
rezident
Цитата(andrei89 @ Oct 5 2010, 01:25) *
С чем это может быть связано?
Утечки на входе АЦП, м.б. за счет наводки на высокоомный вход АЦП.
andrei89
И еще заметил такую штуку - чем дольше держать ресет, тем выше окажется напряжение которое измеряет АЦП. Если нажать и сразу отпустить то все как надо smile.gif Аномалия какая то smile.gif
MrYuran
Цитата(andrei89 @ Oct 4 2010, 23:51) *
Аномалия какая то smile.gif

Два основных правила программиста:
1) Чудес не бывает
2) Дерьмо случается
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.