Подключение двух каналов АЦП

Модератор: boogyman

Аватара пользователя
vsv
Новичок
Сообщения: 2
Зарегистрирован: 08 фев 2012, 12:15

Подключение двух каналов АЦП

#1

Сообщение vsv »

Возникла проблема с двумя каналами АЦП
При включении второго канала (ADC1)полностью игнорируется первый канал (ADC0)
(запись переменных происходит по значениям второго канала).
Симулировал в протеуc
Может где напутал?
Текст программы

#define F_CPU 8000000
#include <avr/io.h>
#include <util/delay.h>
#define sei()
/* Главная функция */
int main (void)
{ //начало главной функции

DDRD = 0xFF; //порт Д выход
PORTD = 0x00; // состояние 0
DDRB = 0x00; //порт B вход
PORTB = 0xFF; //подключаем нагрузочные резисторы
/* включение АЦП,делитель 32*/
ADCSRA |= (1 << ADEN)|(1 << ADPS2)|(0 << ADPS1)|(1 << ADPS0);
ADMUX |= (0 << REFS1)|(0 << REFS0); // внешний ИОН
while(1)
{ // начало бесконечного цикла
/* авто */
if(PINB == 0b11011111) // авто,движение по АЦП
{
sei(); // разрешаем прерывания
ADMUX |=(0 << MUX0)|(0 << MUX1)|(0 << MUX2)|(0 << MUX3); // вход АЦП ADC0
unsigned int u;// инициализируем переменную u (значение датчика)
ADCSRA |= (1 << ADSC); //Начинаем преобразование
if ((ADCSRA&(1 << ADIF))== 0); //Ждем окончания преобразования
{
u = (ADCL|ADCH << 8); //записываем значение в регистр
Аватара пользователя
AntonChip
Администратор
Сообщения: 265
Зарегистрирован: 24 дек 2011, 22:11
Откуда: Киров
Контактная информация:

Re: Подключение двух каналов АЦП

#2

Сообщение AntonChip »

Я так понял это не полный код, где обработчик прерывания.., на будущее вставляй код между тегами [codе]..[/codе]
Аватара пользователя
AntonChip
Администратор
Сообщения: 265
Зарегистрирован: 24 дек 2011, 22:11
Откуда: Киров
Контактная информация:

Re: Подключение двух каналов АЦП

#3

Сообщение AntonChip »

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

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

/***Двухканальный цифровой вольтметр***/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

//------------------------0-----1-----2-----3-----4-----5-----6-----7-----8------9----dp                      
const char SEGMENTE[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x80};

volatile unsigned char segcounter = 0;
volatile int display_1 = 0, display_2 = 0;

//Прерывание по переполнению таймера2
ISR (TIMER2_OVF_vect)
{	
	PORTD = 0x00;
	PORTB = ~(1 << segcounter);
	
	switch (segcounter)
	{	
		case 0:
			PORTD = SEGMENTE[display_1 % 1000/100];
			break;	
		case 1:
			PORTD = (SEGMENTE[display_1 % 100/10])|0x80; //добавляем точку
			break;		
		case 2:
			PORTD = SEGMENTE[display_1 % 10];
			break;
		case 3:
			PORTD = SEGMENTE[display_2 % 1000/100];
			break;
		case 4:
			PORTD = SEGMENTE[display_2 % 100/10]|0x80; //добавляем точку
			break;	
		case 5:
			PORTD = SEGMENTE[display_2 % 10];
			break;
	}
	if ((segcounter++)>5) segcounter = 0;		
}

//Прерывание по окончанию преобразования АЦП

volatile unsigned long value_1, value_2;
volatile unsigned int adc_counter;

ISR (ADC_vect)
{
    ADCSRA = 0;
    if ((ADMUX&0x0F) == 1)
    {
		ADMUX = 0;
        value_1 += ADC*24/45;
     }
    else
    {
		ADMUX = (1 << MUX0);
        value_2 += ADC*24/45;	
		adc_counter++;
    }
ADCSRA = (1 << ADEN) // разрешение АЦП
	    |(1 << ADSC) // запуск преобразования
		|(1 << ADFR) // непрерывный режим работы АЦП
        |(1 << ADPS2)|(1 << ADPS1)|(0 << ADPS0) // предделитель на 64 (частота АЦП 125kHz)
		|(1 << ADIE); // разрешение прерывания
}

/***Главная функция***/
int main (void) 
{ 
	DDRD = 0xFF;
	DDRB = (1 << PB5)|(1 << PB4)|(1 << PB3)|(1 << PB2)|(1 << PB1)|(1 << PB0);
    PORTD = 0x00;    
    PORTB = 0x00;

    TIMSK |= (1 << TOIE2); // разрешение прерывания по таймеру2
	TCCR2 |= (1 << CS21);  //предделитель на 8 
    
    ADCSRA = (1 << ADEN) // разрешение АЦП
	        |(1 << ADSC) // запуск преобразования
		    |(1 << ADFR) // непрерывный режим работы АЦП
            |(1 << ADPS2)|(1 << ADPS1)|(0 << ADPS0) // предделитель на 64 (частота АЦП 125kHz)
		    |(1 << ADIE); // разрешение прерывания

    ADMUX = (1 << MUX0);
    
	_delay_ms(50);

	sei(); //глобально разрешаем прерывания
 
	while(1)
	{ 

	 if (adc_counter > 400) // вычисляем среднее значение АЦП
		{
			display_1 = (value_1/adc_counter);
			display_2 = (value_2/adc_counter);
			adc_counter = 0;
			value_1 = 0;
			value_2 = 0;
		}   
	 _delay_ms(100);
	 }
}
Схема
Схема
Вложения
ADCvoltmeter_2ch.rar
Проект AvrStudio и Proteus
(72.58 КБ) 3701 скачивание
Аватара пользователя
vsv
Новичок
Сообщения: 2
Зарегистрирован: 08 фев 2012, 12:15

Re: Подключение двух каналов АЦП

#4

Сообщение vsv »

Спасибо огромное.
А то жизнь заставляет изучать программирование, информации мало - учусь по примерам
Аватара пользователя
Назим
Новичок
Сообщения: 2
Зарегистрирован: 09 фев 2012, 22:52

Re: Подключение двух каналов АЦП

#5

Сообщение Назим »

Привет всем. Огромное спасибо за уроки. В знак благодарности несклько раз ткнул по объявлениям Я.директа. :D
У меня возникли несколько вопросов. Не стал новую тему заводить. Тема вот эта: АЦП микроконтроллеров AVR. Делаем цифровой вольтметр 0 - 25V

Цитата: существует способ представить число 2,75 по другому, например: (ADC*11)/4
1. а как представить другие (разные) цифры, например, 1,8 - 3,86 - 4,61?
2. ADS=252 как сделать так, чтобы на индикаторе показывал, например 10,8
т.е. как рассчитать этот (разные) коэффициент(ы)?
Аватара пользователя
AntonChip
Администратор
Сообщения: 265
Зарегистрирован: 24 дек 2011, 22:11
Откуда: Киров
Контактная информация:

Re: Подключение двух каналов АЦП

#6

Сообщение AntonChip »

Назим писал(а):Привет всем. Огромное спасибо за уроки. В знак благодарности несклько раз ткнул по объявлениям Я.директа. :D
У меня возникли несколько вопросов. Не стал новую тему заводить. Тема вот эта: АЦП микроконтроллеров AVR. Делаем цифровой вольтметр 0 - 25V

Цитата: существует способ представить число 2,75 по другому, например: (ADC*11)/4
1. а как представить другие (разные) цифры, например, 1,8 - 3,86 - 4,61?
2. ADS=252 как сделать так, чтобы на индикаторе показывал, например 10,8
т.е. как рассчитать этот (разные) коэффициент(ы)?
Cначала высчитай коэффициент 252/108 = 2.3, т.е для вывода 108 надо 252/2.3, а коэф. 2.3 можно представить по разному
Аватара пользователя
Назим
Новичок
Сообщения: 2
Зарегистрирован: 09 фев 2012, 22:52

Re: Подключение двух каналов АЦП

#7

Сообщение Назим »

Понятно.
А 2,3 можно предстваить как (ADC*23)/10? 4,73 (ADC*473)/10 - такие цифры допустимы при написании кода?
Аватара пользователя
AntonChip
Администратор
Сообщения: 265
Зарегистрирован: 24 дек 2011, 22:11
Откуда: Киров
Контактная информация:

Re: Подключение двух каналов АЦП

#8

Сообщение AntonChip »

Назим писал(а):Понятно.
А 2,3 можно предстваить как (ADC*23)/10? 4,73 (ADC*473)/10 - такие цифры допустимы при написании кода?
можно, только 4,73 (ADC*473)/100, и обращай внимание на тип переменных, char, long и т.д
Аватара пользователя
boogyman
Модератор
Сообщения: 183
Зарегистрирован: 25 дек 2011, 22:48
Откуда: Москва

Re: Подключение двух каналов АЦП

#9

Сообщение boogyman »

Добавил еще один канал, получился 3-х канальный измеритель на трех трехразрядных индикаторах, кому надо пишите, выложу
Аватара пользователя
AntonChip
Администратор
Сообщения: 265
Зарегистрирован: 24 дек 2011, 22:11
Откуда: Киров
Контактная информация:

Re: Подключение двух каналов АЦП

#10

Сообщение AntonChip »

По просьбам посетителей сайта выкладываю пример кода программы одноканального вольтметра с выводом показаний на LCD 16x02. Принцип работы тот же, что и в обучающей статье http://radioparty.ru/index.php/prog-avr ... -voltmeter, отличие только в выводе показаний.

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

/*** Использование АЦП. Цифровой вольтметр на LCD ***/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

// Функции работы с LCD 
#define RS PD0 
#define EN PD2
// Функция передачи команды
void lcd_com(unsigned char p)
{
PORTD &= ~(1 << RS); // RS = 0 (запись команд)
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
}
// Функция передачи данных
void lcd_data(unsigned char p)
{
PORTD |= (1 << RS)|(1 << EN); // RS = 1 (запись данных), EN - 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
}

// Функция вывода строки на LCD
void lcd_string(unsigned char command, char *string)
{
lcd_com(0x0C);
lcd_com(command);
while(*string != '\0')
{  lcd_data(*string);
string++;
}
}
// Функция вывода символа типа int
void LCDWriteInt(int val,unsigned int field_length)
{
char str[5]={0,0,0,0,0};
int i=4,j=0;
while(val)
{
str[i]=val%10;
val=val/10;
i--;
}
if(field_length==-1)
while(str[j]==0) j++;
else
j=5-field_length;
if(val<0) lcd_data('-');
for(i=j;i<5;i++)
{
lcd_data(48+str[i]);
}
}
// Функция инициализации LCD
void lcd_init(void)
{
PORTD = 0x00;
DDRD = 0xFF;

_delay_ms(50); // Ожидание готовности ЖК-модуля

// Конфигурирование четырехразрядного режима
PORTD |= (1 << PD5);
PORTD &= ~(1 << PD4);

// Активизация четырехразрядного режима
PORTD |= (1 << EN);
PORTD &= ~(1 << EN);
_delay_ms(5); 

lcd_com(0x28); // шина 4 бит, LCD - 2 строки
lcd_com(0x08); // полное выключение дисплея
lcd_com(0x01); // очистка дисплея
_delay_us(100);
lcd_com(0x06); // сдвиг курсора вправо
lcd_com(0x0C); // включение дисплея, курсор не видим
}

volatile unsigned long value;
volatile unsigned int adc_counter;

/***Прерывание по окончанию преобразования АЦП***/
ISR (ADC_vect)
{
value = value + (ADC*11/4);
adc_counter++;
}

/***Главная функция***/
int main (void) 
{ 
volatile int display = 0;

ADCSRA = (1 << ADEN) // разрешение АЦП
        |(1 << ADSC) // запуск преобразования
        |(1 << ADFR) // непрерывный режим работы АЦП
        |(1 << ADPS2)|(1 << ADPS1)|(0 << ADPS0) // предделитель на 64 (частота АЦП 125kHz)
	|(1 << ADIE); // разрешение прерывания

ADMUX = (1 << REFS1)|(1 << REFS0) // внутренний ИОН 2,56V
        |(0 << MUX3)|(0 << MUX2)|(0 << MUX1)|(0 << MUX0); // вход ADC0
    
_delay_ms(50);

sei(); // глобально разрешаем прерывания

lcd_init(); // Инициализация LCD
 
while(1)
{ 
if(adc_counter > 300) // вычисляем среднее значение АЦП
{
display = value/adc_counter;
lcd_string(0x80,"U =   ,  V");
lcd_com(0x84);
LCDWriteInt(display/100, 2); // Выводим на экран целое значение
lcd_com(0x87);
LCDWriteInt(display%100, 2); // Выводим на экран значение после запятой
adc_counter = 0;
value = 0;
}     
_delay_ms(50);
}
}

Вложения
voltmeter_lcd.rar
Проект AVRStudio4 и Proteus
(49.02 КБ) 3478 скачиваний
Аватара пользователя
Sergey F
Любитель
Сообщения: 13
Зарегистрирован: 13 мар 2014, 15:33

Re: Подключение двух каналов АЦП

#11

Сообщение Sergey F »

Пожалуйста!
Выложите код программы 4-х канального АЦП (2 канала для измерения напряжения и 2 канала для измерения тока).
Напряжение 0-30V, ток 0-10А.
Спасибо.
Аватара пользователя
boogyman
Модератор
Сообщения: 183
Зарегистрирован: 25 дек 2011, 22:48
Откуда: Москва

Re: Подключение двух каналов АЦП

#12

Сообщение boogyman »

Под какой индикатор выложить исходник?
Аватара пользователя
Sergey F
Любитель
Сообщения: 13
Зарегистрирован: 13 мар 2014, 15:33

Re: Подключение двух каналов АЦП

#13

Сообщение Sergey F »

boogyman писал(а):Под какой индикатор выложить исходник?
Под LCD 1602, но и если можно то и под 7-ми сегментный индикатор.
Аватара пользователя
boogyman
Модератор
Сообщения: 183
Зарегистрирован: 25 дек 2011, 22:48
Откуда: Москва

Re: Подключение двух каналов АЦП

#14

Сообщение boogyman »

Вот код для четырехканального вольтметра, но в нем необходимо поменять коэффициенты для измерения напряжения и тока, а также сделать отдельную функцию измерения по каналам.

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

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

// Функции работы с LCD 
#define RS PD0 
#define EN PD2
// Функция передачи команды
void lcd_com(unsigned char p)
{
PORTD &= ~(1 << RS); // RS = 0 (запись команд)
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
}
// Функция передачи данных
void lcd_data(unsigned char p)
{
PORTD |= (1 << RS)|(1 << EN); // RS = 1 (запись данных), EN - 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p & 0xF0); // старший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p << 4); // младший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
}

// Функция вывода строки на LCD
void lcd_string(unsigned char command, char *string)
{
lcd_com(0x0C);
lcd_com(command);
while(*string != '\0')
{  lcd_data(*string);
string++;
}
}
// Функция вывода символа типа int
void LCDWriteInt(uint16_t value, uint8_t nDigit)
{
/* DISPLAY n-DIGIT INTEGER NUMER */
 switch(nDigit)
 {
  case 4: lcd_data((value/1000)+'0');
  case 3: lcd_data(((value/100)%10)+'0');
  case 2: lcd_data(((value/10)%10)+'0');
  case 1: lcd_data((value%10)+'0');
 }
}
// Функция инициализации LCD
void lcd_init(void)
{
PORTD = 0x00;
DDRD = 0xFF;

_delay_ms(50); // Ожидание готовности ЖК-модуля

// Конфигурирование четырехразрядного режима
PORTD |= (1 << PD5);
PORTD &= ~(1 << PD4);

// Активизация четырехразрядного режима
PORTD |= (1 << EN);
PORTD &= ~(1 << EN);
_delay_ms(5); 

lcd_com(0x28); // шина 4 бит, LCD - 2 строки
lcd_com(0x08); // полное выключение дисплея
lcd_com(0x01); // очистка дисплея
_delay_us(100);
lcd_com(0x06); // сдвиг курсора вправо
lcd_com(0x0C); // включение дисплея, курсор не видим
}

/***Главная функция***/
int main (void) 
{ 
ADCSRA |= (1 << ADSC);
ADCSRA = (1 << ADEN) // разрешение АЦП
        |(1 << ADFR) // непрерывный режим работы АЦП
        |(1 << ADPS2)|(1 << ADPS1); // предделитель на 64 (частота АЦП 125kHz)

lcd_init(); // Инициализация LCD

lcd_string(0x80,"U1   ,   U2   , ");
lcd_string(0xC0,"U3   ,   U4   , ");

while(1)
{ 
ADMUX = 0; 
ADCSRA |= (1 << ADSC);     
while((ADCSRA &(1 << ADIF)) == 0){}; // Ждём окончания конвертирования
ADCSRA |= (1 << ADIF); // Сбрасываем флаг записью 1 
lcd_com(0x83);
LCDWriteInt(ADC/(11/4)/100, 2); // Выводим на экран целое значение
lcd_com(0x86);
LCDWriteInt(ADC/(11/4)%100, 2); // Выводим на экран значение после запятой

ADMUX = 1; 
ADCSRA |= (1 << ADSC);     
while((ADCSRA &(1 << ADIF)) == 0){}; // Ждём окончания конвертирования
ADCSRA |= (1 << ADIF); // Сбрасываем флаг записью 1 
lcd_com(0x8C);
LCDWriteInt(ADC/(11/4)/100, 2); // Выводим на экран целое значение
lcd_com(0x8F);
LCDWriteInt(ADC/(11/4)%100, 2); // Выводим на экран значение после запятой

ADMUX = 2; 
ADCSRA |= (1 << ADSC);     
while((ADCSRA &(1 << ADIF)) == 0){}; // Ждём окончания конвертирования
ADCSRA |= (1 << ADIF); // Сбрасываем флаг записью 1 
lcd_com(0xC3);
LCDWriteInt(ADC/(11/4)/100, 2); // Выводим на экран целое значение
lcd_com(0xC6);
LCDWriteInt(ADC/(11/4)%100, 2); // Выводим на экран значение после запятой

ADMUX = 3; 
ADCSRA |= (1 << ADSC);     
while((ADCSRA &(1 << ADIF)) == 0){}; // Ждём окончания конвертирования
ADCSRA |= (1 << ADIF); // Сбрасываем флаг записью 1 
lcd_com(0xCC);
LCDWriteInt(ADC/(11/4)/100, 2); // Выводим на экран целое значение
lcd_com(0xCF);
LCDWriteInt(ADC/(11/4)%100, 2); // Выводим на экран значение после запятой

_delay_ms(50);
}
}
Вложения
voltmeter_lcd.rar
Проект AVRStudio4 и Протеус
(46.74 КБ) 3049 скачиваний
Аватара пользователя
Sergey F
Любитель
Сообщения: 13
Зарегистрирован: 13 мар 2014, 15:33

Re: Подключение двух каналов АЦП

#15

Сообщение Sergey F »

Спасибо!
Аватара пользователя
Sergey F
Любитель
Сообщения: 13
Зарегистрирован: 13 мар 2014, 15:33

Re: Подключение двух каналов АЦП

#16

Сообщение Sergey F »

Вот код для четырехканального вольтметра, но в нем необходимо поменять коэффициенты для измерения напряжения и тока, а также сделать отдельную функцию измерения по канала

А можно производить процесс измерения (4 канала) в прерывании?
Аватара пользователя
AntonChip
Администратор
Сообщения: 265
Зарегистрирован: 24 дек 2011, 22:11
Откуда: Киров
Контактная информация:

Re: Подключение двух каналов АЦП

#17

Сообщение AntonChip »

Конечно можно, также в прерывании нужно поочередно менять канал АЦП
Аватара пользователя
Sergey F
Любитель
Сообщения: 13
Зарегистрирован: 13 мар 2014, 15:33

Re: Подключение двух каналов АЦП

#18

Сообщение Sergey F »

AntonChip писал(а):Конечно можно, также в прерывании нужно поочередно менять канал АЦП
Спасибо за ответ.
Аватара пользователя
Sergey F
Любитель
Сообщения: 13
Зарегистрирован: 13 мар 2014, 15:33

Re: Подключение двух каналов АЦП

#19

Сообщение Sergey F »

Здравствуйте.
Собрал схему вольтметра (1 канал) на LCD, проверил в протеусе - работает, на макете - нет (каша на дисплее).
В чем может быть проблема?
Исходник и протеус во вложении.
Вложения
Вольтметр LCD.rar
(68.37 КБ) 2807 скачиваний
Аватара пользователя
AntonChip
Администратор
Сообщения: 265
Зарегистрирован: 24 дек 2011, 22:11
Откуда: Киров
Контактная информация:

Re: Подключение двух каналов АЦП

#20

Сообщение AntonChip »

Эти строчки я бы убрал до while(1)

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

lcd_string(0x80, "    VOLTAGE     ");
lcd_string(0xC0, "      ,  V      ");
Ответить