Нужна помощь по коду для AVR (ATTiny85)

emax

странный конечно алгоритм, если проц спит, то он уже точно while проверять не может. А если его все таки разбудило прерывание, то зачем проверять флаг lightCycle ?

We-BEER
emax:

странный конечно алгоритм, если проц спит, то он уже точно while проверять не может.

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

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

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

xmailer

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

GIMSK &= ~_BV(PCIE); не достаточно

PCMSK &= ~_BV(PCINT0); обязательно, в противном случае постоянно проваливался в прерывание ISR(PCINT0_vect)

второй момент - while(!lightCycle){} - это не сон, а ожидание, проц будет продолжать жрать питание. Термин “сон” - это перевод камня в состояние, при котором у него часть функции будут отключены и режим потребления питания будет минимален. Про сон погуглите watchdog и как этот таймер можно использовать, я не пользовался им поэтому не могу ничего утверждать.

We-BEER
xmailer:

второй момент - while(!lightCycle){} - это не сон, а ожидание

да, я об этом знаю 😃 пустой цикл я поставил на время отладки, чтобы исключить лишние глюки пока не разберусь с PCINT0.

xmailer:

GIMSK &= ~_BV(PCIE); не достаточно

PCMSK &= ~_BV(PCINT0); обязательно, в противном случае постоянно проваливался в прерывание ISR(PCINT0_vect)

Заработало!!! Господи, спасибо тебе что есть такие люди! Мой второй код заработал, только разогнал таймер до /128, а то моргало очень быстро, мало успевал накрутить. Огромное вам спасибо, я чуть с ума не сошел))

Вот рабочий код:

#define LED_A PB2
#define LED_K PB0
#define F_CPU 1000000L

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

volatile uint8_t lightValue, lightCycle;

ISR(PCINT0_vect) {
	GIMSK &= ~_BV(PCIE);			// Запрещаем прерываения по смене состояния
	lightCycle = 1;				// Взводим флаг
	lightValue = TCNT1;			// Забираем значение счетчика
}

ISR(TIMER1_OVF_vect) {
	TIMSK &= ~_BV(TOIE1);			// Запрещаем прерывания по переполнению счетчика
	lightCycle = 1;				// Устанавливаем флаг окончания измерения
	lightValue = 255;			// Устанавливаем максимальное значение
}

uint8_t inline getLight(){

	DDRB |= _BV(LED_A) | _BV(LED_K);	// Порты на выход
	PORTB |= _BV(LED_A);			// и моргнуть
	PORTB &= ~_BV(LED_K);
	_delay_ms(10);

	PORTB &= ~_BV(LED_A);			// Подаем обратное напряжение
	PORTB |= _BV(LED_K);

	DDRB &= ~_BV(LED_K);			// Катод на вход
	PORTB &= ~_BV(LED_K);			// Отклочаем подтяжку

	PRR &= ~_BV(PRTIM1);			// Включаем питание счетчика
	TCNT1 = 0;
	TCCR1 = 0;
	TCCR1 |= _BV(CS13);			// Запускаем счетчик на частоте clk/128

	TIMSK |= _BV(TOIE1);			// Разрешаем прерывания по переполнению счетчика
	GIMSK |= _BV(PCIE);			// Разрешаем прерывания по смене состояния PCINT0
	PCMSK |= _BV(PCINT0);			// на пине PB0

	lightCycle = 0;
	while(!lightCycle){
		// Тут будем спать
	}

	TIMSK &= ~_BV(TOIE1);			// Запрещаем прерывания по переполнению счетчика
	GIMSK &= ~_BV(PCIE);			// Запрещаем прерывания по смене состояния пина
	PCMSK &= ~_BV(PCINT0);			// <<<<< Иначе не работает
	PRR |= _BV(PRTIM1);			// Отключаем счетчик

	return lightValue;
}

