Mostrando entradas con la etiqueta C18. Mostrar todas las entradas
Mostrando entradas con la etiqueta C18. Mostrar todas las entradas

domingo, 13 de octubre de 2013

Memorias E2PROM embebidas

Es muy común, que en algunos de nuestros diseños sea necesario almacenar una pequeña cantidad de datos de forma que, aunque nuestro sistema se quede sin alimentación, estos datos se mantengan ahí para cuando nos sean necesarios. Esto se hace muy necesario en sistemas, por ejemplo, que están alimentados a partir de energías renovables, ya que estas, aunque puedan ir respaldadas por una batería, es posible que en algún momento no estén disponibles.
Por mi experiencia, estos datos que debemos almacenar, suelen ser datos de configuraciones que hemos seleccionado ya después de la programación, como por ejemplo la tensión de la batería que hemos conectado a un cargador de baterías,  o valores de calibración que son diferentes para cada sistema, por ejemplo si nuestro sistema acepta diferentes tipos de sensores. Otro de los usos que se les da a estas memorias no volátiles, puede ser por ejemplo el de almacenar datos, sin utilizar parte de la memoria RAM que dispone nuestro microcontrolador, como en un datalogger de cualquier magnitud.
Memorias no volátiles existen de 2 tipos básicamente, las memorias del tipo FLASH, o las memorias del tipo E2PROM. Cuando hablamos de microcontroladores, todos disponen de 2 tipos de memorias, que son la FLASH, y la RAM. En la FLASH, que como he dicho, es no volátil, almacenamos el programa, de forma que este siempre está disponible cuando nuestro sistema se alimenta, pero solo es posible escribir en ella mediante la programación mientras que en la RAM, que es una memoria volátil, se almacena el valor de las variables, por lo que se hace imposible retener un valor si la alimentación desaparece.
Vista la necesidad de introducir en nuestros diseños memorias no volátiles, lo que se nos ocurre primero es añadir una memoria E2PROM externa, y la comunicamos con el microcontrolador mediante algún protocolo serie como SPI o el I2C, pero si la cantidad de datos que queremos almacenar no es muy grande, siempre podemos utilizar la memoria E2PROM que llevan incorporados algunos microcontroladores, entre ellos la serie Arduino, además de algunos microcontroladores PIC.
Si estamos utilizando cualquier placa Arduino utilizar la memoria E2PROM interna es muy sencillo, tan solo tenemos que utilizar la librería EEPROM. Como todo lo que tiene que ver con la programación de Arduino, es muy sencilla de utilizar, y de pueden encontrar ejemplos en la página oficial de la comunidad, aquí tenéis uno para escribir, y aquí para leer. En cuanto a capacidad, las placas Arduino no escatiman en este tipo de memoria, ya que en las placas UNO contamos con 512 Bytes, mientras que para las placas MEGA 2650 contamos con 4 kBytes, lo cual es suficiente para muchas de las aplicaciones que hagamos.
En cuanto a los microcontroladores PIC, también hay un gran número de estos en los que podemos encontrar memorias EEPROM embebidas, pero si queremos un micro de gama alta, PIC32 por ejemplo, la cosa se pone muy muy dificil, sin embargo, no hay problema si queremos utilizar uno de gama media baja como PIC24 o, como en el ejemplo que os voy a mostrar, PIC18, en concreto voy a poneros un ejemplo para un PIC18F14K50. A continuación, os pongo la secuencia que hay que seguir para escribir y leer correctamente en los microcontroladores PIC.


Como veis, en cuanto a la lectura no hay problema, además que lo hace realmente rápido, ya que en un solo ciclo de reloj tenemos el dato listo para su lectura. A continuación tenéis el código C que hace la misma función que ese código de ensamblador.
char leer_eeprom(char direccion){

    EEADR = direccion;  // Escribimos la dirección
    EECON1bits.EEPGD = 0;
    EECON1bits.CFGS = 0;
    EECON1bits.RD = 1;
    return EEDATA;

}
En cuanto a la escritura es un poco más largo, pero igualmente sencillo, y aquí os pongo una función ya implementada con la que podéis escribir en las eeprom internas.
void escribir_eeprom(char direccion, char dato){

    di(); //Deshabilitamos las interrupciones

    EEADR = direccion; // Escribimos la dirección
    EEDATA = dato;  // Escribimos el dato
    EECON1bits.EEPGD = 0;
    EECON1bits.CFGS = 0;
    EECON1bits.WREN = 1;

    EECON2 = 0x55; // Secuencia de escritura
    EECON2 = 0xAA;
    EECON1bits.WR = 1;

    ei(); //Habilitamos las interrupciones

}
Espero que os sea útil esta entrada! y si tenéis alguna duda, como siempre, a los comentarios!

jueves, 25 de julio de 2013

FreeRTOS en PIC18 II.

Volviendo con FreeRTOS, en esta entrada os voy a hablar de la tarea IDLE, es decir, la tarea REPOSO. En primer lugar, vamos a recordar como funciona un sistema operativo, que no es más que un gestor de tareas para que aparentemente tengamos un sistema que puede ejecutar varias tareas al mismo tiempo. En la entrada anterior creamos una tareas repetitivas que se repetian cada 250 y 350 milisegundos, pero ¿que pasa cuando esas tareas no se están ejecutando?, bien, en ese momento, el procesador está esperando, no tiene nada que hacer, es lo que se conoce como tiempo ocioso del procesador. Evidentemente, si queremos optimizar al máximo el procesador, lo ideal sería que durante este tiempo, el procesador siguiera trabajando, pero ¿en que?. 

Lo primero que se debe hacer es diferenciar entre 2 tipos de tareas, las tareas síncronas, y las tareas asíncronas. Las primeras de ellas tienen una frecuencia de ejecución, y en ocasiones es altamente recomendable que ese tiempo se cumpla de forma escrupulosa, por ejemplo en sistemas críticos en los que se dos sistemas independientes deben realizar una acción al mismo tiempo. Las segundas son tareas que no tienen porque ejecutarse a una frecuencia determinada, simplemente, deben ejecutarse, por ejemplo la actualización de la información de una pantalla, no es algo tan crítico, como ejecutar un control a una frecuencia que hemos calculado. Los desarrolladores de FreeRTOS ya han pensado en esto, y la tarea idle ya nos la proporcionan sin hacer nada mas que activarla, y después llamarla.
En primer lugar tenemos que abrir el archivo FreeRTOSConfig.h y cambiar la siguiente línea:
//#define configUSE_IDLE_HOOK    0   // No se llama a la función idle
#define configUSE_IDLE_HOOK    1   // Se llama a la función idle
Una vez activada la tarea tan solo tenemos que definirla en el programa principal de la siguiente forma:
//  Tarea reposo
void vApplicationIdleHook( void )
{
    LATBbits.LATB2=!PORTBbits.RB2;
}
Eso es todo lo que se ha de hacer, en la siguiente entrada, como dije, tratare el tema de los semáforos.

domingo, 7 de julio de 2013

FreeRTOS en PIC18.

Una limitación que tienen TODOS los procesadores mononúcleo, es que solo pueden ejecutar una instrucción, y por tanto una tarea al mismo tiempo. Esto, hasta hace bien poco, los fabricantes lo intentaban solucionar aumentando la velocidad de estos procesadores, de ahí que los ultimos Pentium 4 llegaran hasta velocidades cercanas a los 4GHz, con rendimientos que distan mucho de los procesadores actuales de menor velocidad de doble núcleo. Si nos vamos al nivel de microcontrolador, el problema que se nos presenta es el mismo, solo podemos tener un hilo de ejecución, y aunque con las interrupciones podemos asegurar la ejecución a tiempo de una instrucción, no aseguramos que esta se ejecute por completo si salta otra interrupción. Esta limitación de solo poder ejecutar una instrucción al mismo tiempo, no ha evitado que procesadores como los Pentium 4, puedan mover sistemas multitarea, como por ejemplo Windows, en el cual continuamente estamos ejecutando varios procesos al mismo tiempo (Ctrl + Alt + Supr > Procesos). Para que esto sea posible, es necesario que haya "algo", que decida que debe ejecutar el procesador en cada momento, para que, aparentemente, y solo aparentemente, nuestro procesador sea multitarea, y es aquí donde los Sistemas Operativos entran en acción.
A pesar de que cuando leemos las palabras Sistema Operativo se nos viene a la cabeza Windows, Linux o MacOS, existen infinidad de sistemas operativos, cada uno con un fin diferente. Evidentemente, siendo este un blog de electrónica y microcontroladores, no podemos pretender que un microcontrolador de 8 bits mueva cualquiera de estos sistemas operativos, por lo menos las versiones más actuales, por ello en esta entrada voy a hablaros de FreeRTOS, un pequeño sistema operativo que nos permitirá hacer que nuestro pequeño PIC18F4550 pueda ejecutar varias tareas al mismo tiempo, como he dicho antes, aparentemente.


FreeRTOS es un sistema operativo de los denominados en tiempo real (Real Time Operating System), los cuales aseguran que las tareas se vayan a ejecutar en un tiempo determinado, y está diseñado para que quepa en un microcontrolador desde 8 hasta 32 bits. Para estas primeras pruebas, voy a utilizar ese micro que tanto gusta a mis lectores, el PIC18F4550. Lo primero que debemos hacer, es abrir un nuevo proyecto y agregar los archivos necesarios, tanto librerías como archivos de código, que en este caso no son demasiados. Los archivos los podéis descargar desde la página oficial de FreeRTOS. Cuando lo instalamos, no estamos instalando solo los códigos y las librerías para PIC18, también vienen incluidos los archivos para todos los microcontroladores compatibles con FreeRTOS (...\FreeRTOSV7.4.2\FreeRTOS\Demo). En nuestro caso, utilizamos el ejemplo para PIC18 en MPLAB, pero en esta ocasion no vamos a ejecuar los ejemplos directamente, sino que vamos a programar unos ejemplos nosotros más sencillos y que serviran para entender mejor su funcionamiento. Una vez hemos descargado e instalado el FreeRTOS, creamos un proyecto, y agregamos los archivos necesarios de forma que quede de la forma siguiente:


Una vez agregados los archivos es necesario hacer unas modificaciones de las propiedades del proyecto, en primer lugar debemos agregar una macro para el preprocesador, MPLAB_PIC18F_PORT, de foma que quede así.


