Страница 1 из 2

АЦП atmega 16

Добавлено: Ср окт 01, 2014 11:11:09
AlexP
Подскажите пожалуйста, почему АЦП у atmega 16 работает как 9 битное, причём в proteuse работает как 10 битное, а собранное устройство как 9 битное.

Спасибо.

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 11:47:39
vdavid
Подайте на вход АЦП опорное напряжение и убедитесь, что читаете число 1023. С чего Вы вообще взяли, что АЦП 9-ти битное?

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 11:48:13
uk8amk
Работает как и положено - на 10 бит.
Если вы о качестве преобразователя, то протеус моделирует идеальные приборы.
А в реальности надо:
*писать программу с учётом режимов работы АЦП
*правильно делать схему с учётом работы аналоговой части.
*правильно трассировать печатную плату с учетом аналоговой части.
*использовать цифровую фильтрацию для устранения шумов младших разрядов.

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 13:16:46
AlexP
Опорное напряжение внутреннее 2.56В, на входе АЦП 1.1 В, значение после преобразования 224, если посчитать: при 9 бит. преобразовании 2,56/512=0,005В т.е. значение одного кванта 0.005В, следовательно при напряжении на входе 1.1В получаем 1.1/0.005=240, а при 10 битном АЦП 2,56/1024=0,0025В, т.е. значение одного кванта 0.0025В, следовательно при напряжении на входе 1.1В должны получить 1.1*0.0025=440.

Отсюда я делаю вывод, что АЦП работает как 9 бит АЦП.

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 13:39:21
vdavid
AlexP писал(а): Отсюда я делаю вывод, что АЦП работает как 9 бит АЦП.
Еще раз:
1. Соедините вход АЦП с выводом AREF.
2. У Вас точно не диф. режим входов? Что записано в ADMUX?

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 13:55:08
AlexP
Вот так входы переключаются:

interrupt [ADC_INT] void adc_isr(void)
{
ADCdata=ADCW; //считываем значение отцифрованного значения
if(ADMUX==0xc0) I1=ADCdata;
if(ADMUX==0xc1) I2=ADCdata;
if(ADMUX==0xc2) U1=ADCdata;
if(ADMUX==0xc3) U2=ADCdata;
ADMUX++;
if(ADMUX==0xc4) ADMUX=0xc0;
ADCSRA=ADCSRA|0x40;
}

Инициализация:
void init_ADC()
{
ADMUX=0xC0; //0b11000000 Подключаем внутренее опор. напр. 2.56В, Подключаем ADC0 вывод как аналоговый вход
ADCSRA=0x8F; //0b10001110 Активизируем АЦП с коэф. делен. 128, разрешаем прерывание от АЦП 10 бит преобраз
}

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 13:56:33
Kavka
AlexP, а вы точно 16 бит считываете из регистров АЦП? Правильно считываете?

Код: Выделить всё

220 = 0b11011100 - 8 бит
440 = 0b110111000 - 9 бит

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 14:04:34
AlexP
ADCdata=ADCW - я так понимаю этой строчкой считывается весь 16 битный буфер с преобразованным значением. Пробовал отдельно считывать ADCdata=ADCH и ADCdata=ADCL, но там почему то нулевые значения, скорее всего я так думаю необходимо соблюдать последовательность считывания типа вначале младшие потом старшие.

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 14:06:06
Pink-Pank
ADCdata у Вас объявлен как insigned int? Или же Вы его как unsigned char?.. Если последнее, то результат будет обрезаться.

По поводу считывания - в начале младший байт считывается

P.S. И, конечно, ADCdata должен быть volatile

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 14:14:38
vdavid
AlexP, Ну тогда покажите и объявления I1,I2,U1,U2.
AlexP писал(а):Пробовал отдельно считывать ADCdata=ADCH и ADCdata=ADCL
Нужно как-то так:
ADCData=ADCL+ADCH*256;
И почему ADCData не локальная переменная? Сразу было бы видно объявление.

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 14:16:02
AlexP
int sec_1,sec_09,I1,I2,U1,U2,ADCdata;

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 14:18:53
AlexP
Может тут есть какая то загвоздка при выводе на экран?

