Contiki 2.6
|
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 /** @} */