Por ultimo lo que debemos hacer es en esa misma ventana, seleccionamos desde el desplegable "Memory Model", y seleccionamos para ambas memorias el modelo grande (large model). Una vez hecho todo esto, y definidos los paths para nuestros archivos, podemos empezar a escribir el código principal.
El primer programa que voy a poneros es uno sencillo, pero que se le puede sacar bastante juego, y se traba del manejo de 2 salidas del PIC parpadeando a 2 frecuencias, en principio diferentes. El código es el siguiente:
//  Programa de ejemplo FreeRTOS
#include "p18f4550.h"
#include "FreeRTOS.h"
#include "task.h"

#pragma config FOSC=HS
#pragma config IESO=OFF
#pragma config PWRT=ON
#pragma config WDT=OFF
#pragma config MCLRE=ON
#pragma config XINST=OFF
#pragma config DEBUG=OFF
#pragma config FCMEN = OFF
#pragma config LVP=OFF
#pragma config PBADEN = OFF

//  Definimos la prioridad de las tareas
#define PRIORIDAD_TAREA0      ( tskIDLE_PRIORITY + 1 )
#define PRIORIDAD_TAREA1      ( tskIDLE_PRIORITY + 1 )

//  Prototipos de las tareas
static void TAREA0( void *pvParameters );
static void TAREA1( void *pvParameters );

//  Programa principal
void main( void ){
    TRISB=0x00;
    vPortInitialiseBlocks();

    xTaskCreate( TAREA0, ( const char * const ) "T0", configMINIMAL_STACK_SIZE, NULL, PRIORIDAD_TAREA0, NULL );
    xTaskCreate( TAREA1, ( const char * const ) "T1", configMINIMAL_STACK_SIZE, NULL, PRIORIDAD_TAREA1, NULL );

    vTaskStartScheduler();
}
//  Tarea 0
static void TAREA0( void *pvParameters ){
    while(1)
    {
        LATBbits.LATB0=!PORTBbits.RB0;
        vTaskDelay(250/portTICK_RATE_MS);
    }
}

//  Tarea 1
static void TAREA1( void *pvParameters ){
    while(1){
        LATBbits.LATB1=!PORTBbits.RB1;
        vTaskDelay(350/portTICK_RATE_MS);
    }
}

//  Interrupción de recepción del puerto serie
void vSerialRxISR( void ){
    
}

//  Interrupción de transmisión del puerto serie
void vSerialTxISR( void ){

}

Lo primero que se hace es declarar los prototipos de las funciones de cada tarea. Una vez hecho esto debemos crearlas, para ello se utiliza la expresión xTaskCreate( TAREA0, ( const char * const ) "T0", configMINIMAL_STACK_SIZE, NULL, PRIORIDAD_TAREA0, NULL ), de los campos de la función, los más importantes son el primero, que se corresponde con el nombre de la función que va a ejecutar, y el penúltimo que se corresponde con la prioridad, esto es por si se deben ejecutar las dos tareas al mismo tiempo, cual de estas será prioritaria. En este caso las dos tienen la misma prioridad. Una vez creadas las tareas, lo siguiente que hacemos es ejecutar el sistema operativo con vTaskStartScheduler(). Con esto tenemos finalizado el programa principal, lo siguiente es definir las tareas con el nombre que le hemos dado en sus prototipos. Como veis, cada tarea tiene un bucle infinito, pero esto no quiere decir ue se quede aquí indefinidamente, sino que una vez ha hecho su función, se queda "esperando" mediante la función vTaskDelay(tiempo_en_ms/portTICK_RATE_MS). La mayor diferencia de este código utilizando un RTOS, y otro que no lo utilice, es que en este, mientras estamos esperando a que la tarea deba volver a ejecutarse, el procesador no está ocupado, y puede atender a otras peticiones, como por ejemplo, la tarea 2. Habrá momentos, en los que las dos tareas deban ejecutarse al mismo tiempo, en nuestro caso esto ocurrirá en 1.750s (mínimo común múltiplo de 250 y 350 ms), y en este momento entrará en juego la prioridad que hayamos definido en cada tarea, ejecutándose en primer lugar la más prioritaria. Dependiendo de la frecuencia de nuestro sistema, el tiempo de retardo que tendrá la tarea menos prioritaria será mayor o menor (unos 80us para 48MHz). Para evitar problemas, las tareas mas importantes deberán ser las mas prioritarias, de forma que estas se ejecuten siempre a tiempo.
Uno de los problemas que tienen que solucionar los RTOS es el acceso a recursos, es decir, tenemos un LCD conectado a nuestro microcontrolador, y 2 tareas diferentes pueden escribir en el, pero ¿que pasa si uno está escribiendo y otro más prioritario quiere escribir? eso lo veremos las próximas entradas.

DESCARGA EJEMPLO

domingo, 14 de abril de 2013

Proyectos USB III

En otras entradas en las que hablé del USB como esta, utilicé un dispositivo externo como puente entre una comunicación UART y una USB, que en ese caso fué el MCP2200. No es el único que existe, FTDI es la empresa líder en hacer este tipo de pasarelas, pero en esta entrada vamos a realizar una comunicación USB sin utilizar ninguna pasarela, es decir, vamos a programar una comunicación con el USB nativo del microcontrolador PIC18F14K50.
Para no empezar desde cero, vamos a modificar uno de los proyectos que nos da microchip para realizar comunicaciones mediante USB con el protocolo CDC, el cual emula en nuestro PC un puerto COM por el que nos comunicamos como si de una conexión COM se tratara. El ejemplo que amos a utilizar es el USB Device-CDC-BasicDemo, disponible en las MLA, que podéis descargar en http://www.microchip.com/mla. Este ejemplo como todos los de microchip está preparado para muchos microcontroladores, por lo que si utilizamos MPLAB 8 nos encontraremos con muchos proyectos diferentes, por el contrario, si ya hemos cambiado a MPLAB X existe solo un proyecto, esta vez con muchas configuraciones diferentes. En mi caso he utilizado MPLAB X, utilizando la configuración correspondiente para la placa Low Pin Count USB, con el 18F14K50.
En el ejemplo hay muchísimo código de las demás configuraciones que podemos eliminar, aunque esto solo nos va a servir para hacer más entendible el código, ya que todo este código no es compilado por el compilador, ya que se encuentra dentro de instrucciones #ifdef, y el precompilador las elimina. No os recomiendo quitar código que el precompilador no elimine, ya que el protocolo USB requiere de muchas funciones que si las eliminamos puede que luego no funcione bien.
Una vez dicho todo esto, vamos a desgranar un poco el código. Lo primero que tenemos, después de los includes y las configuraciones, es un remapeo de los punteros de reset, interrupciones de alta prioridad e interrupciones de baja prioridad. Esto es necesario en el caso que utilicemos un bootloader que ocupe dichas posiciones, en caso de no utilizarlo esta parte del código se puede borrar sin ningún inconveniente, aunque yo he preferido dejarla. Si queréis dejarla, debéis decirle al microcontrolador que no vais a utilizar bootloader, para ello debéis comentar la siguiente línea en el archivo HardwareProfile.h
    //Uncomment the following line to make the output HEX of this 
    //  project work with the HID Bootloader
    //#define PROGRAMMABLE_WITH_USB_HID_BOOTLOADER
Una vez hecho esto, el programa deja las direcciones por defecto. Después de esto, ya empieza el programa principal, con la función InitializeSystem(). Dentro de esta función se inicializa el sistema. En primer lugar se ponen todas las entradas analógicas como puertos digitales, y luego define los pines de sensado de conexión del USB, y el pin que detecta la autoalimentación del USB. La definición de estos pines es opcional, puesto que dichas opciones no son necesarias si no tenemos, por ejemplo una aplicación portátil y queremos que desactive el USB cuando no esté conectado. En nuestro caso están las dos deshabilitadas. Para deshabilitarlas debemos abrir de nuevo el HardwareProfile.h y dejar estas lineas como se muestran.
//#define USE_SELF_POWER_SENSE_IO 
    #define tris_self_power     TRISAbits.TRISA2    // Input
    #if defined(USE_SELF_POWER_SENSE_IO)
    #define self_power          PORTAbits.RA2
    #else
    #define self_power          1
    #endif

    //#define USE_USB_BUS_SENSE_IO
    #define tris_usb_bus_sense  TRISAbits.TRISA1    // Input
    #if defined(USE_USB_BUS_SENSE_IO)
    #define USB_BUS_SENSE       PORTAbits.RA1
    #else
    #define USB_BUS_SENSE       1
    #endif
Lo siguiente es la inicialización del usuario, es decir, puertos que vayamos a utilizar, inicialización de las salidas… y por ultimo la inicialización de los servicios del USB.
Volviendo al programa principal, lo siguiente que vemos es el bucle sin fin, y dentro otras sentencias para el precompilador. Estas sentencias lo que nos dan a elegir es si queremos nosotros que cuando haya algún cambio en el bus USB salte un interrupción que trate es en cambio, o por el contrario queremos desde el programa principal ir comprobando si se ha realizado algún cámbio en cada ciclo. Esto se configura desde el archivo USBConfig.h con las siguientes líneas:
#define USB_POLLING
//#define USB_INTERRUPT
En este caso lo vamos a hacer mediante polling. OJO! la interrupción SOLO salta cuando hay cambios en el bus USB, es decir, reconocimento correcto por parte del PC, desconexión del cable USB, esperando reconocimiento por parte del PC, pero la interrupción no salta cuando llega un dato, ni cuando este se ha enviado, esto debemos hacerlo testeando continuamente el numero de bytes a leer, que lo haremos dentro de la función ProcessIO. En esta función lo que hacemos es meter nuestro código de usuario, lo primero que se hace en este ejemlo, es, según el estado en el que estemos, los leds d ela placa parpadean de una forma o de otra, con la función BlinkUSBStatus(), y si el USB ya ha sido configurado, entonces empezamos a escribir según el estado del pulsador:
if(buttonPressed)
    {
        if(stringPrinted == FALSE)  // si no hemos enviado todavía la cadena...
        {
            if(mUSBUSARTIsTxTrfReady())  //está el usb listo para enviar??
            {
                putrsUSBUSART("Button Pressed -- \r\n");
                stringPrinted = TRUE;
            }
        }
    }
else
    {
        stringPrinted = FALSE;
    }
