From: richardbarry Date: Sun, 10 Jul 2011 21:10:11 +0000 (+0000) Subject: Get web server and run time stats working in the Kinetis K60 demo. X-Git-Tag: V7.0.2~120 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=d7e472e133509b77f4db54118b2f5ccbb1b2c4b4;p=freertos Get web server and run time stats working in the Kinetis K60 demo. git-svn-id: https://svn.code.sf.net/p/freertos/code/trunk@1490 1d2547de-c912-0410-9cb9-b8ca96c0e9e2 --- diff --git a/Demo/CORTEX_Kinetis_K60_Tower_IAR/FreeRTOSConfig.h b/Demo/CORTEX_Kinetis_K60_Tower_IAR/FreeRTOSConfig.h index e086598ce..2b54877c8 100644 --- a/Demo/CORTEX_Kinetis_K60_Tower_IAR/FreeRTOSConfig.h +++ b/Demo/CORTEX_Kinetis_K60_Tower_IAR/FreeRTOSConfig.h @@ -87,7 +87,7 @@ #define configIDLE_SHOULD_YIELD 1 #define configUSE_MUTEXES 1 #define configQUEUE_REGISTRY_SIZE 0 -#define configGENERATE_RUN_TIME_STATS 0 +#define configGENERATE_RUN_TIME_STATS 1 #define configCHECK_FOR_STACK_OVERFLOW 2 #define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_MALLOC_FAILED_HOOK 1 @@ -114,6 +114,13 @@ to exclude the API function. */ #define INCLUDE_vTaskDelayUntil 1 #define INCLUDE_vTaskDelay 1 +#ifdef __ICCARM__ /* Stop these prototypes being included in the asm files. */ + void vMainConfigureTimerForRunTimeStats( void ); + unsigned long ulMainGetRunTimeCounterValue( void ); +#endif +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() vMainConfigureTimerForRunTimeStats() +#define portGET_RUN_TIME_COUNTER_VALUE() ulMainGetRunTimeCounterValue() + /* Use the system definition, if there is one */ #ifdef __NVIC_PRIO_BITS #define configPRIO_BITS __NVIC_PRIO_BITS diff --git a/Demo/CORTEX_Kinetis_K60_Tower_IAR/Freescale_Code/cpu/vectors.h b/Demo/CORTEX_Kinetis_K60_Tower_IAR/Freescale_Code/cpu/vectors.h index 2f412088d..f32c75f9f 100644 --- a/Demo/CORTEX_Kinetis_K60_Tower_IAR/Freescale_Code/cpu/vectors.h +++ b/Demo/CORTEX_Kinetis_K60_Tower_IAR/Freescale_Code/cpu/vectors.h @@ -23,7 +23,6 @@ extern void vPort_E_ISRHandler( void ); void vEMAC_TxISRHandler( void ); void vEMAC_RxISRHandler( void ); void vEMAC_ErrorISRHandler( void ); -void vEMAC_ISRHandler( void ); // function prototype for default_isr in vectors.c void default_isr(void); @@ -131,9 +130,9 @@ extern void __iar_program_start(void); #define VECTOR_089 default_isr // 0x0000_0164 89 73 USB OTG #define VECTOR_090 default_isr // 0x0000_0168 90 74 USB Charger Detect #define VECTOR_091 default_isr // 0x0000_016C 91 75 ENET IEEE 1588 Timer interrupt -#define VECTOR_092 vEMAC_ISRHandler // 0x0000_0170 92 76 ENET Transmit interrupt -#define VECTOR_093 vEMAC_ISRHandler // 0x0000_0174 93 77 ENET Receive interrupt -#define VECTOR_094 vEMAC_ISRHandler // 0x0000_0178 94 78 ENET Error and miscellaneous interrupt +#define VECTOR_092 vEMAC_TxISRHandler // 0x0000_0170 92 76 ENET Transmit interrupt +#define VECTOR_093 vEMAC_RxISRHandler // 0x0000_0174 93 77 ENET Receive interrupt +#define VECTOR_094 vEMAC_ErrorISRHandler // 0x0000_0178 94 78 ENET Error and miscellaneous interrupt #define VECTOR_095 default_isr // 0x0000_017C 95 79 I2S #define VECTOR_096 default_isr // 0x0000_0180 96 80 SDHC #define VECTOR_097 default_isr // 0x0000_0184 97 81 DAC0 diff --git a/Demo/CORTEX_Kinetis_K60_Tower_IAR/ParTest.c b/Demo/CORTEX_Kinetis_K60_Tower_IAR/ParTest.c index 86087e5ec..2e2d96275 100644 --- a/Demo/CORTEX_Kinetis_K60_Tower_IAR/ParTest.c +++ b/Demo/CORTEX_Kinetis_K60_Tower_IAR/ParTest.c @@ -85,7 +85,7 @@ void vParTestInitialise( void ) GPIOA_PDDR=GPIO_PDDR_PDD( ulLEDs[ 0 ] | ulLEDs[ 1 ] | ulLEDs[ 2 ] | ulLEDs[ 3 ] ); /* Start with LEDs off. */ - GPIOA_PTOR = ~0U; + GPIOA_PTOR = ~0U; } /*-----------------------------------------------------------*/ @@ -93,57 +93,23 @@ void vParTestSetLED( unsigned long ulLED, signed portBASE_TYPE xValue ) { if( ulLED < partstMAX_LEDS ) { - /* A critical section is used as the LEDs are also accessed from an - interrupt. */ - taskENTER_CRITICAL(); + if( xValue == pdTRUE ) { - if( xValue == pdTRUE ) - { - GPIOA_PDOR &= ~GPIO_PDOR_PDO( ulLEDs[ ulLED ] ); - } - else - { - GPIOA_PDOR |= GPIO_PDOR_PDO( ulLEDs[ ulLED ] ); - } + GPIOA_PCOR = ulLEDs[ ulLED ]; } - taskEXIT_CRITICAL(); - } -} -/*-----------------------------------------------------------*/ - -void vParTestToggleLED( unsigned long ulLED ) -{ - if( ulLED < partstMAX_LEDS ) - { - /* A critical section is used as the LEDs are also accessed from an - interrupt. */ - taskENTER_CRITICAL(); + else { - GPIOA_PTOR |= GPIO_PDOR_PDO( ulLEDs[ ulLED ] ); + GPIOA_PSOR = ulLEDs[ ulLED ]; } - taskEXIT_CRITICAL(); } } /*-----------------------------------------------------------*/ -void vParTestSetLEDFromISR( unsigned long ulLED, signed portBASE_TYPE xValue ) +void vParTestToggleLED( unsigned long ulLED ) { -unsigned portBASE_TYPE uxInterruptFlags; - if( ulLED < partstMAX_LEDS ) { - uxInterruptFlags = portSET_INTERRUPT_MASK_FROM_ISR(); - { - if( xValue == pdTRUE ) - { - GPIOA_PDOR &= ~GPIO_PDOR_PDO( ulLEDs[ ulLED ] ); - } - else - { - GPIOA_PDOR |= GPIO_PDOR_PDO( ulLEDs[ ulLED ] ); - } - } - portCLEAR_INTERRUPT_MASK_FROM_ISR( uxInterruptFlags ); + GPIOA_PTOR = ulLEDs[ ulLED ]; } } /*-----------------------------------------------------------*/ @@ -154,22 +120,16 @@ long lReturn = pdFALSE; if( ulLED < partstMAX_LEDS ) { - /* A critical section is used as the LEDs are also accessed from an - interrupt. */ - taskENTER_CRITICAL(); + lReturn = GPIOA_PDOR & ulLEDs[ ulLED ]; + + if( lReturn == 0 ) + { + lReturn = pdTRUE; + } + else { - lReturn = GPIO_PDOR_PDO( ulLEDs[ ulLED ] ); - - if( lReturn == 0 ) - { - lReturn = pdTRUE; - } - else - { - lReturn = pdFALSE; - } + lReturn = pdFALSE; } - taskEXIT_CRITICAL(); } return lReturn; diff --git a/Demo/CORTEX_Kinetis_K60_Tower_IAR/main-full.c b/Demo/CORTEX_Kinetis_K60_Tower_IAR/main-full.c index 04d995d68..aca6483b6 100644 --- a/Demo/CORTEX_Kinetis_K60_Tower_IAR/main-full.c +++ b/Demo/CORTEX_Kinetis_K60_Tower_IAR/main-full.c @@ -134,7 +134,6 @@ #include "QPeek.h" #include "recmutex.h" #include "TimerDemo.h" -#include "comtest2.h" #include "PollQ.h" #include "countsem.h" #include "dynamic.h" @@ -158,11 +157,6 @@ the queue empty. */ #define mainLED0 0UL #define mainLED1 1UL -/* The LED used by the comtest tasks. See the comtest.c file for more -information. In this case, the LED is deliberatly out of the valid range as -all the available LEDs are already used by other tasks and timers. */ -#define mainCOM_TEST_LED ( 4 ) - /* Constant used by the standard timer test functions. */ #define mainTIMER_TEST_PERIOD ( 50 ) @@ -207,9 +201,6 @@ callback functions. */ /* A block time of zero simply means "don't block". */ #define mainDONT_BLOCK ( 0UL ) -/* Baud rate used by the comtest tasks. */ -#define mainCOM_TEST_BAUD_RATE ( 115200UL ) - /* The vector used by the GPIO port E. Button SW2 is configured to generate an interrput on this port. */ #define mainGPIO_E_VECTOR ( 107 - 16 ) @@ -276,6 +267,8 @@ be set to point to a string that identifies the offending task. This is just to make debugging easier. */ static const char *pcStatusMessage = NULL; +/* Used in the run time stats calculation. */ +static unsigned long ulClocksPer10thOfAMilliSecond = 0UL; /*-----------------------------------------------------------*/ void main( void ) @@ -302,7 +295,6 @@ void main( void ) vStartQueuePeekTasks(); vStartRecursiveMutexTasks(); vStartTimerDemoTask( mainTIMER_TEST_PERIOD ); -//_RB_ vAltStartComTestTasks( mainCOM_TEST_PRIORITY, mainCOM_TEST_BAUD_RATE, mainCOM_TEST_LED ); vStartPolledQueueTasks( mainQUEUE_POLL_PRIORITY ); vStartCountingSemaphoreTasks(); vStartDynamicPriorityTasks(); @@ -370,11 +362,6 @@ static long lChangedTimerPeriodAlready = pdFALSE; pcStatusMessage = "Error: RecMutex\n"; } -if( 0 )//_RB_ if( xAreComTestTasksStillRunning() != pdPASS ) - { - pcStatusMessage = "Error: ComTest\n"; - } - if( xAreTimerDemoTasksStillRunning( ( mainCHECK_TIMER_PERIOD_MS ) ) != pdTRUE ) { pcStatusMessage = "Error: TimerDemo\n"; @@ -611,3 +598,53 @@ char *pcGetTaskStatusMessage( void ) } } /*-----------------------------------------------------------*/ + +void vMainConfigureTimerForRunTimeStats( void ) +{ + /* How many clocks are there per tenth of a millisecond? */ + ulClocksPer10thOfAMilliSecond = configCPU_CLOCK_HZ / 10000UL; +} +/*-----------------------------------------------------------*/ + +unsigned long ulMainGetRunTimeCounterValue( void ) +{ +unsigned long ulSysTickCounts, ulTickCount, ulReturn; +const unsigned long ulSysTickReloadValue = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; +volatile unsigned long * const pulCurrentSysTickCount = ( ( volatile unsigned long *) 0xe000e018 ); +volatile unsigned long * const pulInterruptCTRLState = ( ( volatile unsigned long *) 0xe000ed04 ); +const unsigned long ulSysTickPendingBit = 0x04000000UL; + + /* NOTE: There are potentially race conditions here. It is ok to keep + things simple, without using any additional timer peripherals. */ + + + /* The SysTick is a down counter. How many clocks have passed since it was + last reloaded? */ + ulSysTickCounts = ulSysTickReloadValue - *pulCurrentSysTickCount; + + /* How many times has it overflowed? */ + ulTickCount = xTaskGetTickCountFromISR(); + + /* Is there a SysTick interrupt pending? */ + if( ( *pulInterruptCTRLState & ulSysTickPendingBit ) != 0UL ) + { + /* There is a SysTick interrupt pending, so the SysTick has overflowed + but the tick count not yet incremented. */ + ulTickCount++; + + /* Read the SysTick again, as the overflow might have occurred since + it was read last. */ + ulSysTickCounts = ulSysTickReloadValue - *pulCurrentSysTickCount; + } + + /* Convert the tick count into tenths of a millisecond. */ + ulReturn = ( ulTickCount * 10UL ) ; + + /* Add on the number of tenths of a millisecond that have passed since the + tick count last got updated. */ + ulReturn += ( ulSysTickCounts / ulClocksPer10thOfAMilliSecond ); + + return ulReturn; +} +/*-----------------------------------------------------------*/ + diff --git a/Demo/CORTEX_Kinetis_K60_Tower_IAR/main_blinky.c b/Demo/CORTEX_Kinetis_K60_Tower_IAR/main_blinky.c index 7eb2d738b..22b07f9a3 100644 --- a/Demo/CORTEX_Kinetis_K60_Tower_IAR/main_blinky.c +++ b/Demo/CORTEX_Kinetis_K60_Tower_IAR/main_blinky.c @@ -221,7 +221,7 @@ static void prvButtonLEDTimerCallback( xTimerHandle xTimer ) a critical section because it is accessed from multiple tasks, and the button interrupt - in this trivial case, for simplicity, the critical section is omitted. */ - GPIOA_PDOR |= GPIO_PDOR_PDO( mainTIMER_CONTROLLED_LED ); + GPIOA_PSOR = mainTIMER_CONTROLLED_LED; } /*-----------------------------------------------------------*/ @@ -233,7 +233,7 @@ portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; /* The button was pushed, so ensure the LED is on before resetting the LED timer. The LED timer will turn the LED off if the button is not pushed within 5000ms. */ - GPIOA_PDOR &= ~GPIO_PDOR_PDO( mainTIMER_CONTROLLED_LED ); + GPIOA_PCOR = mainTIMER_CONTROLLED_LED; /* This interrupt safe FreeRTOS function can be called from this interrupt because the interrupt priority is below the @@ -293,11 +293,7 @@ unsigned long ulReceivedValue; is it the expected value? If it is, toggle the LED. */ if( ulReceivedValue == 100UL ) { - /* NOTE - accessing the LED port should use a critical section - because it is accessed from multiple tasks, and the button interrupt - - in this trivial case, for simplicity, the critical section is - omitted. */ - GPIOA_PTOR |= GPIO_PDOR_PDO( mainTASK_CONTROLLED_LED ); + GPIOA_PTOR = mainTASK_CONTROLLED_LED; } } } @@ -379,6 +375,22 @@ volatile size_t xFreeHeapSpace; } /*-----------------------------------------------------------*/ +/* The Blinky build configuration does not include Ethernet functionality, +however, the Full and Blinky build configurations share a vectors.h header file. +Therefore, dummy Ethernet interrupt handers need to be defined to keep the +linker happy. */ +void vEMAC_TxISRHandler( void ) {} +void vEMAC_RxISRHandler( void ){} +void vEMAC_ErrorISRHandler( void ) {} + +/* The Blinky build configuration does not include run time stats gathering, +however, the Full and Blinky build configurations share a FreeRTOSConfig.h +file. Therefore, dummy run time stats functions need to be defined to keep the +linker happy. */ +void vMainConfigureTimerForRunTimeStats( void ) {} +unsigned long ulMainGetRunTimeCounterValue( void ) { return 0UL; } + + diff --git a/Demo/CORTEX_Kinetis_K60_Tower_IAR/uIP_Task.c b/Demo/CORTEX_Kinetis_K60_Tower_IAR/uIP_Task.c index 43aadc6a7..f08818b7e 100644 --- a/Demo/CORTEX_Kinetis_K60_Tower_IAR/uIP_Task.c +++ b/Demo/CORTEX_Kinetis_K60_Tower_IAR/uIP_Task.c @@ -319,6 +319,7 @@ static const unsigned long ulPeriodicTimerExpired = uipPERIODIC_TIMER_EVENT; void vApplicationProcessFormInput( char *pcInputString ) { char *c; +const unsigned long ulYellowLED = 2UL; /* Only interested in processing form input if this is the IO page. */ c = strstr( pcInputString, "io.shtml" ); @@ -333,21 +334,18 @@ char *c; if( strstr( c, "LED0=1" ) != NULL ) { /* Turn the LEDs on. */ - vParTestSetLED( 3, 1 ); - vParTestSetLED( 4, 1 ); + vParTestSetLED( ulYellowLED, pdTRUE ); } else { /* Turn the LEDs off. */ - vParTestSetLED( 3, 0 ); - vParTestSetLED( 4, 0 ); + vParTestSetLED( ulYellowLED, pdFALSE ); } } else { - /* Commands to turn LEDs off are not always explicit. */ - vParTestSetLED( 3, 0 ); - vParTestSetLED( 4, 0 ); + /* Some browsers will only imply that a check box is off. */ + vParTestSetLED( ulYellowLED, pdFALSE ); } } } diff --git a/Demo/CORTEX_Kinetis_K60_Tower_IAR/webserver/EMAC.c b/Demo/CORTEX_Kinetis_K60_Tower_IAR/webserver/EMAC.c index db673b200..b58740f6e 100644 --- a/Demo/CORTEX_Kinetis_K60_Tower_IAR/webserver/EMAC.c +++ b/Demo/CORTEX_Kinetis_K60_Tower_IAR/webserver/EMAC.c @@ -529,6 +529,9 @@ unsigned long usReturn = 0; void vEMAC_TxISRHandler( void ) { + /* Clear the interrupt. */ + ENET_EIR = ENET_EIR_TXF_MASK; + /* Check the buffers have not already been freed in the first of the two Tx interrupts - which could potentially happen if the second Tx completed during the interrupt for the first Tx. */ @@ -554,6 +557,9 @@ const unsigned long ulRxEvent = uipETHERNET_RX_EVENT; long lHigherPriorityTaskWoken = pdFALSE; extern xQueueHandle xEMACEventQueue; + /* Clear the interrupt. */ + ENET_EIR = ENET_EIR_RXF_MASK; + /* An Ethernet Rx event has occurred. */ xQueueSendFromISR( xEMACEventQueue, &ulRxEvent, &lHigherPriorityTaskWoken ); portEND_SWITCHING_ISR( lHigherPriorityTaskWoken ); @@ -562,59 +568,13 @@ extern xQueueHandle xEMACEventQueue; void vEMAC_ErrorISRHandler( void ) { - portDISABLE_INTERRUPTS(); - for( ;; ); + /* Clear the interrupt. */ + ENET_EIR = ENET_EIR & ENET_EIMR; + + /* Attempt recovery. Not very sophisticated. */ + prvInitialiseDescriptors(); + ENET_RDAR = ENET_RDAR_RDAR_MASK; } /*-----------------------------------------------------------*/ -volatile unsigned long ulEvent, ulMask; -void vEMAC_ISRHandler( void ) -{ -//unsigned long ulEvent; -long lHigherPriorityTaskWoken = pdFALSE; -const unsigned long ulRxEvent = uipETHERNET_RX_EVENT; -extern xQueueHandle xEMACEventQueue; - - /* What caused the interrupt? */ - ulMask = ENET_EIMR; - ulEvent = ENET_EIR; - ulEvent &= ulMask; - - ENET_EIR = ulEvent; - - if( ( ulEvent & ENET_EIR_TXF_MASK ) != 0UL ) - { - /* Transmit complete. - Check the buffers have not already been freed in the first of the - two Tx interrupts - which could potentially happen if the second Tx completed - during the interrupt for the first Tx. */ - if( xTxDescriptors[ 0 ].data != NULL ) - { - if( ( ( xTxDescriptors[ 0 ].status & TX_BD_R ) == 0 ) && ( ( xTxDescriptors[ 0 ].status & TX_BD_R ) == 0 ) ) - { - configASSERT( xTxDescriptors[ 0 ].data == xTxDescriptors[ 1 ].data ); - - xTxDescriptors[ 0 ].data = ( uint8_t* ) __REV( ( unsigned long ) xTxDescriptors[ 0 ].data ); - prvReturnBuffer( xTxDescriptors[ 0 ].data ); - - /* Just to mark the fact that the buffer has already been released. */ - xTxDescriptors[ 0 ].data = NULL; - } - } - } - - if( ( ulEvent & ENET_EIR_RXF_MASK ) != 0UL ) - { - /* Packet Rxed. */ - xQueueSendFromISR( xEMACEventQueue, &ulRxEvent, &lHigherPriorityTaskWoken ); - portEND_SWITCHING_ISR( lHigherPriorityTaskWoken ); - } - - if( ulEvent & ( ENET_EIR_UN_MASK | ENET_EIR_RL_MASK | ENET_EIR_LC_MASK | ENET_EIR_EBERR_MASK | ENET_EIR_BABT_MASK | ENET_EIR_BABR_MASK | ENET_EIR_EBERR_MASK ) ) - { - /* Error. */ - prvInitialiseDescriptors(); - ENET_RDAR = ENET_RDAR_RDAR_MASK; - } -} diff --git a/Demo/CORTEX_Kinetis_K60_Tower_IAR/webserver/httpd-cgi.c b/Demo/CORTEX_Kinetis_K60_Tower_IAR/webserver/httpd-cgi.c index 432f957bb..127046d4f 100644 --- a/Demo/CORTEX_Kinetis_K60_Tower_IAR/webserver/httpd-cgi.c +++ b/Demo/CORTEX_Kinetis_K60_Tower_IAR/webserver/httpd-cgi.c @@ -210,9 +210,10 @@ static unsigned short generate_io_state( void *arg ) { extern long lParTestGetLEDState( unsigned long ulLED ); ( void ) arg; + const unsigned long ulYellowLED = 2UL; /* Are the dynamically setable LEDs currently on or off? */ - if( lParTestGetLEDState( 8 ) ) + if( lParTestGetLEDState( ulYellowLED ) == pdTRUE ) { pcStatus = "checked"; } @@ -228,27 +229,12 @@ static unsigned short generate_io_state( void *arg ) /*---------------------------------------------------------------------------*/ extern void vTaskGetRunTimeStats( signed char *pcWriteBuffer ); -extern unsigned short usMaxJitter; -static char cJitterBuffer[ 200 ]; static unsigned short generate_runtime_stats( void *arg ) { ( void ) arg; lRefreshCount++; sprintf( cCountBuf, "


