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