Y también empezamos a comprobar el buffer a ver si ha llegado algún dato, y según que dato sea hacer algo en consecuencia.
if(USBUSARTIsTxTrfReady())
    {
         numBytesRead = getsUSBUSART(USB_Out_Buffer,64);
         if(numBytesRead != 0)
        {
 BYTE i;
 for(i=0;i<numBytesRead;i++)
 {
      switch(USB_Out_Buffer[i])
     {
            case 0x0A:
                              PORTBbits.RB7 = 1;
                              break;
            case 0x0D:
  PORTBbits.RB7 = 0;
  break;
             default:
  USB_In_Buffer[i] = USB_Out_Buffer[i] + 1;
  break;
 }
         }

 putUSBUSART(USB_In_Buffer,numBytesRead);
       }
}
En esta caso se ha cambiado un poco el código del ejemplo original, y si recibimos un 0xA se pone a 1 el bit RB7, si recibimos un 0xD, se pone a 0 ese bit, y si recibimos cualquier otro valor, se devuelve sumando 1.
El protoclo CDC no es el único que existe, aunque sí que es el más sencillo de utilizar, sobre todo por parte del PC, así que es el que más utilizaré en el blog. Os dejo el enlace del programa reducido para que podais hacer pruebas.
Firmware

sábado, 17 de noviembre de 2012

Servidor Web desde cero. Archivos

Ya hace mas de un mes que terminé de diseñar y fabricar el servidor web que empecé a hacer aquí en el blog. En la última entrada, os conté como había hecho la placa y puse un vídeo del servidor funcionando. Hoy, para cerrar definitivamente esa fase, voy a poner todos lo archivos necesarios para que podáis hacer vuestro servidor. Los archivos son los que he utilizado yo, por lo que estan hechos para las características de mi servidor, un PIC18f4685 funcionando con un oscilador a 20MHz, leds conectados al puerto E del PIC, ..., aunque supongo que no tendréis mayores problemas para poder adecuarlos a vuestros sistemas.
Algunos de los códigos que hay en el proyecto ya los habéis ido viendo a lo largo de las entradas en las que he hablado del servidor, aunque quizás han sufrido alguna modificación de ultima hora. Los archivos que pongo son totalmente funcionales, y deberían compilar sin mayor problema, una vez le hayamos dicho a nuestro compilador donde encontrar los archivos que necesita. Espero que los disfrutéis.

Proyecto:                                                                 ServidorWEB_P_Trujillo_MIPSANDCHIPS.rar

Placa Componentes:
 

Placa PCB:


Esquema:

Si tenéis algún problema con alguno de los archivos, dejad un comentario e intentaré solucionarlo.

domingo, 9 de septiembre de 2012

Servidor WEB desde cero. Placa (V)

Hola a todos!! por fin después de bastante tiempo, puedo decir que mi pequeño servidor web esta listo, y comienza una nueva fase  que es la fase de experimentar con el. Si habéis seguido el blog, desde hace tiempo, en una serie de entradas que podéis encontrar con la etiqueta Servidor Web, he estado contando como he diseñado un servidor web desde cero, el cual, lleva un PIC18LF4685 como núcleo central. Este microcontrolador, es un micro de gama media-alta de 8 bits, el cual he elegido porque dispone de 96k de memoria de programa. En la primera de las entradas fijamos unos objetivos, de los cuales unos se han conseguido, pero otros no, y en las siguientes entradas os expliqué como diseñar la parte del firmware que llevará dentro nuestro microcontrolador. Todos estos avances, aunque no os lo he mostrado, los estaba verificando sobre una placa de board de prototipos, en la cual estaba montado el micro, y una break-board que contenía toda la circuitería del controlador Ethernet ENC28j60. Este tipo de montaje está muy bien para probar, pero no para experimentar a fondo, así que, si ya teníamos los materiales, y habíamos preparado el firmware del circuito lo único que nos faltaba es llevar nuestro proyecto a una PCB.
Evidentemente, ya que vamos a fabricar una placa, lo ideal sería que tanto la parte del microcontrolador, como la parte del controlador Ethernet estuvieran montados ahí. La parte del microcontrolador no entraña ninguna dificultad, es simplemente el micro, y algunas entradas y salidas, todo esto corriendo con un oscilador de 10MHz, la parte más complicada del diseño, y que tenia dudas de que funcionara es la parte del ENC. En la siguiente figura os muestro el esquema de conexión que podemos encontrar en su datasheet.
image
Como ya os dije, un elemento fundamental de nuestra placa es el conector Ethernet, y este , tal y como vemos en el esquema, necesitamos que disponga de transformadores de aislamiento, y así ha sido, por lo que nuestro trabajo d diseño, comienza desde los transformadores hacia la izquierda. lo primero que vemos es una bonina que viene nombrada como Ferrite Bread, que tal y como nos dice en la nota 3 sirve para eliminar interferencias. Como no encontré un valor claro por ningún sitio, y había leído que era un elemento prescindible, así he hecho, por lo que no la encontrareis en mi diseño. Si seguimos mirando hacia la izquierda, el otro elemento que llama la atención, son las resistencias de 49,9 con una tolerancia del 1%. Estas resistencias, aunque las encontré por internet, y puesto que no son más que resistencias de pull-up y pull-down, decidí sustituirlas por dos resistencias en serie, una de 10 y otra de 39, con lo que tengo unas resistencias de 49 ohms, eso si, con una tolerancia del 1%. Y si bajamos la vista un poco, vemos una resistencia de 2,32k, que al igual que las otras he sustituido por dos resistencias de 2k2 y 120 ohms, lo que hacen 2k32, que es lo que nos pide. Los leds, en mi caso, van incluidos en el conector, y las resistencias son de 220 ohms.  Por último, como estoy utilizando un PIC de la serie LF, y lo alimento con 3,3 voltios, el Level Shift Logic no es necesario.
Cualquiera podría pensar que, tomando todas esas suposiciones, es muy difícil que el proyecto funcione, y así pensaba yo también, aunque no siempre las cosas son como esperamos!! y para no haceros esperar más, a continuación teneis unas fotos de mi SERVIDOR WEB. En primer lugar teneis una foto de como quedó el PCB recién pasado por el ácido.
2012-09-08-525
Y a continuación, tenéis una foto del servidor completamente montado.
2012-09-08-527
Como veis, tenemos el PIC arriba, y perpendicular a este tenéis el ENC, ambos con encapsulado PDIP. Y por último un video de funcionamiento.
Bien, por último os quiero contar una serie de problemas que he tenido y como los he solucionado, en primer lugar, había un pequeño error en el esquema inicial, por lo que la fuente de alimentación de 3,3 voltios integrada no funciona bien, de hecho en el video veis una mini placa que hay con un regulador, ese problema en los archivos que os adjunto está solucionado, otra cosa que pasaba es que el servidor no funcionaba siempre, es decir, lo conectaba una vez y funcionaba, y a los 2 minutos los volvía a conectar y no funcionaba, esto, en principio, lo he achacado a que en un primer momento estaba habilitado el AUTO_IP, de forma que es mi PC es que le da la IP, al quitarlo, y dejar preasignada la IP, el problema se ha solucionado y ahora funciona siempre. Como veis, la página web que utilizo en el video es la que había programado ya antes, por lo que solo manejo un led, además que la lectura de la temperatura es completamente errónea, ya que no hay nada conectado a esta, más adelante crearé una página web exclusiva para esta placa, y os dejaré el firmware completo para que lo podáis descargar, por ahora os voy a dejar el esquema utilizado y el PCB para que, si queréis, podáis fabricaros uno vosotros.
Por último deciros que aquí acaba la primera fase, y ahora que tengo una herramienta sólida con la que trabajar podré realizar diseños más complejos. También quiero dar las Gracias a mi novia Elena que me ha estado aguantando estos últimos días dejándome que le mostrara cada avance que conseguía.
A continuación tenéis los archivos de descarga: Esquema, PCB, Componentes.

jueves, 26 de julio de 2012

Servidor WEB desde cero. Control (IV)

Hasta ahora, en las entradas en las que os he hablado del servidor web hemos hablado en primer lugar de los elementos que nos hacian falta para poder construirnos uno, en la siguiente entrada hablamos de como adaptar el Stack TCPIP para hacerlo funcionar en un PIC que no fuera de la familia 8722 y os expliqué como introducir una página web en el PIC , y lo último que vimos fué la monitorización, es decir, adaptar nuestra página web y nuestro firmware para poder visualizar modificar el valor de las variables dinámicas que aparecen en la página web desde el microcontrolador. En esta entrada vamos a ver la ultima parte que nos queda, que es el control mediante web, es decir, poder controlar salidas del micro mediante acciones realizadas en la página web. Para ello, al igual que pasaba con la monitorización necesitamos, en primer lugar modificar nuestro sitio web, en este caso añadiendo formularios, y después, habrá que modificar nuestro firmware para que el pic sea capaz de enlazar el valor de esos formularios, con las variables que queramos modificar, así pues, empecemos.
En primer lugar, como hicimos en el caso de la monitorización, vamos a ver que modificaciones tenemos que hacer en la página web. Como he dicho antes, lo que tenemos que hacer es añadir formularios. Los formularios son secciones de código html en los que podemos introducir controles como botones, checkboxes, radio_buttons e interactuar con ellos. La forma que la página web informa al servidor de la acción que ha ejecutado el usuario la podemos hacer de dos formas (métodos) diferentes. En primer lugar tenemos el método “get”, el cual plasma las acciones del usuario en la URL de la página web. Por ejemplo, si entramos en la página de google y tecleamos algo para que lo busque, cuando presionamos “buscar”, veremos que en la barra de dirección aparece una nueva dirección, por ejemplo, cuando buscamos “mips and chips”, vemos:
www.google.es/#hl=es&sclient=psy-ab&q=mips+and+chips&…
Si analizamos la dirección tenemos en primer lugar el nombre del servidor como es “www.google.es”, después tenemos los diferentes campos separados por el carácter “&”, y su valor, por ejemplo vemos que hay un campo llamado hl cuyo valor es es, otro campo es sclient cuyo valor es psy-ab, y el último que vemos es el campo q cuyo valor es mips+and+chips. La dirección realmente contiene muchos más campos que siguen la misma estructura. El otro método que existe es el método “post”, el cual la información se plasma en el mismo formulario. Este método es más complicado de utilizar, pero permite enviar muchos más datos. En esta entrada voy a trabajar tan solo en método get, ya que es lo suficientemente potente como para crear aplicaciones de una complejidad media alta.
Lo primero que debemos hacer para habilitar nuestra página para que de órdenes a nuestro micro es, como hemos dicho antes crear formularios, y dentro de estos, crear controles, por ejemplo, vamos a crear 2 radio_buttons que nos permitan poner un led en ON, o en OFF, y después un elemento del tipo submit que nos permita enviar ese comando al PIC.
<form method="get" action="index.htm">
 
    <fieldset>
 
    <span id="led">Estado de la entrada : ~led~<br>
 
    Estado de la salida : </span>&nbsp; 
 
    <input name="salida" type="radio" value="on" ~lights_chk(1)~ /> On
    <input name="salida" type="radio" value="off" ~lights_chk(0)~ /> Off
    
    &nbsp &nbsp <input type="submit" value="Enviar"/> 
    <br> Temperatura de la habitación:&nbsp ~temperatura~ ºC    
 
    </fieldset>
 
</form>
Hemos aprovechado y hemos metido también todas las variables de la monitorización para tenerlo todo más agrupado. Como se ve, los dos radio_buttons tienen el mismo parámetro “name”, de forma que los dos forman un único control y cuando uno está seleccionado el otro se deselecciona. El control submit es necesario para que se actualice la página y cambie la URL de la página de forma ue mandemos la orden al pic. En la página se verá algo como esto:
image
Lo siguiente es la modificación del firmware. El stack de microchip nos permite utilizar cualquiera de los dos métodos, y como en el caso de la monitorización, las funciones que realizan estas acciones se encentran en el archivo CustomHTTPAPP.c, donde encontramos el siguiente código:

HTTP_IO_RESULT HTTPExecuteGet(void)
{
// #### AQUÍ NUESTRO CÓDIGO GET
    return HTTP_IO_DONE;
}
 
#if defined(HTTP_USE_POST)
HTTP_IO_RESULT HTTPExecutePost(void)
{
    // #### AQUÍ NUESTRO CÓDIGO POST
    return HTTP_IO_DONE;
}
#endif
Tenemos 2 funciones, una para cada tipo, la HTTPExecuteGet se encarga de las llamadas de los formularios con método get, y la HTTPExecutePost que hace lo mismo para el método post. Como he dicho antes, nos vamos a centrar en el método get, ya que es más sencillo y es suficientemente potente. A continuación os pongo un ejemplo sencillo de código para la función HTTPExecuteGet:

HTTP_IO_RESULT HTTPExecuteGet(void)
{
    BYTE *ptr, name[20];
 
    // Obtenemos el nombre de archivo .htm
    MPFSGetFilename(curHTTP.file, name, 20);
    
    // Nos aseguramos que estamos en el .htm correcto
    if(strcmppgm2ram((char*)name, (ROM char*)"index.htm") != 0)
        return HTTP_IO_DONE;
    
    // Obtenemos el valor del control con el nombre "salida"
    ptr = HTTPGetROMArg(curHTTP.data, (ROM BYTE *)"salida");
    if(ptr)    
    {// Si el valor del control es on, ponemos LED1_IO = 1, si no, LED_IO = 0
        if(strcmppgm2ram((char*)ptr, (ROM char*)"on") == 0)
        {
            LED1_IO = 1;
        }
        else
        {
            LED1_IO = 0;
        }
    }    
    return HTTP_IO_DONE;
}
Lo primero que hacemos es comprobar que nos encontramos en la página correcta, para ello, una vez obtenido el nombre de la página, con la función strcmppgm2ram, comparamos la cadena obtenida, y el nombre correcto, si el resultado es 0, significa que son iguales, por tanto estamos en la página correcta, si no estamos en la página correcta salimos de la función. A continuación con la instrucción HTTPGetROMArg, obtenemos el valor del control con el nombre “salida”, y almacenamos su valor en la variable *ptr, y al igual que antes lo comparamos con la cadena “on”, y si es igual, ponemos el led a 1, si es diferente lo ponemos a cero.
Como veis no tiene mucha dificultad y es muy vistoso. Una vez hecho esto, ya podemos tanto monitorizar variables como controlar salidas del micro. Lo próximo es diseñar nuestro circuito y transferirlo a un PCB. Como ahora viene el mes de agosto, y tengo cosas que terminar, la fabricación de la placa la dejaré para más adelante, así que tened paciencia!!

miércoles, 18 de julio de 2012

Osciladores y PLLs.

Después de 2 entradas hablando de nuestro servidor WEB, para cambiar un poco de tercio vamos a hablar de un elemento que es fundamental en nuestros diseños con microcontroladores, tanto que sin uno de estos, nuestro micro simplemente no funciona. Este elemento tan importante es el oscilador el cual nos va a dictar a la velocidad que que va a ejecutar nuestro microcontrolador las instrucciones, lo que permitirá que nuestro micro pueda realizar o no ciertas tareas.
El bloque del oscilador en todos los micros es muy parecido, y está compuesto por un cristal de cuarzo, que formará nuestro circuito tanque, y una puerta inversora para que se mantenga la oscilación. Si queremos frecuencias muy bajas de oscilación podemos sustituir el cristal de cuarzo por una red RC, aunque es muchísimo menos estable y no recomendable. Una vez tenemos generada nuestra señal de reloj, los microcontroladores nos ofrecen muchas posibilidades para poder modificar esa señal de reloj a nuestro antojo. A continuación tenéis el esquema del circuito oscilador de un PIC24H:
image
Como veis el bloque del oscilador es bastante complejo, pero sencillo de entender y configurar. En primer lugar tenemos el Bloque oscilador principal, en el que tenemos los dos pines OSC1 y OSC2 que van a la puerta inversora que nos generara la oscilación a la frecuencia del cristal. De la salida de este bloque, tenemos 2 caminos diferentes, podemos utilizar esta frecuencia como frecuencia de oscilación directamente, poniendo el registro NOSC en la posición XT, HS o EC, dependiendo de la velocidad del cristal, o podemos, mediante S3, entrar al PLL, del cual hablaremos más tarde, para modificar esta frecuencia. Una vez tenemos seleccionada la frecuencia de oscilación FOSC, pasamos por un divisor por 2 (recordemos que los PIC24 ejecutan una instrucción por cada 2 ciclos de reloj), y sacamos directamente la frecuencia de oscilación de los periféricos, y mediante el registro DOZE dividimos FOSC para seleccionar una frecuencia para la CPU.
Además de este path (camino) tenemos otros que utilizan  los osciladores internos del micro. Por una parte tenemos el oscilador RC de alta velocidad FRC, que mediante el registro FRCDIV podemos dividir para introducir en el multiplexor principal una frecuencia menor a FRC, o igual a FRC. La frecuencia principal de este oscilador la seleccionamos mediante el registro TUN.  Además la salida del multiplexor FRCDIVC la podemos introducir en el PLL para modificarla y adecuarla a nuestro propósito.
Los otros dos osciladores que quedan son el RC de baja frecuencia y el oscilador secundario. Estos osciladores, en general serán de baja frecuencia y se utilizarán para los relojes en tiempo real, para temporizaciones grandes mediante el TIMER1 o para el WDT.
Hasta ahora hemos hablado del PLL sin que os haya explicado lo que es realmente, así que de os voy a hablar ahora. Como hemos dicho el PLL es un bloque que nos modifica la frecuencia de oscilación, en la mayoría de los casos aumentándola pero, ¿No basta con utilizar un cristal de mayor frecuencia?, si, hasta cierto punto.
image
Existen limitaciones debidas a la fabricación de los propios cristales que hacen que sea difícil hacer osciladores de frecuencias muy elevadas, y los que hay a veces tienen precios excesivos, así que para suplir ese costo existen los PLL. Aunque los PLLs originalmente son elementos puramente analógicos, los que montan los micros son PLLs digitales, aunque su funcionamiento es bastante parecido. El esquema que teneis arriba corresponde al PLL de los PIC24h, y como veis tiene varios bloques. En primer lugar tenemos el registro PLLPRE el cual tenemos que seleccionar para que la frecuencia de entrada al bucle PLL esté comprendida entre 0.8 y 8MHz. A continuación tenemos el PLL, el cual es un bucle. Es este bloque el que vamos a utilizar para multiplicar la frecuencia. El factor de multiplicación lo establece el registro PLLDIV. Los valores que puede tener este registro depende del PIC, siendo tan solo de un valor (4), para algunos PIC18, o pudiendo valer hasta 512 valores para los PIC24, por lo que podemos conseguir cualquier frecuencia de oscilación. El ultimo bloque es un divisor de frecuencia. El valor minimo por el que podemos dividir la frecuencia es 2, por lo que la salda del PLL deberá ser del doble de la que queramos. Así pues la frecuencia de oscilación del micro vendrá dad por la siguiente ecuación:
image
Por ejemplo, si teneos un reloj de 10MHz, PLLDIV = 32, PLLPRE = PLLPOST = 2, tenemos:
image
Con lo que con un reloj de 10MHz, conseguimos 80MHz, que corresponden a 40MIPS.
Por último decir que la mayoria de las caracteristicas del bloque oscilador se pueden configurar mediante los bits de configuración, por lo que no es necesario trabajar con los registros.

viernes, 13 de julio de 2012

Servidor WEB desde cero. Monitorización(III)

Aunque pensaba que en una sola entrada podría explicar todo lo referente al firmware del servidor web, me he dado cuenta que si quiero explicarlo bien deberé hacerlo en varias entradas. En las entradas anteriores he hablado de, en primer lugar de los materiales que vamos a utilizar, y en segundo lugar os hablé de como modificar el stack para utilizar un PIC de una familia diferente a la familia por defecto, y como introducir una página web en nuestro micro. El siguiente paso es la monitorización, es decir, poder observar variables del microcontrolador desde la interfaz web. Para ello tenemos que hacer dos cosas, en primer lugar modificar nuestra página web para que incluya variables dinámicas, y en segundo lugar, modificar el firmware para que el micro de valor a esas variables dinámicas.
Lo primero que vamos a hacer es modificar nuestra página web para introducir en ella variables dinámicas. Estas variables, cuando se cargue la página, esta le pedirán al micro el valor de esas variables, este se las devolverá y aparecerán en la página web. Incluir variables dinámicas en páginas web es tan fácil como poner el nombre de la variable de esta forma: ~nombre_variable~, por ejemplo:
<span>Estado de la entrada : ~led~</span>
De esta forma, cuando el micro llegue a esta línea, sustituirá el texto ~led~, por lo que nosotros queramos. La página quedará de la siguiente manera.