void ekran2()
{
lcd_gotoxy(0,0);
sprintf(text,"Tok1=%.2d",I1);
strcat(text,"A");
lcd_puts(text);
lcd_gotoxy(0,1);
sprintf(text,"Tok2=%.2d",I2);
strcat(text,"A");
lcd_puts(text);
}

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 14:20:15
vdavid
volatile int I1,I2,U1,U2,ADCdata

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 14:21:21
AlexP
При чём при программировании atmega8 по такому же принципу проблем не было. Может у них какая то есть разница в программировании АЦП?

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 14:23:57
AlexP
volatile - не помогло.

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 14:24:54
Pink-Pank
скорее всего, Вы что-то не так делаете. Напряжение 1,1 Вольт чем меряли? тестером? и относительно чего? земли?

volatile должно быть. переменные в прерывании используются.

Схему и код можете выложить?

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 14:31:55
AlexP
#include <mega16.h>
#include <delay.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
//#include <math.h>
#include <1wire.h>
#include <ds1820.h>
#include <lcd.h>


#define SETBIT(ADDRESS,BIT) (ADDRESS |= (1<<BIT)) //Установить бит в регистре
#define CLEARBIT(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT)) //Очистить бит в регистре
#define CHECKBIT(ADDRESS,BIT) (ADDRESS & (1<<BIT)) //Проверить бит в регистре
#define E_1 SETBIT(PORTC,1) // E=1
#define E_0 CLEARBIT(PORTC,1) // E=0
#define RW_1 SETBIT(PORTC,1) // RW=1
#define RW_0 CLEARBIT(PORTC,1) // RW=0
#define RS_1 SETBIT(PORTC,0) // RS=1
#define RS_0 CLEARBIT(PORTC,0) // RS=0
// maximum number of DS1820 devices
// connected to the 1 Wire bus
#define MAX_DS1820 8


unsigned char byte_rx,temp,sec_flag_09,sec_flag_1,sendC,data,inc_TX,devices;

volatile int sec_1,sec_09,I1,I2,U1,U2,ADCdata;

float TMP,Tok1,Tok2,Napr1,Napr2;

float Tt[2];

char tx_buffer[160]; // USART Буфер передатчика

char rx_buffer[10]; // USART Буфер приёма

char text[16];

// DS1820 devices ROM code storage area,
// 9 bytes are used for each device
// (see the w1_search function description in the help)
unsigned char ds1820_rom_codes[MAX_DS1820][9];

#asm
.equ __w1_port=0x18 ;PORTB
.equ __w1_bit=0
#endasm

#asm
.equ __lcd_port=0x15 ;PORTC
#endasm

char *tmpr;
char *ta;
char *tu;
char *oll;
unsigned char ss[20];

interrupt [USART_RXC] void usart_rx_isr(void) //Прерывание по завершению приёма
{
data=UDR;
rx_buffer[byte_rx++]=data;
}

interrupt [USART_TXC] void usart_tx_isr(void) //Прерывание по завершению передачи байта
{
unsigned char i;
if(strlen(tx_buffer)!=sendC) UDR=tx_buffer[sendC++]; //если количество переданных символов не равно количеству символам для передачи
else for(i=0;i<30;i++)tx_buffer=0; //стираем буфер передатчика
}

interrupt [ADC_INT] void adc_isr(void)
{
ADCdata=ADCW; //считываем значение отцифрованного значения
if(ADMUX==0xc0) I1=ADCdata;
if(ADMUX==0xc1) I2=ADCdata;
if(ADMUX==0xc2) U1=ADCdata;
if(ADMUX==0xc3) U2=ADCdata;
ADMUX++;
if(ADMUX==0xc4) ADMUX=0xc0;
ADCSRA=ADCSRA|0x40;
}

interrupt [TIM0_OVF] void timer0_int(void) //прерывание по переполнению второго счётчика
{
TCNT0=1; //17.71ms
sec_1++; //увеличиваем счётчик 1 сек
sec_09++;
if(sec_1>=56)
{
sec_flag_1=1; //если прошла 1 мин 1936
sec_1=0;
}
if(sec_09>=51)
{
sec_flag_09=1; //если прошла 1 мин 1936*/
sec_09=0;
}
}

