Jump to content
123654789

Проблемы С Кодом, Прерывание Вызывается Дважды?

Recommended Posts

Пишу прошивку для управления всяким с пульта от телека, столкнулся с непонятным мне поведением программы (об этом ниже).

Прошивка:

#define F_CPU 4800000L // 4.8 Мгц
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define INVERT_PIN(x) PORTB ^= x
/*
   PB2 (7) - реле (ВЫХОД).
   PB1 (PCINT1) (6) - ИК-приёмник (ВХОД).
   PB0 (PCINT0) (5) - сенсорная панель (ВХОД).
   PB3 (2) - светодиод (ВЫХОД).
   PB4 (PCINT4) (3) - кнопка (ВХОД).
*/

volatile char is_sensor_active = 0;
volatile unsigned int rc_code = 0;
volatile unsigned int etalon_rc_code = 0;

// http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__util__delay_1gad22e7a36b80e2f917324dc43a425e9d3.html
// 262.14 / 4.8 = 54.61 мс - это максимальная задержка для данной частоты, до которой можно использовать _delay_ms.
void delay_ms(unsigned int period) {
   while(--period) {
    _delay_ms(1);
   }
}

ISR(PCINT0_vect) {
   is_sensor_active = 1;
}

int main() {
   DDRB = 0b00001100;
   // Срабатывание прерывания при смене логического уровня на ножках.    
   MCUCR = 0b00000001;
   // Прерывание генерятся при изменении уровня на ножках.
   GIMSK = 0b00100000;
   // Выбор генерящих прерывание пинов.
   PCMSK = 0b00000010;
   sei();
   while(1) {
    if( is_sensor_active ) {
	    cli();
	    rc_code = 0;
	    for( int i = 0; i < sizeof(rc_code) * 8; ++i) {
		    rc_code = (rc_code << 1) + ((PINB & 0b00000010) >> 1);
		    _delay_ms(16);		    
	    }
	    if( etalon_rc_code == 0)
		    etalon_rc_code = rc_code;
	    if( etalon_rc_code == rc_code) {
		    INVERT_PIN(0b00000100);		    
	    } else {
		    for( int i = 0; i < 10; ++i) {
			    INVERT_PIN(0b00001000);
			    _delay_ms(30);
		    }
	    }
	    is_sensor_active = 0;
	    delay_ms(240);
	    sei();	    
    }
   }
   return 0;
}

Как я ожидаю она должна работать: как только на ножке 6 сменится логический уровень, с ножки же 6 будет считано четырехбайтовое беззнаковое целое, принято за эталон команды и на ножке 7 установится лог. 1. При повторном зажигании прерывания снова будет считана команда, сравнена с эталонной и, если совпадает, инвертируется ножка 7. В противном случае помигать ножкой 2.

Как оно на самом деле работает: вне зависимости от была ли команда верной или нет, контроллер дергает ножкой 2. Отсюда я делаю вывод, прерывание почему-то зажигается дважды. Но ведь не должно же! Вопрос: что же я упустил, приводящее к такому поведению? При обработке команды я прерывания запрещаю же, потом обратно включаю.

Edited by 123654789

Share this post


Link to post
Share on other sites

У AVR не нужно сбрасывать флаг прерывания в обработчике, чтобы прерывание не вызывалось по кругу?

И ещё, на всякий случай занулять is_sensor_active лучше после задержки, а не перед, чтобы прерывание от последнего бита не установило в 1 лишний раз.

Edited by Yurkin2015

Share this post


Link to post
Share on other sites

Изготовление 2-х слойных плат от 2$, а 4-х слойных от 5$!

Быстрое изготовление прототипа платы всего за 24 часа! Прямая доставка с нашей фабрики!

Смотрите видео о фабрике JLCPCB: https://youtu.be/_XCznQFV-Mw

Посетите первую электронную выставку JLCPCB https://jlcpcb.com/E-exhibition чтобы получить купоны и выиграть iPhone 12, 3D-принтер и так далее...

Проблема в другом: блок

for( int i = 0; i < 10; ++i) {
INVERT_PIN(0b00001000);
_delay_ms(30);
}

вызывается всякий раз, вне зависимости от принятого кода. Притом блок с условием etalon_rc_code == rc_code работает как ожидалось: срабатывает, когда пришел запомненный код и не срабатывает, когда пришел незапомненный (сужу, жамкая разные кнопки на пульте). Согласно коду, проблемная ситуация может возникнуть только если в блок считывания и проверки выполняется более одного раза.

Тем временем, в паспорте читаю (выделение моё):

There are basically two types of interrupts. The first type is triggered by an event that sets the

