Contiki 2.5
raven-lcd.c
Go to the documentation of this file.
1 /* Copyright (c) 2008, Swedish Institute of Computer Science
2  * All rights reserved.
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * * Neither the name of the copyright holders nor the names of
16  * contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  * $Id: raven-lcd.c,v 1.11 2010/12/22 17:09:03 dak664 Exp $
32 */
33 
34 /**
35  * \brief This module contains code to interface a Contiki-based
36  * project on the AVR Raven platform's ATMega1284P chip to the LCD
37  * driver chip (ATMega3290P) on the Raven.
38  *
39  * \author Blake Leverett <bleverett@gmail.com>
40  *
41 */
42 
43 /** \addtogroup raven
44  * @{
45  */
46 
47 /**
48  * \defgroup ravenserial Serial interface between Raven processors
49  * @{
50  */
51 /**
52  * \file
53  * This file contains code to connect the two AVR Raven processors via a serial connection.
54  *
55  */
56 
57 #define DEBUG 0 //Making this 1 will slightly alter command timings
58 #if DEBUG
59 #define PRINTF(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
60 #else
61 #define PRINTF(...)
62 #endif
63 #define DEBUGSERIAL 0 //Making this 1 will significantly alter command timings
64 
65 #include "contiki.h"
66 #include "contiki-lib.h"
67 #include "contiki-net.h"
68 #include "webserver-nogui.h"
69 #include "httpd-cgi.h"
70 #include "raven-lcd.h"
71 
72 #include <string.h>
73 #include <stdio.h>
74 #include <avr/pgmspace.h>
75 #include <avr/eeprom.h>
76 #include <avr/sleep.h>
77 #include <dev/watchdog.h>
78 
79 static u8_t count = 0;
80 static u8_t seqno;
81 uip_ipaddr_t dest_addr;
82 
83 #define MAX_CMD_LEN 20
84 static struct{
85  u8_t frame[MAX_CMD_LEN];
86  u8_t ndx;
87  u8_t len;
88  u8_t cmd;
89  u8_t done;
90 } cmd;
91 
92 #define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
93 #define UIP_ICMP_BUF ((struct uip_icmp_hdr *)&uip_buf[uip_l2_l3_hdr_len])
94 #define PING6_DATALEN 16
95 
96 void rs232_send(uint8_t port, unsigned char c);
97 
98 /*---------------------------------------------------------------------------*/
99 /* Sends a ping packet out the radio */
100 /* Useful for debugging so allow external calls */
101 void
102 raven_ping6(void)
103 {
104 #if UIP_CONF_IPV6_RPL||1
105 /* No default router, so pick on someone else */
106 #define PING_GOOGLE 1
107 seqno++;
108 #endif
109 
110  UIP_IP_BUF->vtc = 0x60;
111  UIP_IP_BUF->tcflow = 1;
112  UIP_IP_BUF->flow = 0;
113  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
114  UIP_IP_BUF->ttl = uip_ds6_if.cur_hop_limit;
115 #if PING_GOOGLE
116  if (seqno==1) {
117  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, uip_ds6_defrt_choose()); //the default router
118  } else if (seqno==2) {
119  uip_ip6addr(&UIP_IP_BUF->destipaddr,0x2001,0x4860,0x800f,0x0000,0x0000,0x0000,0x0000,0x0093); //ipv6.google.com
120  } else if (seqno==3) {
121  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, uip_ds6_defrt_choose()); //the default router
122  } else {
123 // uip_ip6addr(&UIP_IP_BUF->destipaddr,0x2001,0x0420,0x5FFF,0x007D,0x02D0,0xB7FF,0xFE23,0xE6DB); //?.cisco.com
124  uip_ip6addr(&UIP_IP_BUF->destipaddr,0x2001,0x0420,0x0000,0x0010,0x0250,0x8bff,0xfee8,0xf800); //six.cisco.com
125  }
126 #else
127  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, uip_ds6_defrt_choose()); //the default router
128 #endif
129 
130  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
132  UIP_ICMP_BUF->icode = 0;
133  /* set identifier and sequence number to 0 */
134  memset((void *)UIP_ICMP_BUF + UIP_ICMPH_LEN, 0, 4);
135  /* put one byte of data */
136  memset((void *)UIP_ICMP_BUF + UIP_ICMPH_LEN + UIP_ICMP6_ECHO_REQUEST_LEN,
137  count, PING6_DATALEN);
138 
139 
140  uip_len = UIP_ICMPH_LEN + UIP_ICMP6_ECHO_REQUEST_LEN + UIP_IPH_LEN + PING6_DATALEN;
141  UIP_IP_BUF->len[0] = (u8_t)((uip_len - 40) >> 8);
142  UIP_IP_BUF->len[1] = (u8_t)((uip_len - 40) & 0x00FF);
143 
144  UIP_ICMP_BUF->icmpchksum = 0;
145  UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
146 
147 
148  tcpip_ipv6_output();
149 }
150 #if defined(__AVR_ATmega128RFA1__)
151 /* No raven 3290p to talk to but the lcd process still needed for ping reporting */
152 /* Just dummy out send_frame */
153 static void
154 send_frame(uint8_t cmd, uint8_t len, uint8_t *payload)
155 {
156 }
157 #else
158 /*---------------------------------------------------------------------------*/
159 /* Send a serial command frame to the ATMega3290 Processsor on Raven via serial port */
160 static void
161 send_frame(uint8_t cmd, uint8_t len, uint8_t *payload)
162 {
163  uint8_t i;
164 
165  rs232_send(0, SOF_CHAR); /* Start of Frame */
166  rs232_send(0, len);
167  rs232_send(0, cmd);
168  for (i=0;i<len;i++)
169  rs232_send(0,*payload++);
170  rs232_send(0, EOF_CHAR);
171 }
172 #endif
173 char serial_char_received;
174 /*---------------------------------------------------------------------------*/
175 /* Sleep for howlong seconds, or until UART interrupt if howlong==0.
176  * Uses TIMER2 with external 32768 Hz crystal to sleep in 1 second multiples.
177  * TIMER2 may have already been set up for 125 ticks/second in clock.c
178 
179  *
180  * Until someone figures out how to get UART to wake from powerdown,
181  * a three second powersave cycle is used with exit based on any character received.
182 
183  * The system clock is adjusted to reflect the sleep time.
184  */
185 
186 void micro_sleep(uint8_t howlong)
187 {
188  uint8_t saved_sreg = SREG, saved_howlong = howlong;
189 #if AVR_CONF_USE32KCRYSTAL
190 /* Save TIMER2 configuration if clock.c is using it */
191  uint8_t savedTCNT2=TCNT2, savedTCCR2A=TCCR2A, savedTCCR2B = TCCR2B, savedOCR2A = OCR2A;
192 #endif
193 
194 // if (howlong==0) {
195 // set_sleep_mode(SLEEP_MODE_PWR_DOWN); // UART can't wake from powerdown
196 // } else {
197  set_sleep_mode(SLEEP_MODE_PWR_SAVE); // Sleep for howlong seconds
198  if (howlong==0) howlong=3; // 3*32/(32768/1024) = 3 second sleep cycle if not specified
199  cli(); // Disable interrupts for the present
200  ASSR |= (1 << AS2); // Set TIMER2 asyncronous from external crystal
201  TCCR2A =(1<<WGM21); // CTC mode
202  TCCR2B =((1<<CS22)|(1<<CS21)|(1<<CS20));// Prescale by 1024 = 32 ticks/sec
203 // while(ASSR & (1 << TCR2BUB)); // Wait for TCNT2 write to finish.
204  OCR2A = howlong*32; // Set TIMER2 output compare register
205 // while(ASSR & (1 << OCR2AUB)); // Wait for OCR2 write to finish.
206  SREG = saved_sreg; // Restore interrupt state.
207 // UCSR(USART,B)&= ~(1<<RXCIE(USART)) // Disable the RX Complete interrupt;
208 // UCSR0B|=(1<<RXCIE0); // Enable UART0 RX complete interrupt
209 // UCSR1B|=(1<<RXCIE1); // Enable UART1 RX complete interrupt
210 // TCNT2 = 0; // Reset TIMER2 timer counter value.
211 // while(ASSR & (1 << TCN2UB)); // Wait for TCNT2 write to finish before entering sleep.
212 // TIMSK2 |= (1 << OCIE2A); // Enable TIMER2 output compare interrupt.
213 // }
214 
215  TCNT2 = 0; // Reset timer
216  watchdog_stop(); // Silence annoying distractions
217  while(ASSR & (1 << TCN2UB)); // Wait for TCNT2 write to (which assures TCCR2x and OCR2A are finished!)
218  TIMSK2 |= (1 << OCIE2A); // Enable TIMER2 output compare interrupt
219  SMCR |= (1 << SE); // Enable sleep mode.
220  while (1) {
221 // TCNT2 = 0; // Cleared automatically in CTC mode
222 // while(ASSR & (1 << TCN2UB)); // Wait for TCNT2 write to finish before entering sleep.
223  serial_char_received=0; // Set when chars received by UART
224  sleep_mode(); // Sleep
225 
226  /* Adjust clock.c for the time spent sleeping */
227  extern void clock_adjust_ticks(uint16_t howmany);
228  clock_adjust_ticks(howlong * CLOCK_SECOND);
229 
230 // if (TIMSK2&(1<<OCIE2A)) break; // Exit sleep if not awakened by TIMER2
231  PRINTF(".");
232  if (saved_howlong) break; // Exit sleep if nonzero time specified
233  PRINTF("%d",serial_char_received);
234  if (serial_char_received) break;
235  }
236 
237  SMCR &= ~(1 << SE); //Disable sleep mode after wakeup
238 
239 #if AVR_CONF_USE32KCRYSTAL
240 /* Restore clock.c configuration */
241 // OCRSetup();
242  cli();
243  TCCR2A = savedTCCR2A;
244  TCCR2B = savedTCCR2B;
245  OCR2A = savedOCR2A;
246  TCNT2 = savedTCNT2;
247  sei();
248 #else
249  TIMSK2 &= ~(1 << OCIE2A); //Disable TIMER2 interrupt
250 #endif
251 
252  watchdog_start();
253 }
254 #if !AVR_CONF_USE32KCRYSTAL
255 /*---------------------------------------------------------------------------*/
256 /* TIMER2 Interrupt service */
257 
258 ISR(TIMER2_COMPA_vect)
259 {
260 // TIMSK2 &= ~(1 << OCIE2A); //Just one interrupt needed for waking
261 }
262 #endif /* !AVR_CONF_USE32KCRYSTAL */
263 
264 #if DEBUGSERIAL
265 u8_t serialcount;
266 char dbuf[30];
267 #endif
268 
269 /*---------------------------------------------------------------------------*/
270 extern uint16_t ledtimer;
271 static u8_t
272 raven_gui_loop(process_event_t ev, process_data_t data)
273 {
274  uint8_t i,activeconnections,radio_state;
275 
276 // PRINTF("\nevent %d ",ev);
277 #if DEBUGSERIAL
278  printf_P(PSTR("Buffer [%d]="),serialcount);
279  serialcount=0;
280  for (i=0;i<30;i++) {
281  printf_P(PSTR(" %d"),dbuf[i]);
282  dbuf[i]=0;
283  }
284 #endif
285  if(ev == tcpip_icmp6_event) switch(*((uint8_t *)data)) {
286 
287 // case ICMP6_NS:
288  /*Tell the 3290 we are being solicited. */
289 // send_frame(REPORT_NS,...);
290 // break; //fall through for beep
291 // case ICMP6_RA:
292  /*Tell the 3290 we have a router. */
293 // send_frame(REPORT_NA,...);
294 // break; //fall through for beep
295  case ICMP6_ECHO_REQUEST:
296  /* We have received a ping request over the air. Tell the 3290 */
297  // send_frame(REPORT_PING_BEEP, 0, 0);
298 #if RF230BB_CONF_LEDONPORTE1
299  PORTE|=(1<<PE1);ledtimer=1000; //turn on led, set counter for turnoff
300 #endif
301  break;
302  case ICMP6_ECHO_REPLY:
303  /* We have received a ping reply over the air. Send frame back to 3290 */
304  // send_frame(REPORT_PING, 1, &seqno);
305  break;
306 
307  } else switch (ev) {
308  case SERIAL_CMD:
309  /* Check for command from serial port, execute it. */
310  /* Note cmd frame is written in an interrupt - delays here can cause overwriting by next command */
311  PRINTF("\nCommand %d length %d done %d",cmd.cmd,cmd.len,cmd.done);
312  if (cmd.done){
313  /* Execute the waiting command */
314  switch (cmd.cmd){
315  case SEND_PING:
316  /* Send ping request over the air */
317  seqno = cmd.frame[0];
318  raven_ping6();
319  break;
320  case SEND_TEMP:
321  /* Set temperature string in web server */
322  // web_set_temp((char *)cmd.frame);
323  break;
324  case SEND_ADC2:
325  /* Set ext voltage string in web server */
326  // web_set_voltage((char *)cmd.frame);
327  break;
328  case SEND_SLEEP:
329  /* Sleep radio and 1284p. */
330  if (cmd.frame[0]==0) { //Time to sleep in seconds
331  /* Unconditional sleep. Don't wake until a serial interrupt. */
332  } else {
333  /* Sleep specified number of seconds (3290p "DOZE" mode) */
334  /* It sleeps a bit longer so we will be always be awake for the next sleep command. */
335  /* Only sleep this cycle if no active TCP/IP connections */
336  activeconnections=0;
337  for(i = 0; i < UIP_CONNS; ++i) {
338  if((uip_conns[i].tcpstateflags & UIP_TS_MASK) != UIP_CLOSED) activeconnections++;
339  }
340  if (activeconnections) {
341  PRINTF("\nWaiting for %d connections",activeconnections);
342  break;
343  }
344  }
345  radio_state = NETSTACK_RADIO.off();
346  PRINTF ("\nsleep %d radio state %d...",cmd.frame[0],radio_state);
347 
348  /*Sleep for specified time*/
349  PRINTF("\nSleeping...");
350  micro_sleep(cmd.frame[0]);
351 
352  radio_state = NETSTACK_RADIO.on();
353  if (radio_state > 0) {
354  PRINTF("Awake!");
355  } else {
356  PRINTF("Radio wake error %d\n",radio_state);
357  }
358  break;
359  case SEND_WAKE:
360  /* 3290p requests return message showing awake status */
361  send_frame(REPORT_WAKE, 0, 0);
362  break;
363  default:
364  break;
365  }
366  /* Reset command done flag. */
367  cmd.done = 0;
368  }
369  break;
370  default:
371  break;
372  }
373  return 0;
374 }
375 
376 /*---------------------------------------------------------------------------*/
377 /* Process an input character from serial port.
378  * ** This is called from an ISR!!
379 */
380 
381 int raven_lcd_serial_input(unsigned char ch)
382 {
383  /* Tell sleep routine if a reception occurred */
384  /* Random nulls occur for some reason, so ignore those */
385  if (ch) serial_char_received++;
386 #if DEBUGSERIAL
387  if (serialcount<25) dbuf[serialcount]=ch;
388  serialcount++;
389 #endif
390  /* Don't overwrite an unprocessed command */
391 // if (cmd.done) return 0;
392 
393  /* Parse frame, */
394  switch (cmd.ndx){
395  case 0:
396  /* first byte, must be 0x01 */
397  if (ch == 0x01){
398 // cmd.done = false;
399  } else {
400 #if DEBUGSERIAL
401  dbuf[25]++;
402 #endif
403  return 0;
404  }
405  break;
406  case 1:
407  /* Second byte, length of payload */
408  cmd.len = ch;
409  break;
410  case 2:
411  /* Third byte, command byte */
412  cmd.cmd = ch;
413  break;
414  default:
415  /* Payload and ETX */
416  if (cmd.ndx >= (MAX_CMD_LEN+3)) { //buffer overflow!
417  cmd.ndx=0;
418 #if DEBUGSERIAL
419  dbuf[26]++;
420 #endif
421  return 0;
422  }
423  if (cmd.ndx >= cmd.len+3){
424  /* all done, check ETX */
425  if (ch == 0x04){
426  cmd.done = 1;
427 #if DEBUGSERIAL
428  dbuf[27]++;
429 #endif
430  process_post(&raven_lcd_process, SERIAL_CMD, 0);
431  } else {
432  /* Failed ETX */
433 #if DEBUGSERIAL
434  dbuf[28]++;
435 #endif
436  }
437  cmd.ndx=0; //set up for next command
438  return 0;
439  } else {
440  /* Just grab and store payload */
441  cmd.frame[cmd.ndx - 3] = ch;
442  }
443  break;
444  }
445 
446  cmd.ndx++;
447  return 0;
448 }
449 
450 /*---------------------------------------------------------------------------*/
451 void
452 raven_lcd_show_text(char *text) {
453  uint8_t textlen=strlen(text)+1;
454  if (textlen > MAX_CMD_LEN) textlen=MAX_CMD_LEN;
455  send_frame(REPORT_TEXT_MSG, textlen, (uint8_t *) text);
456 }
457 
458 #if WEBSERVER
459 static void
460 lcd_show_servername(void) {
461 
462 //extern uint8_t mac_address[8]; //These are defined in httpd-fsdata.c via makefsdata.h
463 extern uint8_t server_name[16];
464 //extern uint8_t domain_name[30];
465 char buf[sizeof(server_name)+1];
466  eeprom_read_block (buf,server_name, sizeof(server_name));
467  buf[sizeof(server_name)]=0;
468  raven_lcd_show_text(buf); //must fit in all the buffers or it will be truncated!
469 }
470 #endif
471 /*---------------------------------------------------------------------------*/
472 PROCESS(raven_lcd_process, "Raven LCD interface process");
473 PROCESS_THREAD(raven_lcd_process, ev, data)
474 {
475 
476  PROCESS_BEGIN();
477 
478 #if WEBSERVER
479  lcd_show_servername();
480 #endif
481 
482  /* Get ICMP6 callbacks from uip6 stack, perform 3290p action on pings, responses, etc. */
483  if(icmp6_new(NULL) == 0) {
484 
485  while(1) {
486  PROCESS_YIELD();
487 // if (ev != ?) //trap frequent strobes?
488  raven_gui_loop(ev, data);
489  }
490  }
491  PROCESS_END();
492 }
493 
494 /** @} */