RGB светодиод, INT0 прерывание и все это для ATtiny13

Обсуждаем контроллеры компании Atmel.
Ответить
Открыл глаза
Сообщения: 75
Зарегистрирован: Сб апр 20, 2013 23:15:21

Сообщение robototechnik »

Всем доброго времени суток)
Имею проблемы с программой для управления RGB светодиодом с помощью ATtiny13, который при нажатии на кнопку, подключенную к INT0 должен засыпать и соответственно выходить из спячки(power down). Вот есть у меня такая прога а она что то не пашет, в смысле пашет не корректно. Светодиод меняет цвет только при подключенном программаторе(AVR ISP mkII), при отключении его светодиод не пашет и на кнопку проц не реагирует. При подключенном программаторе при нажатии на кнопку светодиод вырубается и остается в таком состоянии все время пока нажата кнопка. Потом врубается обратно и что странно с разгоном процессора, в фьюзах стоит 9.6 МГц + 14СК+64мс то есть светодиод еще какое то вемя разгорается)такое ощущение что проц ресетится. Стоит так же добавить что при отключенном программаторе если схватится пальцем за VCC светодиод светится где то 2 секунды а потом опять ресетится) Какие то непонятки) Гляньте люди добрые од может что не так написал, всетаки не профи) Буду очень благодарен)

Код: Выделить всё

#define F_CPU 9600000UL 
#include <avr/io.h> 
#include <avr/interrupt.h> 
#include <util/delay.h> 
#include <avr/sleep.h> 
unsigned char e; 
void abc(unsigned char a,unsigned char b,unsigned char c,unsigned char status); 
unsigned char pwm[3]={255,255,255}; 
unsigned char to[3]={255,255,255}; 
int main(void) 
{ 
DDRB = 0b00011001; 
PORTB = 0b00011011; 

GIMSK = 0b01000000; 
MCUCR = 0b00000000; 

TCCR0A = 0x00; 
TCCR0B = 0x01; 
TIMSK0 = 0b00000010; 
sei(); 
while(1){ 
if (pwm[0]<to[0]) pwm[0]++; 
if (pwm[0]>to[0]) pwm[0]--; 
if (pwm[1]<to[1]) pwm[1]++; 
if (pwm[1]>to[1]) pwm[1]--; 
if (pwm[2]<to[2]) pwm[2]++; 
if (pwm[2]>to[2]) pwm[2]--; 
if(pwm[0]==to[0]&&pwm[1]==to[1]&&pwm[2]==to[2]) 
{ 
to[0]=rand()%255; 
to[1]=rand()%255; 
to[2]=rand()%255; 
} 
_delay_ms(20); 
} 
} 
ISR(TIM0_OVF_vect) 
{ 
if(e==255) 
{ 
e=0; 
PORTB = 0b00011001; 
} 
abc(pwm[0],pwm[1],pwm[2],e); 
e++; 
} 
void abc(unsigned char a,unsigned char b,unsigned char c,unsigned char status) 
{ 
if((status==a)) PORTB&= ~(1<<3); 
if((status==b)) PORTB&= ~(1<<4); 
if((status==c)) PORTB&= ~(1<<0); 
} 

ISR(INT0_vect) { 
 TIMSK0 = 0b00000000; 
 cli(); 
 set_sleep_mode(SLEEP_MODE_PWR_DOWN); 
 sleep_mode(); 
 sleep_disable(); 
 TIMSK0 = 0b00000010; 
 sei(); 
} 
Реклама
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 2482
Зарегистрирован: Пт авг 27, 2010 05:57:06
Откуда: Тюмень

Сообщение vitalik_1984 »

А вы уверены, что у вас это компилируется? Я не нашел блока фигурных скобок для функции abc();
И еще в ней переменные всегда анициализируются при каждом вызове.
Нужно добавить static перед объявлением.
И что она вообще делает эта функция?
Контактная информация:
Реклама
Открыл глаза
Сообщения: 75
Зарегистрирован: Сб апр 20, 2013 23:15:21

Сообщение robototechnik »