Interrupt Flag. For these interrupts, the Program Counter is vectored to the actual Interrupt Vec-

tor in order to execute the interrupt handling routine, and hardware clears the corresponding

Interrupt Flag. Interrupt Flags can also be cleared by writing a logic one to the flag bit position(s)

to be cleared. If an interrupt condition occurs while the corresponding interrupt enable bit is

cleared, the Interrupt Flag will be set and remembered until the interrupt is enabled, or the flag is

cleared by software. Similarly, if one or more interrupt conditions occur while the Global Interrupt

Enable bit is cleared, the corresponding Interrupt Flag(s) will be set and remembered until the

Global Interrupt Enable bit is set, and will then be executed by order of priority.

Таким образом, моим следующим шагом будет попробовать очищать флаг PCIF в регистре GIFR перед включением прерывания (после распознавания команды и прочей логики) и посмотреть, что из этого выйдет.

Edited by 123654789

Share this post


Link to post
Share on other sites

Высокая надежность SiC! Как они этого добились?

За несколько лет кропотливых исследований и совершенствования технологии компания Infineon смогла довести показатели надежности и стабильности параметров высоковольтных и быстродействующих карбид-кремниевых транзисторов линейки CoolSiC практически до уровня их кремниевых собратьев.

Подробнее

И надо выкинуть все запреты прерывания sei и cli. Они только мешают.

Прерывания всегда разрешены. При поступлении первого прерывания is_sensor_active устанавливается в 1, и запускается процедура чтения данных. Пусть при этом происходят новые прерывания от каждого бита в коде - это не важно, т.к. is_sensor_active больше не проверяется.

После чтения и паузы зануляем is_sensor_active=0 и снова ждём начала следующей посылки. Главное, чтобы пауза была меньше времени между посылками кодов.

Судя по описанию, флаги сбрасываются автоматически при переходе на обработчик прерывания - тогда ничего и делать не надо в обработчике, а только устанавливать is_sensor_active = 1.

Edited by Yurkin2015

Share this post


Link to post
Share on other sites

Материалы вебинара «STM32L5. Секреты оценки энергопотребления»

Опубликованы запись и материалы вебинара КОМПЭЛ, посвященного первому семейству МК STM32L5 на ядре Cortex-M33. На вебинаре было рассказано о самых распространенных ошибках при расчете энергопотребления микроконтроллеров и о специальном тесте ULPMark, позволяющем дать наиболее объективную оценку энергоэффективности. Измерения проводились на демонстрационной платформе STM32L562E-DK.

Подробнее

Тут до меня дошло, наконец, что была за проблема в Вашем коде: при поступлении первого бита прерывания запрещались, читался весь код, пауза, затем прерывания разрешались. Но пока данные читались, были ещё прерывания от битов. Они были запрещены, но запомнились. Поэтому сразу же после разрешения прерываний тут же вызывалось отложенное, запомненное прерывание и переменная is_sensor_active устанавливалась 1, хотя никакой посылки и не было. Программа честно читала все нули с ноги, или все единицы, и записывала, как эталон.

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

Share this post


Link to post
Share on other sites

Снижена цена на AC/DC и DC/DC преобразователи Mornsun в Компэл!

Компэл и компания Mornsun снизили цены на преобразователи AC/DC-преобразователи семейств LS и LDE. По привлекательной цене также предлагаются DC/DC-преобразователи изолированных семейств поколений R2 и R3 различного конструктивного исполнения.

Подробнее

Да, всё именно так: проблемы вызывает поднятый флаг прерывания. Решить можно двумя способами: убрать из кода запрещение/разрешение прерываний (в данном случае запрещать ничего не требуется, поэтому я выбрал этот путь) или вручную сбрасывать флаг прерывания через регистр GIFR.

Исправленный код прилагаю.

