Attiny13 и ADC

Что-то у меня не получается. Может вопрос не правильно задаю?

У меня входное напряжение от 0 до 5В, опорное 1.1В.
АЦП принимает значение от 0 до 1023. Как вычислить напряжение (4В, например) при этих условиях? Или так нельзя? о_0

Это в простейшем случае.

В реальности, если учесть, что у АЦП паспортная погрешность - 2 lsb, я часто делаю так: выставляю бит ADLARADMUX) и после этого читаю только ADCH – в нём в этом случае сидят 8 старших битов результата, т.е. я получаю значение с отброшенными двумя младшими битами, которые по паспорту мусорные. Реально работает и читать меньше.

Никак. Входное не должно быть больше опорного - всё, что больше опорного будет 1023.

Хм, а для того чтобы использовать в качестве опорного VCC → нужен свободный пин, так?

Делитель используй

Едрид-мадрид!
Точно! )))

Отдыхать пора ))

да, я понимаю.
если в контексте проблемы ТС в коде из первого поста написать

ISR (ADC_vect) {
	analogData = ADCL + (ADCH<<8);
}

протеус перестанет ругаться.
и правильно сделает)

Не понял?! Для чего нужен свободный пин?

analogData = ADC;
Уже говорили.

А вот это уже интересно (хотя не исключаю, что нужно просто идти отдыхать):

	while (1) {
		uint16_t volt; 
		//  mvRefference - опорное напряжение в милливольтах
		//  divider - 1024 (или 512 если 9 бит - это бывает на 85-ой тиньке)
		//  value - попугаи, снятые с АЦП
		//  результат в милливольтах
		//  Vmv = (mvRefference * value + divider / 2) / divider;

		volt = ((1100L * analogData) + 512L) / 1024;
		
		if (volt > 43120){
			PORTB |= (1<<PORTB3);
			} else {
			PORTB &= ~(1<<PORTB3);
		}
}

Светодиод на порту PB3 загорается если значение в IF выставить в 43110 и тухнет, если выставить 43120.
Но этого не может быть! На входе протеус показывает напряжение 0.673484В.

Считаем по формуле какое значение АЦП должно ожидаться на выходе:
1100 → 1023
673 → X
X = (673 * 1023) / 1100;
X = 627;

Подставляем в формулу:
volt = (mvRefference * value + divider / 2) / divider;
volt = (1100 * 627 + 512) / 1024;
volt = 674;

674, а условие срабатывает только при 43120.

Капец какой-то…

Вот уж не думал, что Вам придётся это писать :frowning:

Дайте полный код, чтобы я смог тупо запустить его у себя не додумывая, что у Вас там ещё написано.

#define	 F_CPU			1200000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

volatile uint16_t analogData;

ISR (ADC_vect) {
	analogData = ADC;
}

int main(void) {

	DDRB  |= (1<<PORTB3);
	PORTB &= ~(1<<PORTB3);
	
	DDRB  &= ~(1<<PORTB4);
	
	// Настраиваем работу с ADC (АЦП)
	ADMUX  |= (1<<REFS0) | (1<<ADLAR) | (1<<MUX1);  				// опорное напряжение - Internal, левое ориентирование данных, выбран вход ADC2 (на него подается измеряемое напряжение)
	ADCSRA |= (1<<ADEN)  | (1<<ADSC)  | (1<<ADATE) | (1<<ADIE) | (1<<ADPS2);	// АЦП включен, запуск преобразования, режим автоизмерения, прерывание по окончанию преобразования, частота CLK/4
	ADCSRB  = 0x00;									// режим автоизмерения: постоянно запущено

	DIDR0 |= (1<<ADC2D);	// запрещаем цифровой вход на ноге аналогового входа
	
	sei(); //разрешаем глобально прерывания
	
	while (1) {
		uint16_t volt;
		//  mvRefference - опорное напряжение в милливольтах
		//  divider - 1024 (или 512 если 9 бит - это бывает на 85-ой тиньке)
		//  value - попугаи, снятые с АЦП
		//  результат в милливольтах
		//  Vmv = (mvRefference * value + divider / 2) / divider;

		volt = ((1100L * analogData) + 512L) / 1024;
	
		if (volt > 43100){
			PORTB |= (1<<PORTB3);
		} else {
			PORTB &= ~(1<<PORTB3);
		}
	}
}

Если отбросить ADCL (оставив только ADCH), то при входящих 0.9 В порог срабатывания 220…230. Бррррр…
Голова кипит (и даже побаливает)…

Ну, в том, что у Вас там бешенные тыщщи, Вы сами виноваты. Что у Вас делает бит ADLAR в строке 20? Уберите нахрен, будет нормальный результат. Зачем Вы его взводите? Он же форматирует результат влево! Его тогда не так читать надо!

Другое дело, что это не единственная ошибка - оно у Вас срабатывает один раз. Нет фрераннинга. Дать Вам нормальную настройку АЦП? Или проспитесь и сами сделаете?

2 лайка

Лишним не будет ))


Я уже в отладке протеуса начал разбираться )))

Ну, ADLAR убрали? Нормальные числа стали?

Да! Ура! ))) Спасибо огромное!

1 лайк

Ну, я на схеме не стал мудрить с делителем – просто запитал потенциометр от 1.1В, также нагло воспользовался тем, что в протеусе светодиоды как рукописи – не горят и упростил схему донельзя:

Всё отлично работает при 50% на потенциометре светодиод ещё не светится, а уже при 51% – включается.

Код, в целом Ваш, чуток подправил и причесал
#define	 F_CPU	1200000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define LED_PIN	3

#define HIGH	1
#define LOW		0
#define OUTPUT	1
#define INPUT	0

#define digitalWrite(pin, val) do { (val) ? PORTB |= (1 << (pin)) : PORTB &= ~(1 << (pin)); } while(false)
#define pinMode(pin, val) do { (val) ? DDRB |= (1 << (pin)) : DDRB &= ~(1 << (pin)); } while(false)

volatile uint16_t analogData;

ISR (ADC_vect) {
	analogData = ADC;
}

static inline void adcInit(void) {
	ADCSRA = 
		(1<<ADEN)	|	// АЦП включен
		(1<<ADATE)	|	// режим автоизмерения
		(1<<ADIE)	|	// прерывание по окончанию преобразования
		(1<<ADPS1) | (1<<ADPS0);	// делитель частоты - 8 (частота АЦП - 1200 / 8 = 150кГц)
	ADMUX  =
		(1<<REFS0)	|	// опорное напряжение - Internal 1.1в
		(1<<MUX1);		// вход ADC2
	ADCSRB = 0x00;		// режим автоизмерения: Free Running
	DIDR0 |= (1<<ADC2D);	// запрещаем цифровой вход на ноге аналогового входа
	ADCSRA |= (1<<ADSC);	// запуск первого преобразования
}

int main(void) {
	pinMode(LED_PIN, OUTPUT);
	adcInit();
	sei(); //разрешаем глобально прерывания

	while (true) {
		static uint16_t oldData = 0;
		cli();
		const uint16_t aData = analogData;
		sei();
		if (oldData != aData) {
			oldData = aData;
			const uint16_t volt = ((1100L * aData) + 512L) / 1024;
			digitalWrite(LED_PIN, volt > 550);
		}
	}
}
2 лайка