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