Bloque sensor: Software final

Una vez verificado el funcionamiento de los módulos diseñados, he procedido a diseñar el software del módulo sensor.

Tal y como se ha definido, el funcionamiento a grandes trazos se muestra en el siguiente diagrama de flujo.

Es decir, ante una señal de reset o ante el encendido del microcontrolador el microcontrolador realiza una inicialización del sistema.

Inicialización

Esta inicialización, en primer lugar, inicializa el microcontrolador; establece la frecuencia de funcionamiento del reloj interno, las interrupciones y el estado y la dirección de las líneas, haciendo especial hincapié en el bajo consumo.

Una vez inicializado el microcontrolador, se inicializa el sensor a su configuración por defecto de medidas de 14bits para la temperatura y 12bits para la humedad relativa y no recargar la memoria de calibración. No obstante, antes de configurar el sensor se realiza una medida para cargar al menos una vez la memoria de calibración.

El siguiente dispositivo a inicializar es el transceptor. El transceptor se configura con los 15bytes de configuración inicial. Esta palabra de configuración establece lo comentado acerca de la comunicación.

Barrido de canales

Una vez inicializado el sistema, se realiza un barrido de canales hasta lograr la conexión.

El transceptor se ha inicializado con el canal 0. Para implementar la conexión en "caliente", al resetear o encender el módulo sensor, éste realiza un barrido de canales buscando establecer conexión con el módulo maestro. Este barrido se ha implementado como muestra el diagrama de flujo siguiente.

Si no se ha establecido conexión con el módulo maestro, el dispositivo envía el comando de inicio de conexión a la dirección del maestro por el canal inicial. Ante este comando el maestro debe responder con el paquete de respuesta, por lo que se entra en modo recepción durante un tiempo ajustado de respuesta del maestro.

Si pasado dicho tiempo no se ha recibido el paquete de respuesta o se ha recibido un paquete no válido, se comprueba si se está trabajando en el canal máximo, es decir, el canal 124. De no ser así, se incrementa el canal, se configura el transceptor y se repite el proceso. Si se está operando en el canal máximo, se comprueba si se está trabajando también a la máxima potencia, esto es 0dBm. De no ser así, se configura el transceptor con el canal inicial 0, se incrementa la potencia y se repite el proceso. Si se está operando a la máxima potencia, significa que tras realizar los barridos de canales para todo el rango de potencias no se ha logrado establecer comunicación, es decir el sensor se encuentra fuera de rango o no hay ningún nodo maestro a la escucha. Esto lo indica al usuario mediante el LED de estado y detiene el barrido hasta una señal de reset.

En caso de recibir el paquete de respuesta, se establece conexión y se continúa con el funcionamiento normal del sistema.

Medidas y envío

Una vez establecida la conexión, se procede a realizar las medidas. Primero se realiza la medida de humedad relativa y después la de temperatura. Estas medidas se realizan mediante la función de S_measure implementada en el módulo sensor.c/.h .

Una vez se dispone de los datos de las medidas. Éstos se envían, sin procesar, como un único paquete al nodo maestro. Tal y como se ha definido la comunicación, cuando el nodo maestro recibe un paquete de medidas, éste puede enviar algún comando al sensor. Entonces, una vez enviado el paquete, se configura en modo recepción el transceptor durante un tiempo ajustado.

Recepción y procesado

Si durante el tiempo de recepción se recibe un comando del maestro, el comando debe ser procesado.

El procesado se encarga de interpretar el comando recibido y actuar en consecuencia. El funcionamiento del procesado se muestra en el siguiente diagrama de flujo.

En primer lugar se procede a la verificación del dato. Tal y como se ha comentado, la estructura del comando hace que sea facilmente verificable, es decir, es fácil discernir entre un comando incorrecto y uno correcto. De este modo, al realizar la verificación antes de evaluar el comando, se evita seguir con el procesado ahorrándose tiempo y reduciendo el consumo.

Si el comando es correcto, se procede a su evaluación. Se busca que coincida con

Si durante el tiempo de recepción no se ha recibido ningún comando del maestro, se espera a la medida siguiente.

Tiempo entre medidas

El watchdog del microcontrolador, al disponer de la combinación de dos prescalers, permite un rango muy ámplio de tiempo de desbordamiento, en conreto permite desde 1.24ms a 325.36s. Estos tiempos son incómodos al no ser enteros, por lo que se utilizará el tiempo más exacto como base de tiempos. Para cierta configuración de ambos prescalers, se puede lograr un tiempo de 5,08366 segundos el cual es muy interesante como base de tiempos ya que se pueden lograr valores redondos como 60s (12x5), 120s (24x5).

El número que multiplica a la base, múltiplo de la base de tiempos, es de tipo byte por lo que se puede obtener un tiempo entre medidas de hasta 1270 segundos que son aproximadamente 21 minutos, claramente suficiente.

La forma de realizar el tiempo entre medidas se describe en el diagrama siguiente.