все компилируется и ошибок не выдает, без понятия)
я ж поэтому и в недоумении)все пучком, а прога не пашет)
признаться код составлял из частей своего и из частей инетовского кода)поэтому не до конца доезжаю что за чем)знаю только что если убрать из кода все что связано с прерыванием INT0, код работает без проблем.
как только добавляю регистры INT0 и внизу функцию для выполнения по вектору сразу проблемы((((((
есть идеи в чем может быть ошибка?
спасибо)
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 2482
Зарегистрирован: Пт авг 27, 2010 05:57:06
Откуда: Тюмень

Сообщение vitalik_1984 »

А все, понял. Недосмотрел что у вас еще и внизу объявление функции есть. Просто редко кто прототипами пользуется :))
Функция abc не нужна, можете все это сделать в самом прерывании и сэкономите несколько тактов в каждом прерывании по переполнению.

По внешнему прерыванию: что конкретно должно происходить при нажатии на кнопку?

Еще важный момент

при инициализации объявляете
DDRB = 0b00011001;
PORTB = 0b00011011;

далее в самой программе
пишете для погашения светодиодов:
PORTB = 0b00011001;
но ведь у вас на ноге 1 кнопка и получается происходит смена уровня с 1 на 0 за счет отключения подтяжки.
используйте
PORTB &= ~0b00011001;
для сброса битов, так не будет остальной порт задевать.
Контактная информация:
Реклама
Эиком - электронные компоненты и радиодетали
Открыл глаза
Сообщения: 75
Зарегистрирован: Сб апр 20, 2013 23:15:21

Сообщение robototechnik »

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

Код: Выделить всё

#define F_CPU 9600000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/sleep.h>
unsigned char e;
void abc(unsigned char a,unsigned char b,unsigned char c,unsigned char status);
unsigned char pwm[3]={255,255,255};
unsigned char to[3]={255,255,255};
int main(void)
{
DDRB = 0b00011001; 
PORTB = 0b00011011;

GIMSK = (1<<INT0);

TCCR0A = 0x00;
TCCR0B = (1<<CS00);
TIMSK0 = (1<<TOIE0);
sei();
while(1){
if (pwm[0]<to[0]) pwm[0]++;
if (pwm[0]>to[0]) pwm[0]--;
if (pwm[1]<to[1]) pwm[1]++;
if (pwm[1]>to[1]) pwm[1]--;
if (pwm[2]<to[2]) pwm[2]++;
if (pwm[2]>to[2]) pwm[2]--;
if(pwm[0]==to[0]&&pwm[1]==to[1]&&pwm[2]==to[2]) 
{
to[0]=rand()%255; 
to[1]=rand()%255;
to[2]=rand()%255;
}
_delay_ms(20);
}
}
ISR(TIM0_OVF_vect)
{
if(e==255)
{
e=0;
PORTB |= 0b00011001;
}
abc(pwm[0],pwm[1],pwm[2],e);
e++;
}
void abc(unsigned char a,unsigned char b,unsigned char c,unsigned char status)
{
if((status==a)) PORTB&= ~(1<<3);
if((status==b)) PORTB&= ~(1<<4);
if((status==c)) PORTB&= ~(1<<0);
}

ISR(INT0_vect) { 
 TIMSK0 = (0<<TOIE0);
 PORTB |= 0b00011001;
 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 sleep_enable(); 
 sleep_mode();
 sleep_disable();
 TIMSK0 = (1<<TOIE0);
}	
Реклама
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 2482
Зарегистрирован: Пт авг 27, 2010 05:57:06
Откуда: Тюмень

Сообщение vitalik_1984 »

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

Еще я не разбирался что там за режим прерывания по int0 и вас назначен, нужно чтобы перед сном был установлен режим по низкому уровню, иначе проц не проснется.
Контактная информация:
Реклама
Открыл глаза
Сообщения: 75
Зарегистрирован: Сб апр 20, 2013 23:15:21

Сообщение robototechnik »

немного не понял каким образом и что разделить)можно на примере?)
очищать флаг прерывания нет смысла так как в даташите написано что при конфигурации на низкий уровень,так как у меня сконфигурировано,флаг всегда ноль!
режим по низкому уровню у меня)не знаю в чем затык)
я думл что при засыпании проц не выходит из прерывания, а остается на том месте где я написал ему спать, при включении он просыпается сразу в прерывании и заканчивает его работу....получается если он выходит то....кажется понял идею с переменной)а ее значение будет сохранятся при спячке?надо наверное в епром писать?
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 2482
Зарегистрирован: Пт авг 27, 2010 05:57:06
Откуда: Тюмень

Сообщение vitalik_1984 »

для каждого прерывания есть один или несколько регистров управления, и регистр состояния.
То есть активировать разрешение прерывания можно через регистр управления, а считать через регистр состояния.
То есть аппаратно при наступлении события прерывания выставляется флаг.
Далее через один такт идет сравнение с разрешенными прерываниями если это прерывание разрешено и установлены флаги прерывания и глобальное разрешение, тогда происходим переход на вектор прерывания.и далее к обработке.
Все векторы прописаны в паспорте на Мк.(datasheet)

А для очистки флага нужно присвоить ему значение 1.

поищите в паспорте interrupt flag мне на телефоне лениво искать.

Разделить это значит вошли в прерывание
если А равна 1, то спать и А =0, если нет, то просыпаться и А =1
Контактная информация:
Открыл глаза
Сообщения: 75
Зарегистрирован: Сб апр 20, 2013 23:15:21

Сообщение robototechnik »

прежде чем я пожалуюсь на то что у меня не получилось, скажите пожалуйста, после ухода в спячку где останавливается программа и после выходи из спячки где она начинается...и что происходит с флагом после входа в спячку и после выхода?
если после этой информации код не налажу, скину что получилось...
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 2482
Зарегистрирован: Пт авг 27, 2010 05:57:06
Откуда: Тюмень

Сообщение vitalik_1984 »

Вообще должен с того же места где и заснул. Только вот незадача- просыпаясь по ИНТ0 одновременно устанавливается флаг необходимости вызова прерывания( сбрасывается он после входа в прерывание, но они у нас аппаратно запрещены так как мы уже в нем) то есть сначала выходит из него, и тут же опять заходит и ложится спать.
Вот выдержка из паспорта МК :
ATtiny13 писал(а):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.
далее смотрим:
GIFR – General Interrupt Flag Register
Bit 6 – INTF0: External Interrupt Flag 0
When an edge or logic change on the INT0 pin triggers an interrupt request, INTF0 becomes set
(one). If the I-bit in SREG and the INT0 bit in GIMSK are set (one), the MCU will jump to the cor-
responding Interrupt Vector. The flag is cleared when the interrupt routine is executed.
Alternatively, the flag can be cleared by writing a logical one to it. This flag is always cleared
when INT0 is configured as a level interrupt.
Но раз тут еще приписочка о том что он всегда очищен когда настроен на прерывание по уровню, то лучше воспользоваться старым добрым способом насчет выбора нужного действия при входе.
Можно вообще переключить на прерывание PIN CHANGE
Bit 5 – PCIF: Pin Change Interrupt Flag
When a logic change on any PCINT5:0 pin triggers an interrupt request, PCIF becomes set
(one). If the I-bit in SREG and the PCIE bit in GIMSK are set (one), the MCU will jump to the cor-
responding Interrupt Vector. The flag is cleared when the interrupt routine is executed.
Alternatively, the flag can be cleared by writing a logical one to it.
Вроде как из POWER DOWN могут вывести прерывания INT0 and
Pin Change
из Table 7-1

Короче вот нарыл, реально сначала выполняется прерывание, потом уже только возвращается к ранее исполнявшемуся коду, так что однозначно нужно разделить на спать и не спать.
If an enabled interrupt occurs while the MCU is in a sleep mode, the MCU wakes up. The MCU
is then halted for four cycles in addition to the start-up time, executes the interrupt routine, and
resumes execution from the instruction following SLEEP.
The contents of the Register File and
SRAM are unaltered when the device wakes up from sleep. If a reset occurs during sleep mode,
the MCU wakes up and executes from the Reset Vector.
Вот и получается, что мк постоянно входит в прерывание и засыпает.
Контактная информация:
Открыл глаза
Сообщения: 75
Зарегистрирован: Сб апр 20, 2013 23:15:21

Сообщение robototechnik »

получается сразу перед входом в спячку надо флаг очистить?или как?
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 2482
Зарегистрирован: Пт авг 27, 2010 05:57:06
Откуда: Тюмень

Сообщение vitalik_1984 »

При входе в прерывание нужно сделать выбор спать или просыпаться. Так если входит в прерывание после спячки просто будет мимо проходить наружу из прерывания и все.
Контактная информация:
Открыл глаза
Сообщения: 75
Зарегистрирован: Сб апр 20, 2013 23:15:21

Сообщение robototechnik »

