NeuronC y RTC
En las redes Lonworks es necesario incluir un patrón de hora y puede que no se disponga de conexión a Internet para tomarla de un servidor o que no exista un gateway que pueda realizar esta función. Para ello es muy fácil conectar a una CPU de Echelon (FT3120 o FT3150) mediante el bus I2C un chip del tipo PCF8583 (RTC).
Mediante el código adjunto y la documentación técnica del fabricante del chip podremos dotar a la red de una hora fiable basada en un auténtico reloj / calendario. Esta es la versión base con las funciones mínimas. El código se puede completar con la inclusión de escenas, plug-in de ajuste de hora, conexión de sincronización via GPS, etc.
- //----------------------------------------------------
- // Software necesario para implementar un RTC en
- // un nodo Lonworks con un chip I2C PCF8583
- // Este reloj sería el 'master' de horario
- // en ausencia de otras referencias externas
- //----------------------------------------------------
- // En versiones posteriores se incluye la gestión
- // automática del horario verano/invierno
- // así como la sincronización via GPS
- //-----------------------------------------------------
- #define rtc_addr 0x50 // dirección del RTC
- network output sd_string("Dia de la Semana") SNVT_date_day nvoDiaSema;
- network input sd_string("Entrada Ajuste Hora") SNVT_time_stamp nviTimeSet;
- // Variables Errores I2C
- network output sd_string("Fallos bus I2C")SNVT_count I2CFails;
- // Salidas RTC, como suele vincularse a decenas o cientos de nodos, se ajusta como
- unackd_rpt network output sd_string("Time Stamp propagado") SNVT_time_stamp bind_info(unackd_rpt) nvoTimeRef;
- network output sd_string("Day of Week") SNVT_count nvoDOW;
- //////////////////////////////////////////////////////////////////
- // Funciones - Timers - Variables - I/O's - etc. //
- //////////////////////////////////////////////////////////////////
- int es_bisi(long ano);
- unsigned short dow(unsigned int d, unsigned int m, unsigned long a);
- /////////////////////// Timers ///////////////////////////////////
- mtimer repeating lectura=250;
- ////////////////////// I/O Objects //////////////////////////////
- IO_8 i2c io_i2c_bus;
- ////////////////////// Vars ////////////////////////////////////
- far unsigned int rtcbuff[18];
- far unsigned int rtcset[7];
- far unsigned int rtc_cont;
- far unsigned long ano;
- far unsigned int ano_ant, ano_act, minut;
- unsigned int horas_adel;
- // Variable interna para manejo reloj
- SNVT_time_stamp nvoTimeSet;
- /////////////////////////////////// Tasks ///////////////////////////////////
- // Reset task
- // This task is called whenever the node is reset.
- when(reset){ rtc_cont=0;
- // Lectura Inicial RTC
- if (!io_out(io_i2c_bus,&rtc_cont,rtc_addr,1)) I2CFails++;
- if (!io_in(io_i2c_bus,rtcbuff,rtc_addr,18)) I2CFails++;
- // Si se desea se puede manejar la variable I2CFails aquí...
- // No hay fallos nunca salvo avería directa del chip
- ano_act=( (rtcbuff[5]&0xC0)>>6);
- nvoTimeSet.second=(((rtcbuff[2]&0xF0)>>4)*10)+(rtcbuff[2]&0x0F);
- nvoTimeSet.minute=(((rtcbuff[3]&0xF0)>>4)*10)+(rtcbuff[3]&0x0F);
- nvoTimeSet.hour=(((rtcbuff[4]&0x30)>>4)*10)+(rtcbuff[4]&0x0F);
- nvoTimeSet.day=(((rtcbuff[5]&0x30)>>4)*10)+(rtcbuff[5]&0x0F);
- nvoTimeSet.month=(((rtcbuff[6]&0x10)>>4)*10)+(rtcbuff[6]&0x0F);
- ano=(rtcbuff[16]*256)+rtcbuff[17]+ano_act;
- nvoTimeSet.year=ano;
- nvoDiaSema=(rtcbuff[6]&0xE0)>>5;
- nvoTimeRef=nvoTimeSet;
- }
- /////////////////////////////////////
- //***************************************
- // Puesta en hora del Reloj desde SNVT's
- //***************************************
- when (nv_update_occurs(nviTimeSet)) {
- unsigned int x, y, t;
- unsigned long z;
- x=dow(nviTimeSet.day,(unsigned int)nviTimeSet.month,nviTimeSet.year);
- nvoDOW=x;
- if (x==7) {y=0;}
- else {y=x;}
- rtcset[0]=2;
- t=(unsigned int)(nviTimeSet.year % 4);
- ano_act=t;
- ano_ant=t;
- z=nviTimeSet.year-t;
- rtcset[1]=((nviTimeSet.second/10)*16)+(nviTimeSet.second%10);
- rtcset[2]=((nviTimeSet.minute/10)*16)+(nviTimeSet.minute%10);
- rtcset[3]=((nviTimeSet.hour/10)*16)+(nviTimeSet.hour%10);
- rtcset[4]=((nviTimeSet.day/10)*16)+(nviTimeSet.day%10)+(t<<6);
- rtcset[5]=((nviTimeSet.month/10)*16)+(nviTimeSet.month%10)+(y*32);
- if (!io_out(io_i2c_bus,&rtcset,rtc_addr,6)) I2CFails++;
- rtcset[0]=16;
- rtcset[1]=(unsigned int)(z / 256);
- rtcset[2]=(unsigned int)(z % 256);
- if (!io_out(io_i2c_bus,&rtcset,rtc_addr,3)) I2CFails++;
- nvoTimeRef=nviTimeSet;
- nvoTimeSet=nviTimeSet;
- // Nueva hora, propagar salida....
- propagate(nvoTimeRef);
- }
- //*************************************************************
- /////////////////////////////////////////////////////////////////////////////
- // I/O event tasks //
- /////////////////////////////// Functions /////////////////////////////////
- //--------------------------------------------------------------------------
- // Volver a leer el reloj... para otro ritmo, ajustar timer
- //--------------------------------------------------------------------------
- when (timer_expires(lectura)){
- unsigned int sumador;
- signed int z;
- rtc_cont=0;
- if (!io_out(io_i2c_bus,&rtc_cont,rtc_addr,1)) I2CFails++;
- if (!io_in(io_i2c_bus,rtcbuff,rtc_addr,18)) I2CFails++;
- // Ver posibilidad de comunicar error en caso de fallo en lectura reloj
- ano_act=( (rtcbuff[5]&0xC0)>>6);
- if ( (ano_act!=ano_ant) && (ano_act==0) ){
- sumador=4;
- }
- else {
- sumador=0;
- }
- ano=(rtcbuff[16]*256)+rtcbuff[17]+ano_act+sumador;
- ano_ant=ano_act;
- // Igualar comparadores
- nvoTimeSet.second=(((rtcbuff[2]&0xF0)>>4)*10)+(rtcbuff[2]&0x0F);
- nvoTimeSet.minute=(((rtcbuff[3]&0xF0)>>4)*10)+(rtcbuff[3]&0x0F);
- nvoTimeSet.hour=(((rtcbuff[4]&0x30)>>4)*10)+(rtcbuff[4]&0x0F);
- nvoTimeSet.day=(((rtcbuff[5]&0x30)>>4)*10)+(rtcbuff[5]&0x0F);
- nvoTimeSet.month=(((rtcbuff[6]&0x10)>>4)*10)+(rtcbuff[6]&0x0F);
- nvoTimeSet.year=ano;
- nvoDiaSema=(rtcbuff[6]&0xE0)>>5;
- if (sumador==4) {
- rtcset[0]=16;
- // Escribir a partir de dirección 16
- rtcset[1]=(unsigned int)(nvoTimeSet.year / 256);
- rtcset[2]=(unsigned int)(nvoTimeSet.year % 256);
- if (!io_out(io_i2c_bus,&rtcset,rtc_addr,3)) I2CFails++;
- sumador=0;
- }
- //********************************************************************//
- // Para no saturar la red, la hora se propaga cada minuto, suficiente
- // para la mayoría de los sistemas de control
- // Estamos en un nuevo minuto ?
- if ( nvoTimeSet.minute != minut ) {
- nvoTimeRef=nvoTimeSet;
- minut=nvoTimeSet.minute;
- }
- //--------------------------------------------------------------------
- }
- //***************************//
- // Calcula dia de la Semana //
- //***************************//
- // Lunes=1....Domingo=7
- unsigned short dow(unsigned short d, unsigned short m, unsigned long a) {
- unsigned int an, dato;
- an=(unsigned int)(a-1900);
- if (m >= 3) m -= 3;
- else {
- m += 9;
- an--;
- }
- dato=(an + an / 4 + (m * 13 + 2) / 5 + d + 3) % 7 + 1;
- dato--;
- if (dato==0) {dato=7;}
- return (dato);
- }
- //*****************************
- //*************
- // Año bisiesto
- //*************
- // Devuelve '0' si es bisiesto y '1' si no lo es para restar a 29 dias de febrero...
- int es_bisi(long ano){
- int a;
- if ((ano % 4)!=0) {a=0;} else {a=1;};
- return a;
- }
- //*************