Se utiliza una variable, t_count, para contabilizar el número de desbordamientos producidos. Esta variable se inicializa a cero antes de activar el watchdog. El watchdog se configura con un tiempo de desbordamiento de 5.08 segundos (prescaler del registro OPTION 001 y el del watchdog WDTCON 1011) y se activa el watchdog.

Una vez activado el watchdog, se entra en modo de bajo consumo esperando el desbordamiento del watchdog. Una vez esto ocurre, se resetea el contador del watchdog, se incrementa la variable t_count, pues se ha producido un desbordamiento, y si esta variable, el número de desbordamientos producidos, es menor o igual al múltiplo de la base de tiempos establecido, es porque no se ha alcanzado el número de desbordamientos deseados por lo que se vuelve a entrar en modo de bajo consumo. Si se ha alcanzado el número de desbordamientos deseados, la variable t_count es mayor que el múltiplo establecido, se desactiva el watchdog y se continúa con el programa principal.

Bloque sensor: Diseño Software para bajo consumo.

El microcontrolador tiene líneas que pueden configurarse como entradas analógicas o como entradas/salidas digitales. Hay que tener en cuenta las señales que se aplican a estos pines ya que pueden suponer un consumo de corriente elevado.

Una línea de entrada digital consume la mayor cantidad de corriente cuando la tensión de entrada está entre la tensión de alimentación y la referencia. Esto es debido a que si la tensión de entrada está próxima al punto medio entre la alimentación y la referencia, los transistores que forman el buffer de entrada se polarizan en la región lineal lo que introduce un consumo de corriente considerable. Esto se puede evitar si cada línea puede configurarse como una entrada analógica, ya que este buffer se desconecta reduciendo así la corriente de la línea. Esto es debido a que las entradas analógicas tienen una impedancia de entrada muy elevada por lo que su consumo es mínimo.

Es por esto que si una línea no se utiliza, puede dejarse desconectada y configurada como línea de salida con un nivel lógico definido o se puede configurar como una entrada fija, externamente, a un nivel lógico definido.

También se debe tener en cuenta a la hora de inicializar un puerto, que tras un Power-on Reset, reset al alimentar al dispositivo, algunos registros como los PORT, los registros que contienen el valor de la línea, tienen un valor desconocido. Si los registros TRIS, los registros que configuran la dirección de la línea, se configuran antes que los PORT, es posible que se generen pulsos de corriente durante la inicialización del puerto. Por ejemplo, una forma segura de inicializar un puerto es primero borrar el contenido del registro PORT y luego configurar las líneas del puerto como salidas.

Referencias

Microchip Low Power solutions: Tips'n'Tricks.

Bloque sensor: Software, módulos principales

Seleccionadas las herramientas, he procedido a realizar el programa del sensor.

Antes de programar la funcionalidad completa del módulo sensor, se ha realizado la programación de los módulos de comunicación con el transceptor y con el sensor. De este modo, se puede verificar su funcionamiento independientemente. Así se reducen los problemas de depurado.

Comunicación con el transceptor

Tal y como se comentó en la entrada del transceptor, el protocolo de comunicación utiliza una línea de reloj y otra de datos, a parte de las líneas de configuración. Donde la frecuencia de la línea de reloj no podía ser superior a 1Mbps, es decir la duración mínima de cada bit debe ser de 1µs. Para reducir el tiempo de comunicación durante el cual el microcontrolador y el transceptor están activos, es decir suponen un consumo de corriente, se intentará operar a la frecuencia máxima.

Revisando la entrada del transceptor, se deduce que este módulo necesitará de, como mínimo, las siguientes funciones:

  • Enviar un paquete de datos a una dirección.
  • Entrar en modo recepción durante un tiempo y obtener el paquete recibido si es el caso.
  • Configurar el transceptor. Ya sea configurar la palabra completa (15bytes) o sólo una parte.

Donde se observa que a la función de enviar se le manda un paquete de datos y la de recibir lo devuelve. Este paquete que se enviará consiste en una serie de bytes que contienen la dirección y el dato. Para realizar un código más inteligible, se ha realizado una estructura que contiene un vector de bytes para la dirección y otro para los datos:

 
struct package
{
        unsigned char address[LEN_ADDR];
        unsigned char data[LEN_DATA];
};

Donde LEN_ADDR y LEN_DATA son macros que contienen la longitud de la dirección y de los datos respectivamente.

Tal y como se observa en la figura siguiente, el módulo dispone de tres funciones de uso interno o privadas (con fondo gris) cuya función se muestra a continuación. Son de alcance privado porque implementan una funcionalidad que se utiliza en más de una ocasión.

  • void RF_putByte(unsigned char databyte)

    Esta función le pasa bit a bit el byte al transceptor. Para ello, se le aplica al byte una máscara de un bit que se va desplazando.

  • void RF_configTx(void) y void RF_configRx(void)

    Estos métodos configuran al transceptor con un único bit el cual fija el modo de operación; en transmisión o en recepción.

Entonces, a la función enviar (RF_send) se le pasa la estructura package que contiene la dirección y los datos a enviar. Esta función configura el transceptor en modo transmisión (usa el método RF_configTx) y le pasa los datos del paquete al transceptor (mediante la función RF_putByte).