image
Bien, una vez preparada la página web, tenemos que modificar el firmware del micro para que sea capaz de sustituir el nombre de esas variables, por su valor. Para ello tenemos que modificar el archivo CustomHTTPApp.c. En archivo se encuentran las callback, las cuales son llamadas a funciones que darán valor a las variables dinámicas. Las Callback tienen la siguiente forma:

void HTTPPrint_NombreVariable(void)
{    
TCPPutROMString(sktHTTP, [valor asignado]);
}
Hay que tener en cuenta que por ethernet solo podemos enviar strings, por lo que utilizaremos bastantes las sentencias del tipo itoa. Por ejemplo, si queremos que la variable led valga “on” cuando el led1 este encendido, y valga “off” cuando el led1 esté apagado, sería algo así.

void HTTPPrint_led(void)
{    
TCPPutROMString(sktHTTP, (LED1_IO?"ON":"OFF"));
}
y si queremos, por ejemplo mostrar una temperatura que adquirimos desde un sensor analógico sería algo así:

void HTTPPrint_temperatura(void)
{    
    BYTE AN0String[8];    
    unsigned int ADval;    
    WORD ADval_ch;    
    ADCON0 = 0x02;    
    while(ADCON0bits.GO);        
    ADval = ADRESH;    
    ADval = ADval * 330;    
    ADval = ADval / 255;   
    ADval = ADval - 50;    
    ADval_ch = (WORD)ADval;    
    uitoa(ADval_ch, AN0String);       
    TCPPutString(sktHTTP, AN0String);
}
De forma que nuestra página quedará se verá de la siguiente forma:
image
En la próxima entrada os explicaré el tema de control mediante WEB, que aunque es sencillo también, lleva más tiempo que la monitorización.

lunes, 9 de julio de 2012

Servidor WEB desde cero. (II)

Voy a seguir con esta pequeña aventura que he empezado y que por ahora parece que va viento en popa. En la entrada Servidor WEB desde cero. (I), fue donde os puse todos los materiales que había comprado para fabricar mi propio servidor, en esta entrada vamos a empezar crear el firmware que irá dentro del micro con el que vamos a hacer que nuestro servidor funcione. Os dije que quería utilizar un PIC18LF4685, pues ese es el que voy a utilizar y ahora os voy a comentar los cambios que hay que hacer en el firmware.
Como dije en la entrada anterior, vamos a partir del la aplicación Demo App que se instala junto a las Microchip aplication libraries que podéis descargar de aquí. El primer archivo que vamos a modificar para hacerlo funcionar en el PIC18f4685 es el Hardware Profile, que en el proyecto se llama HWP PIC18EX_ENC28.h. 
#ifndef HARDWARE_PROFILE_H
#define HARDWARE_PROFILE_H
 
#include "Compiler.h"
 
// Define a macro describing this hardware set up (used in other files)
#define PIC18_EXPLORER
 
// Set configuration fuses (but only in MainDemo.c where THIS_IS_STACK_APPLICATION is defined)
#if defined(THIS_IS_STACK_APPLICATION)
 
//CONFIGURACIÓN PARA PIC18F4685
        #pragma config OSC = HSPLL, FCMEN = OFF, IESO = OFF, WDT=OFF, LVP=OFF
 
    // Automatically set Extended Instruction Set fuse based on compiler setting
    #if defined(__EXTENDED18__)
        #pragma config XINST=ON
    #else
        #pragma config XINST=OFF
    #endif
#endif
 
 
// Clock frequency values
#define GetSystemClock()    (40000000ul)
 
#define GetInstructionClock()    (GetSystemClock()/4)    // Should be GetSystemClock()/4 for PIC18
#define GetPeripheralClock()    (GetSystemClock()/4)    // Should be GetSystemClock()/4 for PIC18
 
 
// Hardware I/O pin mappings
 
// LEDs
#define LED0_TRIS            (TRISDbits.TRISD0)    // Ref D1
#define LED0_IO                (LATDbits.LATD0)
#define LED1_TRIS            (TRISDbits.TRISD1)    // Ref D2
#define LED1_IO                (LATDbits.LATD1)
#define LED2_TRIS            (TRISDbits.TRISD2)    // Ref D3
#define LED2_IO                (LATDbits.LATD2)
#define LED3_TRIS            (TRISDbits.TRISD3)    // Ref D4
#define LED3_IO                (LATDbits.LATD3)
#define LED4_TRIS            (TRISDbits.TRISD4)    // Ref D5
#define LED4_IO                (LATDbits.LATD4)
#define LED5_TRIS            (TRISDbits.TRISD5)    // Ref D6
#define LED5_IO                (LATDbits.LATD5)
#define LED6_TRIS            (TRISDbits.TRISD6)    // Ref D7
#define LED6_IO                (LATDbits.LATD6)
#define LED7_TRIS            (TRISDbits.TRISD7)    // Ref D8
#define LED7_IO                (LATDbits.LATD7)
#define LED_GET()            (LATD)
#define LED_PUT(a)            (LATD = (a))
 
// Momentary push buttons
#define BUTTON0_TRIS        (TRISAbits.TRISA5)
#define    BUTTON0_IO            (PORTAbits.RA5)
#define BUTTON1_TRIS        (TRISBbits.TRISB0)
#define    BUTTON1_IO            (PORTBbits.RB0)
#define BUTTON2_TRIS        (TRISBbits.TRISB0)    // No Button2 on this board
#define    BUTTON2_IO            (1u)
#define BUTTON3_TRIS        (TRISBbits.TRISB0)    // No Button3 on this board
#define    BUTTON3_IO            (1u)
 
// ENC28J60 I/O pins
#define ENC_RST_TRIS        (TRISBbits.TRISB5)
#define ENC_RST_IO            (LATBbits.LATB5)
#define ENC_CS_TRIS            (TRISBbits.TRISB3)
#define ENC_CS_IO            (LATBbits.LATB3)
#define ENC_SCK_TRIS        (TRISCbits.TRISC3)
#define ENC_SDI_TRIS        (TRISCbits.TRISC4)
#define ENC_SDO_TRIS        (TRISCbits.TRISC5)
#define ENC_SPI_IF            (PIR1bits.SSPIF)
#define ENC_SSPBUF            (SSPBUF)
#define ENC_SPISTAT            (SSPSTAT)
#define ENC_SPISTATbits        (SSPSTATbits)
#define ENC_SPICON1            (SSPCON1)
#define ENC_SPICON1bits        (SSPCON1bits)
#define ENC_SPICON2            (SSPCON2)
 
// LCD I/O pins
// TODO: Need to add support for LCD behind MCP23S17 I/O expander.  This 
// requires code that isn't in the TCP/IP stack, not just a hardware 
// profile change.
 
// UART mapping functions for consistent API names across 8-bit and 16 or 
// 32 bit compilers.  For simplicity, everything will use "UART" instead 
// of USART/EUSART/etc.
#define BusyUART()            BusyUSART()
#define CloseUART()            CloseUSART()
#define ConfigIntUART(a)    ConfigIntUSART(a)
#define DataRdyUART()        DataRdyUSART()
#define OpenUART(a,b,c)        OpenUSART(a,b,c)
#define ReadUART()            ReadUSART()
#define WriteUART(a)        WriteUSART(a)
#define getsUART(a,b,c)        getsUSART(b,a)
#define putsUART(a)            putsUSART(a)
#define getcUART()            ReadUSART()
#define putcUART(a)            WriteUSART(a)
#define putrsUART(a)        putrsUSART((far rom char*)a)
 
#endif // #ifndef HARDWARE_PROFILE_H
Lo principal que hemos modificado en este archivo son las configuraciones del micro, utilizando los pragma configs correspondientes para cada PIC. A partir de como lo hayamos configurado, debemos también cambiar el #define GetSystemClock() por la frecuencia a la que va a funciona nuestro micro, en mi caso, utilizo un cristal de 10Mz, y he configurado el PLL para que multiplique por 4 esta frecuencia por lo que tengo una frecuencia de 40MHz. Otra cosa que debéis modificar según el micro que utilicéis es la posición de los pines de comunicación SPI que son SCK, SDI y SDO. Los pines CS y RST los podéis configurar en el pin que queráis.
El siguiente archivo que vamos a modificar es el MainDemo.c. Este es el programa principal, y básicamente lo que he hecho ha sido eliminar todo lo que no voy a utilizar como memoria, wifi, uart o lcd y me ha quedado así.

#define THIS_IS_STACK_APPLICATION
 
// Include all headers for any enabled TCPIP Stack functions
#include "TCPIP Stack/TCPIP.h"
 
#if defined(STACK_USE_ZEROCONF_LINK_LOCAL)
#include "TCPIP Stack/ZeroconfLinkLocal.h"
#endif
#if defined(STACK_USE_ZEROCONF_MDNS_SD)
#include "TCPIP Stack/ZeroconfMulticastDNS.h"
#endif
 
#include "MainDemo.h"
 
// Declare AppConfig structure and some other supporting stack variables
APP_CONFIG AppConfig;
static unsigned short wOriginalAppConfigChecksum;    // Checksum of the ROM defaults for AppConfig
 
static void InitAppConfig(void);
static void InitializeBoard(void);
static void ProcessIO(void);
 
    #pragma interruptlow LowISR    // Interrupciones de baja prioridad
    void LowISR(void)
    {
        TickUpdate();
    }
    
 
    #pragma interruptlow HighISR // Interrupciones de alta prioridad
    void HighISR(void)
    {
 
    }
 
 
    #pragma code lowVector=0x18
    void LowVector(void){_asm goto LowISR _endasm}
    #pragma code highVector=0x8
    void HighVector(void){_asm goto HighISR _endasm}
    #pragma code // Return to default code section
 
//
// Main application entry point.
//
 
