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 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 u8_t count = 0;
00083 static u8_t seqno;
00084 uip_ipaddr_t dest_addr;
00085 
00086 #define MAX_CMD_LEN 20
00087 static struct{
00088     u8_t frame[MAX_CMD_LEN];
00089     u8_t ndx;
00090     u8_t len;
00091     u8_t cmd;
00092     u8_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] = (u8_t)((uip_len - 40) >> 8);
00141     UIP_IP_BUF->len[1] = (u8_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 125 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        extern void clock_adjust_seconds(uint8_t howmany);
00219        clock_adjust_seconds(howlong);
00220 
00221 //     if (TIMSK2&(1<<OCIE2A)) break;           // Exit sleep if not awakened by TIMER2
00222        PRINTF(".");
00223        if (saved_howlong) break;                // Exit sleep if nonzero time specified
00224 //     PRINTF("%d",serial_char_received);
00225        if (serial_char_received) break;
00226    }
00227 
00228     SMCR  &= ~(1 << SE);                        //Disable sleep mode after wakeup
00229 
00230 #if AVR_CONF_USE32KCRYSTAL
00231 /* Restore clock.c configuration */
00232 //  OCRSetup();
00233     cli();
00234     TCCR2A = savedTCCR2A;
00235     TCCR2B = savedTCCR2B;
00236     OCR2A  = savedOCR2A;
00237     TCNT2  = savedTCNT2;
00238     sei();
00239 #else
00240     TIMSK2 &= ~(1 << OCIE2A);                   //Disable TIMER2 interrupt
00241 #endif
00242 
00243     watchdog_start();
00244 }
00245 #if !AVR_CONF_USE32KCRYSTAL
00246 /*---------------------------------------------------------------------------*/
00247 /* TIMER2 Interrupt service */
00248 
00249 ISR(TIMER2_COMPA_vect)
00250 {
00251 //    TIMSK2 &= ~(1 << OCIE2A);       //Just one interrupt needed for waking
00252 }
00253 #endif /* !AVR_CONF_USE32KCRYSTAL */
00254 
00255 #if DEBUGSERIAL
00256 u8_t serialcount;
00257 char dbuf[30];
00258 #endif 
00259 
00260 /*---------------------------------------------------------------------------*/
00261 static u8_t
00262 raven_gui_loop(process_event_t ev, process_data_t data)
00263 {
00264     uint8_t i,activeconnections,radio_state;
00265     
00266 // PRINTF("\nevent %d ",ev);
00267 #if DEBUGSERIAL
00268     printf_P(PSTR("Buffer [%d]="),serialcount);
00269     serialcount=0;
00270     for (i=0;i<30;i++) {
00271        printf_P(PSTR(" %d"),dbuf[i]);
00272        dbuf[i]=0;
00273     }
00274 #endif
00275     if(ev == tcpip_icmp6_event) switch(*((uint8_t *)data)) {
00276 
00277 //   case ICMP6_NS:
00278         /*Tell the 3290 we are being solicited. */
00279 //       send_frame(REPORT_NS,...);
00280 //       break;  //fall through for beep
00281 //   case ICMP6_RA:
00282         /*Tell the 3290 we have a router. */
00283 //       send_frame(REPORT_NA,...);
00284 //       break;  //fall through for beep
00285     case ICMP6_ECHO_REQUEST:
00286         /* We have received a ping request over the air. Tell the 3290 */
00287         send_frame(REPORT_PING_BEEP, 0, 0);
00288         break;
00289     case ICMP6_ECHO_REPLY:
00290         /* We have received a ping reply over the air.  Send frame back to 3290 */
00291         send_frame(REPORT_PING, 1, &seqno);
00292         break;
00293 
00294     } else switch (ev) {
00295      case SERIAL_CMD:        
00296         /* Check for command from serial port, execute it. */
00297         /* Note cmd frame is written in an interrupt - delays here can cause overwriting by next command */
00298         PRINTF("\nCommand %d length %d done %d",cmd.cmd,cmd.len,cmd.done);
00299         if (cmd.done){
00300             /* Execute the waiting command */
00301             switch (cmd.cmd){
00302             case SEND_PING:
00303                 /* Send ping request over the air */
00304                 seqno = cmd.frame[0];
00305                 raven_ping6();
00306                 break;
00307             case SEND_TEMP:
00308 #if WEBSERVER
00309                 /* Set temperature string in web server */
00310                 web_set_temp((char *)cmd.frame);
00311 #endif
00312                 break;
00313             case SEND_ADC2:
00314 #if WEBSERVER
00315                 /* Set ext voltage string in web server */
00316                 web_set_voltage((char *)cmd.frame);
00317 #endif
00318                 break;
00319             case SEND_SLEEP:
00320                 /* Sleep radio and 1284p. */
00321                 if (cmd.frame[0]==0) {  //Time to sleep in seconds
00322                 /* Unconditional sleep. Don't wake until a serial interrupt. */
00323                 } else {
00324                 /* Sleep specified number of seconds (3290p "DOZE" mode) */
00325                 /* It sleeps a bit longer so we will be always be awake for the next sleep command. */
00326 #if UIP_CONF_TCP
00327                 /* Only sleep this cycle if no active TCP/IP connections, for fast browser responsed */
00328                    activeconnections=0;
00329                    for(i = 0; i < UIP_CONNS; ++i) {
00330                       if((uip_conns[i].tcpstateflags & UIP_TS_MASK) != UIP_CLOSED) activeconnections++;
00331                    }
00332                    if (activeconnections) {
00333                      PRINTF("\nWaiting for %d connections",activeconnections);
00334                      break;
00335                    }
00336 #endif
00337                 }
00338                 radio_state = NETSTACK_RADIO.off();
00339                 PRINTF ("\nsleep %d radio state %d...",cmd.frame[0],radio_state);
00340 
00341                 /*Sleep for specified time*/
00342                 PRINTF("\nSleeping...");
00343                 micro_sleep(cmd.frame[0]);
00344 
00345                 radio_state = NETSTACK_RADIO.on();
00346                 if (radio_state > 0) {
00347                    PRINTF("Awake!");
00348                 } else {
00349                     PRINTF("Radio wake error %d\n",radio_state);
00350                 }
00351                 break;
00352             case SEND_WAKE:
00353                /* 3290p requests return message showing awake status */
00354                 send_frame(REPORT_WAKE, 0, 0);
00355                 break;
00356             default:
00357                 break;
00358             }
00359             /* Reset command done flag. */
00360             cmd.done = 0;
00361         }
00362         break;
00363     default:
00364         break;
00365     }
00366     return 0;
00367 }
00368 
00369 /*---------------------------------------------------------------------------*/
00370 /* Process an input character from serial port.  
00371  *  ** This is called from an ISR!!
00372 */
00373 
00374 int raven_lcd_serial_input(unsigned char ch)
00375 {
00376     /* Tell sleep routine if a  reception occurred */
00377     /* Random nulls occur for some reason, so ignore those */
00378     if (ch) serial_char_received++;
00379 #if DEBUGSERIAL
00380     if (serialcount<25) dbuf[serialcount]=ch;
00381     serialcount++;
00382 #endif
00383     /* Don't overwrite an unprocessed command */
00384 //    if (cmd.done) return 0;
00385     
00386     /* Parse frame,  */
00387     switch (cmd.ndx){
00388     case 0:
00389         /* first byte, must be 0x01 */
00390         if (ch == 0x01){
00391 //            cmd.done = false;
00392         } else {
00393 #if DEBUGSERIAL
00394             dbuf[25]++;
00395 #endif
00396             return 0;
00397         }
00398         break;
00399     case 1: 
00400         /* Second byte, length of payload */
00401         cmd.len = ch;
00402         break;
00403     case 2:
00404         /* Third byte, command byte */
00405         cmd.cmd = ch;
00406         break;
00407     default:
00408         /* Payload and ETX */
00409         if (cmd.ndx >= (MAX_CMD_LEN+3)) {  //buffer overflow!
00410             cmd.ndx=0;
00411 #if DEBUGSERIAL
00412             dbuf[26]++;
00413 #endif
00414             return 0;
00415         }
00416         if (cmd.ndx >= cmd.len+3){
00417             /* all done, check ETX */
00418             if (ch == 0x04){
00419                 cmd.done = 1;
00420 #if DEBUGSERIAL
00421                 dbuf[27]++;
00422 #endif
00423                 process_post(&raven_lcd_process, SERIAL_CMD, 0);
00424             } else {
00425                 /* Failed ETX */
00426 #if DEBUGSERIAL
00427                 dbuf[28]++;
00428 #endif
00429             }
00430             cmd.ndx=0;             //set up for next command
00431             return 0;
00432         } else {
00433             /* Just grab and store payload */
00434             cmd.frame[cmd.ndx - 3] = ch;
00435         }
00436         break;
00437     }
00438     
00439     cmd.ndx++;
00440     return 0;
00441 }
00442 
00443 /*---------------------------------------------------------------------------*/
00444 void
00445 raven_lcd_show_text(char *text) {
00446     uint8_t textlen=strlen(text)+1;
00447     if (textlen > MAX_CMD_LEN) textlen=MAX_CMD_LEN;
00448     send_frame(REPORT_TEXT_MSG, textlen, (uint8_t *) text);
00449 }
00450 
00451 #if WEBSERVER
00452 static void
00453 lcd_show_servername(void) {
00454 
00455 //extern uint8_t mac_address[8];     //These are defined in httpd-fsdata.c via makefsdata.h 
00456 extern uint8_t server_name[16];
00457 //extern uint8_t domain_name[30];
00458 char buf[sizeof(server_name)+1];
00459     eeprom_read_block (buf,server_name, sizeof(server_name));
00460     buf[sizeof(server_name)]=0;
00461     raven_lcd_show_text(buf);  //must fit in all the buffers or it will be truncated!
00462 }
00463 #endif
00464 /*---------------------------------------------------------------------------*/
00465 PROCESS(raven_lcd_process, "Raven LCD interface process");
00466 PROCESS_THREAD(raven_lcd_process, ev, data)
00467 {
00468 
00469   PROCESS_BEGIN();
00470 
00471 #if WEBSERVER
00472   lcd_show_servername();
00473 #endif
00474 
00475   /* Get ICMP6 callbacks from uip6 stack, perform 3290p action on pings, responses, etc. */
00476   if(icmp6_new(NULL) == 0) {
00477   
00478     while(1) {
00479       PROCESS_YIELD();
00480 //      if (ev != ?)      //trap frequent strobes?
00481         raven_gui_loop(ev, data);
00482     } 
00483   }
00484   PROCESS_END();
00485 }
00486 
00487 /** @} */
00488 /** @} */

Generated on Mon Apr 11 14:23:41 2011 for Contiki 2.5 by  doxygen 1.6.1