Специальные цены   новые товары
+ Ответить в теме
Показано с 1 по 16 из 16

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

Тема раздела Самодельная электроника, компьютерные программы в категории Общие вопросы; Привет друзья, третий день бьюсь но лыжи все не едут Есть способ измерения освещенности с помощью обычного светодиода, работает это ...

  1. #1

    Регистрация
    09.07.2017
    Адрес
    Сургут
    Возраст
    43
    Сообщений
    431

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

    Привет друзья, третий день бьюсь но лыжи все не едут

    Есть способ измерения освещенности с помощью обычного светодиода, работает это так: подаем на светодиод обратное напряжение, переключаем ногу катода на вход и считаем за какое время напряжение упадет до логического нуля. Чем темнее, тем дольше будет падать. Для определения ночь сейчас или день точности более чем достаточно. Метод проверено рабочий, но...

    код в лоб, считаем интервалы просто накручивая переменную, всё работает
    Код:
    volatile uint8_t lightCount;
    
    uint8_t inline getLight(){
    	
    	lightCount = 0;
    	while (PINB & (1<<LED_K)) {		
    		lightCount++;
    		if(lightCount == 255) break;					
    	}
    	return lightCount;
    }
    
    int main(void) {
    	
    	while(1){
    		
    		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) | _BV(LED_K));	// Отключаем подтяжку
    		
    		getLight();
    		
    		while (lightCount--) {			// Чем светлее, тем чаще мигает 
    			_delay_ms(1);			// просто для отладки
    		}
    	}
    }
    но это как-то не по фэншую, хотелось бы считать встроенным счетчиком, а пока он считает спать для экономии энергии. Но код c прерыванием ни в какую не работает, где накосячил не пойму
    Код:
    volatile uint8_t flag;
    
    ISR(PCINT0_vect) {
    	GIMSK &= ~_BV(PCIE);				// Отключаем прерываения по смене состояния 
    	flag = 1;					// Взводим флаг
    }
    
    int main(void) {
    	
    	sei();
    	
    	while(1){
    		
    		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) | _BV(LED_K));	// Отклочаем подтяжку
    		
    		GIMSK |= _BV(PCIE);			// Разрешаем прерывание по смене состояния PB0
    		PCMSK |= _BV(PCINT0);
    
    
    		flag = 0;
    		while (!flag) {				// Будем спать пока не встанет 0 на PB0
    			_delay_ms(1);			// а пока просто ждем
    		}
    	}
    }
    Теоретически должно работать так же как первый кусок, но не работает. Счетчик пока убрал, и так в трех соснах заблудилося Светодиод подключен к голому контроллеру, катод на PB0, анод через резистор 100 Ом на PB2, больше ничего нет. Буду очень признателен за помощь или хотя бы подсказку что не так.

  2.  
  3. #2

    Регистрация
    30.04.2014
    Адрес
    Russia
    Возраст
    43
    Сообщений
    20
    Код:
    volatile uint8_t flag = 1;
    
    ISR(PCINT0_vect) {
    	GIMSK &= ~_BV(PCIE);				// Отключаем прерываения по смене состояния
    	flag = 1;					// Взводим флаг
    }
    
    int main(void)
    {
        while (1) 
        {
    		if(flag){
    			flag = 0;
    			cli();
    
    			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) | _BV(LED_K));	// Отклочаем подтяжку
    
    			GIMSK |= _BV(PCIE);			// Разрешаем прерывание по смене состояния PB0
    			PCMSK |= _BV(PCINT0);
    
    			sei();
    		}
    
        }
    }
    Последний раз редактировалось xmailer; 25.12.2018 в 15:06.

  4. #3

    Регистрация
    30.04.2014
    Адрес
    Russia
    Возраст
    43
    Сообщений
    20
    не нашел как повторно редактировать свое сообщение, прерывание
    Код:
    ISR(PCINT0_vect) {
    	// Отключаем прерываения по смене состояния на INT0
    	GIMSK &= ~_BV(PCIE);
    	PCMSK &= ~_BV(PCINT0);
    	flag = 1;	// Взводим флаг
    }

  5. #4

    Регистрация
    09.07.2017
    Адрес
    Сургут
    Возраст
    43
    Сообщений
    431
    Спасибо что откликнулись, но чет не работает или я не понимаю как это должно работать... Мой первый код мигает светодиодом, чем светлее тем чаще он мигает, я это вижу невооруженным глазом просто поднося к нему фонарик. Ваш код просто светит (полагаю на самом деле очень быстро мигает), и я не пойму куда поставить задержку чтобы в железе это выглядело как первый вариант. И еще, в будущем я хочу чтобы контроллер спал пока ждет перехода уровня, а у вас он бодро крутит пустой цикл получается? То есть потом, когда все наладится, после if(flag){} можно будет уходить в сон с пробуждением по переполнению таймера или по смене состояния на входе?

    Вот к каким изысканиям я пришел, но это все еще не работает как задумано. Мне надо чтобы частота мигания светодиода зависела от текущего уровня освещенности.
    Код:
    #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) {
    	lightValue = 255;			// Устанавливаем максимальное значение
    	lightCycle = 1;				// Устанавливаем флаг окончания измерения
    }
    
    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);
    	_delay_ms(10);
    
    	DDRB &= ~_BV(LED_K);			// Катод на вход
    	PORTB &= ~(_BV(LED_K) | _BV(LED_A));	// Отклочаем подтяжку
    	
    	PRR &= ~_BV(PRTIM1);			// Включаем питание счетчика
    	TCNT1 = 0;
    	TCCR1 = 0;
    	TCCR1 |= _BV(CS13) | _BV(CS12) | _BV(CS11); // Запускаем счетчик на частоте clk/8192
    	
    	TIMSK |= _BV(TOIE1);			// Разрешаем прерывания по переполнению счетчика
    	GIMSK |= _BV(PCIE);			// Включаем прерывание по смене состояния PB0
    	PCMSK |= _BV(PCINT0);
    	
    	lightCycle = 0;
    	while(!lightCycle){
    		// ---------------------Тут будем спать
    	}
    	
    	TIMSK &= ~_BV(TOIE1);			// Запрещаем прерывания по переполнению счетчика
    	GIMSK &= ~_BV(PCIE);			// Запрещаем прерывания по смене состояния пина
    	PRR |= _BV(PRTIM1);			// Отключаем счетчик
    	
    	return lightValue;
    }
    
    int main(void) {
    	
    	sei();
    	
    	while(1){
    		
    		getLight();			// Получаем освещенность
    		
    		while (lightValue--) {		// Чем темнее, тем больше пауза
    			_delay_ms(1);		// просто для наглядности
    		}
    		
    	}
    }
    Только начал разбираться во всех этих дебрях, вероятно где-то сильно туплю

  6.  
  7. #5

    Регистрация
    22.03.2015
    Адрес
    Пермь
    Возраст
    52
    Сообщений
    240
    на первый взгляд, похоже с частотой счетчика не попали. В первом коде крутится практически пустой цикл измерения, пусть это тактов 10-20, а в последнем коде устанавливаете частоту /512.

  8. #6

    Регистрация
    09.07.2017
    Адрес
    Сургут
    Возраст
    43
    Сообщений
    431
    Во втором коде делитель /8192, переполнение счетчика наступает каждые чуть более 2 секунд, с такой частотой светодиод и моргает независимо от освещенности. Не срабатывает прерывание по смене состояния пина PCINT0, и я не пойму почему. На этом же железе первый код прекрасно отрабатывает появление на этом же пине "0", что собственно и есть смена состояния, просто отслеживается не через прерывание а прямым чтением регистра.

  9. #7

    Регистрация
    30.04.2014
    Адрес
    Russia
    Возраст
    43
    Сообщений
    20
    Попробуйте, следующий код, в симуляторе atmel studio отрабатывает, но конечно pb0 я в ручную сбрасываю в 0, чтобы прерывание сработало. Если не решите вопрос - на выходных соберу прототип, пока со временем вилы.
    Код:
    volatile uint8_t flag = 1;
    uint8_t light = 0;
    
    ISR(PCINT0_vect) {
        GIMSK &= ~_BV(PCIE);                // Отключаем прерываения по смене состояния
        PCMSK &= ~_BV(PCINT0);
        flag = 1;                    // Взводим флаг
    }
    
    int main(void)
    {
        while (1) 
        {
            if(flag){
                // задержка для визуализации результата замера
                if(light){
                    while(light--){
                        _delay_ms(1);
                    }
                }
                cli();
                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) | _BV(LED_K));    // Отклочаем подтяжку
    
                GIMSK |= _BV(PCIE);            // Разрешаем прерывание по смене состояния PB0
                PCMSK |= _BV(PCINT0);
                
                light = 0;
                flag = 0;
                sei();
            }
            if(light<255)
            {
                light++;
            }
        }
    }

  10.  
  11. #8

    Регистрация
    09.07.2017
    Адрес
    Сургут
    Возраст
    43
    Сообщений
    431
    Да, так работает так же как мой первый пример, при увеличении освещенности моргает чаще, в темноте реже. А как сделать чтобы light считался встроенным счетчиком, как в моей втором варианте? Если усыпить проц до срабатывания прерывания, он же не будет гонять цикл и считать.

  12. #9

    Регистрация
    30.04.2014
    Адрес
    Russia
    Возраст
    43
    Сообщений
    20
    Цитата Сообщение от We-BEER Посмотреть сообщение
    А как сделать чтобы light считался встроенным счетчиком, как в моей втором варианте? Если усыпить проц до срабатывания прерывания, он же не будет гонять цикл и считать.
    1. если как у Вас, то я бы исправил
    Код:
     
    ISR(PCINT0_vect) {
    	...
    	lightValue = TCNT1L;		// Значение, до которого досчитал таймер low byte 
    	//lightValue = TCNT1;		// если сделать lightValue типом uint16_t, то можно так 
    }
    2. есть один момент: цикл main->while(1) должен крутиться всегда и работать с последним значением освещенности, расчитанным параллельно в таймерах, нельзя вот так останавливать работу до получения значения while(!lightCycle){}. Если конечно это не особые требования функционала.

  13. #10

    Регистрация
    09.07.2017
    Адрес
    Сургут
    Возраст
    43
    Сообщений
    431
    1. У ATTiny85 нет TCNT1L/TCNT1H, у нее оба счетчика 8-битные.
    2. Я представлял себе алгоритм работы так: всегда крутим main->while(1), в котором вызываем getLight(), вся работа со светодиодом будет там, и там же проц будет засыпать внутри цикла while(!lightCycle){}. то есть после подачи обратного напряжения и настройки прерываний проц уснет, появление "0" на входе или переполнение таймера его будит и взводит флаг, после обработки прерываний идет возврат в цикл где условие уже не выполняется и происходит возврат в основную программу и мы продолжаем что-то там делать... ну как-то так
    Код:
    ISR(PCINT0_vect){...lightCycle=1;...} // появление "0" после "1"
    
    ISR(TIMER1_OVF_vect){...lightCycle=1;...} // переполнение счетчика
    
    int getLight(){
      ...                                    // настройка портов и прерываний
    
      flag = 0;
      while(!lightCycle){sleep}       // тут спим
    
      return;                        
    }
    
    int main(void){
      ...
      getLight();
      ...
    }

  14. #11

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

  15. #12

    Регистрация
    09.07.2017
    Адрес
    Сургут
    Возраст
    43
    Сообщений
    431
    Цитата Сообщение от emax Посмотреть сообщение
    странный конечно алгоритм, если проц спит, то он уже точно while проверять не может.
    он засыпает внутри while, и когда проснется тоже будет внутри, сходит в обработчик прерывания которое его разбудило, там он поднимет флаг, вернется из обработчика обратно в while, теперь условие уже не выполняется и он перепрыгнув while вернется в основную программу.

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

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

  16. #13

    Регистрация
    30.04.2014
    Адрес
    Russia
    Возраст
    43
    Сообщений
    20
    у Вас рабочий код, на железе пока не могу проверить, но в симуляторе

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

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


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

  17. #14

    Регистрация
    09.07.2017
    Адрес
    Сургут
    Возраст
    43
    Сообщений
    431
    Цитата Сообщение от 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; 26.12.2018 в 18:42.

  18. #15

    Регистрация
    09.07.2017
    Адрес
    Сургут
    Возраст
    43
    Сообщений
    431
    В общем-то не долго я радовался, опять застрял, на этот раз с 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);
      }
    }
    убираю вызов любой из функций (подставив константу в соответствующую переменную), и оставшаяся все делает правильно, вместе блин ни в какую... гуглил, но толком не нагуглил, предлагают использовать прерывания, в обработчике переключать каналы... нафига не понял, и зачем оно мне. Надо чтобы функции работали независимо друг от друга, просто вызвал и получил результат. Контроль напряжения будет идти постоянно по вачдогу, а опрос датчика только при необходимости. Наверняка забыл в какой-то регистр что-то записать, но весь моск сломал не вижу, олень оленем...

  19. #16

    Регистрация
    09.07.2017
    Адрес
    Сургут
    Возраст
    43
    Сообщений
    431
    Отбой, разобрались...
    неправильно ждал, вместо
    while ((ADCSRA & (1 << ADSC)) == 1);
    надо
    while (ADCSRA & (1 << ADSC));

+ Ответить в теме

Похожие темы

  1. Опасная самодеятельность передатчика... нужна помощь
    от s7air в разделе Аппаратура радиоуправления
    Ответов: 9
    Последнее сообщение: 11.07.2018, 17:07
  2. Модель застряла на дереве ,нужна помощь
    от БОГДАН в разделе Курилка
    Ответов: 5
    Последнее сообщение: 24.06.2018, 22:38
  3. коллеги нужна помощь и рекомендации по saito fg-40
    от DenisGrek в разделе Бензиновые двигатели
    Ответов: 15
    Последнее сообщение: 08.06.2018, 16:50
  4. Ответов: 8
    Последнее сообщение: 09.05.2018, 21:48
  5. Нужна помощь teu 104bk
    от Denisredstar в разделе Аппаратура и аксессуары для автомоделей
    Ответов: 3
    Последнее сообщение: 12.02.2018, 11:19

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения