Форум РадиоКот https://radiokot.ru/forum/ |
|
Использование внешних прерываний + Таймера Т0. https://radiokot.ru/forum/viewtopic.php?f=57&t=144884 |
Страница 1 из 1 |
Автор: | 7seg [ Пт май 05, 2017 03:41:41 ] | ||
Заголовок сообщения: | Использование внешних прерываний + Таймера Т0. | ||
Здравствуйте форумчане, может кто подсказать как организовать использования двух типов прерываний на одном МК. Используются внешние прерывания для определения перехода фазы через ноль. За его обработку у меня отвечает INT0 СпойлерКод: //настройка внешнего прерывния INT0 void int0_init( void ) { //настраиваем на срабатывание INT0 по переднему фронту MCUCR |= (1<<ISC01)|(0<<ISC00); //разрешаем внешнее прерывание INT0 En_INT0 } ISR(INT0_vect) { int i=100; int tempMS=(value+1); for (;;) { i--; _delay_us(10); if (i<=tempMS) { break; } } int k; for(k=0; k<5; k++) { PORTB |= (1<<1); _delay_us(2); PORTB &= ~(1<<1); _delay_us(2); } } Внутрение прерывания которые отвечают за индикацию используют Таймер Т0. СпойлерКод: void init_io() { //порт, к которому подкл. сегменты PORTA = 0xff; DDRA = 0xff; //порт, к которому подкл. катод DDRB |=(1<<0)|(1<<1)|(1<<2); PORTB =0x00; //инициализация таймера Т0 TIMSK = (1<<TOIE0); TCCR0B = (1<<CS02)|(0<<CS01)|(1<<CS00); // правка на 461 TCNT0L = T0_TICKS; //правка на 461 //иницилизация прерываний } //прерывания таймера Т0 - вывод на индикатор ISR(TIMER0_OVF_vect) //правка на 461 { static unsigned char count = 0; TCNT0L += T0_TICKS; //правка на 461 //гасим оба разряда PORTB |=(1<<PB0); PORTB |=(1<<PB2); PORTA = 0b11111110; //зажигаем следующий разряд if (count == 0) { segchar(data2); PORTB &= ~(1<<2); } if (count == 1) { segchar(data1); PORTB &= ~(1<<0); } count++; if (count == 2) count = 0; } Но при испытаниях в протеусе , при внешних прерываниях в 100гц. Складывается такое ощущения что обработка кнопок сильно зависает от прерываний по INT0. Можно ли сделать на МК ATTINY461 организовать подобие много поточности. Чтобы 2 типа прерываний оказывали друг на друга минимальное влияние ?.. Весь код: СпойлерКод: #define F_CPU 8000000 //Hz #include <util/delay.h> #include <avr/interrupt.h> #include <avr/io.h> #define T0_TICK_RATE 200 // Hz #define T0_CLK ((F_CPU)/1024) #define T0_TICKS (0x100 - T0_CLK / T0_TICK_RATE) #define DEBOUNCE_TIME 20 #define BUTTON_PIN PINB #define POWER_BIT PB3 #define UP_BIT PB4 #define DOWN_BIT PB5 #define KEY_POWER 0b00001000 #define KEY_DOWN 0b00100000 #define KEY_UP 0b00010000 #define En_INT0 GIMSK|=(1<<6); #define Dis_INT0 GIMSK&=~(1<<6); // выключаем прерывание INT0 volatile unsigned char data1=0; volatile unsigned char data2=0; volatile unsigned char value; volatile int _buzzer = 0; volatile int _pressed = 0; void init_io(); void segchar(unsigned char seg); void handle_button(int key); void handle_buttons(); void process_power(); void process_up(); void process_down(); //настройка внешнего прерывния INT0 void int0_init( void ) { //настраиваем на срабатывание INT0 по переднему фронту MCUCR |= (1<<ISC01)|(0<<ISC00); //разрешаем внешнее прерывание INT0 En_INT0 } ISR(INT0_vect) { int i=100; int tempMS=(value+1); for (;;) { i--; _delay_us(10); if (i<=tempMS) { break; } } int k; for(k=0; k<5; k++) { PORTB |= (1<<1); _delay_us(2); PORTB &= ~(1<<1); _delay_us(2); } } int main(void) { value=0; init_io(); sei(); int0_init(); while(1) { handle_buttons(); } return 0; } void segchar (unsigned char seg) { switch (seg) { case 1: PORTA = 0b01111100; break; case 2: PORTA = 0b10000100; break; case 3: PORTA = 0b01000100; break; case 4: PORTA = 0b01101000; break; case 5: PORTA = 0b01000010; break; case 6: PORTA = 0b00000010; break; case 7: PORTA = 0b01110100; break; case 8: PORTA = 0b00000000; break; case 9: PORTA = 0b01000000; break; case 0: PORTA = 0b00010000; break; } } void init_io() { //порт, к которому подкл. сегменты PORTA = 0xff; DDRA = 0xff; //порт, к которому подкл. катод DDRB |=(1<<0)|(1<<1)|(1<<2); PORTB =0x00; //инициализация таймера Т0 TIMSK = (1<<TOIE0); TCCR0B = (1<<CS02)|(0<<CS01)|(1<<CS00); // правка на 461 TCNT0L = T0_TICKS; //правка на 461 //иницилизация прерываний } //прерывания таймера Т0 - вывод на индикатор ISR(TIMER0_OVF_vect) //правка на 461 { static unsigned char count = 0; TCNT0L += T0_TICKS; //правка на 461 //гасим оба разряда PORTB |=(1<<PB0); PORTB |=(1<<PB2); PORTA = 0b11111110; //зажигаем следующий разряд if (count == 0) { segchar(data2); PORTB &= ~(1<<2); } if (count == 1) { segchar(data1); PORTB &= ~(1<<0); } count++; if (count == 2) count = 0; } void handle_button(int key) { int bit; switch (key) { case KEY_POWER: bit = POWER_BIT; break; case KEY_UP: bit = UP_BIT; break; case KEY_DOWN: bit = DOWN_BIT; break; default: return; } if (bit_is_clear(BUTTON_PIN, bit)) { if (_pressed == 0) { _delay_ms(DEBOUNCE_TIME); if (bit_is_clear(BUTTON_PIN, bit)) { _pressed |= key; // key action switch (key) { case KEY_POWER: process_power(); break; case KEY_UP: process_up(); break; case KEY_DOWN: process_down(); break; } } } } else { _pressed &= ~key; } } void handle_buttons() { handle_button(KEY_POWER); handle_button(KEY_DOWN); handle_button(KEY_UP); } void process_power() { } void process_up() { if (value < 99) { value++; unsigned char tmp; tmp = value % 10; data1 = tmp; tmp = value/10; data2= tmp; } } void process_down() { if (value > 0) { value--; unsigned char tmp; tmp = value % 10; data1 = tmp; tmp = value/10; data2= tmp; } }
|
Автор: | krash_artem [ Пт май 05, 2017 04:05:40 ] |
Заголовок сообщения: | Re: Использование внешних прерываний + Таймера Т0. |
Чтобы прерывания оказывали друг на друга минимальное влияние их обработка должна занимать как можно меньше времени. А у вас в обработчике delay, который за зря тратит время. |
Автор: | 7seg [ Пт май 05, 2017 04:20:46 ] |
Заголовок сообщения: | Re: Использование внешних прерываний + Таймера Т0. |
Это просто моя наработка для регулирования яркостью лампы через симистор путем путем детектирования нуля при переходе фазы через нейтраль. а дальше отсчет времени на включение симистра по процентам. Конечно дальше хотел заменить делай на таймеры и записывать значение в переменные т1 и т2 - это 2 перехода Через ноль. А включение высчитывать по формуле (т2-n1)/100 - это и есть 1% мощности. Походу надо в целом пересмотреть подход. Хотя еще думаю о вложенных прерываниях но это кажется как то не очень разумно. |
Автор: | ARV [ Пт май 05, 2017 06:51:00 ] |
Заголовок сообщения: | Re: Использование внешних прерываний + Таймера Т0. |
Во-первых, у вас в прерываниях сплошь и рядом обращения к функциям, причем даже к библиотечным. Это не кошерная практика. Как минимум, сделайте все функции, к которым идут обращения из прерываний, static, но лучше обойдитесь без них вообще. Во-вторых, логика у вас сильно хромает. Делать обработку кнопок поочередно?! Впервые такое встречаю. Ну и по остальному коду то же самое - зачем в синхронизации с 0 у вас какие-то циклы, задержки? Логика фазового управления (для диммера) должна быть такой или очень похожей на такую: в момент перехода через 0 (в обработчике прерывания то есть) запускаем таймер на отсчет нужной фазы. Когда таймер отсчитает фазу, по его прерыванию включаем импульс управления тиристором и перезапускаем таймер на интервал длительности импульса, по истечению интервала импульс отключаем, таймер останавливаем. Не забывайте о том, что схема может сильно влиять на поведение программы: как у вас цепь синхронизации с сетью сделана? Советы общего плана, как и вопрос ваш не очень конкретный... |
Автор: | 7seg [ Пт май 05, 2017 08:25:40 ] | ||
Заголовок сообщения: | Re: Использование внешних прерываний + Таймера Т0. | ||
AVR, Большое спасибо за советы. Сейчас есть над чем подумать. Добавлено after 17 minutes 3 seconds: Схемка [img] download/file.php?id=284457 [/img]
|
Автор: | ARV [ Пт май 05, 2017 08:59:45 ] |
Заголовок сообщения: | Re: Использование внешних прерываний + Таймера Т0. |
по схеме тоже предлагаю подумать ![]() ну или точно так же без всяких транзисторов завел бы сигнал на INT0... хотя первый вариант мне нравится больше ![]() |
Автор: | 7seg [ Пт май 05, 2017 09:10:39 ] |
Заголовок сообщения: | Re: Использование внешних прерываний + Таймера Т0. |
Насчет предлагаемых вами изменениях конечно подумаю , если честно не рассматривал с данного ракурса. Но пока охота все-таки через прерывания по ИНТ0 все это сделать. Эхх первый проект и должен быть таким тяжелым, и лишь тогда что то останется в голове. Пока решил по изучать датишь на 128мегу( единственный что мне попался на русском). А пока буду его читать заодно по анализирую ответы на форуме. |
Автор: | 7seg [ Сб май 06, 2017 05:40:24 ] |
Заголовок сообщения: | Re: Использование внешних прерываний + Таймера Т0. |
А есть у кого примеры фазо импульсного управления на аттини ? или примеры димеров ? |
Автор: | ARV [ Сб май 06, 2017 08:01:24 ] |
Заголовок сообщения: | Re: Использование внешних прерываний + Таймера Т0. |
А есть у кого примеры фазо импульсного управления на аттини ? или примеры димеров ? допустим, у меня есть. на тини13. я даже многократно схему приводил: ![]() можете посмотреть, как синхронизация с сетью реализована - 2 резистора всего-навсего. хоть схема и не от устройства-диммера, сделать диммер - будет точно такая же схема. |
Автор: | 7seg [ Вс май 07, 2017 06:19:23 ] |
Заголовок сообщения: | Re: Использование внешних прерываний + Таймера Т0. |
ARV, А можно на листинг программы взглянуть ? если это не тайна конечно ? |
Автор: | ARV [ Вс май 07, 2017 08:16:17 ] |
Заголовок сообщения: | Re: Использование внешних прерываний + Таймера Т0. |
взглянуть можно, но вряд ли вы разберетесь... СпойлерКод: /* * main.c * * Created on: 05.09.2010 * Author: ARV */ #include <avr/io.h> #include <avr/interrupt.h> #include <avr/wdt.h> #include <avr/pgmspace.h> #include <avr/eeprom.h> #include <avr/sleep.h> #include <util/delay.h> #include <avr_helper.h> #include "rc5.h" /// рабочий порт для выхода на симистор #define OUTPORT PORTB /// выходной пин порта для управления симистором #if defined(__AVR_ATtiny2313__) #define OUT _BV(PB7) #else #define OUT _BV(PB4) #endif /// длительность выходного импульса управления симистором #define PULSE_LEN 50 /// действие по совпадению канала А #define do_comp_A() if(on) OUTPORT &= ~OUT /// действие по совпадению канала В #define do_comp_B() OUTPORT |= OUT /// предделитель таймера #define TMR_DIV 8 #define MAX_P 200 #define PHASE_STEP 5 #define MIN_P PHASE_STEP #define PHASE_OFF (MAX_P + PHASE_STEP) /// частота следования битов по стандарту RC5 (1778 мкс на 1 бит) #define F_RC 562UL /// полное значение делителя таймера 1 #define T1_DIV (TMR_DIV * CTC) /// Длительность интервала передачи 1 бита #define T1 ((F_CPU / T1_DIV / F_RC) + 5) /// Длительность 1 бита при "соседних" битах 01 или 10 #define T2 (T1*3UL/2UL) /// Максимальный интервал протокола #define T3 (2UL*T1) /// Установка сброса для режима СТС #define CTC (F_CPU/TMR_DIV/MAX_P/100) volatile uint8_t COUNTER;// = 0; volatile uint8_t COMP_A; volatile uint8_t COMP_B; volatile uint16_t COMP_C; volatile uint8_t timeout; volatile uint8_t out_phase = PHASE_OFF; volatile uint8_t sel_phase;// = PHASE_OFF; //volatile uint8_t ocr_c_enable = 0; volatile uint8_t on = 0; /// локальная переменная, в которой хранится принятый код volatile uint16_t rc_code; /// переменная, накапливающая принимаемые биты volatile uint16_t code; /// счетчик принятых бит volatile uint8_t count; EEMEM rc5_t cmd[4] = { make_rc5_code(RC5ADDR_PHONO,RC5CMD_STOP), make_rc5_code(RC5ADDR_PHONO,RC5CMD_PLAY), make_rc5_code(RC5ADDR_PHONO,RC5CMD_VOLPLUS), make_rc5_code(RC5ADDR_PHONO,RC5CMD_VOLMINUS) }; #ifndef TIM0_COMPA_vect #define TIM0_COMPA_vect TIMER0_COMPA_vect #endif /** Прерывание по переполнению таймера. * Таймер без делителя, прерывание возникает каждые 256 тактов. * Обработчик прерыания реализует виртуальный 16-битный таймер с регистрами сравнения и т.п. */ ISR(TIM0_COMPA_vect){ COUNTER++; // ведем программный таймер if(COUNTER == COMP_A){ do_comp_A(); } if(COUNTER == COMP_B){ do_comp_B(); } if(COMP_C){ if(!--COMP_C){ // проанализируем число принятых бит switch(count){ case RC5_CODELEN-1: // если принято на 1 бит меньше, чем нужно - дополняем нулем code <<= 1; case RC5_CODELEN: // если принято нормальное число бит - обновляем код rc_code = ignore_toggle(code); default: // что бы ни было принято - всегда обнуляется счетчик бит // который является признаком готовности к новому приему count = 0; } } } } #ifndef PCINT0_vect #define PCINT0_vect PCINT_vect #endif /** Прерывание по изменению состояния синхро-пина. * Возникает по фронту и спаду синхроимпульса. * Реализует синхронизацию с сетью. */ ISR(PCINT0_vect){ uint8_t tmp = COUNTER; // запоминаем счетчик OUTPORT |= OUT; // выключаем на всякий случай выход COMP_A = tmp + out_phase; // задаем момент включения выходного сигнала COMP_B = tmp + out_phase + PULSE_LEN; if(on){ if(sel_phase > out_phase){ out_phase += 1;//PHASE_STEP; } else if(sel_phase < out_phase){ out_phase -= 1;//PHASE_STEP; } } if(timeout) timeout--; } #ifndef TIMSK0 #define TIMSK0 TIMSK #endif #define SND _BV(PB3) static void beep(void){ for(uint8_t i=255; i; i--){ PORTB ^= SND; _delay_us(150); } } static uint16_t get_ir(void){ uint16_t code = 0; while(PINB & _BV(PB0)) wdt_reset(); _delay_us(BIT_TIME / 4.0); for(uint8_t i=0; i < RC5_CODELEN; i++){ wdt_reset(); code <<= 1; if(!(PINB & _BV(PB0))) code |= 1; _delay_us(BIT_TIME); } return code; } MAIN(){ uint8_t off_phase = MIN_P; uint16_t code, oldcode; // настройка периферии wdt_enable(WDTO_1S); // сторожа разрешим для страховки ACSR = _BV(ACD); // компаратор отключается для экономии энергии DDRB = OUT | SND; // выходной пин PORTB = OUT; MCUCR = _BV(ISC01); // запрос прерывания по падающему фронту TCCR0A = _BV(WGM01); // режим СТС OCR0A = CTC; // порог сброса TCCR0B = TIMER_CLK_DIV(TMR_DIV); // делитель таймера TIMSK0 = _BV(OCIE0A); // прерывания по сбросу таймера PCMSK = _BV(PCINT0); // 0-й пин используется для синхронизации с сетью GIMSK = _BV(INT0) | _BV(PCIE); // разрешаем внешний запрос и прерывание по смене уровня пина // временно #if defined(__AVR_ATtiny2313__) DDRD = _BV(PD5); PORTD = _BV(PD5) | _BV(PD2); #endif sei(); while(1){ while(!(code = get_ir())){ wdt_reset(); _delay_ms(10); } if(oldcode != code){ oldcode = code; } else if(timeout) continue; // обработка принятого кода if(code == eeprom_read_word((void*)&cmd[0])){ timeout = 200; beep(); if(on){ off_phase = sel_phase; m1: sel_phase = PHASE_OFF; out_phase = PHASE_OFF; on = 0; } else { m2: sel_phase = off_phase; on = 1; } } if(code == eeprom_read_word((void*)&cmd[2])){ if(!on) goto m2; timeout = 5; // изменение if(sel_phase > PHASE_STEP){ sel_phase -= PHASE_STEP; beep(); } } else if(code == eeprom_read_word((void*)&cmd[3])){ timeout = 5; // изменение if(sel_phase < PHASE_OFF){ beep(); sel_phase += PHASE_STEP; } else { off_phase = PHASE_STEP; goto m1; } } } } |
Автор: | oleg110592 [ Вс май 07, 2017 09:10:39 ] |
Заголовок сообщения: | Re: Использование внешних прерываний + Таймера Т0. |
А можно на листинг программы взглянуть ? можно листинг на Си и немного теории подсмотреть в документе от Микрочип "PICDIM Lamp Dimmer for the PIC12C508" 1997 года, хоть и для пика, но Си он и в Африке Си. Там диаграмки и схема есть. Прерываний нет. |
Автор: | 7seg [ Чт май 11, 2017 12:06:23 ] |
Заголовок сообщения: | Re: Использование внешних прерываний + Таймера Т0. |
Опытным путем и научным методом тыка переделал немного программку, таким образом как думаете вот такой димер если правильно рассчитать время переполнения таймера. Имеет право на жизнь ? Код: #define F_CPU 8000000 //Hz #include <util/delay.h> #include <avr/interrupt.h> #include <avr/io.h> #define T0_TICK_RATE 1000 // Hz #define T0_CLK ((F_CPU)/1024) #define T0_TICKS (0x100 - T0_CLK / T0_TICK_RATE) #define DEBOUNCE_TIME 20 #define BUTTON_PIN PINB #define POWER_BIT PB3 #define UP_BIT PB4 #define DOWN_BIT PB5 #define KEY_POWER 0b00001000 #define KEY_DOWN 0b00100000 #define KEY_UP 0b00010000 #define En_INT0 GIMSK|=(1<<6); #define Dis_INT0 GIMSK&=~(1<<6); // выключаем прерывание INT0 volatile unsigned char data1=0; volatile unsigned char data2=0; volatile unsigned char value; volatile int _buzzer = 0; volatile int _pressed = 0; volatile unsigned char sw=0; void init_io(); void segchar(unsigned char seg); void handle_button(int key); void handle_buttons(); void process_power(); void process_up(); void process_down(); //настройка внешнего прерывни¤ INT0 void int0_init( void ) { //настраиваем на срабатывание INT0 по переднему фронту MCUCR |= (1<<ISC01)|(0<<ISC00); //разрешаем внешнее прерывание INT0 En_INT0 } ISR(INT0_vect) { sw=1; static unsigned char count = 0; //гасим оба разр¤да PORTB |=(1<<PB0); PORTB |=(1<<PB2); PORTA = 0b11111110; //зажигаем следующий разр¤д if (count == 0) { segchar(data2); PORTB &= ~(1<<2); } if (count == 1) { segchar(data1); PORTB &= ~(1<<0); } count++; if (count == 2) count = 0; } int main(void) { value=0; init_io(); sei(); int0_init(); while(1) { handle_buttons(); } return 0; } void segchar (unsigned char seg) { switch (seg) { case 1: PORTA = 0b01111100; break; case 2: PORTA = 0b10000100; break; case 3: PORTA = 0b01000100; break; case 4: PORTA = 0b01101000; break; case 5: PORTA = 0b01000010; break; case 6: PORTA = 0b00000010; break; case 7: PORTA = 0b01110100; break; case 8: PORTA = 0b00000000; break; case 9: PORTA = 0b01000000; break; case 0: PORTA = 0b00010000; break; } } void init_io() { //порт, к которому подкл. сегменты PORTA = 0xff; DDRA = 0xff; //порт, к которому подкл. катод DDRB |=(1<<0)|(1<<1)|(1<<2); PORTB =0x00; //инициализаци¤ таймера “0 TIMSK = (1<<TOIE0); TCCR0B=(0<<CS01)|(1<<CS00); // правка на 461 TCNT0H=0x00; TCNT0L=0xff; //правка на 461 //иницилизаци¤ прерываний } volatile unsigned char lvl=0; //прерывани¤ таймера “0 - вывод на индикатор ISR(TIMER0_OVF_vect) //правка на 461 { //cli(); TCNT0L=0xff; //правка на 461 if (sw==1) { lvl++; if (lvl==(value)) { for(int k=0; k<5; k++) { PORTB |= (1<<1); _delay_us(10); PORTB &= ~(1<<1); _delay_us(1); } } } } void handle_button(int key) { int bit; switch (key) { case KEY_POWER: bit = POWER_BIT; break; case KEY_UP: bit = UP_BIT; break; case KEY_DOWN: bit = DOWN_BIT; break; default: return; } if (bit_is_clear(BUTTON_PIN, bit)) { if (_pressed == 0) { _delay_ms(DEBOUNCE_TIME); if (bit_is_clear(BUTTON_PIN, bit)) { _pressed |= key; // key action switch (key) { case KEY_POWER: process_power(); break; case KEY_UP: process_up(); break; case KEY_DOWN: process_down(); break; } } } } else { _pressed &= ~key; } } void handle_buttons() { handle_button(KEY_POWER); handle_button(KEY_DOWN); handle_button(KEY_UP); } void process_power() { } void process_up() { if (value < 99) { value++; unsigned char tmp; tmp = value % 10; data1 = tmp; tmp = value/10; data2= tmp; } } void process_down() { if (value > 0) { value--; unsigned char tmp; tmp = value % 10; data1 = tmp; tmp = value/10; data2= tmp; } } |
Страница 1 из 1 | Часовой пояс: UTC + 3 часа |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |