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

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