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