int main(void) {

	sei();

	while(1){

		getLight();

		while (lightValue--) {		// Чем темнее, тем дольше пауза
			_delay_ms(1);
		}

	}
}
We-BEER

В общем-то не долго я радовался, опять застрял, на этот раз с ADC… В общем мне надо контролировать напряжение питания контроллера, и сигнализировать при падении ниже заданного уровня (скажем 3.3V). Самым простым видиться измерение внутреннего источника 1.1V относительно текущего напряжения Vcc. Получилась такая функция, которая отлично работает:


uint16_t vBat;

uint16_t getVbat() {

  ADMUX = 1 << MUX3 | 1 << MUX2;        // Измеряем внутренний ИОН 1.1V относительно Vcc
  _delay_ms(10);                        // Ждем стабилизации напряжения

  ADCSRA |= _BV(ADSC);                  // Запускаме измерение
  while ((ADCSRA & (1 << ADSC)) == 1);  // Ждем окончания

  uint16_t result = ADCL;               // Забираем младший байт
  result |= ADCH << 8;                  // забираем старший байт

  vBat = 1099366L / result;             // Вычисляем Vcc в mV, 4.880V / 5.000V =  0.976
                                        // (1.1V*1024*1000) * 0.976 = 1099366L
  return vBat;

}

int main(void)
{

  ADCSRA |= _BV(ADEN);                  // Включаем АЦП
  ADCSRA |= _BV(ADPS1) | _BV(ADPS0);    // Частота АЦП clk/8 = 125кГц

  while (1) {
    ...
    getVbat();
    if(vBat < 3300) beep(2);
    ...
  }
}

Кроме постоянного контроля напряжения питания, мне надо иногда опрашивать датчик - измерять напряжение на PB3. Получилась вторая функция, которая тоже отлично работает:

uint16_t sens;
uint16_t getSens() {

  DDRB |= _BV(SENS_PW);                 // Питание сенсора на выход
  PORTB |= _BV(SENS_PW);                // Включаем
  DDRB &= ~_BV(SENS);                   // Сигнал сенсора на вход

  ADMUX = 1 << MUX0 | 1 << MUX1;    // Выбираем вход ADC3 (PB3)
  _delay_ms(10);

  ADCSRA |= _BV(ADSC);                  // Запускаме измерение
  while ((ADCSRA & (1 << ADSC)) == 1);  // Ждем окончания измерения
  PORTB &= ~_BV(SENS_PW);               // Выключаем питание сенсора

  uint16_t result = ADCL;             // Забираем младший байт
  result |= ADCH << 8;                // забираем старший байт

  sens = vBat / 1024.0 * result;       // Получаем текущее напряжение в mV

  return sens;
}

но если я пытаюсь использовать их совместно, ничего не получается, они начинают друг другу мешать, и такой код работает неправильно

int main(void)
{

  ADCSRA |= _BV(ADEN);                  // Включаем АЦП
  ADCSRA |= _BV(ADPS1) | _BV(ADPS0);    // Частота АЦП clk/8 = 125кГц

  while (1) {
    ...
    getVbat();
    if(vBat < 3300) beep(2);
    ...
    getSens();
    while(sens--) _delay_ms(1);
    blink(1);
  }
}

убираю вызов любой из функций (подставив константу в соответствующую переменную), и оставшаяся все делает правильно, вместе блин ни в какую… гуглил, но толком не нагуглил, предлагают использовать прерывания, в обработчике переключать каналы… нафига не понял, и зачем оно мне. Надо чтобы функции работали независимо друг от друга, просто вызвал и получил результат. Контроль напряжения будет идти постоянно по вачдогу, а опрос датчика только при необходимости. Наверняка забыл в какой-то регистр что-то записать, но весь моск сломал не вижу, олень оленем… 😵

We-BEER

Отбой, разобрались…
неправильно ждал, вместо
while ((ADCSRA & (1 << ADSC)) == 1);
надо
while (ADCSRA & (1 << ADSC));