Собственно суть проблемы. К ногам тиньки 5 и 6 (РВ0 и РВ1) подключены две кнопки. Инициализированы прерывания PCINT0 и, в качестве источника прерываний, определены две ноги:
Спойлер
в симулятора 7 студии все отрабатывает без вопросо. Но в железе у меня получается бесконечный цикл вызова прерыания, при этом виновником является вход РВ1 (BUTTON_LUX_MEMORY). Если програмно я не определяю этот вход как источник прерывания (PCMSK |= (1<<BUTTON_SYGNAL);), то кнопка на РВ0 (BUTTON_SYGNAL) работает без нареканий. Заходим в прерывание, выполняем нужное действие, возвращаемся в основной цикл.
Осциллограмма на РВ1 в норме, напряжение питания стоит, никаких дерганий уровня не видно.
Может у кого есть идеи, в какую сторону копать?
Это - лишнее. Прерывания глобально и так запрещаются при входе в ISR, а флаг прерывания сбрасывается по выходу.
Вы нигде PB1 не дергаете случаем из программы? Ну вот, например, чему равно LED_OUT_PIN?
Ну и чтение АЦП и запись в EEPROM из ISR - очень плохая идея. В прерывании просто поднимайте флаг (переменная), что надо это сделать. А сам замер + запись делайте в основном коде. Там уже предварительно перед записью в EEPROM отключив временно глобально прерывания через cli(), а потом включив через sei().
что прерывания глобально запрещаются, а флаги сбрасываются -- знаю. Но вряд-ли эти строки загоняют контроллер в цикл с прерываниями. Но для красоты кода -- уберу.
Т.е. вы считаете, что в цикл падаю из-за чтения adc и записи в eeprom ? Ок, как рабочую версию приму, проверю. Спасибо
В тиньке13 PCINT на что реагирует? На смену уровня или на уровень? Если на уровень, то так и не будете вылезать из прерывания, пока не сменится уровень на ноге.
Добавлено after 1 minute 30 seconds:
[uquote="ARV",url="/forum/viewtopic.php?p=3998445#p3998445"]Вообще-то обрабатывать прерывание одну секунду - это, имхо, явно не хорошо.[/uquote]
Если прерывание одно, то по фигу. Можно из него вообще не вылезать. Да даже если и не одно. Как в MSP430...
[uquote="ARV",url="/forum/viewtopic.php?p=3998445#p3998445"]Вообще-то обрабатывать прерывание одну секунду - это, имхо, явно не хорошо.[/uquote]
аргументируйте, плз.
Добавлено after 2 minutes 24 seconds:
[uquote="parovoZZ",url="/forum/viewtopic.php?p=3998446#p3998446"]В тиньке13 PCINT на что реагирует? На смену уровня или на уровень? Если на уровень, то так и не будете вылезать из прерывания, пока не сменится уровень на ноге.[/uquote]
PCINT реагирует на любое изменение уровня на ноге.
Land писал(а):[uquote="parovoZZ",url="/forum/viewtopic.php?p=3998446#p3998446"]В тиньке13 PCINT на что реагирует? На смену уровня или на уровень?[/uquote]
Только на смену.
Land писал(а):аргументируйте, плз.
В любом учебнике, во многих мануалах это написано - обработчик прерывания должен быть как можно более коротким. По разным причинам. Хотя бы потому что в это время у вас не будут выполняться другие прерывания, зато потом "выстрелят" отложенно и можете на этом поиметь грабли.
Land писал(а):Т.е. вы считаете, что в цикл падаю из-за чтения adc и записи в eeprom ? Ок, как рабочую версию приму, проверю. Спасибо
Это не может быть прямой причиной, но вот много всяких граблей бывает из-за долгой обработки прерывания. Лучше сразу привыкайте никогда так не делать, даже когда кажется, что в данном случае неважно.
Опять же в read_adc() и при работе с LUX_IN_PIN нигде не можете затрагивать скажем весь регистр PORTB? Случайно где-нибудь вместо R-M-W сделали присваивание может... Ну или приведите весь код в конце концов.
А я вот вижу сброс вачдога, сброс флага INT0....либо это сделано со знанием дела, но сия тайна великая есть, либо это откуда-то "нахапано" кусками. Пока я склоняюсь к версии
затрагивать скажем весь регистр PORTB?
В любом учебнике, во многих мануалах это написано - обработчик прерывания должен быть как можно более коротким.
Это кто-то придумал, а другие тупо скопипастили. Старые тиньки никак не предназначены для обработки прерывания вне его - флаги сбрасываются в прерывании, поэтому надо свои вводить, если обработчик пустой. Либо же лупом проверять флаги постоянно...Вообщем, костыльные микры...
[uquote="NStorm",url="/forum/viewtopic.php?p=3998472#p3998472"]Опять же в read_adc() и при работе с LUX_IN_PIN нигде не можете затрагивать скажем весь регистр PORTB? Случайно где-нибудь вместо R-M-W сделали присваивание может... Ну или приведите весь код в конце концов.[/uquote]
я же не случайно написал, что программное отключение генерации прерывания по РВ1 убирает проблему. Если бы где-то затрагивался весь регистр PORTB, то...
Добавлено after 3 minutes 53 seconds:
[uquote="parovoZZ",url="/forum/viewtopic.php?p=3998479#p3998479"]А я вот вижу сброс вачдога, сброс флага INT0....либо это сделано со знанием дела, но сия тайна великая есть, либо это откуда-то "нахапано" кусками. Пока я склоняюсь к версии
затрагивать скажем весь регистр PORTB?
[/uquote]
думаете wdr зацикливает в прерывании? Или сброс флага INT0 вызывает сбой? Само-собой, как у каждого, у меня есть свои куски кода и я пользуюсь копи-пастом... пока объем памяти позволяет. Если не позволяет -- перехожу на asm.
По умолчанию INT0 сидит на прерывании по уровню. Если разрешить на INT0 прерывание, то, как я и говорил, из прерывания вылезать не будем при нуле на этом пине. Если обработчика вообще нет, то компилятор GCC выполнение кода по вектору уводит на ресет...
[uquote="Land",url="/forum/viewtopic.php?p=3998361#p3998361"]Собственно суть проблемы. К ногам тиньки 5 и 6 (РВ0 и РВ1) подключены две кнопки. Инициализированы прерывания PCINT0 и, в качестве источника прерываний, определены две ноги:[/uquote]
Попробуй… прошивка для тини13… кнопки на РВ0 и РВ1… светики на РВ3 и РВ4…
Фьюзы прошивать не нужно, тактируется на заводских установках (1,2 МГц).
Test_INT.hex
Нажал кнопку – светик загорелся. Нажал повторно – потух.
[uquote="Land",url="/forum/viewtopic.php?p=3998484#p3998484"]я же не случайно написал, что программное отключение генерации прерывания по РВ1 убирает проблему. Если бы где-то затрагивался весь регистр PORTB, то...[/uquote]
То что "то"? То нет. Вы можете остальные пины и не менять, программа по другим пинам будет как надо работать.
parovoZZ писал(а):Это кто-то придумал, а другие тупо скопипастили. Старые тиньки никак не предназначены для обработки прерывания вне его - флаги сбрасываются в прерывании, поэтому надо свои вводить, если обработчик пустой. Либо же лупом проверять флаги постоянно...Вообщем, костыльные микры...
Не совсем. Это стандартная практика. Никаких проблем нет вести свои флаги, их можно вполне расширить и не просто тупо копировать о срабатывании прерывания. Дело не в этом.
1 - кнопки это человекоинтерфейс. Реакция человека десятки, сотни миллисекунд. Реакция микроконтроллера, если кнопка повешана на прерывание - несколько тактов. Хочется спросить, куда вы так торопитесь, все равно не успеете.
На прерывание есть смысл вешать кнопки только в случае энергосбережения, спящего режима. Ещё в случае экономии ввода вывода. И то варианты.
2 - дребезг контактов.
3 - режим внешнего прерывания, по уровню или фронту.
Как здесь уже правильно подметили, могут быть неучтенные отложенные прерывания.
Вывод, разберитесь что такое дребезг контактов. Какие режимы внешнего прерывания, как правильно инициализировать внешние прерывания. И нужно ли вешать кнопки на внешние прерывания, если нет энергосбережения.
.INCLUDE "tn13def.inc"
.CSEG
.ORG $000
RJMP START
.ORG $002
IN R22,PINB
RETI
;*************************************************
START:
CBI ACSR,ACD
SBI DDRB,2
SBI DDRB,3
LDI R16, 0b00000011
OUT PORTB, R16
LDI R16,1<<PCINT1|1<<PCINT0
OUT PCMSK, R16
LDI R16,1<<PCIE ;PCIE: разрешение прерывания при смене состояния лапы
OUT GIMSK, R16
OUT GIFR,R16
LDI R16,1<<SE
OUT MCUCR,R16
SEI
WAIT_PCINT:
SLEEP
ANDI R22,0b00000011
LSL R22
LSL R22
ORI R22,0b00000011
OUT PORTB,R22
RJMP WAIT_PCINT
.EXIT
[uquote="NStorm",url="/forum/viewtopic.php?p=3998597#p3998597"][uquote="Land",url="/forum/viewtopic.php?p=3998484#p3998484"]я же не случайно написал, что программное отключение генерации прерывания по РВ1 убирает проблему. Если бы где-то затрагивался весь регистр PORTB, то...[/uquote]
То что "то"? То нет. Вы можете остальные пины и не менять, программа по другим пинам будет как надо работать.[/uquote]
то, что если бы что-то писалось в PORB тупо в лоб, то это бы вызывало в том числе и прерывание по PB0. А здесь проблема в РВ1. POTB упоминается в коде 7 раз:
Что самое забавное, зацикливание происходит даже при простой инициализации входа PB1 в качестве источника прерывания. В обработчике ISR можно вообще все закомментить, относящееся к обработке этого пина. Если кому не лень в чужом коде колупаться, листинг под катом. Ткните меня носом. Только, умоляю, не в wdr Спойлер
#define F_CPU 1200000UL // 1.2 MHz
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include <avr/sfr_defs.h>
#include <avr/sleep.h>
#include <util/delay.h>
// Declare your global variables here
#define LED_OUT_PIN PB3 //выход на светодиоды
#define Ubat_IN_PIN (1<<MUX0) //вход измерения напряжения батареи Ubat_IN_PIN ADC1D
#define BUTTON_LUX_MEMORY PB0 //вход определения нажатой кнопоки 1
#define BUTTON_SYGNAL PB1 //вход определения нажатой кнопоки 0
#define LUX_IN_PIN (1<<MUX1) //вход измерения уровня освещения LUX_IN_PIN ADC2D
#define ADC_VREF_TYPE ((0<<REFS0) | (0<<ADLAR)) //рефернесное напряжение для АЦП -- напряжение питания
uint16_t LUX_MEM EEMEM; //создаем метку адреса для значения освещенности в EEPROM
unsigned char SYGNAL_MEM EEMEM; //создаем метку адреса для сигнала мигания uint8_t
volatile unsigned char TIMER_CYCLE; //счетчик циклов таймера для мигания
volatile unsigned char SYGNAL=0xAA; //вид сигнала мигания по умолчанию
unsigned char NUM_SYGNAL=1; //номер сигнала мигания
//unsigned char BUTTON; //номер кнопки 1- запись уровня освещенности, 2 - изменение номера сигнала мигания
volatile unsigned char TIMER_SLEEP; //счетчик таймера сна
unsigned char mask; //локальная переменная маски
uint16_t value_LUX=0; //уровень освещенности с датчика
//uint16_t value_BUTTON=0; //нажатая кнопка
volatile uint16_t MEM_value_LUX; //сохраненное значение value_LUX
void key_func(void);
void sygnal_out(unsigned char);
unsigned char bat_level(void);
void LUX_level(void);
uint16_t read_adc(int);
// Pin change interrupt service routine
/*===================== ФУНКЦИИ =======================*/
void init(void)
{
// Declare your local variables here
// Port B initialization
DDRB |= (1<<LED_OUT_PIN); //выход
PORTB |= (1<<BUTTON_LUX_MEMORY) | (1<<BUTTON_SYGNAL); //входы кнопок
// INT0: Off
GIFR |= (1<<INTF0) | (1<<PCIF); // сброс флагов прерывания
PCMSK |= (1<<BUTTON_SYGNAL);//(1<<BUTTON_LUX_MEMORY) | (1<<BUTTON_SYGNAL); //прерывания по входам PCINT0, PCINT1 (PB0, PB1)
// Analog Comparator: Off
//ADCSRA |= (1<<ADPS0) | (1<<ADPS1);
ADCSRB &= ~(1<<ADTS2) & ~(1<<ADTS1) & ~(1<<ADTS0);
ADMUX |= (1<<AIN0D) | (1<<AIN1D); // Digital input buffer on AIN0: Off Digital input buffer on AIN1: Off
// ADC enabled
ADCSRA |= (1<<ADPS0) | (1<<ADPS1); // ADPS2=1, ADPS1=0,ADPS0=0 -- коэфф. делителя 8
ADMUX &= ~(1<<REFS0);
DIDR0 |= (1<<Ubat_IN_PIN) | (1<<LUX_IN_PIN); // | (0<<ADC3D); //ADC10 -- VCC, ADC20 -- LUX
// Timer/Counter 0 initialization
TCCR0A = 0x00;
TIMSK0 |= (1<<TOIE0); //прерывания по переполнению разрешены
TCNT0 = 0x00;
OCR0A = 0x00;
OCR0B = 0x00;
//MCUCR |= (1<<SM1); //режим POWER DOWN (1<<SE) Sleep Enable
// Watchdog timeout action: Reset + Interrupt
asm("wdr");
WDTCR |= (1<<WDCE) | (1<<WDE);
WDTCR |= (1<<WDTIF) | (1<<WDTIE) | (1<<WDP0) | (1<<WDP3); // | (1<<WDIE) разрешаем прерывание WDT, 8 сек.
MEM_value_LUX = eeprom_read_word(&LUX_MEM); //читаем из памяти сохраненное значение уровня освещенности
}
/*====================*/
uint16_t read_adc(int adc_input)
{
TCCR0B = 0; //останов таймера
ADMUX= adc_input | ADC_VREF_TYPE;
_delay_us(10); // Delay needed for the stabilization of the ADC input voltage
ADCSRA|=(1<<ADEN)|(1<<ADSC); //Запускаем преобразование
while ((ADCSRA & (1<<ADIF))==0); //Wait for the AD conversion to complete
ADCSRA|=(1<<ADIF); // сброс флага прерывания
ADCSRA &= ~(1<<ADEN); //останавливаем преобразование
return ADCW;
}
void sleepy(void)
{
cli();
TCCR0B = 0x00; //останоавливаем таймер
set_sleep_mode(SLEEP_MODE_PWR_DOWN); //устанавливаем режим сна Power Down
// set_sleep_mode(SLEEP_MODE_IDLE);
// WDTCR |= (1<<WDTIE); // //запускаем WDT
sleep_enable();
sei(); // Globally enable interrupts
}
unsigned char bat_level(void)
{
//модуль измерения и индикации низкого напряжения батареи
//PCMSK |= (1<<BUTTON_LUX_MEMORY) | (1<<BUTTON_SYGNAL); //разрешаем прерывания
TIMER_CYCLE = 0x00; //счетчик циклов таймера
uint16_t value_VCC=0; //определяем локальные переменные
value_VCC = read_adc(Ubat_IN_PIN); //измеряем напряжение батареи
if (value_VCC < 0x239) {SYGNAL=0xA8;} else //если напряжение ниже 1.6В (3.2В/2), то выставляем сигнал аларма, иначе
{NUM_SYGNAL = eeprom_read_byte(&SYGNAL_MEM); //читаем значение номера сигнала из EEPROM
switch (NUM_SYGNAL)
{
case 1: SYGNAL=0xAA; break;
case 2: SYGNAL=0xCC; break;
case 3: SYGNAL=0xF0; break;
case 4: SYGNAL=0x63; break;
case 5: SYGNAL=0xD4; break;
default: SYGNAL=0xE6; break;
}
}
return SYGNAL;
}
void LUX_level(void)
{
GIFR |= (1<<INTF0) | (1<<PCIF); // сброс флагов прерывания
GIMSK |= (1<<PCIE); //разрешаем внешние прерывание
value_LUX = read_adc(LUX_IN_PIN); //измеряем уровень освещенности
asm("wdr"); //сброс WDT
if (value_LUX > MEM_value_LUX)
{TIMER_SLEEP = 0x80; PORTB &= ~(1<<LED_OUT_PIN); sleepy(); //TIMER_SLEEP = 0x80 !!!!!!!!!!!!!!!!
while (TIMER_SLEEP)
{
sleep_cpu(); // спим
}
LUX_level(); //перепроверяем уровень освещенности
} //иначе -- спим 0x25 = 37 циклов таймера сна = 5 мин
//else {;}
{mask=0x80; TIMER_SLEEP = 0xFF;} // ,если темно, разрешаем прерывания, запускаем WDT ; sygnal_out(SYGNAL) TIMER_SLEEP = 0xFF !!!!!!!!!!!!!!!!
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
int main(void)
{
init();
LUX_level();
bat_level();
while (1)
{
TIFR0 |= (1<<TOV0); //сброс флага переполнения TIMR0
TCCR0B |= (1<<CS02); //прескалер /256 1.2Mhz/256=1171.875 Hz Period 0.055 sec.
TIMER_CYCLE = 0x00; //счетчик циклов таймера
sei();
//while (TIMER_SLEEP != 0)
while(1)
{
while (TIMER_CYCLE == 0) //если TMER_CYLE = 0, выполняем вложенное
{
if (SYGNAL & mask) //проверяем по маске бит выводимого сигнала логическое "или"
{
PORTB |= (1<<LED_OUT_PIN); //если в результате 1, выставляем 1
}
else
{
PORTB &= ~(1<<LED_OUT_PIN); //если в результате 0, выставляем 0
}
mask = mask >> 1; while (mask == 0) mask=0x80; //сдвинуть маску. обновить маску
TIMER_CYCLE = 0x03; //обновить счетчик таймера
GIFR |= (1<<INTF0) | (1<<PCIF); // сброс флагов прерывания
GIMSK |= (1<<PCIE); //разрешаем внешние прерывание
if (TIMER_SLEEP == 0) LUX_level(); else{}
}
}
}
}
// External Interrupt 0 service routine
ISR (PCINT0_vect) //обработка прерывания кнопки
{
GIMSK &= ~(1<<PCIE); //запрещаем прерывания
GIFR |= (1<<INTF0) | (1<<PCIF); // сброс флагов прерывания
TCCR0B = 0x00; //останоавливаем таймер
asm("wdr"); //сброс WDT
PORTB |= (1<<LED_OUT_PIN);
_delay_ms(250); //пауза 500 мС
if bit_is_clear (PINB,BUTTON_LUX_MEMORY)
{
PORTB &= ~(1<<LED_OUT_PIN);
//MEM_value_LUX = read_adc(LUX_IN_PIN); //измеряем уровень освещенности
//eeprom_write_word(&LUX_MEM,MEM_value_LUX); // записываем в память eeprom
}
if bit_is_clear (PINB,BUTTON_SYGNAL)
{
NUM_SYGNAL+=1; while (NUM_SYGNAL == 7) {NUM_SYGNAL=0x01;} eeprom_write_byte(&SYGNAL_MEM,NUM_SYGNAL);
switch (NUM_SYGNAL)
{
case 1: SYGNAL=0xAA; break;
case 2: SYGNAL=0xCC; break;
case 3: SYGNAL=0xF0; break;
case 4: SYGNAL=0x63; break;
case 5: SYGNAL=0xD4; break;
default: SYGNAL=0xE6; break;
}
}
_delay_ms(500);
PORTB &= ~(1<<LED_OUT_PIN);
GIFR |= (1<<INTF0) | (1<<PCIF); // сброс флагов прерывания
TCCR0B |= (1<<CS02); // запуск таймера
}
// Timer 0 overflow interrupt service routine
ISR (TIM0_OVF_vect)
{
--TIMER_CYCLE; // Декремент счетчика циклов таймера
}
ISR(WDT_vect) // Watchdog timeout interrupt service routine// обработка прерыания WDT
{
//cli();
MCUSR &= ~(1<<WDRF); //сбрасываем Watchdog Reset Flag
asm("wdr");
--TIMER_SLEEP; //декремент счетчика циклов таймера сна
WDTCR |= (1<<WDTIE); // запуск WDT
}
Добавлено after 5 minutes 8 seconds:
[uquote="akl",url="/forum/viewtopic.php?p=3998637#p3998637"]Разрешать прерывания логическим ИЛИ не совсем хорошо. ][/uquote]
вы говорите, что в регистр GINSK нельзя писать через "или"? Откровенно в первый раз об этом слышу. Ну и потом, для пина PB0 такая конструкция работает. Попробовать 6 ногу лучше пропаять?..
вы говорите, что в регистр GINSK нельзя писать через "или"?
Ну а зачем так делать? Сразу целиком писать весь регистр без его предварительного чтения. Ведь какие прерывания необходимы, а каие нет - известно. Тоже самое и регистрами флагов. Там всего 2 флага. Ну так и пиши туда сразу 0xFF. Зачем его читать???
[uquote="parovoZZ",url="/forum/viewtopic.php?p=3998479#p3998479"]Старые тиньки никак не предназначены для обработки прерывания вне его - флаги сбрасываются в прерывании, поэтому надо свои вводить, если обработчик пустой. Либо же лупом проверять флаги постоянно...Вообщем, костыльные микры...[/uquote]Старые, это какие?
в 13, 2313 и прочих, с которыми я работал можно и без прерываний флаги читать, программно сбоасывать... т.е. тело прерывания и разрешение прерывания совсем необязательны чтобы работать с флагами событий.
Для тех, кто не учил магию мир полон физики
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
Это 8-ми битки, кроме: xmega, серия DA, 0-ая и 1-ая серия. В трёх последних флаги в прерываниях не сбрасываются, либо сбрасываются после выполнения определённых условий. Появились два уровня приорита прерываний, карусель прерываний... Это некая коллаборация mega и xmega.
Добавлено after 3 minutes 16 seconds:
[uquote="Ivanoff-iv",url="/forum/viewtopic.php?p=3998783#p3998783"][uquote="parovoZZ",url="/forum/viewtopic.php?p=3998479#p3998479"]Старые тиньки никак не предназначены для обработки прерывания вне его - флаги сбрасываются в прерывании, поэтому надо свои вводить, если обработчик пустой. Либо же лупом проверять флаги постоянно...Вообщем, костыльные микры...[/uquote]
в 13, 2313 и прочих, с которыми я работал можно и без прерываний флаги читать, программно сбоасывать... т.е. тело прерывания и разрешение прерывания совсем необязательны чтобы работать с флагами событий.[/uquote]
Флаги можно читать в любых МК. Они абсолютно независмы и отражают наступление какого-либо события. Речь о другом: выйти из сна в AVR можно только с помощью разрешённого прерывания, а его обработчик сбросит флаги автоматом.