|
Вывод на ЖКИ значения напряжения |
|
|
|
Oct 1 2010, 13:03
|
Группа: Участник
Сообщений: 12
Регистрация: 1-10-10
Пользователь №: 59 865

|
АЦП встроенный в контроллер (ADuC812) оцифровывает аналоговый сигнал, в результате имеем два регистра с результатом оцифровки - ADCDATAH и ADCDATAL. Каким образом можно преобразовать значения в этих регистрах в напряжение и выдать на дисплей? Функция вывода на дисплей строки работает. Если можно то примером кода, пожалуйста. Заранее спасибо.
Сообщение отредактировал andrei89 - Oct 1 2010, 13:03
|
|
|
|
2 страниц
1 2 >
|
 |
Ответов
(1 - 27)
|
Oct 1 2010, 14:28
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Код unsigned int ADCresult; ADCresult=ADCDATAH<<8; ADCresult+=ADCDATAL; Ну или наоборот в зависимости от требуемого порядка чтения регистров Код unsigned int ADCresult; ADCresult=ADCDATAL; ADCresult+=ADCDATAH<<8; Затем вычисление напряжения Код unsigned int Voltage; Voltage=(ADCresult*2500U)>>12; Результат получается в миллиВольтах.
|
|
|
|
|
Oct 1 2010, 14:32
|
Группа: Участник
Сообщений: 12
Регистрация: 1-10-10
Пользователь №: 59 865

|
Контроллер 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); }
|
|
|
|
|
Oct 2 2010, 07:18
|
Группа: Участник
Сообщений: 12
Регистрация: 1-10-10
Пользователь №: 59 865

|
Ну значение с плавающей точкой же должно быть? Паузы не делаю так как значение выводится только один раз. Измеряю постоянный сигнал с батарейки ~1.5В.
|
|
|
|
|
Oct 2 2010, 19:33
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(andrei89 @ Oct 2 2010, 13:18)  Ну значение с плавающей точкой же должно быть? Это ко мне вопрос? Откуда я знаю, в каком формате вам нужно выводить  Я бы на вашем месте избавился от вычислений во 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В. Если напряжение на входе постоянное и выводите один раз, то что за "прыгающие значения"?
|
|
|
|
|
Oct 4 2010, 09:55
|
Группа: Участник
Сообщений: 12
Регистрация: 1-10-10
Пользователь №: 59 865

|
Ну если смотреть после ресета, то напряжение постоянно скачет. Из - за чего не могу понять. Если выводить вот такую строку 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); }
|
|
|
|
|
Oct 4 2010, 14:14
|

Гуру
     
Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954

|
Цитата(andrei89 @ Oct 4 2010, 13:55)  Где тут ошибка? Вот тут Цитата(andrei89 @ Oct 4 2010, 13:55)  ADCValue=ADCValue*2500/4096; После умножения 16-ти разрядов не всегда достаточно для представления результата. Нужно записать, например, так: ADCValue= (long)ADCValue*2500/4096;
|
|
|
|
|
Oct 4 2010, 14:48
|
Группа: Участник
Сообщений: 12
Регистрация: 1-10-10
Пользователь №: 59 865

|
Все равно выводит только 0. Может еще можно каким нибудь другим способом преобразовать значения регистров в напряжение?
А почему 16 разрядов? МК 8ми разрядный же.
|
|
|
|
|
Oct 4 2010, 14:56
|
Группа: Участник
Сообщений: 12
Регистрация: 1-10-10
Пользователь №: 59 865

|
Сейчас начал цифры показывать вместо нуля, но неверные - то 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); }
|
|
|
|
|
Oct 4 2010, 15:01
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(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;
|
|
|
|
|
Oct 4 2010, 15:12
|
Группа: Участник
Сообщений: 12
Регистрация: 1-10-10
Пользователь №: 59 865

|
Вывожу значение регистра 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 это ведь старший байт, а в старшей его тетраде хранится номер канала, поэтому как я понимаю ее нужно обнулить и передвинуть в самый конец. Или я неправильно понимаю? Заметил что значения все эти прыгают независимо от того подключена ли батарейка к АЦП или нет
|
|
|
|
|
Oct 4 2010, 15:27
|
Группа: Участник
Сообщений: 12
Регистрация: 1-10-10
Пользователь №: 59 865

|
Все равно не реагирует. Странные дела  Может поможет полный код программы: #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(); } }
|
|
|
|
|
Oct 4 2010, 15:45
|
Группа: Участник
Сообщений: 12
Регистрация: 1-10-10
Пользователь №: 59 865

|
Исправил, но толку это не принесло( ADCValue = ADCDATAL + (ADCDATAH&0x0F)<<8;
|
|
|
|
|
Oct 4 2010, 17:06
|

Частый гость
 
Группа: Участник
Сообщений: 98
Регистрация: 20-06-05
Пользователь №: 6 150

|
Цитата(andrei89 @ Oct 4 2010, 18:12)  Вывожу значение регистра ADCDATAL, в шестнадцатеричном виде, в результате показывает постоянно разные значения: 3400, 2а00, 2800, 2600 и тп. Байтовые значения выводите пожалуйста в однобайтовом формате. Не путайте сами себя. Добейтесь правильной работы АЦП, а потом переходите к нормализации (арифметике). Цитата Ну ADCDATAH это ведь старший байт, а в старшей его тетраде хранится номер канала Откуда сведения? Цитату, пожалуйста. Я не знаю АЦП, которые в регистрах данных возвращают номер канала. Сообразите, наконец, как из двух отдельных байтовых величин получить целое двухбайтовое. Вес старшего байта - 256. Младшего - 1. Не путайтесь в сдвигах и масках. Это от лукавого. Старший байт умножьте на 256 и прибавьте младший. Получите двухбайтовое целое. Может так понятнее будет: Код unsigned int measure = ADCDATAL + ADCDATAH*256; Цитата Заметил что значения все эти прыгают независимо от того подключена ли батарейка к АЦП или нет  Это отдельная тема. Сначала с получением результат и его отображением в hex-форматах разберитесь.
|
|
|
|
|
Oct 4 2010, 18:31
|
Группа: Участник
Сообщений: 12
Регистрация: 1-10-10
Пользователь №: 59 865

|
Так, процесс кажется пошел. Вывожу значения ADCDATAH и ADCDATAL в 16-м виде, показал он мне А38, что соответствует напряжению 1.59. Ровно столько же показывает мультиметр. Интересно что если сменить канал, и произвести преобразование то все разряды этих регистров установлены в 1. (FFF)
|
|
|
|
|
Oct 4 2010, 19:25
|
Группа: Участник
Сообщений: 12
Регистрация: 1-10-10
Пользователь №: 59 865

|
Цитата(rezident @ Oct 4 2010, 23:03)  Так может порядок считывания регистров тоже имеет значение? Код ADCValue = (ADCDATAH&0x0F)<<8; ADCValue |= ADCDATAL; Работает Сделал пока тип флоат, выводит в десятичном виде  Заработало все  Потом переделаю в инт с точкой и дробной частью. Вот только странно что если отключить батарейку от входа АЦП, оно показывает опорное напряжение 2.5В, и то не всегда. После очередного ресета показывало и 2.12В. С чем это может быть связано?
|
|
|
|
|
Oct 4 2010, 19:51
|
Группа: Участник
Сообщений: 12
Регистрация: 1-10-10
Пользователь №: 59 865

|
И еще заметил такую штуку - чем дольше держать ресет, тем выше окажется напряжение которое измеряет АЦП. Если нажать и сразу отпустить то все как надо  Аномалия какая то
|
|
|
|
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|