Refresh count = %d", ( int ) lRefreshCount ); - - #ifdef INCLUDE_HIGH_FREQUENCY_TIMER_TEST - { - sprintf( cJitterBuffer, "


Max high frequency timer jitter = %d peripheral clock periods.


", ( int ) usMaxJitter ); - vTaskGetRunTimeStats( uip_appdata ); - strcat( uip_appdata, cJitterBuffer ); - } - #else - { - ( void ) cJitterBuffer; - strcpy( uip_appdata, "

Run time stats are only available in the debug_with_optimisation build configuration.

" ); - } - #endif - + vTaskGetRunTimeStats( uip_appdata ); strcat( uip_appdata, cCountBuf ); return strlen( uip_appdata ); diff --git a/Demo/CORTEX_Kinetis_K60_Tower_IAR/webserver/httpd-fs/io.shtml b/Demo/CORTEX_Kinetis_K60_Tower_IAR/webserver/httpd-fs/io.shtml index 819e2d39b..7fd1c051c 100644 --- a/Demo/CORTEX_Kinetis_K60_Tower_IAR/webserver/httpd-fs/io.shtml +++ b/Demo/CORTEX_Kinetis_K60_Tower_IAR/webserver/httpd-fs/io.shtml @@ -12,7 +12,8 @@