#define F_CPU 4800000L // 4.8 Мгц
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define INVERT_PIN(x) PORTB ^= x
/*
   PB2 (7) - реле (ВЫХОД).
   PB1 (PCINT1) (6) - ИК-приёмник (ВХОД).
   PB0 (PCINT0) (5) - сенсорная панель (ВХОД).
   PB3 (2) - светодиод (ВЫХОД).
   PB4 (PCINT4) (3) - кнопка (ВХОД).
*/
volatile char is_sensor_active = 0;
volatile unsigned int rc_code = 0;
volatile unsigned int etalon_rc_code = 0;
// http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__util__delay_1gad22e7a36b80e2f917324dc43a425e9d3.html
// 262.14 / 4.8 = 54.61 мс - это максимальная задержка для данной частоты, до которой можно использовать _delay_ms.
void delay_ms(unsigned int period) {
   while(--period) {
    _delay_ms(1);
   }
}
ISR(PCINT0_vect) {
   is_sensor_active = 1;
}
int main() {
   DDRB = 0b00001100;
   // Срабатывание прерывания при смене логического уровня на ножках.   
   MCUCR = 0b00000001;
   // Прерывание генерятся при изменении уровня на ножках.
   GIMSK = 0b00100000;
   // Выбор генерящих прерывание пинов.
   PCMSK = 0b00000010;
   sei();
   while(1) {
    if( is_sensor_active ) {
	    rc_code = 0;
	    for( int i = 0; i < sizeof(rc_code) * 8; ++i) {
		    rc_code = (rc_code << 1) + ((PINB & 0b00000010) >> 1);
		    _delay_ms(16);		   
	    }
	    if( etalon_rc_code == 0)
		    etalon_rc_code = rc_code;
	    if( etalon_rc_code == rc_code) {
		    INVERT_PIN(0b00000100);		   
	    } else {
		    for( int i = 0; i < 10; ++i) {
			    INVERT_PIN(0b00001000);
			    _delay_ms(30);
		    }
	    }
	    is_sensor_active = 0;
	    delay_ms(240);	   
    }
   }
   return 0;
}

Share this post


Link to post
Share on other sites

Ну, и для полноты совершенства программы нужно добавить следующее. При обнаружении первого перепада на ножке у Вас сразу же стартует цикл считывания состояния этой ножки и задвига значения в переменную rc-code. То есть опрос входа происходит на границе между битами, в самом-самом начале бита.

Это не хорошо. За время приема 48 битов период передатчика и период опроса неизбежно разъедутся по времени. Хорошо, если момент опроса уедет внутрь бита. Но если эти 16мс в реале немного меньше, чем период передатчика, то за время приёма кода опрос сдвинется и выйдет за пределы бита. Таким образом предыдущий бит почитается дважды, будет ошибка приёма.

Для исправления ситуации после обнаружения is-sensor-active = 1 надо добавить задержку на половину длительности бита, 8мс, а уже потом начинать цикл опроса ножки. Тогда момент опроса будет посредине бита, и небольшие расхождения периодов передатчика и приёмника уже не волнуют.

Share this post


Link to post
Share on other sites

На счет прерываний лучше поступить немного по-другому. При обработке флага запретить только PCINT, а лучше - только PCINT.1. Если будете использовать другие прерываний, от таймеров, от АЦП, мало ли от чего еще, они не будут заблокированы. А после приема данных, сбрасываете флаг (мало ли как он мог взвестись), и разрешаете именно нужное прерывание.

А на аппаратных модулях не получается сделать? Таймер, UART, SPI, USI теоретически могли бы пригодиться.

Ну и традиционно, совет по форматированию:

DDRB = 0b00001100;

Неплохая запись, но можно использовать DDRB = (1<<2 | 1<<3);

MCUCR = 0b00000001;

Как и с большинством регистров настроек, лучше записать сдвигом. MCUCR = (1<<ISC00); что сразу даст понять, что в данном случае заполнять его не нужно вовсе, он не влияет на PCINT, а прерывания INT0, INT1,... не используются

GIMSK = 0b00100000;

Аналогично, лучше записать GIMSK = (1<<PCIE); чтобы не пришлось лезть в даташит смотреть, за что же отвечает 5-й бит.

PCMSK = 0b00000010;

Как и с DDRB можно записать PCMSK = (1<<1); но, в принципе, обе записи неплохи.


Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Share this post


Link to post
Share on other sites

Советы применил, спасибо. Обнаружил еще одну неточность: выше я пишу

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

Это неверно. int в AVR занимает 2 байта, не 4. Источник: https://gcc.gnu.org/wiki/avr-gcc

Для четырехбайтового целого следовало использовать long.

А на аппаратных модулях не получается сделать?

Не понимаю вопроса, аппаратные модули это что в данном контексте? Конструкторы a-la Arduino? Я же так в контроллеры не научусь никогда.

Edited by 123654789

Share this post


Link to post
Share on other sites

Я же написал - таймер, UART, USI. Я не знаю, что у вас за контроллер и какие модули там доступны. Arduino или cvavr я точно не имел в виду.


Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Share this post


Link to post
Share on other sites

В нем есть обычный таймер, собака и, в порядке извращения, АЦП. Если таймер нигде не используется, можно воспользоваться им, будет немного точнее, чем задержками. Достаточно ли точности у watchdog и АЦП не знаю (сомнительно), как и целесообразность подобных извращений.


Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Сообщения

    • Ещё один туда же. А почему вы все считаете, что если человек попросил помочь с ремонтом, то обязательно идиот или *****й? Посмотрите на себя лет так ...дцать назад. И я ничего не делаю без консультаций со специалистом. У меня с электроникой давно дружеские отношения, вот с инверторами только третий раз сталкиваюсь. До этого монитор отремонтировал с помощью спецов, был ещё телевизор. И не надо смотреть на других свысока! Это не культурно! Никто её не выдирал!!! Видимо не те буквы смотрели)))
    • 24 В при токе 2.5 А это 60 Вт. 27000 вольт при 60 Вт это ток один-два миллиампера. Которые имеют ток утечки выше миллиампера. Собственно, заряжать столь слабым источником не получится, что ты и получил на практике. Перематывай трансформатор на гораздо меньшее напряжение, но на гораздо больший ток, хотя бы в 30 раз!
    • Карочь ребя  , все по  фне шую ...  Холодильник в кухню , капутер  в гостинную ...  
    • Делаю зарядное устройство для электролитических конденсаторов, заряжать решил от ТДКС и схемы ZVS драйвера. Питание беру от лабораторного источника питания (выставляю около 24В и 2,5 А). Зарядить нужно блок флэш электролитов (те, что применяются в вспышке фотоаппаратов, 330В, 120 мкФ каждый) со смешанным подключением (общей емкостью 1280 мФ и напряжением 990В). Этот способ уже рассмотрен здесь: https://youtu.be/et0DtOzbB0U https://youtu.be/t7iZNVMdrU8 Подключил ТДКС к блоку - напряжение зарядки выше 60В не растёт. То есть, показание на мультиметре доходит до 60В и остаётся на месте. При этом, без нагрузки ТДКС работает стабильно, выдаёт хорошую дугу и напряжение выше киловольта. Сами конденсаторы - рабочие. Чем может быть связана такая просадка? Пробовал подключать отдельно линию из 10 параллельных конденсаторов - напряжение достигает максимум 30 В.
    • Если жесткий диск механический, то ему такое не понравится, надо ццд ставить.

  • Прецизионный многооборотный потенциометр 10к

  • Similar Content

    • By andre40
      Привет всем.
      Дано - ATTiny13a, 3 светодиода(обычные, 5 мм) и кнопка без фиксации. Задача - при подаче напруги загорается свет. №1 и горит. При нажатии на кнопу он гаснет и загораются свет. №2 и 3. Нажали ещё раз - всё наоборот, т.е. №2 и 3 гаснут, а загорается №1. И т.д. Кнопа должна срабатывать на отпускание, т.е. держи её нажатой сколько хочешь, но срабатывание происходит при её отпускании. Ну и дребезг учесть. Результат в виде листинга(если это так называется?), чтоб я вставил в Atmel Studio 7 и скомпилировал. Хорошо, если будут комментарии. Бюджет - 150р.  Срок - лучше сегодня, можно завтра.
    • By Сергей Фомин
      Изучаю прерывания на attiny13. Пока остановился на прерываниях по переполнению. Сделал тестовый код в Atmel Studio и сразу через программатор заливаю на тиньку со светодиодом. Проблема в том что гореть он должен 10 секунд и выключаться, а горит примерно 20-23 секунды. Прошу помощи в правильном расчёте. Код ниже (пока учусь сильно не ругайтесь) :
      #define F_CPU 1000000 #define LED PB2 #include <avr/io.h> //#include <util/delay.h> #include <avr/interrupt.h> unsigned char work_time =384;    //  1000000/1024/256=3.8  (0.026 сек)    10/0.026=384 volatile unsigned char temp =0; ISR (TIM0_OVF_vect) {      TCNT0=0x00;     temp ++;     if (temp>=work_time)     {         PORTB &=~(1<<LED);  //Инвертируем состояние         TCCR0B=0x00    // остановка таймера         cli (); //общее запрещение прерываний     } } int main (void) {     init();               while (1)     {              } } void init () {          DDRB |= (1<<LED); // выход     PORTB =(1<<LED); //включен     TCCR0B =0x05; // установка делителя на 1024     TIMSK0 |= _BV(TOIE0);     sei();   // Либо SREG |= (1<<SREG_I); //Разрешаем прерывания глобально     TCNT0 = 0X00;        //Обнулить счётный регистр }  
    • By katet
      Добрый день. Может быть,кто-нибудь уже сталкивался с таким. 
      Занимаюсь доработкой чужого проекта в среде STM32CubeMX, первый раз работаю с библиотекой HAL.
      В этом проекте осуществлялся прием байт по USART1 из ComMon. Проект был открыт в STM32CubeMX, где мной дополнительно были активированы новые модули - RTC, SD, USART2. Настройки USART1 не менялись. 
      Часы реального времени RTC: питание от батарейки, тактирование – от LSE. 
      При приеме байт по USART1 в новом проекте (активны  USART1, RTC, SD, USART2) было выявлено, что после приема 2 байт по USART1 перестает инкрементироваться значение переменной uwTick, отвечающей за прерывания Systick, в результате чего дальнейшая отладка невозможна. Не удается выяснить, в каком месте и почему перестает увеличиваться значение uwTick. При работе со старым проектом (где активен только USART1) uwTick инкрементируется после приема 2 байт.
      Смены приоритета прерываний не происходит, в  функцию HAL_Delay() отладчик не попадает. При вызове функции HAL_ResumeTick(), возобновляющей прерывания Systick, ничего не меняется, значение uwTick остается неизменным.
      Остановка прерываний была обнаружена при попадании в функцию:
      static HAL_StatusTypeDef UART_WaitOnFlagUntilTimeout(UART_HandleTypeDef *huart, uint32_t Flag, FlagStatus Status, uint32_t Tickstart, uint32_t Timeout)
      {
        /* Wait until flag is set */
        while((__HAL_UART_GET_FLAG(huart, Flag) ? SET : RESET) == Status) 
        {
          /* Check for the Timeout */
          if(Timeout != HAL_MAX_DELAY)
          {            
            if((Timeout == 0U)||((HAL_GetTick() - Tickstart ) > Timeout))
            {
              /* Disable TXE, RXNE, PE and ERR (Frame error, noise error, overrun error) interrupts for the interrupt process */
              CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR1_TXEIE));
              CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
              
              huart->gState  = HAL_UART_STATE_READY;
              huart->RxState = HAL_UART_STATE_READY;
              
              /* Process Unlocked */
              __HAL_UNLOCK(huart);
              
              return HAL_TIMEOUT;
            }
          }
        }
        
        return HAL_OK;
      }
      В новом проекте текущее значение uwTick, возвращаемое функцией HAL_GetTick(), всегда равно значению Tickstart. В старом проекте сначала также, но затем uwTick начинает увеличиваться. Выяснить, в каком именно месте кода значение uwTick должно начать увеличиваться, не удается.
      Может быть, у кого-нибудь есть хоть какие-то идеи, с чем может быть связана остановка увеличения значений uwTick, помогите пожалуйста)
      Распиновка и конфигурация обоих проектов: верхняя часть рисунков - первоначальный, работающий вариант,  нижняя - сбой прерываний Systick.


    • By IgnatiusF
      Не могу настроить таймер 0 на работу, и даже не получается понять в чем проблема. Делаю в Proteus, так как это быстрее и нагляднее.
      Пробовал и просто по переполнению делать прерывание (WGM[1..0]  00) и по совпадению (WGM[1..0]  10; OCR0A = 0 - 255), однако прерываний нет. Перед циклом ставлю TCNT0 = 0;
      Тактирование выставляю TCCR0B (CS[2..0] 001, 100, 101).
      Прерывания TIMSK0 (OCIE0A, TOIE0) выставляю, не вызываются. Даже принудительным заносом значения в регистр TIFR0 (OCF0A, TOV0).
      Прерывание пытаюсь выполнить таким образом:
      #include <avr/interrupt.h> volatile unsigned int A = 0; ISR(TIMER0_OVF_vect) { A = 1; } ... while(1) { if (A == 1) PORTB |= (1<<0); } Proteus показывает, что вывод настроен как выход, но всегда 0;
      Конкретный код привести не могу, так как у меня не заработало совсем ничего.
      Внизу я сделал вырезку из даташита на ATMEGA328 по 0 таймеру 8-бит, и занес в один PDF файл.
      ATmega328-106-112.pdf
      Прошу помочь разобраться с таймером и прерываниями для него.
    • By Trisector
      Привет всем.
      Помогите с кодом, нужно озвучить нажатие кнопки, подключенной к порту PB0, то есть просто издать короткий "бип" одной частоты (примерно 1000 Гц) и длительностью примерно 0.2 секунды.
      Везде, где искал, сразу создают кучу нот, разные тона, используют прерывания, мне же надо только один короткий бип.
      Выполняться оно должно в теле цикла:
      while (1) 
          {
              while(PINB&0b00000001); // ждем нажатия на кнопку на порте PB0
      <вот здесь должен быть бип>
       
      п.с. забыл добавить - выполняться код будет на Attiny13a
×
×
  • Create New...