Мистическое поведение программы на attiny10

Тема в разделе "Микроконтроллеры AVR", создана пользователем Антон336699, 6 дек 2023.

Метки:
  1. Антон336699

    Антон336699 Нерд

    имеется устройство на Attiny10, работает следующим образом. Порт PB2 сконфигурирован как вход, на него приходит сигнал с компаратора, порт РВ0 выводит ШИМ с таймера0 для управления сервоприводом, РВ1 управляет мосфетом, через который подается питание на сервопривод.

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

    Данное устройство является мозгом мышеловки, мышка перекрывает сигнал, сервопривод поворачивает и ловушка закрывается. После подачи питания подается управляющий сигнал на сервопривод, затем подается сигнал на мосфет, сервопривод поворачивается в начальное положение, спустя 2 секунды мосфет отключается, серва обесточивается, спустя еще 3 секунды задержки программа переходит в бесконечный цикл, где ожидает срабатывания комраратора, логического 0., как только это происходит меняется управляющий сигнал для сервопривода, спустя 0,1 сек включается мосфет, сервоприводу дается 2 секунды на занятие нового положения, затем мосфет снова закрывается и снимает питание с сервы, и далее программа зависает в цикле for.

    Собственно в чем мистика, если запустить устройство и сигнал проходит, то есть сработки нет, устройство загружается и встает дежурить в ожидании сработки, Если перекрыть луч происходит сработка, серва поворачивается, и выключается. А вот если перед включением закрыть фотодиод, тем самым запустить устройство с состоянии сработки, серва занимает срартовое положение, но в положение сработки не переходит. Подкидывал осциллограф, на нем видно что управляющий сигнал меняется, так же видно что первый раз мосфет отрабатывает четко, дает 2 сек на поворот сервопривода, а вот в момент сработки мосфет кратковременно открывается и закрывается, почему то в данной ситуации не выполняется задержка, как будто ее нет, она игнорируется, код в обоих случаях один и тот же, залитый на одно и то же устройство. Второй день ломаю голову но понять не могу в чем проблема, как на поведение программы влияет то что на порт PB2 на старте подаются разные логические уровни, хотя такое поведение ни как не предусмотрено в коде. Код прилагаю.

    Всем заранее спасибо за помощь. Как говорится скоро от непонимания двинуся умом.

    Код (C++):
    /*
    * MousKiller_1.cpp
    *
    * Created: 02.12.2023 11:25:52
    * Author : Администратор
    */


    #define F_CPU 8000000UL
    #define F_TIMER 1000000UL         // частота таймера для рассчетов.

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

    #define timer0_max_count 20000UL    // до этого числа считает таймер.
    #define servo_poz_start 1400U        // позиция сервопривода в режиме ожидания, в нее он возвращается при загрузке
    #define servo_poz_alarm 2350U        // позиция сервопривода когда происходит срабатывание механизна

    volatile uint32_t timer0_overflow_count = 0;  // счетчик количества циклов переполнения таймера0
    uint8_t flag = 0x00;    // флаг срабатывания

    void delay_ms(uint32_t value);

    ISR(TIM0_OVF_vect)  // Обработчик прерывания переполнения таймера0.
    {
        timer0_overflow_count++;
        // счетчик работает с частотой 50Гц
    }


    int main(void)
    {  
        cli();            // запрещаем прерывания
        CCP = 0xD8;        // разблокируем
            CLKPSR = 0x00;    // выстявляем предделитель частоты 1
            //CLKPSR = 0x01;    // выстявляем предделитель частоты 2
            //CLKPSR = 0x02;    // выстявляем предделитель частоты 4
            //CLKPSR = 0x03;    // выстявляем предделитель частоты 8 (Default)
            //CLKPSR = 0x04;    // выстявляем предделитель частоты 16
       
        CCP = 0xD8;        // на всякий случай еще раз разблокируем
            CLKMSR = 0x00;    // Calibrated Internal 8 MHz Oscillator (Default)
            //CLKMSR = 0x01;  // Internal 128 kHz Oscillator (WDT Oscillator)
            //CLKMSR = 0x02;    // выбыраем тактирование от внешнего источника, 0x00 от внутреннего LC генератора 8МГц.       |= (1 << CLKMS1)
       
        //............................... Конфигурируем регистры портов
            // PB0(выход) - подключена серва, PB1(выход) - мосфет, PB2(вход) - вход с компаратора
            DDRB = 0b00000011;      // устанавили пин PB0 и PB1 как выходы.
            PUEB = 0x00;            // отключаем подтягивающие резисторы порта B
            PORTB &= ~(1 << PORTB1);    // устанавливаем низкий уровень на PB1 (мосфет питания сервопривода)
           
        //............................... Выбор предделителя таймера0
            TCCR0A = 0; // обнуляем регистры таймера
            TCCR0B = 0;
           
            //TCCR0B |= (1 << CS00);                // 1
            TCCR0B |= (1 << CS01);                    // 8
            //TCCR0B |= (1 << CS00) | (1 << CS01);  // 64   // точность 8мк секунд, но работает стабильнее
            //TCCR0B |= (1 << CS02);                // 256
            //TCCR0B |= (1 << CS02) | (1 << CS00);  // 1024
        //............................... Быбор режима работы таймера0
            //TCCR0A |= (1 << WGM00); TCCR0B |= (1 << WGM02);                   // Fast PWM 8-bit, 255
            //TCCR0A |= (1 << WGM01); TCCR0B |= (1 << WGM02);                   // Fast PWM 9-bit, 511
            //TCCR0A |= (1 << WGM00) | (1 << WGM01); TCCR0B |= (1 << WGM02);    // Fast PWM 10-bit, 1023
            TCCR0A |= (1 << WGM01); TCCR0B |= (1 << WGM02) | (1 << WGM03); ICR0 = (timer0_max_count - 1);  // Fast PWM любой битности
            OCR0A = servo_poz_start;    // задаем стартовое положение сервопривода
            PORTB |= (1 << PORTB1);        // включаем мосфет, тем самым подаем питание на сервопривод
        //............................... Разрешаем прерывания
            TIMSK0 |= (1 << TOIE0);   // Разрешаем прерывание по переполнению таймера0
            //EICRA = 0b00000011;      // прерывание INT0 на пине PB2 срабатывает по восходящему фронту, 0b10 по сходящему
       
            TCCR0A |= 1 << COM0A1;                        //не инвертный режим
            // TCCR0A |= ((1 << COM0A1) | (1 << COM0A0));    // инвертный режим

        sei();

        while(timer0_overflow_count < 100){}    // задержка примерно 2 секунды
        PORTB &= ~(1 << PORTB1);                // выключаем мосфет, отключаем питание сервопривода
            //EIMSK = 0b00000001;                // включаем прерывание INT0
        while(timer0_overflow_count < 250){}    // задержка примерно 3 секунды
       
        // настройка сторожевого таймера
        //cli();                                // запрещаем прерывания
        //CCP = 0xD8;                            // разблокируем запись в защищенный регистр
        //WDTCSR |= (0b00001110);                // включаем вочдог на перезагрузку, период 1 секунда
        //sei();                                // разрешаем прерывания
       
        /* Replace with your application code */
        while (1)
        {
            if ((PINB & (1 << PINB2)) == 0 && flag == 0)
                {
                    flag = 1;                    // на будущее.
                    OCR0A = servo_poz_alarm;    // задаем положение сервопривода когда ловушка сработала
                    delay_ms(100);
                    PORTB |= (1 << PORTB1);        // включаем мосфет, тем самым подаем питание на сервопривод  
                    delay_ms(2000);                // даем сервоприводу 2 секунды на то что бы повернуться
                   
                        for (;;)
                            {
                                PORTB &= ~(1 << PORTB1);    // выключаем мосфет    и зависаем на этом месте
                            }
                }
        }
    }

    void delay_ms(uint32_t value)
    {
        uint32_t tmr = timer0_overflow_count;
    //    tmr += value / 20;
        while(timer0_overflow_count < (tmr + (value / 20))){}
       
        //{
        //    __asm("NOP");
        //}
    }
     
  2. parovoZZ

    parovoZZ Гуру

    Я на таких мелкозявых мк выкидывал цифры наружу из разных мест программы. По последовательности цифр определял последовательность выполнения программы. Выкинуть можно через любой доступный интерфейс или ногодрыгом (в случае ТС)
     
  3. Антон336699

    Антон336699 Нерд

    Спасибо за помощь, отец мне точно так же сказал.
    Но я пошел своим путем, и добавил в функцию ассемблерную команду NOP, все заработало вообще как задумывалось.

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

    void delay_ms(uint32_t value)
    {
    uint32_t tmr = timer0_overflow_count;
    // tmr += value / 20;
    while(timer0_overflow_count < (tmr + (value / 20)))
    {
    __asm("NOP");
    }
    }
     
    Последнее редактирование: 7 дек 2023
  4. Asper Daffy

    Asper Daffy Иксперд

    Тем не менее, ошибка осталась, Вы её просто замаскировали. Она запросто вылезет обратно при малейших изменениях в коде.

    Кроме того, в коде имеется ещё как минимум один (может больше, внимательнее пока не смотрел) источник "редких, необъяснимых глюков".

    Я бы на Вашем месте таки дожал бы это - так не стоит оставлять. Если нужна помощь, скажите.
     
    DetSimen нравится это.
  5. Антон336699

    Антон336699 Нерд

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

    На данный момент был проведен эксперимент (по совету одного очень грамотного человека), задействован вывод РВ3 в качестве контрольки, в начале функции delay_ms на пине РВ3 поднимается высокий уровень, в конце низкий, все задержки в коде происходят с помощью этой функции. Эксперимент показал, что все работает как положено, на осциллографе временные задержки так же соответствуют по продолжительности, глюка, когда сработал компаратор при старте устройства так же не замечено. По своей неграмотности могу только строить догадки, но понять природу до конца не могу.

    У меня такое чувство, не знаю как это сказать правильно, возможно той функции задержки не хватало веса,
    что бы компилятор не считал ее бесполезной, в ней был всего один цикл, да и тот имел только условия, а его тело было пустым,
    первый раз я добавил в тело команду, что дало какой то вес и придало с лице компилятора какой то смысл ее существования,
    в крайней же раз, там добавилось действие с регистром порта, что должно было прибавить еще больше веса. Но опять же, это не объясняет почему устройство работало по разному, стартуя с разными уровнями на пине РВ2, программа то была загружена одна, но в одном случае выполнялся весь код, а в другом то ли неправильно считывалась переменная количества переполнений счетчика, возможно, перед считыванием нужно было запретить прерывания, надо будет попробовать. Возможно все так совпало, что в тот момент происходило прерывание, и данные не корректно считывались.
    Это лишь мои предположения, мне не хватает ни знания, ни опыта, что бы понять всю природу происходящего.

    код участвующий в эксперименте
    Код (C++):
    #define F_CPU 8000000UL
    #define F_TIMER 1000000UL         // частота таймера для рассчетов.

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

    #define timer0_max_count 20000UL    // до этого числа считает таймер.
    #define servo_poz_start 1400U        // позиция сервопривода в режиме ожидания, в нее он возвращается при загрузке
    #define servo_poz_alarm 2350U        // позиция сервопривода когда происходит срабатывание механизна

    volatile uint32_t timer0_overflow_count = 0;  // счетчик количества циклов переполнения таймера0
    uint8_t flag = 0x00;    // флаг срабатывания

    void delay_ms(uint32_t value);

    ISR(TIM0_OVF_vect)  // Обработчик прерывания переполнения таймера0.
    {
        timer0_overflow_count++;
        // счетчик работает с частотой 50Гц
    }


    int main(void)
    {  
        cli();            // запрещаем прерывания
        CCP = 0xD8;        // разблокируем
            CLKPSR = 0x00;    // выстявляем предделитель частоты 1
            //CLKPSR = 0x01;    // выстявляем предделитель частоты 2
            //CLKPSR = 0x02;    // выстявляем предделитель частоты 4
            //CLKPSR = 0x03;    // выстявляем предделитель частоты 8 (Default)
            //CLKPSR = 0x04;    // выстявляем предделитель частоты 16
       
        CCP = 0xD8;        // на всякий случай еще раз разблокируем
            CLKMSR = 0x00;    // Calibrated Internal 8 MHz Oscillator (Default)
            //CLKMSR = 0x01;  // Internal 128 kHz Oscillator (WDT Oscillator)
            //CLKMSR = 0x02;    // выбыраем тактирование от внешнего источника, 0x00 от внутреннего LC генератора 8МГц.       |= (1 << CLKMS1)
       
        //............................... Конфигурируем регистры портов
            // PB0(выход) - подключена серва, PB1(выход) - мосфет, PB2(вход) - вход с компаратора
            DDRB = 0b00001011;      // устанавили пин PB0 и PB1 как выходы.
            PUEB = 0x00;            // отключаем подтягивающие резисторы порта B
            PORTB &= ~((1 << PORTB1) | (1 << PORTB3));    // устанавливаем низкий уровень на PB1 (мосфет питания сервопривода)
           
        //............................... Выбор предделителя таймера0
            TCCR0A = 0; // обнуляем регистры таймера
            TCCR0B = 0;
           
            //TCCR0B |= (1 << CS00);                // 1
            TCCR0B |= (1 << CS01);                // 8  // с данным предделителем работает не стабильно, но точность 1мк секунда
            //TCCR0B |= (1 << CS00) | (1 << CS01);  // 64   // точность 8мк секунд, но работает стабильнее
            //TCCR0B |= (1 << CS02);                // 256
            //TCCR0B |= (1 << CS02) | (1 << CS00);  // 1024
        //............................... Быбор режима работы таймера0
            //TCCR0A |= (1 << WGM00); TCCR0B |= (1 << WGM02);                   // Fast PWM 8-bit, 255
            //TCCR0A |= (1 << WGM01); TCCR0B |= (1 << WGM02);                   // Fast PWM 9-bit, 511
            //TCCR0A |= (1 << WGM00) | (1 << WGM01); TCCR0B |= (1 << WGM02);    // Fast PWM 10-bit, 1023
            TCCR0A |= (1 << WGM01); TCCR0B |= (1 << WGM02) | (1 << WGM03); ICR0 = (timer0_max_count - 1);  // Fast PWM любой битности
            OCR0A = servo_poz_start;    // задаем стартовое положение сервопривода
            PORTB |= (1 << PORTB1);        // включаем мосфет, тем самым подаем питание на сервопривод
        //............................... Разрешаем прерывания
            TIMSK0 |= (1 << TOIE0);   // Разрешаем прерывание по переполнению таймера0
            //EICRA = 0b00000011;      // прерывание INT0 на пине PB2 срабатывает по восходящему фронту, 0b10 по сходящему
       
            TCCR0A |= 1 << COM0A1;                        //не инвертный режим
            // TCCR0A |= ((1 << COM0A1) | (1 << COM0A0));    // инвертный режим

        sei();

        delay_ms(2000);    // задержка примерно 2 секунды
        PORTB &= ~(1 << PORTB1);                // выключаем мосфет, отключаем питание сервопривода
            //EIMSK = 0b00000001;                // включаем прерывание INT0
        delay_ms(3000);    // задержка примерно 3 секунды
       
        // настройка сторожевого таймера
        //cli();                                // запрещаем прерывания
        //CCP = 0xD8;                            // разблокируем запись в защищенный регистр
        //WDTCSR |= (0b00001110);                // включаем вочдог на перезагрузку, период 1 секунда
        //sei();                                // разрешаем прерывания
       
        /* Replace with your application code */
        while (1)
        {
            if ((PINB & (1 << PINB2)) == 0 && flag == 0)
                {
                    flag = 1;
                    OCR0A = servo_poz_alarm;    // задаем положение сервопривода когда ловушка сработала
                    delay_ms(100);
                    PORTB |= (1 << PORTB1);        // включаем мосфет, тем самым подаем питание на сервопривод  
                    delay_ms(2000);                // даем сервоприводу 2 секунды на то что бы повернуться
                   
                        for (;;)
                            {
                                PORTB &= ~(1 << PORTB1);    // выключаем мосфет  
                            }
                }
        }
    }

    void delay_ms(uint32_t value)
    {
        PORTB |= 1 << PORTB3;
        uint32_t tmr = timer0_overflow_count;
    //    tmr += value / 20;
        while(timer0_overflow_count < (tmr + (value / 20))){}
            //{
            //    __asm("NOP");
            //}
        PORTB &= ~(1 << PORTB3);
    }
     
  6. Asper Daffy

    Asper Daffy Иксперд

    Да, нет, конечно, дело не в "весе", постараюсь сегодня в течении дня объяснить.

    Вот этот последний код - он "плохой", а если раскомментировать строку "__asm("NOP");" (ну, и со скобками разобраться), то становится "хорошим", правильно? Скажите, чтобы я имел отправную точку. Если это не так, то дайте мне "плохой" и "хороший" коды чтобы я время не терял.

    По поводу ещё одного источника "редких, необъяснимых глюков".

    Посмотрите на свою строку
    При такой записи, Вы словите глюк когда переменная timer0_overflow_count подойдёт к своей верхней границе настолько, что tmr +(value /20) переполнится и начнёт "снова с нуля". Тогда задержка слетит, т.к. выражение timer0_overflow_count <(tmr +(value /20)) сразу же будет истинным. Вот здесь написано как такие вещи делать правильно, посмотрите и переделайте.
     
    KindMan и DetSimen нравится это.
  7. Антон336699

    Антон336699 Нерд

    В моем первом посте код, который работал не правильно, но если в нем раскоментировать строку "__asm("NOP");", то он начинает работать нормально. Это есть пример плохого и хорошего кода.

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

    И еще хотел добавить, что бы не сбивать с толку про плохой и хороший код. Тот код что выложил в пятом посте, тоже работает хорошо, там был эксперимент, который попросил сделать один человек.
     
  8. parovoZZ

    parovoZZ Гуру

    а с какой целью применено деление?
     
  9. Антон336699

    Антон336699 Нерд

    если вы об этом
    while(timer0_overflow_count < (tmr + (value /20)))
    делю на 20 что бы перевести миллисекунды в мои попугаи, сугубо для моего удобства восприятия, от него можно легко отказаться, и это будет сделано позже. Код не заработал как нужно, и все мысли ушли в сторону почему он так работает, мысли по оптимизации пока ждут в стороне.
     
  10. parovoZZ

    parovoZZ Гуру

    так отсчитывай в другой переменной свои миллисекунды, если это невозможно сделать на компораторе таймера. Деление- это история мутная всегда.
     
  11. Антон336699

    Антон336699 Нерд

    Знаю, что деление и float зло, и занимает много времени на расчет, так как не имеет аппаратной поддержки, да и код этот больше набросок скелета, как уже писал, появилась заморочка, которая увела акцент в сторону))))))
     
  12. Антон336699

    Антон336699 Нерд

    Вчера слегка оптимизировал код, и проблема что описывал вообще ушла, подозреваю что это было стечение обстоятельств.
    Вот крайняя версия
    Код (C++):
    #define F_CPU 8000000UL
    #define F_TIMER 1000000UL                            // частота таймера для рассчетов.

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

    #define timer0_max_count 20000UL                    // до этого числа считает таймер.
    #define servo_poz_start 1400U                        // позиция сервопривода в режиме ожидания, в нее он возвращается при загрузке
    #define servo_poz_alarm 2350U                        // позиция сервопривода когда происходит срабатывание механизна

    volatile uint32_t timer0_overflow_count = 0;        // счетчик количества циклов переполнения таймера0
    uint8_t flag = 0x00;                                // флаг срабатывания

    void delay_ms(uint32_t value);

    ISR(TIM0_OVF_vect)                                    // Обработчик прерывания переполнения таймера0.
    {
        timer0_overflow_count++;
        // счетчик работает с частотой 50Гц, переполнение произойдет через 994 дня
    }


    int main(void)
    {  
        cli();                                            // запрещаем прерывания
        CCP = 0xD8;                                        // разблокируем
            CLKPSR = 0x00;                                // выстявляем предделитель частоты 1
             
        CCP = 0xD8;                                        // на всякий случай еще раз разблокируем
            CLKMSR = 0x00;                                // Calibrated Internal 8 MHz Oscillator (Default)
             
        //............................... Конфигурируем регистры портов
            // PB0(выход) - подключена серва, PB1(выход) - мосфет, PB2(вход) - вход с компаратора
            DDRB = 0b00000011;                            // устанавили пин PB0 и PB1 как выходы.
            PUEB = 0x00;                                // отключаем подтягивающие резисторы порта B
            PORTB &= ~(1 << PORTB1);                    // устанавливаем низкий уровень на PB1 (мосфет питания сервопривода)
         
        //............................... Выбор предделителя таймера0
            TCCR0A = 0;                                    // обнуляем регистры таймера
            TCCR0B = 0;
         
            TCCR0B |= (1 << CS01);                        // предделитель таймера на 8
                 
        //............................... Быбор режима работы таймера0
            TCCR0A |= (1 << WGM01); TCCR0B |= (1 << WGM02) | (1 << WGM03); ICR0 = (timer0_max_count - 1);    // Fast PWM любой битности
            OCR0A = servo_poz_start;                                                                        // задаем стартовое положение сервопривода
            PORTB |= (1 << PORTB1);                                                                            // включаем мосфет, тем самым подаем питание на сервопривод
         
        //............................... Разрешаем прерывания
            TIMSK0 |= (1 << TOIE0);                            // Разрешаем прерывание по переполнению таймера0
             
            TCCR0A |= 1 << COM0A1;                            //не инвертный режим
         
        sei();

        delay_ms(100);                                        // задержка примерно 2 секунды
        PORTB &= ~(1 << PORTB1);                            // выключаем мосфет, отключаем питание сервопривода
        delay_ms(150);                                        // задержка примерно 3 секунды
         
        while (1)
        {
            if ((PINB & (1 << PINB2)) == 0 && flag == 0)
                {
                    flag = 1;
                    OCR0A = servo_poz_alarm;                // задаем положение сервопривода когда ловушка сработала
                    delay_ms(5);                            // задержка 0,1 секунды
                    PORTB |= (1 << PORTB1);                    // включаем мосфет, тем самым подаем питание на сервопривод  
                    delay_ms(100);                            // даем сервоприводу 2 секунды на то что бы повернуться
                    PORTB &= ~(1 << PORTB1);                // выключаем мосфет  
                 
                }
        }
    }

    void delay_ms(uint32_t value)
    {
        // 50 попугаев это 1 секунда  
        uint32_t tmr = timer0_overflow_count + value;

        while(timer0_overflow_count < tmr){}
             
    }
     
    Последнее редактирование: 8 дек 2023
  13. Asper Daffy

    Asper Daffy Иксперд

    Не надо никаких проверок, просто привыкните всегда использовать вычитание, а не сложение и забудьте о переполнении от слова совсем. Это (использование вычитания) должно стать железной привычкой.

    Вчера не получилось, сейчас займусь Вашим кодом.
     
  14. Антон336699

    Антон336699 Нерд

    Как вариант, согласен, попробую применить у себя, спасибо.
     
  15. b707

    b707 Гуру

    откуда эта цифра?
     
  16. Asper Daffy

    Asper Daffy Иксперд

    К сожалению, модель 10-ки в протеусе у меня нерабочая, а живого чипа нет, так что придётся "вслепую".

    Я тут попробовал немного поменять Ваш код из первого сообщения. Попробуйте так и скажите поменялось ли что-нибудь.

    Код (C++):
    /*
    * MousKiller_1.cpp
    *
    * Created: 02.12.2023 11:25:52
    * Author : Администратор
    */


    #define F_CPU 8000000UL
    #define F_TIMER 1000000UL         // частота таймера для рассчетов.

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

    #define timer0_max_count 20000UL    // до этого числа считает таймер.
    #define servo_poz_start 1400U        // позиция сервопривода в режиме ожидания, в нее он возвращается при загрузке
    #define servo_poz_alarm 2350U        // позиция сервопривода когда происходит срабатывание механизна

    volatile uint16_t timer0_overflow_count = 0;  // счетчик количества циклов переполнения таймера0
    uint8_t flag = 0x00;    // флаг срабатывания

    void delay_ms(uint16_t value);

    uint16_t getOverflowCount() {
        cli();
        const uint16_t result = timer0_overflow_count;
        sei();
        return result;
    }

    ISR(TIM0_OVF_vect)  // Обработчик прерывания переполнения таймера0.
    {
        timer0_overflow_count++;
        // счетчик работает с частотой 50Гц
    }


    int main(void)
    {
        cli();            // запрещаем прерывания
        CCP = 0xD8;        // разблокируем
        CLKPSR = 0x00;    // выстявляем предделитель частоты 1
        //CLKPSR = 0x01;    // выстявляем предделитель частоты 2
        //CLKPSR = 0x02;    // выстявляем предделитель частоты 4
        //CLKPSR = 0x03;    // выстявляем предделитель частоты 8 (Default)
        //CLKPSR = 0x04;    // выстявляем предделитель частоты 16
       
        CCP = 0xD8;        // на всякий случай еще раз разблокируем
        CLKMSR = 0x00;    // Calibrated Internal 8 MHz Oscillator (Default)
        //CLKMSR = 0x01;  // Internal 128 kHz Oscillator (WDT Oscillator)
        //CLKMSR = 0x02;    // выбыраем тактирование от внешнего источника, 0x00 от внутреннего LC генератора 8МГц.       |= (1 << CLKMS1)
       
        //............................... Конфигурируем регистры портов
        // PB0(выход) - подключена серва, PB1(выход) - мосфет, PB2(вход) - вход с компаратора
        DDRB = 0b00000011;      // устанавили пин PB0 и PB1 как выходы.
        PUEB = 0x00;            // отключаем подтягивающие резисторы порта B
        PORTB &= ~(1 << PORTB1);    // устанавливаем низкий уровень на PB1 (мосфет питания сервопривода)
       
        //............................... Выбор предделителя таймера0
        TCCR0A = 0; // обнуляем регистры таймера
        TCCR0B = 0;
       
        //TCCR0B |= (1 << CS00);                // 1
        TCCR0B |= (1 << CS01);                    // 8
        //TCCR0B |= (1 << CS00) | (1 << CS01);  // 64   // точность 8мк секунд, но работает стабильнее
        //TCCR0B |= (1 << CS02);                // 256
        //TCCR0B |= (1 << CS02) | (1 << CS00);  // 1024
        //............................... Быбор режима работы таймера0
        //TCCR0A |= (1 << WGM00); TCCR0B |= (1 << WGM02);                   // Fast PWM 8-bit, 255
        //TCCR0A |= (1 << WGM01); TCCR0B |= (1 << WGM02);                   // Fast PWM 9-bit, 511
        //TCCR0A |= (1 << WGM00) | (1 << WGM01); TCCR0B |= (1 << WGM02);    // Fast PWM 10-bit, 1023
        TCCR0A |= (1 << WGM01); TCCR0B |= (1 << WGM02) | (1 << WGM03); ICR0 = (timer0_max_count - 1);  // Fast PWM любой битности
        OCR0A = servo_poz_start;    // задаем стартовое положение сервопривода
        PORTB |= (1 << PORTB1);        // включаем мосфет, тем самым подаем питание на сервопривод
        //............................... Разрешаем прерывания
        TIMSK0 |= (1 << TOIE0);   // Разрешаем прерывание по переполнению таймера0
        //EICRA = 0b00000011;      // прерывание INT0 на пине PB2 срабатывает по восходящему фронту, 0b10 по сходящему
       
        TCCR0A |= 1 << COM0A1;                        //не инвертный режим
        // TCCR0A |= ((1 << COM0A1) | (1 << COM0A0));    // инвертный режим

        sei();

        while(getOverflowCount() < 100){}    // задержка примерно 2 секунды
        PORTB &= ~(1 << PORTB1);                // выключаем мосфет, отключаем питание сервопривода
        //EIMSK = 0b00000001;                // включаем прерывание INT0
        while(getOverflowCount() < 250){}    // задержка примерно 3 секунды
       
        // настройка сторожевого таймера
        //cli();                                // запрещаем прерывания
        //CCP = 0xD8;                            // разблокируем запись в защищенный регистр
        //WDTCSR |= (0b00001110);                // включаем вочдог на перезагрузку, период 1 секунда
        //sei();                                // разрешаем прерывания
       
        /* Replace with your application code */
        while (1)
        {
            if ((PINB & (1 << PINB2)) == 0 && flag == 0)
            {
                flag = 1;                    // на будущее.
                OCR0A = servo_poz_alarm;    // задаем положение сервопривода когда ловушка сработала
                delay_ms(100);
                PORTB |= (1 << PORTB1);        // включаем мосфет, тем самым подаем питание на сервопривод
                delay_ms(2000);                // даем сервоприводу 2 секунды на то что бы повернуться
               
                for (;;)
                {
                    PORTB &= ~(1 << PORTB1);    // выключаем мосфет    и зависаем на этом месте
                }
            }
        }
    }

    void delay_ms(uint16_t value) {
        const uint16_t startDelayTime = getOverflowCount();
        while(getOverflowCount() - startDelayTime < value / 20) {
        //    __asm("NOP");
        }
    }

    Кроме того, мне неясно зачем Вы вообще затеяли эту бодягу с самодельным delay_ms, чем Вас не устраивают штатные _delay_ms и _delay_us? Заодно бы избавились от переменной timer0_overflow_count, которая у Вас почти половину памяти отжирает. Но, давайте сначала отладимся в учебных целях, а потом у от неё избавимся.
     
  17. Антон336699

    Антон336699 Нерд

    2 в 32 степени = 4 294 967 296 переполнение происходит 50 раз в секунду
    4 294 967 296 / 50 = 85 899 345,92 столько секунд будет переполняться переменная
    85 899 345,92 / 60 = 1 431 655,76533333 столько минут будет переполняться переменная
    1 431 655,76533333 / 60 = 23 860,92942222 столько часов.....
    23 860,92942222 / 24 = 994,205 столько дней.
     
  18. Asper Daffy

    Asper Daffy Иксперд

    А мой код попробуете?
     
  19. Антон336699

    Антон336699 Нерд

    Спасибо, у меня все на работе, попробовать смогу только завтра. Основную идею по считыванию счетчика переполнения понял, на счет нее у меня была другая идея, в том же обработчике прерывания производить копирование этого счетчика в другую глобальную переменную или локальную статическую переменную, а уже ее считывать. По поводу протеуса и тиньки10, у меня он позволяет запустить код, но код нужно писать на ассемблере.
    По поводу штатной функции задержки, согласен, она более подходящая, сейчас почитал про нее, она оказывается работает на циклах работает, а я думал что она таймер задействует, тогда мне прерывания вообще не нужны, таймер работает автономно, я ему только регистр сравнения для генерации шим изменяю. Завтра перепишу код.
     
  20. Asper Daffy

    Asper Daffy Иксперд

    Нельзя. Вернее можно, но также, через cli/sei при чтении, а тогда смысл?

    Там проблема в чём? Чтение неатомарно, Вы все байты читаете по очереди. Если один уже прочитали, а тут прерывание и переменная изменилась, Вы после прерывания будете дочитывать хвост уже новой переменной, а прочитанная до прерывания голова от старой останется.

    Думаю Ваша проблема была именно в этом, а добавления NOP (или любой другой хрени) чуть сместило тайминги и чтение перестало прерываться прерыванием.
     
    Антон336699 нравится это.