ну я вот так вот пока что переделал.....что опять не так?всеравно не пашет(((((((((((

Код: Выделить всё

#define F_CPU 9600000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/sleep.h>
unsigned char e;
void abc(unsigned char a,unsigned char b,unsigned char c,unsigned char status);
unsigned char pwm[3]={255,255,255};
unsigned char to[3]={255,255,255};
int main(void)
{
DDRB = 0b00011001; 
PORTB = 0b00011011;

ACSR = (1<<ACD);
GIMSK = (1<<INT0);

TCCR0A = 0x00;
TCCR0B = (1<<CS00);
TIMSK0 = (1<<TOIE0);
sei();
while(1){
if (pwm[0]<to[0]) pwm[0]++;
if (pwm[0]>to[0]) pwm[0]--;
if (pwm[1]<to[1]) pwm[1]++;
if (pwm[1]>to[1]) pwm[1]--;
if (pwm[2]<to[2]) pwm[2]++;
if (pwm[2]>to[2]) pwm[2]--;
if(pwm[0]==to[0]&&pwm[1]==to[1]&&pwm[2]==to[2]) 
{
to[0]=rand()%255; 
to[1]=rand()%255;
to[2]=rand()%255;
}

}
}
ISR(TIM0_OVF_vect)
{
if(e==255)
{
e=0;
PORTB |= 0b00011001;
}
abc(pwm[0],pwm[1],pwm[2],e);
e++;
}
void abc(unsigned char a,unsigned char b,unsigned char c,unsigned char status)
{
if((status==a)) PORTB&= ~(1<<3);
if((status==b)) PORTB&= ~(1<<4);
if((status==c)) PORTB&= ~(1<<0);
}

ISR(INT0_vect) { 
 PORTB |= 0b00011001;
 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 sleep_enable(); 
 sleep_mode();
 GIFR = (1<<INTF0);
 sleep_disable();
}	
Открыл глаза
Сообщения: 75
Зарегистрирован: Сб апр 20, 2013 23:15:21

Сообщение robototechnik »

если делаю выбор спать не спать тоже не пашет(((((((((((((
Открыл глаза
Сообщения: 75
Зарегистрирован: Сб апр 20, 2013 23:15:21

Сообщение robototechnik »

переделал код!!радикально!!
вроде работает, но не всегда, плюс память закончилась((((((уже не могу сделать регулировку скорости смены цветов и паузу после выхода из спячки чтобы я руку от кнопки убрать успел((
может можно как нибудь код оптимизировать для освобождения еще около 4 процентов памяти????а то здесь 100 под завязку
буду благодарен)
вот он код

Код: Выделить всё

#define F_CPU 9600000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/sleep.h>
unsigned char e;
unsigned char state = 0;
void abc(unsigned char a,unsigned char b,unsigned char c,unsigned char status);
unsigned char pwm[3]={255,255,255};
unsigned char to[3]={255,255,255};
int main(void)
{
DDRB = 0b00011001; 
PORTB = 0b00011011;

ACSR = (1<<ACD);
GIMSK = (1<<INT0);

TCCR0A = 0x00;
TCCR0B = (1<<CS00);
TIMSK0 = (1<<TOIE0);
sei();
while(1){
if (pwm[0]<to[0]) pwm[0]++;
if (pwm[0]>to[0]) pwm[0]--;
if (pwm[1]<to[1]) pwm[1]++;
if (pwm[1]>to[1]) pwm[1]--;
if (pwm[2]<to[2]) pwm[2]++;
if (pwm[2]>to[2]) pwm[2]--;
if(pwm[0]==to[0]&&pwm[1]==to[1]&&pwm[2]==to[2]) 
{
to[0]=rand()%255; 
to[1]=rand()%255;
to[2]=rand()%255;
}

if (state == 0){
sleep_disable();
}
else{
PORTB |= 0b00011001;
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable(); 
sleep_mode();
}
}
}
ISR(TIM0_OVF_vect)
{
if(e==255)
{
e=0;
PORTB |= 0b00011001;
}
abc(pwm[0],pwm[1],pwm[2],e);
e++;
}
void abc(unsigned char a,unsigned char b,unsigned char c,unsigned char status)
{
if((status==a)) PORTB&= ~(1<<3);
if((status==b)) PORTB&= ~(1<<4);
if((status==c)) PORTB&= ~(1<<0);
}

ISR(INT0_vect) { 
if (state == 1) state = 0;
else state = 1;
}
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1502
Зарегистрирован: Ср сен 08, 2010 20:33:39
Откуда: Воронеж

Сообщение Stalker007 »

Кроме чисел ещё есть логические переменные. Может их использовать?
Тогда вместо:

unsigned char state = 0;
...
if (state == 0){
sleep_disable();
}
else{
PORTB |= 0b00011001;
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_mode();
}
...
ISR(INT0_vect) {
if (state == 1) state = 0;
else state = 1;

будет:

boolean state = false;
...
if (!state){
sleep_disable();
}
else{
PORTB |= 0b00011001;
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_mode();
}
...
ISR(INT0_vect) {
state=!state;

Чутьё мне подсказывает, что код уменьшится, но вот насколько? Нужно пробовать.
С шерстью на сайте постоянные проблемы. Обещали исправить, но никак. Вот работающая ссылка, если кому нужно. http://radiokot.ru/circuit/power/charger/40/
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 2482
Зарегистрирован: Пт авг 27, 2010 05:57:06
Откуда: Тюмень

Сообщение vitalik_1984 »

Stalker007 , нет в студии никаких логических переменных.
robototechnik 1.зачем два раза сравнивать, если можно все в одном месте все сделать?
2. кардинально это когда весь код переписан или хотя бы 60%. еще раз спрашиваю, зачем нужна функция abc? если ее объявить как inline, тоже код может уменьшиться, а так при ее вызове могут делаться ненужные сохранения регистров.
Контактная информация:
Открыл глаза
Сообщения: 75
Зарегистрирован: Сб апр 20, 2013 23:15:21

Сообщение robototechnik »

Stalker007, идея хорошая но что то не определяет у меня winavr такую переменную(
vitalik_1984, 1. откуда такие точные проценты?)я просто поменял принцип, и высказался образно)что тут такого?)
2.а как можно сравнить в одном месте???
на счет авс без понятия)эту часть я скопировал из примера)давайте пробовать все подряд)
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 1900
Зарегистрирован: Сб фев 21, 2009 13:11:40
Откуда: Москва

Сообщение ibiza11 »

так попробуйте:

Код: Выделить всё

#define F_CPU 9600000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/sleep.h>
unsigned char e;
unsigned char state = 0;
void abc(unsigned char a, unsigned char b, unsigned char c, unsigned char status);
unsigned char pwm[3]={255,255,255};
unsigned char to[3]={255,255,255};
int main(void)
{
   DDRB  = 0b00011001; 
   PORTB = 0b00011011;

   ACSR = (1<<ACD);
   GIMSK = (1<<INT0);

   TCCR0A = 0x00;
   TCCR0B = (1<<CS00);
   TIMSK0 = (1<<TOIE0);
   sei();
   while(1){
      if (pwm[0]<to[0]) pwm[0]++;
      if (pwm[0]>to[0]) pwm[0]--;
      if (pwm[1]<to[1]) pwm[1]++;
      if (pwm[1]>to[1]) pwm[1]--;
      if (pwm[2]<to[2]) pwm[2]++;
      if (pwm[2]>to[2]) pwm[2]--;
      if(pwm[0]==to[0]&&pwm[1]==to[1]&&pwm[2]==to[2]){
         to[0]=rand()%255;
         to[1]=rand()%255;
         to[2]=rand()%255;
      }
      if (state == 0){
         sleep_disable();
      } else {
         PORTB |= 0b00011001;
         set_sleep_mode(SLEEP_MODE_PWR_DOWN);
         sleep_enable(); 
         sleep_mode();
      }
   }
}

ISR(TIM0_OVF_vect)
{
   if(++e == 0)
      PORTB |= 0b00011001;
   else {
      if(pwm[0] == e)
         PORTB &= ~(1<<3);
      if(pwm[1] == e)
         PORTB &= ~(1<<4);
      if(pwm[2] == e)
         PORTB &= ~(1<<0);
   }
}


ISR(INT0_vect) { 
   state ^= 0x01;
   while(!(PINB&0b00000010));
} 
Ставим плюсы: )
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1502
Зарегистрирован: Ср сен 08, 2010 20:33:39
Откуда: Воронеж

Сообщение Stalker007 »

Stalker007 писал(а):будет:

boolean state = false;
...
if (!state){
sleep_disable();
}
else{
PORTB |= 0b00011001;
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_mode();
}
...
ISR(INT0_vect) {
state=!state;
Извиняюсь, я в основном на паскале писал. Никак не привыкну к тому что в Си всё более коротко. Нужно объявлять так:
bool state;
С шерстью на сайте постоянные проблемы. Обещали исправить, но никак. Вот работающая ссылка, если кому нужно. http://radiokot.ru/circuit/power/charger/40/
Ответить

Вернуться в «AVR»