void main(void)
{
    static DWORD t = 0;
    static DWORD dwLastIP = 0;
 
    // Inicializamos la placa
    InitializeBoard();
 
    //Inicializamos la rutina que hace que parpadee el led.
    //Utiliza el TMR1, por lo que este queda inutilizado. (tick.c)
    TickInit();
    
    // Inicializamos los archivos del STACK.
    MPFSInit();
    InitAppConfig();
    StackInit();
 
    
    while(1)
    {
        // Invierte la señal del led cada segundo
        if(TickGet() - t >= TICK_SECOND/2ul)
        {
            t = TickGet();
            LED0_IO ^= 1;
        }
        
        // Tareas delk STACK
        StackTask();
        StackApplications();
        
        PingDemo(); //esta aplicación permite al STACK responder a PINGs
 
        ProcessIO();
 
        // Comprobamos si la dirección IP ha cambiado, si es así mostramos la nueva direccion IP.
        if(dwLastIP != AppConfig.MyIPAddr.Val)
        {
            dwLastIP = AppConfig.MyIPAddr.Val;
            
            DisplayIPValue(AppConfig.MyIPAddr);
 
        }
    }
}
 
// Mostramos la direccion IP en el LCD
void DisplayIPValue(IP_ADDR IPVal)
{
    //#####################################################
    // Lo dejamos en blanco de momento,luego crearemos un #
    //programa que haga que la placa muestre la IP en un  #
    // LCD de 16x2 ########################################
    //#####################################################
}
 
static void ProcessIO(void)
{    
    //#####################################################
    // AQUÍ VA NUESTRO CÓDIGO #############################
    //#####################################################
}
 
/****************************************************************
    Función: InitializeBoard(void)
Esta función se encarga de inicializar la placa se´gun el hardware 
que tengamos conectado, lo utilizaremos tambien más tarde para 
incializar el LCD, o la comunicación UART si la implementamos
*****************************************************************/
static void InitializeBoard(void)
{    
    // LEDs
    LED0_TRIS = 0;
    LED1_TRIS = 0;
    LED2_TRIS = 0;
    LED3_TRIS = 0;
    LED4_TRIS = 0;
    LED5_TRIS = 0;
    LED6_TRIS = 0;
    LED7_TRIS = 0;
    LED_PUT(0x00);
 
    // Enable internal PORTB pull-ups
    INTCON2bits.RBPU = 0;
 
    // Enable Interrupts
    RCONbits.IPEN = 1;        // Enable interrupt priorities
    INTCONbits.GIEH = 1;
    INTCONbits.GIEL = 1;
 
    //INICIALIZACION DEL PIN CHIP SELECT DEL ENC
    ENC_CS_IO = 1;
    ENC_CS_TRIS = 0;
 
    
}
 
/****************************************************************
    Función: InitAppCondig(void)
Esta función inicializa variables del stack, hemos eliminado 
toda la inicialización referente a memorias y WIFI.
*****************************************************************/
 
static ROM BYTE SerializedMACAddress[6] = {MY_DEFAULT_MAC_BYTE1, MY_DEFAULT_MAC_BYTE2, MY_DEFAULT_MAC_BYTE3, MY_DEFAULT_MAC_BYTE4, MY_DEFAULT_MAC_BYTE5, MY_DEFAULT_MAC_BYTE6};
 
 
static void InitAppConfig(void)
{
    while(1)
    {
        // Start out zeroing all AppConfig bytes to ensure all fields are 
        // deterministic for checksum generation
        memset((void*)&AppConfig, 0x00, sizeof(AppConfig));
        
        AppConfig.Flags.bIsDHCPEnabled = TRUE;
        AppConfig.Flags.bInConfigMode = TRUE;
        memcpypgm2ram((void*)&AppConfig.MyMACAddr, (ROM void*)SerializedMACAddress, sizeof(AppConfig.MyMACAddr));
//        {
//            _prog_addressT MACAddressAddress;
//            MACAddressAddress.next = 0x157F8;
//            _memcpy_p2d24((char*)&AppConfig.MyMACAddr, MACAddressAddress, sizeof(AppConfig.MyMACAddr));
//        }
        AppConfig.MyIPAddr.Val = MY_DEFAULT_IP_ADDR_BYTE1 | MY_DEFAULT_IP_ADDR_BYTE2<<8ul | MY_DEFAULT_IP_ADDR_BYTE3<<16ul | MY_DEFAULT_IP_ADDR_BYTE4<<24ul;
        AppConfig.DefaultIPAddr.Val = AppConfig.MyIPAddr.Val;
        AppConfig.MyMask.Val = MY_DEFAULT_MASK_BYTE1 | MY_DEFAULT_MASK_BYTE2<<8ul | MY_DEFAULT_MASK_BYTE3<<16ul | MY_DEFAULT_MASK_BYTE4<<24ul;
        AppConfig.DefaultMask.Val = AppConfig.MyMask.Val;
        AppConfig.MyGateway.Val = MY_DEFAULT_GATE_BYTE1 | MY_DEFAULT_GATE_BYTE2<<8ul | MY_DEFAULT_GATE_BYTE3<<16ul | MY_DEFAULT_GATE_BYTE4<<24ul;
        AppConfig.PrimaryDNSServer.Val = MY_DEFAULT_PRIMARY_DNS_BYTE1 | MY_DEFAULT_PRIMARY_DNS_BYTE2<<8ul  | MY_DEFAULT_PRIMARY_DNS_BYTE3<<16ul  | MY_DEFAULT_PRIMARY_DNS_BYTE4<<24ul;
        AppConfig.SecondaryDNSServer.Val = MY_DEFAULT_SECONDARY_DNS_BYTE1 | MY_DEFAULT_SECONDARY_DNS_BYTE2<<8ul  | MY_DEFAULT_SECONDARY_DNS_BYTE3<<16ul  | MY_DEFAULT_SECONDARY_DNS_BYTE4<<24ul;
    
    
        // Load the default NetBIOS Host Name
        memcpypgm2ram(AppConfig.NetBIOSName, (ROM void*)MY_DEFAULT_HOST_NAME, 16);
        FormatNetBIOSName(AppConfig.NetBIOSName);
    
        // Compute the checksum of the AppConfig defaults as loaded from ROM
        wOriginalAppConfigChecksum = CalcIPChecksum((BYTE*)&AppConfig, sizeof(AppConfig));
 
        break;
    }
}
 
He dejado el archivo main muy reducido de forma que se entienda todo lo que hay en el. Hay funciones que no he implementado todavía como DisplayIp(IPAddres), ya que en un principio dije que no iba a poner LCD pero he estado revisando los pines y quizás tenga suficientes, así que lo dejo preparado por si luego quisiera. La otra función que está vacía es ProcessIO(), ya que en la aplicación que he preparado, no ha hecho falta poner nada aquí, pero si nuestra aplicación debe hacer cosas a parte de su función como servidor, irá aquí.
Por último el archivo que debemos modificar es el TCPIPConfig.h, en este, como ya comenté le decimos al STACK que funciones vamos a utilizar y que funciones no, a parte de la dirección IP por defecto, o el nombre del HOST. Queda así.

#ifndef __TCPIPCONFIG_H
#define __TCPIPCONFIG_H
 
#include "GenericTypeDefs.h"
#include "Compiler.h"
#define GENERATED_BY_TCPIPCONFIG "Version 1.0.4168.28618"
 
// =======================================================================
//   Application Options
// =======================================================================
 
/* Application Level Module Selection
 *   Uncomment or comment the following lines to enable or
 *   disabled the following high-level application modules.
 */
//#define STACK_USE_UART                    // Application demo using UART for IP address display and stack configuration
//#define STACK_USE_UART2TCP_BRIDGE        // UART to TCP Bridge application example
//#define STACK_USE_IP_GLEANING
#define STACK_USE_ICMP_SERVER            // Ping query and response capability
#define STACK_USE_ICMP_CLIENT            // Ping transmission capability
#define STACK_USE_HTTP2_SERVER            // New HTTP server with POST, Cookies, Authentication, etc.
//#define STACK_USE_SSL_SERVER            // SSL server socket support (Requires SW300052)
//#define STACK_USE_SSL_CLIENT            // SSL client socket support (Requires SW300052)
//#define STACK_USE_AUTO_IP               // Dynamic link-layer IP address automatic configuration protocol
//#define STACK_USE_DHCP_CLIENT            // Dynamic Host Configuration Protocol client for obtaining IP address and other parameters
//#define STACK_USE_DHCP_SERVER            // Single host DHCP server
//#define STACK_USE_FTP_SERVER            // File Transfer Protocol (old)
//#define STACK_USE_SMTP_CLIENT            // Simple Mail Transfer Protocol for sending email
//#define STACK_USE_SNMP_SERVER            // Simple Network Management Protocol v2C Community Agent
//#define STACK_USE_SNMPV3_SERVER            // Simple Network Management Protocol v3 Agent
//#define STACK_USE_TFTP_CLIENT            // Trivial File Transfer Protocol client
//#define STACK_USE_GENERIC_TCP_CLIENT_EXAMPLE    // HTTP Client example in GenericTCPClient.c
//#define STACK_USE_GENERIC_TCP_SERVER_EXAMPLE    // ToUpper server example in GenericTCPServer.c
//#define STACK_USE_TELNET_SERVER            // Telnet server
#define STACK_USE_ANNOUNCE                // Microchip Embedded Ethernet Device Discoverer server/client
#define STACK_USE_DNS                    // Domain Name Service Client for resolving hostname strings to IP addresses
//#define STACK_USE_DNS_SERVER            // Domain Name Service Server for redirection to the local device
#define STACK_USE_NBNS                    // NetBIOS Name Service Server for repsonding to NBNS hostname broadcast queries
#define STACK_USE_REBOOT_SERVER            // Module for resetting this PIC remotely.  Primarily useful for a Bootloader.
#define STACK_USE_SNTP_CLIENT            // Simple Network Time Protocol for obtaining current date/time from Internet
//#define STACK_USE_UDP_PERFORMANCE_TEST    // Module for testing UDP TX performance characteristics.  NOTE: Enabling this will cause a huge amount of UDP broadcast packets to flood your network on the discard port.  Use care when enabling this on production networks, especially with VPNs (could tunnel broadcast traffic across a limited bandwidth connection).
//#define STACK_USE_TCP_PERFORMANCE_TEST    // Module for testing TCP TX performance characteristics
//#define STACK_USE_DYNAMICDNS_CLIENT        // Dynamic DNS client updater module
//#define STACK_USE_BERKELEY_API            // Berekely Sockets APIs are available
//#define STACK_USE_ZEROCONF_LINK_LOCAL    // Zeroconf IPv4 Link-Local Addressing
//#define STACK_USE_ZEROCONF_MDNS_SD        // Zeroconf mDNS and mDNS service discovery
 
 
// =======================================================================
//   Data Storage Options
// =======================================================================
 
