...
// #include "iwslave.h"
///////////////////////// contents of iwslave.h
/*! переход в состояние требующее сброса (начальное) */
void iwire_reset(void);
/*! функция вызывается при старте программы */
int iwire_init(void);
//#include "iwslave_hal.c"
//////////////////////////// contents of iwslave_hal.c
/// XXX файл содержит функции связывающие логику модуля с аппаратурой
/* задержка на вход в прерывание, мкс */
#define LATENCY 1
/* пересчёт микросекунд в такты таймера */
#define US2TMR(us) (((us)-LATENCY)*(SYSCLK/4/1000L)/1000L)
static NEAR bit iw_pin; /* сохранённое состояние входа */
/* чтение состояния шины */
#define iwire_in_poll() (RB4)
#define iwire_in_edge() (iw_pin)
/* управление выходом (0 - активный 0, 1 - пассивная 1) */
#define iwire_out(v) do { \
if (!(v)) LATC0=1; \
else LATC0=0; \
} while(0)
/* разрешение прерывания от 1-wire */
#define iw_ei() do { RBIE=1; RBIF=0; } while(0)
#define iw_di() do { RBIE=0; RBIF=0; } while(0)
/* флаг разрешённого прерывания */
#define iw_if() (RBIE)
/* флаг прерывания таймера */
#define tint() (TMR3IF)
/* обработчик высокоприоритетного прерывания */
#define IWIRE_INTERRUPT_HANDLER() void interrupt iwire_interrupt(void)
#define IWIRE_INT_PROLOGUE do { do { iw_pin=RB4; RBIF=0; } while(0)
#define IWIRE_INT_EPILOGUE } while((RBIF && RBIE) || (TMR3IF && TMR3IE))
/* короткая задержка на установление сигналов на ножках */
#define iodelay() do { \
asm("nop"); asm("nop"); asm("nop"); asm("nop"); \
} while(0)
/* срабатывание таймера через tm тактов таймера */
#define after(tm) do { \
unsigned t=-(tm); \
TMR3ON=0; \
TMR3H=t>>8, TMR3L=t&0xFF; \
TMR3IF=0; \
TMR3ON=1; \
} while(0)
/* считывание значения таймера оставшегося до срабатывания */
#define tmget() (-TMR3)
/* останов таймера */
#define tmstop() do { \
TMR3ON=0; \
TMR3IF=0; \
} while(0)
static void iwire_start_hw(void)
{
T3CON=0x80; /* RD16=1, no prescaler, Fosc/4 clock source */
TMR3IE=1, TMR3IF=0;
TMR3IP=1; /* timer is high priority interrupt */
RBIE=0, RBIF=0;
IOCB=0x10; /* interrupts enabled only for RB4 */
RBIP=1; /* pin change is high priority too */
C2IP=0, C2IE=1, C2IF=0;
RBIE=1;
}
static void iwire_stop_hw(void)
{
TMR3IE=0;
C2IE=0;
RBIE=0;
}
static void iwire_init_hw(void)
{
LATC0=0, TRISC0=0;
TRISB4=1;
}
//////////////// end of iwslave_hal.c
//////////////// continue of iwslave.c ///////////////////////
/* временные интервалы в мкс */
#define RST_MAX_LENGTH US2TMR(650) /* макс. длительность импульса сброса */
#define RST_MIN_LENGTH US2TMR(450) /* мин. длительность импульса сброса */
#define PRESENCE_PAUSE US2TMR(45-35) /* пауза перед импульсом присутствия */
#define PRESENCE_LENGTH US2TMR(250) /* длительность импульса присутствия */
#define BIT_SAMPLE US2TMR(30) /* считывание бита после начала импульса */
#define BIT_WRITE US2TMR(35) /* длительность импульса записи 0 */
#define BIT_MAX_LENGTH US2TMR(400) /* макс. длительность бита при чтении-записи */
/* состояние автомата */
enum e_iwire_s { IWIRE_RESET=0, IWIRE_READ, IWIRE_WRITE };
NEAR enum e_iwire_s iwire_S;
/* вложенные состояния (сброс) */
NEAR static enum { RST0, RST1, RST2, RST3 } iwire_RS;
typedef uint_fast8_t iwsize_t;
static NEAR uint_fast8_t bits, byte;
static uint8_t *NEAR rxdata, *NEAR rxbuf; // TODO объединить rxdata и txdata?
static NEAR iwsize_t rxsize;
uint8_t *NEAR txdata;
static NEAR iwsize_t txsize;
/* переход в состояние требующее сброса (начальное) */
void iwire_reset(void)
{
USE_CRITICAL;
BEGIN_CRIT();
tmstop();
iwire_S=IWIRE_RESET, iwire_RS=RST0;
END_CRIT();
}
/* переход в состояние приёма (из состояния записи), вызывается из iwdevice.c */
#define iwire_read(_data, _size) do { \
iwire_S=IWIRE_READ, bits=8; \
rxbuf=rxdata=/*(void*)*/(_data), rxsize=(_size); \
} while(0)
/* переход в состояние записи (передаст указанные данные), вызывается из iwdevice.c */
#define iwire_write(_data, _size) do { \
txdata=/*(const void*)*/(_data), txsize=(_size); \
byte=*txdata++; \
iwire_S=IWIRE_WRITE, bits=8; \
} while(0)
/* включение файла реализующего конкретный протокол обмена */
//// XXX файл реализует интерфейс, функции:
//// void iw_fail(void) -- вызывается при сбое в обмене
//// void iw_reset(void) -- вызывается при приёме сигнала сброса
//// void iw_read(iwsize_t size) -- вызывается после принятия данных (буфер задаётся через iwire_read()
//// void iw_write(void) -- вызывается после передачи данных (заданных через iwire_write()
//// описываемые функции реализуют конечный автомат управлямый вызовами данных
//// функций и реализующий логику работы прибора (т.е. iwdevice.c -- только транспортный уровень).
//// XXX: принципиально включение через include, т.к. в разных единицах трансляции
//// компилятор не будет понимать какие регистры не испольуются и будет сохранять
//// все регистры на входе в обработчик прерывания.
#include "iwdevice.c"
/* обработчик прерывания, tint()==1 если прерывание от таймера */
IWIRE_INTERRUPT_HANDLER()
{
IWIRE_INT_PROLOGUE;
do {
if (iwire_S == IWIRE_WRITE) {
if (!tint()) {
if (iwire_in_edge()) {
tmstop(); break;
}
if (!(byte&1)) {
iwire_out(0);
iw_di();
after(BIT_WRITE);
byte>>=1;
break;
}
byte>>=1;
}
else if (iw_if()) {
after(RST_MAX_LENGTH-BIT_MAX_LENGTH);
iwire_S=IWIRE_RESET, iwire_RS=RST1;
iw_fail(); break;
}
else { /* TINT, write 0 */
iw_ei();
iodelay();
iwire_out(1);
}
/* TINT (write 0) or writing 1 */
bits--;
if (!bits) {
bits=8;
txsize--;
if (!txsize) {
iwire_S=IWIRE_READ;
iw_write();
}
else
byte=*txdata++;
}
after(BIT_MAX_LENGTH-BIT_WRITE);
break;
}
else if (iwire_S == IWIRE_READ) {
if (!tint()) {
if (iwire_in_edge()) {
tmstop(); break;
}
after(BIT_SAMPLE);
iw_di();
}
else if (!iw_if()) {
byte>>=1;
if (iwire_in_edge())
byte|=0x80;
if (!--bits) {
bits=8;
if (!rxsize) {
iw_ei();
iwire_S=IWIRE_RESET;
if (!iwire_in_poll()) {
tmstop(); iwire_RS=RST0;
}
else {
after(RST_MAX_LENGTH);
iwire_RS=RST1;
}
iw_fail(); break;
}
*rxdata++=byte;
if (!--rxsize)
iw_read(rxdata-rxbuf);
}
iw_ei();
if (iwire_in_poll())
tmstop();
else
after(BIT_MAX_LENGTH-BIT_SAMPLE);
}
else /* iw_if() */ {
after(RST_MAX_LENGTH-BIT_MAX_LENGTH);
iwire_S=IWIRE_RESET, iwire_RS=RST1;
iw_fail();
}
break;
}
else /* IWIRE_RESET: */ {
PSTATIC NEAR unsigned t;
switch (iwire_RS) {
case RST1:
t=-tmget(), t=-t; /* PIC18 suxx */
if (tint()) {
tmstop(); iwire_RS=RST0; break;
}
else if (t >= RST_MAX_LENGTH-RST_MIN_LENGTH) {
tmstop(); iwire_RS=RST0;
}
else if (iwire_in_edge()) {
iwire_RS=RST2;
after(PRESENCE_PAUSE);
iw_di();
}
break;
case RST2:
iwire_out(0);
after(PRESENCE_LENGTH);
iwire_RS=RST3;
break;
case RST3:
iwire_out(1);
tmstop();
iwire_RS=RST0;
bits=8;
iwire_S=IWIRE_READ;
iw_reset();
/*iodelay();*/
iw_ei();
break;
case RST0:
default:
if (iwire_in_edge()) break;
after(RST_MAX_LENGTH);
iwire_RS=RST1;
}
}
} while(0);
IWIRE_INT_EPILOGUE;
}
/* функция вызывается при старте программы */
int iwire_init()
{
uint_fast8_t v;
ftime_t t;
iwire_init_hw();
iwire_stop_hw();
/* test */
iwire_out(0);
iodelay(); iodelay(); iodelay(); iodelay();
if (iwire_in_poll()) {
iwire_out(1);
writelog(LOG_CRIT, "1-wire output(0) failure (can't set to zero)");
return -1;
}
iwire_out(1);
t=timer(); v=0;
do {
if (iwire_in_poll()) {
v=1; break;
}
} while (timer()-t < FTIME_PER_SEC/1000);
if (!v) {
writelog(LOG_CRIT, "1-wire intput failure (always 0, must be 1)");
return -1;
}
iwire_S=IWIRE_RESET, iwire_RS=RST0;
iwire_start_hw();
iwire_reset();
return 0;
}
[ZX]