La función recibir (RF_receive) se le pasa un puntero con la estructura package donde almacenará los datos recibidos y un byte el cual fija el tiempo durante el cual el transceptor estará recibiendo. Durante este tiempo el microcontrolador está en modo de bajo consumo y es despertado por el perro guardián. Entonces el byte que hace configurable el tiempo fija el valor del prescaler del perro guardián. Al igual que para la función enviar, esta función hace uso de las funciones RF_putByte y RF_configRx.

Para configurar el transceptor está la función RF_configure, a la cual se le pasa un vector de bytes con los bytes de configuración y un byte que indica la longitud del vector. De este modo, se pueden configurar el número de bytes que se desee, 15 como máximo, teniendo en cuenta que el primer byte, posición 0 del vector, deberá ser el más significativo. Esta función hace uso de la función RF_putByte.

Para configurar de forma más cómoda los 15bytes del transceptor, se han creado una serie de macros o etiquetas que se encuentran en el fichero de cabecera transceiver.h. Se muestra un ejemplo a continuación.

 
const unsigned char transceiver_config[15]={DATA2_W,DATA1_W,ADDR2_4,ADDR2_3,ADDR2_2,ADDR2_1,
ADDR2_0,ADDR1_4,ADDR1_3,ADDR1_2,MY_ADDRESS_H,MY_ADDRESS_L,ADDR_W_16b|CRC_16b|CRC_EN_ENABLE,
RX2_EN_DISABLE|CM_SHOCKBURST|RFDR_SB_250KB|XO_F16MHZ|RF_PWR_20,RF_CH|RXEN_TX};

Comunicación con el sensor

La comunicación con el sensor se realiza mediante dos líneas, una de reloj y otra de datos. Como se ha visto, el sensor dispone de un registro de estado de un byte, que puede ser leído y escrito, y de una serie de comandos que permiten escritura/lectura de el registro de estado, realizar una medida de temperatura/humedad relativa y realizar un reset "suave".

Las funciones implementadas se muestran en la figura siguiente. Las de fondo gris son de uso interno o privadas. Su funcionalidad es la siguiente:

  • void S_putByte(const unsigned char databyte)

    Esta función gestiona la línea de datos y la de reloj enviándole al sensor el byte bit a bit. Para ello hace uso de una máscara con la que se obtiene el contenido del byte bit a bit.

  • unsigned char S_getByte(const unsigned char ack)

    Realiza el proceso inverso de la función anterior. Se comprueba la línea de datos y se va desplazando una máscara de un bit cada flanco de la línea de reloj. De este modo, cuando la línea de datos tenga el nivel lógico 1, su posición en el byte a obtener viene indicada por la máscara. El parámetro ack indica si se debe confirmar o no la recepción del byte. Mediante este parámetro se puede controlar el leer o no el CRC al confirmar o no la recepción del dato completo.

  • void S_transStart(void)

    Es un método que realiza la secuencia de inicio de transmisión. Esta secuencia se realiza cada vez que se desea comunicarse con el sensor.

De la figura se observa que existen dos métodos accesibles por el programador, el método S_resetConnection y S_softReset. Ambos realizan un reset del sensor con la diferencia de que el método S_resetConnection resetea únicamente la comunicación, y el método S_softReset resetea la comunicación y el registro de estado.

En cuanto a las funciones, la función S_read_status obtiene el registro de estado del sensor ( un byte). La función S_write_status envía al sensor el registro de estado que se le indica. Para facilitar la escritura del registro de estado, el fichero de cabecera sensor.h dispone de diversas macros. A continuación se muestra un ejemplo de su uso.

 
S_write_status(HEATER_OFF|OTP_NO_RELOAD|T14b_HR12b);    //Sensor wont reload OTP mem

La función más importante es S_measure, la encargada de realizar las medidas. Se le pasan como argumentos un puntero a una estructura del tipo measures, que se describe a continuación, y un parámetro que indica el tipo de medida; de humedad relativa o de temperatura. Para este último argumento se han definido dos macros, TEMP y RHUM, los cuales facilitan su uso. Respecto a la estructura del tipo measures se trata de una estructura que contiene un vector de dos bytes. Esto se ha realizado de este modo, para hacer más eficiente la función al operar con un puntero. A continuación se muestra la definición de la estructura measures.

 
struct measures
{
        unsigned char data[2]; //2 bytes because the maximum measure resolution is 14bits
};

Tal y como se comentó en la entrada del sensor, éste tarda un tiempo considerable en realizar las medidas. Es por esto que ese tiempo, para reducir el consumo, el microcontrolador se encuentra en modo de bajo consumo. Para sacar del modo de bajo consumo al microcontrolador una vez la medida esté disponible, se utiliza la interrupción ante un cambio de nivel (InterruptOnChange) que implementan las líneas del puerto A del microcontrolador. Este cambio de nivel es realizado por el sensor sobre la línea de datos; cuando la medida ha finalizado, pone la línea de datos a nivel bajo tal y como se comentó en la entrada del sensor.