void init_ADC()
{
ADMUX=0xC0; //0b11000000 Подключаем внутренее опор. напр. 2.56В, Подключаем ADC0 вывод как аналоговый вход
//ADMUX=0xE0; //
//ADMUX=0x0; //0b01000000 Внешнее опорное напрячжение. Подключаем ADC0 вывод как аналоговый вход
ADCSRA=0x8F; //0b10001110 Активизируем АЦП с коэф. делен. 128, разрешаем прерывание от АЦП 10 бит преобраз
//ADCSRA=0x8E; //0b10001110 Активизируем АЦП с коэф. делен. 64, разрешаем прерывание от АЦП 9 бит преобр
//ADCSRA=0x8D; //0b10001100 Активизируем АЦП с коэф. делен. 32, разрешаем прерывание от АЦП
//ADCSRA=0x8A; //0b10001110 Активизируем АЦП с коэф. делен. 4, разрешаем прерывание от АЦП
//ADCSRA=0x89; //0b10001000 Активизируем АЦП с коэф. делен. 1, разрешаем прерывание от АЦП
}

void init_time0() //инициализация второго счётчика
{
TCCR0=5; //Устанавливаем предделитель на 1024
TCNT0=1; //Предзагрузка счётчика 30ms
TIFR=0; //Сбрасываем все флаги прерываний для Т/С1
TIMSK=0x01; //Устанавливаем флаг по переполнению Т/С1
GIMSK=0; //Запрет внешних прерываний
}

void init_USART()
{
UBRRH=0; //Скорость передачи 9600 бит/с
UBRRL=95; //Скорость передачи 9600 бит/с
UCSRA=0x0;
//UCSRA=0x02; //двойная скорость, только для асинхронной передачи
UCSRB=0xD8; //Активизируется передатчик, приёмник, прерывание по передаче и по приёму
UCSRC=0x86; //Устанавливаем данные 8 бит, стоповый бит 1
}

void zap_buf_str(char *b) //запись в буфер передатчика строкового выражения
{
unsigned char i;
sendC=1; //для организации передачи со второго байта
for(i=inc_TX;i<strlen(b)+1+inc_TX;i++) tx_buffer=b[i-inc_TX]; //записываем в буфер передатчика
inc_TX=i-1; //счётчик буфера передатчика
}

void zap_buf_cifr(float t) //запись в буфер передатчика значения с плавающей запятой
{
unsigned char i,k;
sendC=1; //для организации передачи со второго байта
k=0;
ftoa(t,1,ss);
if(t==TMP) //если пришёл запрос для записи в буфер температуры то
{
if(temp!=0) //если температура отрицательная то
{
tx_buffer[inc_TX]=0x2d; //запишем минус
for(i=inc_TX+1;i<strlen(ss)+2+inc_TX;i++) tx_buffer=ss[k++];
}
else for(i=inc_TX;i<strlen(ss)+1+inc_TX;i++) tx_buffer=ss[k++]; //иначе если температура положительная записываем в буфер передатчика
}
else for(i=inc_TX;i<strlen(ss)+1+inc_TX;i++) tx_buffer=ss[k++]; //иначе если пришёл запрос не для записи температуры записываем в буфер передатчика

inc_TX=i-1;
}

void zap_buf_cifr_ch(unsigned char t) // запись в буфер передатчика числа (для команд терминалу)
{
sendC=1; //для организации передачи со второго байта
tx_buffer[inc_TX]=t;
inc_TX++;
}

void ekran1()
{
if(Tt[0]>-999)
{
lcd_gotoxy(0,0);
sprintf(text,"Uli=%+.1f",Tt[0]); //+ -выводит + при положит. - - при отриц. .1f 1 знак после запятой тип float
strcat(text,"C ");
lcd_puts(text);
}
if(Tt[1]>-999)
{
lcd_gotoxy(0,1);
sprintf(text,"Dom=%+.1f",Tt[1]);
strcat(text,"C ");
lcd_puts(text);
}
lcd_gotoxy(11,0);
sprintf(text,"DT=%0d",devices);
lcd_puts(text);
}

void ekran2()
{
lcd_gotoxy(0,0);
sprintf(text,"Tok1=%.2d",I1);
strcat(text,"A");
lcd_puts(text);
lcd_gotoxy(0,1);
sprintf(text,"Tok2=%.2d",I2);
strcat(text,"A");
lcd_puts(text);
}

void ekran3()
{
lcd_gotoxy(0,0);
//sprintf(text,"Napr1=%.2f",Napr1);
sprintf(text,"Napr1=%.2d",U1);
strcat(text,"V");
lcd_puts(text);
lcd_gotoxy(0,1);
//sprintf(text,"Napr2=%.2f",Napr2);
sprintf(text,"Napr2=%.2d",U2);
strcat(text,"V");
lcd_puts(text);
}

void main(void)
{
unsigned char i,ekr,u;
float k,h;
u=0;
ekr=1;
DDRC=0xff;
SETBIT(PORTD,5);
SETBIT(PORTD,6);
init_USART(); //
init_time0();
lcd_init(16);
init_ADC();
ADCSRA=ADCSRA|0x40; //запуск преобразования
devices=w1_search(0xf0,ds1820_rom_codes); //определяем количество датчиков
#asm("sei"); //Разрешаем глобальные прерывания

while (1)
{
//goto a;
//////////////////////////////обработка кнопки нажатия ВВЕРХ//////////////////////////////////////////////////////////////////////////////
if(!(CHECKBIT(PIND,6))) //если нажали кнопку ввод (выход из работы)
{
delay_ms(70); //задержка от дребезга
m2: if(CHECKBIT(PIND,6)) //если отпустили кнопку
{
ekr++;
if(ekr==4)ekr=1;
lcd_clear();
}
else goto m2; //иначе если не отпустили кнопку
}
//////////////////////////////обработка кнопки нажатия ВНИЗ//////////////////////////////////////////////////////////////////////////////
if(!(CHECKBIT(PIND,5))) //если нажали кнопку ввод (выход из работы)
{
delay_ms(70); //задержка от дребезга
m3: if(CHECKBIT(PIND,5)) //если отпустили кнопку
{
ekr--;
if(ekr==0)ekr=3;
lcd_clear();
}
else goto m3; //иначе если не отпустили кнопку
}
if(ekr==1)ekran1(); //вывод первого экрана
if(ekr==2)ekran2(); //вывод первого экрана
if(ekr==3)ekran3(); //вывод первого экрана

if(data==13) //если в буфере приёма RS232 пришёл ENTER
{ //начинаем выводить содержимое буфера
tmpr=strstr(rx_buffer,"temper"); //ищем первое сочетание в пришедшей строке "temper"
ta=strstr(rx_buffer,"tok"); //ищем первое сочетание в пришедшей строке "tok"
tu=strstr(rx_buffer,"napr"); //ищем первое сочетание в пришедшей строке "napr"
oll=strstr(rx_buffer,"all"); //ищем первое сочетание в пришедшей строке "napr"

if(tmpr!=NULL) //Обработка команды "temper"
{
zap_buf_str("Temperatura na ulice=");
zap_buf_cifr(Tt[0]);
zap_buf_str("C");
zap_buf_cifr_ch(0x0d); //перевод строки
zap_buf_str("Temperatura v pomechenie=");
zap_buf_cifr(Tt[1]);
zap_buf_str("C");
zap_buf_cifr_ch(0x0d);
UDR=tx_buffer[0]; //начинаем передавать первый байт
inc_TX=0; //сброс счётчика буфера передатчика
for(i=0;i<10;i++)rx_buffer=0; //стираем буфер приёма
tmpr=NULL; //обнуляем флаг для температуры
byte_rx=0; //обнуляем счётчик приёмника
}
if(ta!=NULL) //Обработка команды "tok"
{
zap_buf_str("Tok kontorlera=");
zap_buf_cifr(Tok1);
zap_buf_str("A");
zap_buf_cifr_ch(0x0d);
zap_buf_str("Tok zariada=");
zap_buf_cifr(Tok2);
zap_buf_str("A");
zap_buf_cifr_ch(0x0d);
UDR=tx_buffer[0];
inc_TX=0;
for(i=0;i<10;i++)rx_buffer=0; //стираем буфер приёма
ta=NULL; //обнуляем флаг для температуры
byte_rx=0; //обнуляем счётчик приёмника
}

if(tu!=NULL) //Обработка команды "napr"
{
zap_buf_str("Napriagenie kontrolera=");
zap_buf_cifr(Napr1);
zap_buf_str("V");
zap_buf_cifr_ch(0x0d);
zap_buf_str("Napriagenie akkumuliatore=");
zap_buf_cifr(Napr2);
zap_buf_str("V");
zap_buf_cifr_ch(0x0d);
UDR=tx_buffer[0]; //оправляем первый символ, после чего происходит передача по прерыванию после отправки очередного символа
inc_TX=0;
for(i=0;i<10;i++)rx_buffer=0; //стираем буфер приёма
tu=NULL; //обнуляем флаг для температуры
byte_rx=0; //обнуляем счётчик приёмника
}
if(oll!=NULL) //Обработка команды "oll"
{
zap_buf_str("Temperatura na ulice=");
zap_buf_cifr(Tt[0]);
zap_buf_str("C");
zap_buf_cifr_ch(0x0d); //перевод строки
zap_buf_str("Temperatura v pomechenie=");
zap_buf_cifr(Tt[1]);
zap_buf_str("C");
zap_buf_cifr_ch(0x0d);
zap_buf_str("Tok kontorlera=");
zap_buf_cifr(Tok1);
zap_buf_str("A");
zap_buf_cifr_ch(0x0d);
zap_buf_str("Tok zariada=");
zap_buf_cifr(Tok2);
zap_buf_str("A");
zap_buf_cifr_ch(0x0d);
zap_buf_str("Napriagenie kontrolera=");
zap_buf_cifr(Napr1);
zap_buf_str("V");
zap_buf_cifr_ch(0x0d);
zap_buf_str("Napriagenie akkumuliatore=");
zap_buf_cifr(Napr2);
zap_buf_str("V");
zap_buf_cifr_ch(0x0d);
UDR=tx_buffer[0];
inc_TX=0; //оправляем первый символ, после чего происходит передача по прерыванию после отправки очередного символа
for(i=0;i<10;i++)rx_buffer=0; //стираем буфер приёма
oll=NULL; //обнуляем флаг для температуры
byte_rx=0; //обнуляем счётчик приёмника
}
}
a:
if(sec_flag_1==1)
{
Tt=ds1820_temperature_10(&ds1820_rom_codes[0])*0.1; //для DS18S20
if(Tt<0)Tt=Tt+0.5;
u++;
if(u==2)u=0;
//T2=ds1820_temperature_10(&ds1820_rom_codes[1][0])/10*0.125; //для DS18B20
//k=9.377; //коэфф. делителя
k=10;
h=0.18; //шаг изменения напряжения на выходе датчика тока от тока
Tok1=(((I1*0.005)*k)-2.5)/h;
Tok2=(((I2*0.005)*k)-2.5)/h;
//Napr1=U1*0.0025*17.7;
Napr1=U1;
//Napr2=U2*0.0025*18;
Napr2=U2;
sec_flag_1=0;
//ADCSRA=ADCSRA|0x40;
}
}
}


Если есть необходимость могу весь проект выложить со схемой в протеусе.

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 14:35:20
AlexP
Изображение

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 14:50:09
Pink-Pank
По поводу вывода на экран.. Для целых чисел не используется модификатор точности с числом знаков после запятой. Попробуйте заменить просто на %d. В Протеусе симулировали или на реальном устройстве собирали и такой результат получили?

Re: АЦП atmega 16

Добавлено: Ср окт 01, 2014 14:53:54
AlexP
В протеусе всё работает как часики, точно такую же схему собрал на плате - АЦП работает вроде как 9 битное, но с погрешностью примерно 5 бит. Может я не правильно считываю или не правильно инициализирую, на меге 8 всё так же делал всё работало.