Contiki 2.6

raven-lcd.c

Go to the documentation of this file.
00001 /*   Copyright (c) 2008, Swedish Institute of Computer Science
00002  *  All rights reserved.
00003  *
00004  *   All rights reserved.
00005  *
00006  *   Redistribution and use in source and binary forms, with or without
00007  *   modification, are permitted provided that the following conditions are met:
00008  *
00009  *   * Redistributions of source code must retain the above copyright
00010  *     notice, this list of conditions and the following disclaimer.
00011  *   * Redistributions in binary form must reproduce the above copyright
00012  *     notice, this list of conditions and the following disclaimer in
00013  *     the documentation and/or other materials provided with the
00014  *     distribution.
00015  *   * Neither the name of the copyright holders nor the names of
00016  *     contributors may be used to endorse or promote products derived
00017  *     from this software without specific prior written permission.
00018  *
00019  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00020  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00021  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00022  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00023  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00024  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00025  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00026  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00027  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00028  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00029  *  POSSIBILITY OF SUCH DAMAGE.
00030  *
00031  * $Id: raven-lcd.c,v 1.11 2010/12/22 17:09:03 dak664 Exp $
00032 */
00033 
00034 /**
00035  *  \brief This module contains code to interface a Contiki-based
00036  *  project on the AVR Raven platform's ATMega1284P chip to the LCD
00037  *  driver chip (ATMega3290P) on the Raven.
00038  *  
00039  *  \author Blake Leverett <bleverett@gmail.com>
00040  *
00041 */
00042 
00043 /**  \addtogroup raven
00044  * @{ 
00045  */
00046 
00047 /**
00048  *  \defgroup ravenserial Serial interface between Raven processors
00049  * @{
00050  */
00051 /**
00052  *  \file
00053  *  This file contains code to connect the two AVR Raven processors via a serial connection.
00054  *
00055  */
00056 
00057 #define DEBUG 0        //Making this 1 will slightly alter command timings
00058 #if DEBUG
00059 #define PRINTF(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
00060 #else
00061 #define PRINTF(...)
00062 #endif
00063 #define DEBUGSERIAL 0  //Making this 1 will significantly alter command timings
00064 
00065 #include "contiki.h"
00066 #include "contiki-lib.h"
00067 #include "contiki-net.h"
00068 #include "webserver-nogui.h"
00069 #include "httpd-cgi.h"
00070 #include "raven-lcd.h"
00071 
00072 #include <string.h>
00073 #include <stdio.h>
00074 #include <avr/pgmspace.h>
00075 #include <avr/eeprom.h>
00076 #include <avr/sleep.h>
00077 #include <dev/watchdog.h>
00078 
00079 static uint8_t count = 0;
00080 static uint8_t seqno;
00081 uip_ipaddr_t dest_addr;
00082 
00083 #define MAX_CMD_LEN 20
00084 static struct{
00085     uint8_t frame[MAX_CMD_LEN];
00086     uint8_t ndx;
00087     uint8_t len;
00088     uint8_t cmd;
00089     uint8_t done;
00090 } cmd;
00091 
00092 #define UIP_IP_BUF                ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
00093 #define UIP_ICMP_BUF            ((struct uip_icmp_hdr *)&uip_buf[uip_l2_l3_hdr_len])
00094 #define PING6_DATALEN 16
00095 
00096 void rs232_send(uint8_t port, unsigned char c);
00097 
00098 /*---------------------------------------------------------------------------*/
00099 /* Sends a ping packet out the radio */
00100 /* Useful for debugging so allow external calls */
00101 void
00102 raven_ping6(void)
00103 {
00104 #if UIP_CONF_IPV6_RPL||1
00105 /* No default router, so pick on someone else */
00106 #define PING_GOOGLE 1
00107 seqno++;
00108 #endif
00109 
00110     UIP_IP_BUF->vtc = 0x60;
00111     UIP_IP_BUF->tcflow = 1;
00112     UIP_IP_BUF->flow = 0;
00113     UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
00114         UIP_IP_BUF->ttl = uip_ds6_if.cur_hop_limit;
00115 #if PING_GOOGLE
00116     if (seqno==1) {
00117            uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, uip_ds6_defrt_choose());   //the default router
00118         } else if (seqno==2) {
00119            uip_ip6addr(&UIP_IP_BUF->destipaddr,0x2001,0x4860,0x800f,0x0000,0x0000,0x0000,0x0000,0x0093);  //ipv6.google.com
00120         } else if (seqno==3) {
00121            uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, uip_ds6_defrt_choose());   //the default router
00122         } else {
00123 //         uip_ip6addr(&UIP_IP_BUF->destipaddr,0x2001,0x0420,0x5FFF,0x007D,0x02D0,0xB7FF,0xFE23,0xE6DB);  //?.cisco.com
00124            uip_ip6addr(&UIP_IP_BUF->destipaddr,0x2001,0x0420,0x0000,0x0010,0x0250,0x8bff,0xfee8,0xf800);  //six.cisco.com
00125         }       
00126 #else
00127           uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, uip_ds6_defrt_choose());    //the default router
00128 #endif
00129 
00130     uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
00131     UIP_ICMP_BUF->type = ICMP6_ECHO_REQUEST;
00132     UIP_ICMP_BUF->icode = 0;
00133     /* set identifier and sequence number to 0 */
00134     memset((void *)UIP_ICMP_BUF + UIP_ICMPH_LEN, 0, 4);
00135     /* put one byte of data */
00136     memset((void *)UIP_ICMP_BUF + UIP_ICMPH_LEN + UIP_ICMP6_ECHO_REQUEST_LEN,
00137            count, PING6_DATALEN);
00138      
00139     
00140     uip_len = UIP_ICMPH_LEN + UIP_ICMP6_ECHO_REQUEST_LEN + UIP_IPH_LEN + PING6_DATALEN;
00141     UIP_IP_BUF->len[0] = (uint8_t)((uip_len - 40) >> 8);
00142     UIP_IP_BUF->len[1] = (uint8_t)((uip_len - 40) & 0x00FF);
00143     
00144     UIP_ICMP_BUF->icmpchksum = 0;
00145     UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
00146    
00147     
00148     tcpip_ipv6_output();
00149 }
00150 #if defined(__AVR_ATmega128RFA1__)
00151 /* No raven 3290p to talk to but the lcd process still needed for ping reporting */
00152 /* Just dummy out send_frame */
00153 static void
00154 send_frame(uint8_t cmd, uint8_t len, uint8_t *payload)
00155 {
00156 }
00157 #else
00158 /*---------------------------------------------------------------------------*/
00159 /* Send a serial command frame to the ATMega3290 Processsor on Raven via serial port */
00160 static void
00161 send_frame(uint8_t cmd, uint8_t len, uint8_t *payload)
00162 {
00163     uint8_t i;
00164 
00165     rs232_send(0, SOF_CHAR);    /* Start of Frame */
00166     rs232_send(0, len);
00167     rs232_send(0, cmd);
00168     for (i=0;i<len;i++)
00169         rs232_send(0,*payload++);
00170     rs232_send(0, EOF_CHAR);
00171 }
00172 #endif
00173 char serial_char_received;
00174 /*---------------------------------------------------------------------------*/
00175 /* Sleep for howlong seconds, or until UART interrupt if howlong==0.
00176  * Uses TIMER2 with external 32768 Hz crystal to sleep in 1 second multiples.
00177  * TIMER2 may have already been set up for 125 ticks/second in clock.c
00178 
00179  *
00180  * Until someone figures out how to get UART to wake from powerdown,
00181  * a three second powersave cycle is used with exit based on any character received.
00182  
00183  * The system clock is adjusted to reflect the sleep time.
00184  */
00185 
00186 void micro_sleep(uint8_t howlong)
00187 {
00188     uint8_t saved_sreg = SREG, saved_howlong = howlong;
00189 #if AVR_CONF_USE32KCRYSTAL
00190 /* Save TIMER2 configuration if clock.c is using it */
00191     uint8_t savedTCNT2=TCNT2, savedTCCR2A=TCCR2A, savedTCCR2B = TCCR2B, savedOCR2A = OCR2A;
00192 #endif
00193 
00194 //    if (howlong==0) {
00195 //        set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // UART can't wake from powerdown
00196 //     } else {
00197         set_sleep_mode(SLEEP_MODE_PWR_SAVE);    // Sleep for howlong seconds
00198         if (howlong==0) howlong=3;              // 3*32/(32768/1024) = 3 second sleep cycle if not specified
00199         cli();                                  // Disable interrupts for the present
00200         ASSR |= (1 << AS2);                     // Set TIMER2 asyncronous from external crystal
00201         TCCR2A =(1<<WGM21);                     // CTC mode
00202         TCCR2B =((1<<CS22)|(1<<CS21)|(1<<CS20));// Prescale by 1024 = 32 ticks/sec
00203 //      while(ASSR & (1 << TCR2BUB));           // Wait for TCNT2 write to finish.
00204         OCR2A = howlong*32;                     // Set TIMER2 output compare register
00205 //      while(ASSR & (1 << OCR2AUB));           // Wait for OCR2 write to finish.
00206         SREG = saved_sreg;                      // Restore interrupt state.
00207 //      UCSR(USART,B)&= ~(1<<RXCIE(USART))      // Disable the RX Complete interrupt;
00208 //      UCSR0B|=(1<<RXCIE0);                    // Enable UART0 RX complete interrupt
00209 //      UCSR1B|=(1<<RXCIE1);                    // Enable UART1 RX complete interrupt 
00210 //      TCNT2 = 0;                              // Reset TIMER2 timer counter value.
00211 //      while(ASSR & (1 << TCN2UB));            // Wait for TCNT2 write to finish before entering sleep.
00212 //      TIMSK2 |= (1 << OCIE2A);                // Enable TIMER2 output compare interrupt.
00213 //    }
00214 
00215     TCNT2 = 0;                                  // Reset timer
00216     watchdog_stop();                            // Silence annoying distractions
00217     while(ASSR & (1 << TCN2UB));                // Wait for TCNT2 write to (which assures TCCR2x and OCR2A are finished!)
00218     TIMSK2 |= (1 << OCIE2A);                    // Enable TIMER2 output compare interrupt
00219     SMCR |= (1 <<  SE);                         // Enable sleep mode.
00220     while (1) {
00221 //    TCNT2 = 0;                                // Cleared automatically in CTC mode
00222 //     while(ASSR & (1 << TCN2UB));             // Wait for TCNT2 write to finish before entering sleep.
00223        serial_char_received=0;                  // Set when chars received by UART
00224        sleep_mode();                            // Sleep
00225 
00226        /* Adjust clock.c for the time spent sleeping */
00227        extern void clock_adjust_ticks(uint16_t howmany);
00228        clock_adjust_ticks(howlong * CLOCK_SECOND);
00229 
00230 //     if (TIMSK2&(1<<OCIE2A)) break;           // Exit sleep if not awakened by TIMER2
00231        PRINTF(".");
00232        if (saved_howlong) break;                // Exit sleep if nonzero time specified
00233        PRINTF("%d",serial_char_received);
00234        if (serial_char_received) break;
00235    }
00236 
00237     SMCR  &= ~(1 << SE);                        //Disable sleep mode after wakeup
00238 
00239 #if AVR_CONF_USE32KCRYSTAL
00240 /* Restore clock.c configuration */
00241 //  OCRSetup();
00242     cli();
00243     TCCR2A = savedTCCR2A;
00244     TCCR2B = savedTCCR2B;
00245     OCR2A  = savedOCR2A;
00246     TCNT2  = savedTCNT2;
00247     sei();
00248 #else
00249     TIMSK2 &= ~(1 << OCIE2A);                   //Disable TIMER2 interrupt
00250 #endif
00251 
00252     watchdog_start();
00253 }
00254 #if !AVR_CONF_USE32KCRYSTAL
00255 /*---------------------------------------------------------------------------*/
00256 /* TIMER2 Interrupt service */
00257 
00258 ISR(TIMER2_COMPA_vect)
00259 {
00260 //    TIMSK2 &= ~(1 << OCIE2A);       //Just one interrupt needed for waking
00261 }
00262 #endif /* !AVR_CONF_USE32KCRYSTAL */
00263 
00264 #if DEBUGSERIAL
00265 uint8_t serialcount;
00266 char dbuf[30];
00267 #endif 
00268 
00269 /*---------------------------------------------------------------------------*/
00270 extern uint16_t ledtimer;
00271 static uint8_t
00272 raven_gui_loop(process_event_t ev, process_data_t data)
00273 {
00274     uint8_t i,activeconnections,radio_state;
00275     
00276 // PRINTF("\nevent %d ",ev);
00277 #if DEBUGSERIAL
00278     printf_P(PSTR("Buffer [%d]="),serialcount);
00279     serialcount=0;
00280     for (i=0;i<30;i++) {
00281        printf_P(PSTR(" %d"),dbuf[i]);
00282        dbuf[i]=0;
00283     }
00284 #endif
00285     if(ev == tcpip_icmp6_event) switch(*((uint8_t *)data)) {
00286 
00287 //   case ICMP6_NS:
00288         /*Tell the 3290 we are being solicited. */
00289 //       send_frame(REPORT_NS,...);
00290 //       break;  //fall through for beep
00291 //   case ICMP6_RA:
00292         /*Tell the 3290 we have a router. */
00293 //       send_frame(REPORT_NA,...);
00294 //       break;  //fall through for beep
00295     case ICMP6_ECHO_REQUEST:
00296         /* We have received a ping request over the air. Tell the 3290 */
00297  //       send_frame(REPORT_PING_BEEP, 0, 0);
00298 #if RF230BB_CONF_LEDONPORTE1
00299           PORTE|=(1<<PE1);ledtimer=1000; //turn on led, set counter for turnoff
00300 #endif
00301         break;
00302     case ICMP6_ECHO_REPLY:
00303         /* We have received a ping reply over the air.  Send frame back to 3290 */
00304  //       send_frame(REPORT_PING, 1, &seqno);
00305         break;
00306 
00307     } else switch (ev) {
00308      case SERIAL_CMD:        
00309         /* Check for command from serial port, execute it. */
00310         /* Note cmd frame is written in an interrupt - delays here can cause overwriting by next command */
00311         PRINTF("\nCommand %d length %d done %d",cmd.cmd,cmd.len,cmd.done);
00312         if (cmd.done){
00313             /* Execute the waiting command */
00314             switch (cmd.cmd){
00315             case SEND_PING:
00316                 /* Send ping request over the air */
00317                 seqno = cmd.frame[0];
00318                 raven_ping6();
00319                 break;
00320             case SEND_TEMP:
00321                 /* Set temperature string in web server */
00322  //               web_set_temp((char *)cmd.frame);
00323                 break;
00324             case SEND_ADC2:
00325                 /* Set ext voltage string in web server */
00326   //              web_set_voltage((char *)cmd.frame);
00327                 break;
00328             case SEND_SLEEP:
00329                 /* Sleep radio and 1284p. */
00330                 if (cmd.frame[0]==0) {  //Time to sleep in seconds
00331                 /* Unconditional sleep. Don't wake until a serial interrupt. */
00332                 } else {
00333                 /* Sleep specified number of seconds (3290p "DOZE" mode) */
00334                 /* It sleeps a bit longer so we will be always be awake for the next sleep command. */
00335                 /* Only sleep this cycle if no active TCP/IP connections */
00336                    activeconnections=0;
00337                    for(i = 0; i < UIP_CONNS; ++i) {
00338                       if((uip_conns[i].tcpstateflags & UIP_TS_MASK) != UIP_CLOSED) activeconnections++;
00339                    }
00340                    if (activeconnections) {
00341                      PRINTF("\nWaiting for %d connections",activeconnections);
00342                      break;
00343                    }
00344                 }
00345                 radio_state = NETSTACK_RADIO.off();
00346                 PRINTF ("\nsleep %d radio state %d...",cmd.frame[0],radio_state);
00347 
00348                 /*Sleep for specified time*/
00349                 PRINTF("\nSleeping...");
00350                 micro_sleep(cmd.frame[0]);
00351 
00352                 radio_state = NETSTACK_RADIO.on();
00353                 if (radio_state > 0) {
00354                    PRINTF("Awake!");
00355                 } else {
00356                     PRINTF("Radio wake error %d\n",radio_state);
00357                 }
00358                 break;
00359             case SEND_WAKE:
00360                /* 3290p requests return message showing awake status */
00361                 send_frame(REPORT_WAKE, 0, 0);
00362                 break;
00363             default:
00364                 break;
00365             }
00366             /* Reset command done flag. */
00367             cmd.done = 0;
00368         }
00369         break;
00370     default:
00371         break;
00372     }
00373     return 0;
00374 }
00375 
00376 /*---------------------------------------------------------------------------*/
00377 /* Process an input character from serial port.  
00378  *  ** This is called from an ISR!!
00379 */
00380 
00381 int raven_lcd_serial_input(unsigned char ch)
00382 {
00383     /* Tell sleep routine if a  reception occurred */
00384     /* Random nulls occur for some reason, so ignore those */
00385     if (ch) serial_char_received++;
00386 #if DEBUGSERIAL
00387     if (serialcount<25) dbuf[serialcount]=ch;
00388     serialcount++;
00389 #endif
00390     /* Don't overwrite an unprocessed command */
00391 //    if (cmd.done) return 0;
00392     
00393     /* Parse frame,  */
00394     switch (cmd.ndx){
00395     case 0:
00396         /* first byte, must be 0x01 */
00397         if (ch == 0x01){
00398 //            cmd.done = false;
00399         } else {
00400 #if DEBUGSERIAL
00401             dbuf[25]++;
00402 #endif
00403             return 0;
00404         }
00405         break;
00406     case 1: 
00407         /* Second byte, length of payload */
00408         cmd.len = ch;
00409         break;
00410     case 2:
00411         /* Third byte, command byte */
00412         cmd.cmd = ch;
00413         break;
00414     default:
00415         /* Payload and ETX */
00416         if (cmd.ndx >= (MAX_CMD_LEN+3)) {  //buffer overflow!
00417             cmd.ndx=0;
00418 #if DEBUGSERIAL
00419             dbuf[26]++;
00420 #endif
00421             return 0;
00422         }
00423         if (cmd.ndx >= cmd.len+3){
00424             /* all done, check ETX */
00425             if (ch == 0x04){
00426                 cmd.done = 1;
00427 #if DEBUGSERIAL
00428                 dbuf[27]++;
00429 #endif
00430                 process_post(&raven_lcd_process, SERIAL_CMD, 0);
00431             } else {
00432                 /* Failed ETX */
00433 #if DEBUGSERIAL
00434                 dbuf[28]++;
00435 #endif
00436             }
00437             cmd.ndx=0;             //set up for next command
00438             return 0;
00439         } else {
00440             /* Just grab and store payload */
00441             cmd.frame[cmd.ndx - 3] = ch;
00442         }
00443         break;
00444     }
00445     
00446     cmd.ndx++;
00447     return 0;
00448 }
00449 
00450 /*---------------------------------------------------------------------------*/
00451 void
00452 raven_lcd_show_text(char *text) {
00453     uint8_t textlen=strlen(text)+1;
00454     if (textlen > MAX_CMD_LEN) textlen=MAX_CMD_LEN;
00455     send_frame(REPORT_TEXT_MSG, textlen, (uint8_t *) text);
00456 }
00457 
00458 #if WEBSERVER
00459 static void
00460 lcd_show_servername(void) {
00461 
00462 //extern uint8_t mac_address[8];     //These are defined in httpd-fsdata.c via makefsdata.h 
00463 extern uint8_t server_name[16];
00464 //extern uint8_t domain_name[30];
00465 char buf[sizeof(server_name)+1];
00466     eeprom_read_block (buf,server_name, sizeof(server_name));
00467     buf[sizeof(server_name)]=0;
00468     raven_lcd_show_text(buf);  //must fit in all the buffers or it will be truncated!
00469 }
00470 #endif
00471 /*---------------------------------------------------------------------------*/
00472 PROCESS(raven_lcd_process, "Raven LCD interface process");
00473 PROCESS_THREAD(raven_lcd_process, ev, data)
00474 {
00475 
00476   PROCESS_BEGIN();
00477 
00478 #if WEBSERVER
00479   lcd_show_servername();
00480 #endif
00481 
00482   /* Get ICMP6 callbacks from uip6 stack, perform 3290p action on pings, responses, etc. */
00483   if(icmp6_new(NULL) == 0) {
00484   
00485     while(1) {
00486       PROCESS_YIELD();
00487 //      if (ev != ?)      //trap frequent strobes?
00488         raven_gui_loop(ev, data);
00489     } 
00490   }
00491   PROCESS_END();
00492 }
00493 
00494 /** @} */