/* MPFS Configuration
 *   MPFS is automatically included when required for other
 *   applications.  If your custom application requires it
 *   otherwise, uncomment the appropriate selection.
 */
#define STACK_USE_MPFS2
 
/* MPFS Storage Location
 *   If html pages are stored in internal program memory,
 *   comment both MPFS_USE_EEPROM and MPFS_USE_SPI_FLASH, then
 *   include an MPFS image (.c or .s file) in the project.
 *   If html pages are stored in external memory, uncomment the
 *   appropriate definition.
 *
 *   Supported serial flash parts include the SST25VFxxxB series.
 */
//#define MPFS_USE_EEPROM
//#define MPFS_USE_SPI_FLASH
 
/* EEPROM Addressing Selection
 *   If using the 1Mbit EEPROM, uncomment this line
 */
//#define USE_EEPROM_25LC1024
 
/* EEPROM Reserved Area
 *   Number of EEPROM bytes to be reserved before MPFS storage starts.
 *   These bytes host application configurations such as IP Address,
 *   MAC Address, and any other required variables.
 *
 *   For MPFS Classic, this setting must match the Reserved setting
 *     on the Advanced Settings page of the MPFS2 Utility.
 */
#define MPFS_RESERVE_BLOCK                (137ul)
 
/* MPFS File Handles
 *   Maximum number of simultaneously open MPFS2 files.
 *   For MPFS Classic, this has no effect.
 */
#define MAX_MPFS_HANDLES                (7ul)
 
 
// =======================================================================
//   Network Addressing Options
// =======================================================================
 
/* Default Network Configuration
 *   These settings are only used if data is not found in EEPROM.
 *   To clear EEPROM, hold BUTTON0, reset the board, and continue
 *   holding until the LEDs flash.  Release, and reset again.
 */
//#define MY_DEFAULT_HOST_NAME            "MCHPBOARD"
#define MY_DEFAULT_HOST_NAME            "MIPSBOARD"
 
#define MY_DEFAULT_MAC_BYTE1            (0x00)    // Use the default of 00-04-A3-00-00-00
#define MY_DEFAULT_MAC_BYTE2            (0x04)    // if using an ENCX24J600, MRF24WB0M, or
#define MY_DEFAULT_MAC_BYTE3            (0xA3)    // PIC32MX6XX/7XX internal Ethernet 
#define MY_DEFAULT_MAC_BYTE4            (0x00)    // controller and wish to use the 
#define MY_DEFAULT_MAC_BYTE5            (0x00)    // internal factory programmed MAC
#define MY_DEFAULT_MAC_BYTE6            (0x00)    // address instead.
 
#define MY_DEFAULT_IP_ADDR_BYTE1        (169ul)
#define MY_DEFAULT_IP_ADDR_BYTE2        (254ul)
#define MY_DEFAULT_IP_ADDR_BYTE3        (1ul)
#define MY_DEFAULT_IP_ADDR_BYTE4        (1ul)
 
#define MY_DEFAULT_MASK_BYTE1           (255ul)
#define MY_DEFAULT_MASK_BYTE2           (255ul)
#define MY_DEFAULT_MASK_BYTE3           (0ul)
#define MY_DEFAULT_MASK_BYTE4           (0ul)
 
#define MY_DEFAULT_GATE_BYTE1           (169ul)
#define MY_DEFAULT_GATE_BYTE2           (254ul)
#define MY_DEFAULT_GATE_BYTE3           (1ul)
#define MY_DEFAULT_GATE_BYTE4           (1ul)
 
#define MY_DEFAULT_PRIMARY_DNS_BYTE1    (169ul)
#define MY_DEFAULT_PRIMARY_DNS_BYTE2    (254ul)
#define MY_DEFAULT_PRIMARY_DNS_BYTE3    (1ul)
#define MY_DEFAULT_PRIMARY_DNS_BYTE4    (1ul)
 
#define MY_DEFAULT_SECONDARY_DNS_BYTE1    (0ul)
#define MY_DEFAULT_SECONDARY_DNS_BYTE2    (0ul)
#define MY_DEFAULT_SECONDARY_DNS_BYTE3    (0ul)
#define MY_DEFAULT_SECONDARY_DNS_BYTE4    (0ul)
 
// =======================================================================
//   PIC32MX7XX/6XX MAC Layer Options
//   If not using a PIC32MX7XX/6XX device, ignore this section.
// =======================================================================
#define    ETH_CFG_LINK            0        // set to 1 if you need to config the link to specific following parameters
                                        // otherwise the default connection will be attempted
                                        // depending on the selected PHY
    #define    ETH_CFG_AUTO        1        // use auto negotiation
    #define    ETH_CFG_10            1        // use/advertise 10 Mbps capability
    #define    ETH_CFG_100            1        // use/advertise 100 Mbps capability
    #define    ETH_CFG_HDUPLEX        1        // use/advertise half duplex capability
    #define    ETH_CFG_FDUPLEX        1        // use/advertise full duplex capability
    #define    ETH_CFG_AUTO_MDIX    1        // use/advertise auto MDIX capability
    #define    ETH_CFG_SWAP_MDIX    1        // use swapped MDIX. else normal MDIX
 
#define EMAC_TX_DESCRIPTORS        2        // number of the TX descriptors to be created
#define EMAC_RX_DESCRIPTORS        8        // number of the RX descriptors and RX buffers to be created
 
#define    EMAC_RX_BUFF_SIZE        1536    // size of a RX buffer. should be multiple of 16
                                        // this is the size of all receive buffers processed by the ETHC
                                        // The size should be enough to accomodate any network received packet
                                        // If the packets are larger, they will have to take multiple RX buffers
                                        // The current implementation does not handle this situation right now and the packet is discarded.
 
 
// =======================================================================
//   Transport Layer Options
// =======================================================================
 
/* Transport Layer Configuration
 *   The following low level modules are automatically enabled
 *   based on module selections above.  If your custom module
 *   requires them otherwise, enable them here.
 */
//#define STACK_USE_TCP
//#define STACK_USE_UDP
 
/* Client Mode Configuration
 *   Uncomment following line if this stack will be used in CLIENT
 *   mode.  In CLIENT mode, some functions specific to client operation
 *   are enabled.
 */
#define STACK_CLIENT_MODE
 
/* TCP Socket Memory Allocation
 *   TCP needs memory to buffer incoming and outgoing data.  The
 *   amount and medium of storage can be allocated on a per-socket
 *   basis using the example below as a guide.
 */
    // Allocate how much total RAM (in bytes) you want to allocate
    // for use by your TCP TCBs, RX FIFOs, and TX FIFOs.
    #define TCP_ETH_RAM_SIZE                    (1238ul)
    #define TCP_PIC_RAM_SIZE                    (0ul)
    #define TCP_SPI_RAM_SIZE                    (0ul)
    #define TCP_SPI_RAM_BASE_ADDRESS            (0x00)
 
    // Define names of socket types
    #define TCP_SOCKET_TYPES
        #define TCP_PURPOSE_GENERIC_TCP_CLIENT 0
        #define TCP_PURPOSE_GENERIC_TCP_SERVER 1
        #define TCP_PURPOSE_TELNET 2
        #define TCP_PURPOSE_FTP_COMMAND 3
        #define TCP_PURPOSE_FTP_DATA 4
        #define TCP_PURPOSE_TCP_PERFORMANCE_TX 5
        #define TCP_PURPOSE_TCP_PERFORMANCE_RX 6
        #define TCP_PURPOSE_UART_2_TCP_BRIDGE 7
        #define TCP_PURPOSE_HTTP_SERVER 8
        #define TCP_PURPOSE_DEFAULT 9
        #define TCP_PURPOSE_BERKELEY_SERVER 10
        #define TCP_PURPOSE_BERKELEY_CLIENT 11
    #define END_OF_TCP_SOCKET_TYPES
 
    #if defined(__TCP_C)
        // Define what types of sockets are needed, how many of
        // each to include, where their TCB, TX FIFO, and RX FIFO
        // should be stored, and how big the RX and TX FIFOs should
        // be.  Making this initializer bigger or smaller defines
        // how many total TCP sockets are available.
        //
        // Each socket requires up to 56 bytes of PIC RAM and
        // 48+(TX FIFO size)+(RX FIFO size) bytes of TCP_*_RAM each.
        //
        // Note: The RX FIFO must be at least 1 byte in order to
        // receive SYN and FIN messages required by TCP.  The TX
        // FIFO can be zero if desired.
        #define TCP_CONFIGURATION
        ROM struct
        {
            BYTE vSocketPurpose;
            BYTE vMemoryMedium;
            WORD wTXBufferSize;
            WORD wRXBufferSize;
        } TCPSocketInitializer[] = 
        {
            //{TCP_PURPOSE_GENERIC_TCP_CLIENT, TCP_ETH_RAM, 125, 100},
            //{TCP_PURPOSE_GENERIC_TCP_SERVER, TCP_ETH_RAM, 20, 20},
            //{TCP_PURPOSE_TELNET, TCP_ETH_RAM, 200, 150},
            //{TCP_PURPOSE_TELNET, TCP_ETH_RAM, 200, 150},
            //{TCP_PURPOSE_TELNET, TCP_ETH_RAM, 200, 150},
            //{TCP_PURPOSE_FTP_COMMAND, TCP_ETH_RAM, 100, 40},
            //{TCP_PURPOSE_FTP_DATA, TCP_ETH_RAM, 0, 128},
            {TCP_PURPOSE_TCP_PERFORMANCE_TX, TCP_ETH_RAM, 200, 1},
            //{TCP_PURPOSE_TCP_PERFORMANCE_RX, TCP_ETH_RAM, 40, 1500},
            //{TCP_PURPOSE_UART_2_TCP_BRIDGE, TCP_ETH_RAM, 256, 256},
            {TCP_PURPOSE_HTTP_SERVER, TCP_ETH_RAM, 200, 200},
            {TCP_PURPOSE_HTTP_SERVER, TCP_ETH_RAM, 200, 200},
            //{TCP_PURPOSE_DEFAULT, TCP_ETH_RAM, 200, 200},
            {TCP_PURPOSE_BERKELEY_SERVER, TCP_ETH_RAM, 25, 20},
            //{TCP_PURPOSE_BERKELEY_SERVER, TCP_ETH_RAM, 25, 20},
            //{TCP_PURPOSE_BERKELEY_SERVER, TCP_ETH_RAM, 25, 20},
            //{TCP_PURPOSE_BERKELEY_CLIENT, TCP_ETH_RAM, 125, 100},
        };
        #define END_OF_TCP_CONFIGURATION
    #endif
 
/* UDP Socket Configuration
 *   Define the maximum number of available UDP Sockets, and whether
 *   or not to include a checksum on packets being transmitted.
 */
#define MAX_UDP_SOCKETS     (8u)
#define UDP_USE_TX_CHECKSUM        // This slows UDP TX performance by nearly 50%, except when using the ENCX24J600 or PIC32MX6XX/7XX, which have a super fast DMA and incurs virtually no speed pentalty.
 
 
/* Berkeley API Sockets Configuration
 *   Note that each Berkeley socket internally uses one TCP or UDP socket
 *   defined by MAX_UDP_SOCKETS and the TCPSocketInitializer[] array.
 *   Therefore, this number MUST be less than or equal to MAX_UDP_SOCKETS + the
 *   number of TCP sockets defined by the TCPSocketInitializer[] array
 *   (i.e. sizeof(TCPSocketInitializer)/sizeof(TCPSocketInitializer[0])).
 *   This define has no effect if STACK_USE_BERKELEY_API is not defined and
 *   Berkeley Sockets are disabled.  Set this value as low as your application
 *   requires to avoid waisting RAM.
 */
#define BSD_SOCKET_COUNT (5u)
 
 
// =======================================================================
//   Application-Specific Options
// =======================================================================
 
// -- HTTP2 Server options -----------------------------------------------
 
    // Maximum numbers of simultaneous HTTP connections allowed.
    // Each connection consumes 2 bytes of RAM and a TCP socket
    #define MAX_HTTP_CONNECTIONS    (2u)
 
    // Optional setting to use PIC RAM instead of Ethernet/Wi-Fi RAM for
    // storing HTTP Connection Context variables (HTTP_CONN structure for each 
    // HTTP connection).  Undefining this macro results in the Ethernet/Wi-Fi 
    // RAM being used (minimum PIC RAM usage, lower performance).  Defining 
    // this macro results in PIC RAM getting used (higher performance, but uses 
    // PIC RAM).  This option should not be enabled on PIC18 devices.  The 
    // performance increase of having this option defined is only apparent when 
    // the HTTP server is servicing multiple connections simultaneously.
    //#define HTTP_SAVE_CONTEXT_IN_PIC_RAM
 
    // Indicate what file to serve when no specific one is requested
    #define HTTP_DEFAULT_FILE        "index.htm"
    #define HTTPS_DEFAULT_FILE        "index.htm"
    #define HTTP_DEFAULT_LEN        (10u)        // For buffer overrun protection.
                                                // Set to longest length of above two strings.
 
    // Configure MPFS over HTTP updating
    // Comment this line to disable updating via HTTP
    #define HTTP_MPFS_UPLOAD        "mpfsupload"
    //#define HTTP_MPFS_UPLOAD_REQUIRES_AUTH    // Require password for MPFS uploads
        // Certain firewall and router combinations cause the MPFS2 Utility to fail
        // when uploading.  If this happens, comment out this definition.
 
    // Define which HTTP modules to use
    // If not using a specific module, comment it to save resources
    #define HTTP_USE_POST                    // Enable POST support
    #define HTTP_USE_COOKIES                // Enable cookie support
//    #define HTTP_USE_AUTHENTICATION            // Enable basic authentication support
 
    //#define HTTP_NO_AUTH_WITHOUT_SSL        // Uncomment to require SSL before requesting a password
 
    // Define the listening port for the HTTP server
      #define HTTP_PORT               (80u)
    
    // Define the listening port for the HTTPS server (if STACK_USE_SSL_SERVER is enabled)
    #define HTTPS_PORT                (443u)
    
    // Define the maximum data length for reading cookie and GET/POST arguments (bytes)
    #define HTTP_MAX_DATA_LEN        (100u)
    
    // Define the minimum number of bytes free in the TX FIFO before executing callbacks
    #define HTTP_MIN_CALLBACK_FREE    (16u)
 
    #define STACK_USE_HTTP_APP_RECONFIG        // Use the AppConfig web page in the Demo App (~2.5kb ROM, ~0b RAM)
    //#define STACK_USE_HTTP_MD5_DEMO            // Use the MD5 Demo web page (~5kb ROM, ~160b RAM)
    //#define STACK_USE_HTTP_EMAIL_DEMO        // Use the e-mail demo web page
 
// -- SSL Options --------------------------------------------------------
 
    #define MAX_SSL_CONNECTIONS        (2ul)    // Maximum connections via SSL
    #define MAX_SSL_SESSIONS        (2ul)    // Max # of cached SSL sessions
    #define MAX_SSL_BUFFERS            (4ul)    // Max # of SSL buffers (2 per socket)
    #define MAX_SSL_HASHES            (5ul)    // Max # of SSL hashes  (2 per, plus 1 to avoid deadlock)
 
    // Bits in SSL RSA key.  This parameter is used for SSL sever
    // connections only.  The only valid value is 512 bits (768 and 1024
    // bits do not work at this time).  Note, however, that SSL client
    // operations do currently work up to 1024 bit RSA key length.
    #define SSL_RSA_KEY_SIZE        (512ul)
 
 
// -- Telnet Options -----------------------------------------------------
 
    // Number of simultaneously allowed Telnet sessions.  Note that you
    // must have an equal number of TCP_PURPOSE_TELNET type TCP sockets
    // declared in the TCPSocketInitializer[] array above for multiple
    // connections to work.  If fewer sockets are available than this
    // definition, then the the lesser of the two quantities will be the
    // actual limit.
    #define MAX_TELNET_CONNECTIONS    (1u)
 
    // Default local listening port for the Telnet server.  Port 23 is the
    // protocol default.
    #define TELNET_PORT                (23u)
 
    // Default local listening port for the Telnet server when SSL secured.
    // Port 992 is the telnets protocol default.
    #define TELNETS_PORT            (992u)
 
    // Force all connecting clients to be SSL secured and connected via
    // TELNETS_PORT.  Connections on port TELNET_PORT will be ignored.  If
    // STACK_USE_SSL_SERVER is undefined, this entire setting is ignored
    // (server will accept unsecured connections on TELNET_PORT and won't even
    // listen on TELNETS_PORT).
    //#define TELNET_REJECT_UNSECURED
 
    // Default username and password required to login to the Telnet server.
    #define TELNET_USERNAME            "admin"
    #define TELNET_PASSWORD            "microchip"
 
 
// -- SNMP Options -------------------------------------------------------
 
    // Comment following line if SNMP TRAP support is needed
    //#define SNMP_TRAP_DISABLED
 
    //#define SNMP_STACK_USE_V2_TRAP
    #if defined(STACK_USE_SNMPV3_SERVER)
        #define SNMP_V1_V2_TRAP_WITH_SNMPV3
    #endif
 
    // This is the maximum length for community string.
    // Application must ensure that this length is observed.
    // SNMP module adds one byte extra after SNMP_COMMUNITY_MAX_LEN
    // for adding '\0' NULL character.
    #define SNMP_COMMUNITY_MAX_LEN      (8u)
    #define SNMP_MAX_COMMUNITY_SUPPORT    (3u)
    #define NOTIFY_COMMUNITY_LEN        (SNMP_COMMUNITY_MAX_LEN)
 
    // Default SNMPv2C community names.  These can be overridden at run time if
    // alternate strings are present in external EEPROM or Flash (actual
    // strings are stored in AppConfig.readCommunity[] and
    // AppConfig.writeCommunity[] arrays).  These strings are case sensitive.
    // An empty string means disabled (not matchable).
    // For application security, these default community names should not be
    // used, but should all be disabled to force the end user to select unique
    // community names.  These defaults are provided only to make it easier to
    // start development.  Specifying more strings than
    // SNMP_MAX_COMMUNITY_SUPPORT will result in the later strings being
    // ignored (but still wasting program memory).  Specifying fewer strings is
    // legal, as long as at least one is present.  A string larger than
    // SNMP_COMMUNITY_MAX_LEN bytes will be ignored.
    #define SNMP_READ_COMMUNITIES        {"public", "read", ""}
    #define END_OF_SNMP_READ_COMMUNITIES
    #define SNMP_WRITE_COMMUNITIES        {"private", "write", "public"}
    #define END_OF_SNMP_WRITE_COMMUNITIES
#endif
 
 
//#define MPFS_USE_FAT        
 
#define MDD_ROOT_DIR_PATH        "\\"
De este archivo no he eliminado nada ya que luego puede que me sea útil, además que la mayoría de las modificaciones no las realizo yo, sino la aplicación que os mostré en la entrada anterior. He cambiado el nombre de la placa a MIPSBOARD/ de forma que poniendo esto en el navegador, no es necesario saber la dirección IP que se le ha asignado.
Una vez tenemos configurado todo el firmware del micro, debemos crear una página web, para ello debemos crear un directorio en el que pongamos todos los archivos de la página web, y llamar al archivo principal index.htm, o si no queremos llamarlo así debemos cambiarlo en el TCPIPConfig.h. Podemos crear una página web sencilla para probar. Una vez creada la página y todos los archivos que la componen vamos a utilizar otra de las aplicaciones que se instalan junto con las Microchip Aplication Libraries, que es la aplicación MPFS2.

image
Microchip utiliza un formato especial para introducir las páginas web en el microcontrolador, ese formato es el MPFS. Esta aplicación convierte una página web completa, imágenes, textos, enlaces… a un archivo cuyo formato dependerá de lo que nosotros queramos. En la parte superior de la ventana debemos seleccionar la carpeta donde se encuentra la página web, y es en el punto 2 donde le decimos que formato queremos. Como no utilizamos memoria exterior, debemos crear un archivo .c y agregarlo al proyecto, por lo que escogeremos la opción C18/C32 Image. Una vez hecho esto, seleccionamos un nombre para la imagen y le damos a Generate. La imagen se crea en el directorio del proyecto que le hemos dicho en el espacio Project Directory. Luego solo queda agregar el archivo de imagen al proyecto y volver a compilarlo y ya estamos listos para arrancar nuestro servidor web.
Como esta entrada ha quedado bastante larga, dejo para la próxima todo lo referente a monitorización y control de la página. En las próximas entradas también os podré todos los archivos para que los veáis y un vídeo demostrativo.