-Use the check box to turn on or off LED 4, then click "Update IO". +The check box and "Update IO" button can also be used to turn the yellow LED on and off. +

diff --git a/Demo/CORTEX_Kinetis_K60_Tower_IAR/webserver/httpd-fsdata.c b/Demo/CORTEX_Kinetis_K60_Tower_IAR/webserver/httpd-fsdata.c index 47823d100..13d7f8afb 100644 --- a/Demo/CORTEX_Kinetis_K60_Tower_IAR/webserver/httpd-fsdata.c +++ b/Demo/CORTEX_Kinetis_K60_Tower_IAR/webserver/httpd-fsdata.c @@ -220,31 +220,33 @@ static const char data_io_shtml[] = { 0x3e, 0xd, 0xa, 0x3c, 0x62, 0x3e, 0x4c, 0x45, 0x44, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x4c, 0x43, 0x44, 0x20, 0x49, 0x4f, 0x3c, 0x2f, 0x62, 0x3e, 0x3c, 0x62, 0x72, 0x3e, 0xd, 0xa, - 0xd, 0xa, 0x3c, 0x70, 0x3e, 0xd, 0xa, 0xd, 0xa, 0x55, - 0x73, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x68, 0x65, - 0x63, 0x6b, 0x20, 0x62, 0x6f, 0x78, 0x20, 0x74, 0x6f, 0x20, - 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x6f, 0x72, - 0x20, 0x6f, 0x66, 0x66, 0x20, 0x4c, 0x45, 0x44, 0x20, 0x34, - 0x2c, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x63, 0x6c, 0x69, - 0x63, 0x6b, 0x20, 0x22, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x20, 0x49, 0x4f, 0x22, 0x2e, 0xd, 0xa, 0xd, 0xa, 0xd, - 0xa, 0x3c, 0x70, 0x3e, 0xd, 0xa, 0x3c, 0x66, 0x6f, 0x72, - 0x6d, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x61, 0x46, - 0x6f, 0x72, 0x6d, 0x22, 0x20, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x3d, 0x22, 0x2f, 0x69, 0x6f, 0x2e, 0x73, 0x68, 0x74, - 0x6d, 0x6c, 0x22, 0x20, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, - 0x3d, 0x22, 0x67, 0x65, 0x74, 0x22, 0x3e, 0xd, 0xa, 0x25, - 0x21, 0x20, 0x6c, 0x65, 0x64, 0x2d, 0x69, 0x6f, 0xd, 0xa, - 0x3c, 0x70, 0x3e, 0xd, 0xa, 0x3c, 0x69, 0x6e, 0x70, 0x75, - 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x22, 0x73, 0x75, - 0x62, 0x6d, 0x69, 0x74, 0x22, 0x20, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3d, 0x22, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, - 0x49, 0x4f, 0x22, 0x3e, 0xd, 0xa, 0x3c, 0x2f, 0x66, 0x6f, - 0x72, 0x6d, 0x3e, 0xd, 0xa, 0x3c, 0x62, 0x72, 0x3e, 0x3c, - 0x70, 0x3e, 0xd, 0xa, 0x3c, 0x2f, 0x66, 0x6f, 0x6e, 0x74, - 0x3e, 0xd, 0xa, 0x3c, 0x2f, 0x62, 0x6f, 0x64, 0x79, 0x3e, - 0xd, 0xa, 0x3c, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 0xd, - 0xa, 0xd, 0xa, 0}; + 0xd, 0xa, 0x3c, 0x70, 0x3e, 0xd, 0xa, 0xd, 0xa, 0x54, + 0x68, 0x65, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x62, + 0x6f, 0x78, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x22, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x20, 0x49, 0x4f, 0x22, 0x20, 0x62, + 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x20, 0x63, 0x61, 0x6e, 0x20, + 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, + 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x79, 0x65, 0x6c, 0x6c, 0x6f, + 0x77, 0x20, 0x4c, 0x45, 0x44, 0x20, 0x6f, 0x6e, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x6f, 0x66, 0x66, 0x2e, 0xd, 0xa, 0xd, + 0xa, 0xd, 0xa, 0xd, 0xa, 0x3c, 0x70, 0x3e, 0xd, 0xa, + 0x3c, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x6e, 0x61, 0x6d, 0x65, + 0x3d, 0x22, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x22, 0x20, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x22, 0x2f, 0x69, 0x6f, + 0x2e, 0x73, 0x68, 0x74, 0x6d, 0x6c, 0x22, 0x20, 0x6d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x3d, 0x22, 0x67, 0x65, 0x74, 0x22, + 0x3e, 0xd, 0xa, 0x25, 0x21, 0x20, 0x6c, 0x65, 0x64, 0x2d, + 0x69, 0x6f, 0xd, 0xa, 0x3c, 0x70, 0x3e, 0xd, 0xa, 0x3c, + 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, + 0x3d, 0x22, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x22, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3d, 0x22, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x20, 0x49, 0x4f, 0x22, 0x3e, 0xd, 0xa, + 0x3c, 0x2f, 0x66, 0x6f, 0x72, 0x6d, 0x3e, 0xd, 0xa, 0x3c, + 0x62, 0x72, 0x3e, 0x3c, 0x70, 0x3e, 0xd, 0xa, 0x3c, 0x2f, + 0x66, 0x6f, 0x6e, 0x74, 0x3e, 0xd, 0xa, 0x3c, 0x2f, 0x62, + 0x6f, 0x64, 0x79, 0x3e, 0xd, 0xa, 0x3c, 0x2f, 0x68, 0x74, + 0x6d, 0x6c, 0x3e, 0xd, 0xa, 0xd, 0xa, 0}; static const char data_logo_jpg[] = { /* /logo.jpg */