Всем салют!
Пишу библиотеку на подобие OneWireSlave, в отличии от тех что есть на GitHub'е работает на внешнем прерывании и с несколькими датчиками на одной шине (по задумке). Столкнулся с такой ерундой: необходимо отлавливать Reset от мастера, причем мастер ведь может в любой момент прекратить сессию с ведомым послав тот же Reset, поэтому использую Timer2 на Atnega328p (Tmer0 используется delay(), milis() и micros(), поэтому исключаю, Tmer1 - Servo.h.
Настройка таймера в конструкторе класса:
Теперь собственно в чем проблема:
как видно из кода ниже я запускаю таймер в обработчике внешнего прерывания:
... и вот тут начинается...
если в тестовом проекте мастера сделать так:
... то все норм - presets отправляется, мастером детектируется.
Но, как только раскомментишь delay(), то начинаются "чертовы пляски" пропуски иvпульсов Reset от мастера в неопределенной последовательности.
Если же отказаться от таймера в функции Process() и сделать так:
... то все работает безукоризненно.
Подскажите в чем проблема, плиз, а то два дня парюсь, думаю уже отказаться от таймера вообще, но тогда жестко надо быть привязанным к правилу: ни каких лишних Reset!
Пишу библиотеку на подобие OneWireSlave, в отличии от тех что есть на GitHub'е работает на внешнем прерывании и с несколькими датчиками на одной шине (по задумке). Столкнулся с такой ерундой: необходимо отлавливать Reset от мастера, причем мастер ведь может в любой момент прекратить сессию с ведомым послав тот же Reset, поэтому использую Timer2 на Atnega328p (Tmer0 используется delay(), milis() и micros(), поэтому исключаю, Tmer1 - Servo.h.
Настройка таймера в конструкторе класса:
C++:
//OWSLAVE.cpp
#include "OWSLAVE.h"
#include "Arduino.h"
#include <avr/io.h>
#include <avr/interrupt.h>
volatile uint8_t *Intrrpt_Msk_Rgstr_Addr;
volatile uint8_t *Intrrpt_Flg_Rgstr_Addr;
volatile uint8_t *Ext_Intrrpt_Cntrl_Rgstr_Addr;
volatile uint8_t *reg, *out;
uint8_t INTx, INTFx, ISC0_bit, ISC1_bit,_Pin, _bit, _port;
#define EN_INT {*Intrrpt_Msk_Rgstr_Addr|=(1<<INTx);*Intrrpt_Flg_Rgstr_Addr|=(1<<INTFx);} //enable interrupt
#define DIS_INT *Intrrpt_Msk_Rgstr_Addr&=~(1<<INTx); //disable interrupt
#define SET_RISING (*Ext_Intrrpt_Cntrl_Rgstr_Addr=(1<<ISC1_bit)|(1<<ISC0_bit)) //set interrupt at rising edge
#define SET_FALLING {*Ext_Intrrpt_Cntrl_Rgstr_Addr &= ~(1 << ISC0_bit);*Ext_Intrrpt_Cntrl_Rgstr_Addr|=(1<<ISC1_bit);} //set interrupt at falling edge
#define SET_CHANGE {*Ext_Intrrpt_Cntrl_Rgstr_Addr &= ~(1 << ISC1_bit) ;*Ext_Intrrpt_Cntrl_Rgstr_Addr|=(1<<ISC0_bit);}//set interrupt at change edge
#define CHK_INT_EN (*Intrrpt_Msk_Rgstr_Addr|=(1<<INTx))==(1<<INTx) //test if interrupt enabled
#define SET_LOW {*reg |= _bit;} //линия 1-Wire на выход и на низкий уровень *out &= ~_bit;
#define ENTR_LINE {*reg &= ~_bit;} //линия 1-Wire на вход и Z уровень
#define READ_HIGH (*portInputRegister(_port) & _bit)//считать высокий сигнал с линии
#define READ_LOW (!(*portInputRegister(_port) & _bit))//считать низкий сигнал с линии
#if defined ([B]AVR_ATmega328P[/B])
#define TIMER_INT ISR(TIMER2_COMPA_vect) //the timer interrupt service routine
#define TCNT_REG TCNT2 //register of timer-counter
#define EN_TIMER {TCNT_REG=0;TIMSK2|= (1 << OCIE2A); TIFR2|=(1<<TOV2);} //enable timer interrupt
#define DIS_TIMER {TIMSK2 &=~(1<<TOIE2);}// disable timer interrupt
#endif
#define OWT_RESET 480
#define clock_MC (F_CPU / 1000000L )
#define clock_RESET (OWT_RESET/(64/clock_MC))
ISR (INT1_vect, ISR_ALIASOF (INT0_vect));
ISR (INT0_vect){
// код внешнего прерывания
}
TIMER_INT {
//код прерывания таймера
}
OWSLAVE::OWSLAVE(uint8_t pin){
_Pin = pin;
_bit = digitalPinToBitMask(_Pin);
_port = digitalPinToPort(_Pin);
if (_port == NOT_A_PIN) return;
out = portOutputRegister(_port);
reg = portModeRegister(_port);
#if defined ([B]AVR_ATmega328P[/B])
Intrrpt_Msk_Rgstr_Addr=&EIMSK;
Intrrpt_Flg_Rgstr_Addr=&EIFR;
Ext_Intrrpt_Cntrl_Rgstr_Addr=&EICRA;
if (_Pin==2){
INTx=INT0;
INTFx=INTF0;
ISC0_bit=ISC00;
ISC1_bit=ISC01;
}else if(_Pin==3){
INTx=INT1;
INTFx=INTF1;
ISC0_bit=ISC10;
ISC1_bit=ISC11;
}
TCCR2A = 0;
TCCR2B = 0;
TCNT2 = 0;
TIMSK2 = 0;
OCR2A = clock_RESET;
TCCR2A |=(1<<WGM21);
TCCR2B |=(1<<CS22) | (0<<CS21) | (0<<CS20);//clkT2S/64 (From prescaler)
DIS_TIMER;
#endif
}
как видно из кода ниже я запускаю таймер в обработчике внешнего прерывания:
C++:
static OWSLAVE *instance = NULL;
//====== Спасибо Гайверу!!! =====================
void attachFunction(void (*function)()) { // передача указателя на функцию
p_function = *function;
}
void (*p_function)(); // указатель на p_function
//=============================================
ISR (INT1_vect, ISR_ALIASOF (INT0_vect));
ISR (INT0_vect){
if(!status){ // эта переменная активна только при SEARCH_ROM приеме
EN_TIMER;// запускаем таймер детектора Reset
return;
}
}
TIMER_INT {
DIS_TIMER;//тормозим внешнее прерывание
DIS_INT;//тормозим таймер
mode=OW_RESET;
(*p_function)();// void Process()
}
void Process(){
uint8_t err;
byte addr[8];
instance-> errno = ONEWIRE_NO_ERROR;
uint8_t r=READ_HIGH;
switch (mode) {
case OW_RESET:
presence();
//mode=OW_COMMAND;
EN_INT;
return;
}
}
void presence(){
delayMicroseconds(30);
SET_LOW;//установить на входе низкий уровень
delayMicroseconds(120);
ENTR_LINE;//ВХОД НА ПРИЕМ(шина отпущена)
while (READ_LOW); //ждем высокий уровень на входе
delayMicroseconds(30);
}
void OWSLAVE::begin(uint8_t *rom) {//по прерыванию
cli();//откл. глоб.прер.
ENTR_LINE;//пин на вход
SET_FALLING;//отслеживаем FALLING
setRom(rom); //стандартно
attachFunction(Process);// спасибо Гайверу
instance = this;
EN_INT;
sei(); // вкл. глоб.прер.
}
void OWSLAVE::setRom(byte rom[8]) {
#if ONEWIRESLAVE_CRC
for (int i=0; i<7; i++)
this->rom[i] = rom[i];
this->rom[7] = crc8(this->rom, 7);
#else
for (int i=0; i<8; i++)
this->rom[i] = rom[i];
#endif
}
tstOWSLAVE.ino:
//тестинг ведомого
#include "OWSLAVE.h"
OWSLAVE ds(2);
// {Fami, <---, ----, ----, ID--, ----, --->, CRC}
byte rom[8] = {0x44, 0x1A, 0x00, 0x00, 0x0F, 0x00, 0x00, 0xFF}; //адрес устройства, 8 байт по протоколу
void setup() {
Serial.begin(9600);
ds.begin(rom);
}
void loop() {
}
если в тестовом проекте мастера сделать так:
tst_OneWire.ino:
#include <OneWire.h>
#define TX_PIN 5 // пин
OneWire ds(TX_PIN);
byte addr[8];
void setup() {
Serial.begin(9600);
}
void loop() {
ds.reset();
//delay(50;)
}
Но, как только раскомментишь delay(), то начинаются "чертовы пляски" пропуски иvпульсов Reset от мастера в неопределенной последовательности.
Если же отказаться от таймера в функции Process() и сделать так:
OWSLAVE.cpp:
volatile uint32_t old_previous = 0;
ISR (INT1_vect, ISR_ALIASOF (INT0_vect));
ISR (INT0_vect){
if(!status){
(*p_function)();
return;
}
}
void Process(){
uint8_t err;
byte addr[8];
instance-> errno = ONEWIRE_NO_ERROR;
uint8_t r=READ_HIGH;
switch (mode) {
case OW_START:
old_previous = micros();
mode=OW_RESET;
SET_RISING;
return;
case OW_RESET:
DIS_INT;
if(micros()-old_previous <480){
mode=OW_START;SET_FALLING;EN_INT;
return;
}
old_previous=0;
presence();
//mode=OW_COMMAND;
EN_INT;
return;
}
}
Подскажите в чем проблема, плиз, а то два дня парюсь, думаю уже отказаться от таймера вообще, но тогда жестко надо быть привязанным к правилу: ни каких лишних Reset!