ebus: Add command to read the reset flags.
authorWerner Koch <wk@gnupg.org>
Sun, 15 Jan 2012 20:09:26 +0000 (21:09 +0100)
committerWerner Koch <wk@gnupg.org>
Sun, 15 Jan 2012 20:09:26 +0000 (21:09 +0100)
ebus/README
ebus/ebus.h
ebus/hardware.c
ebus/housed.c
ebus/proto-busctl.h
ebus/shutter.c

index c315945..1e031ee 100644 (file)
@@ -16,6 +16,8 @@ Running an CSMA/CD protocol on the Elektor Bus.              -*- org -*-
     avrdude -c usbasp -pm88 -v -B 4 -U lfuse:w:0xFF:m
     avrdude -c usbasp -pm88 -v -B 4 -U hfuse:w:0xD7:m
 
+  (You may use 0xD4 for hfuse to enable the BOD at 4.3V)
+
   You also need to assign an different node-id to each node in the
   same collision domain by storing them in the EEPROM.  Due to
   restriction of RS485 protocol more than 32 nodes are not possible
index 54ac12b..add1474 100644 (file)
@@ -37,6 +37,7 @@ struct
   byte nodeid_hi;
   byte nodeid_lo;
   byte debug_flags;
+  byte reset_flags;
 } config;
 
 
index 99e37d1..ab8436c 100644 (file)
@@ -211,6 +211,9 @@ hardware_setup (byte nodetype)
   config.nodeid_hi = eeprom_read_byte (&ee_config.nodeid_hi);
   config.nodeid_lo = eeprom_read_byte (&ee_config.nodeid_lo);
   config.debug_flags = eeprom_read_byte (&ee_config.debug_flags);
+  /* Take a copy the reset flags and clear them.  */
+  config.reset_flags = MCUSR;
+  MCUSR &= ~(_BV(WDRF) | _BV(BORF) | _BV(EXTRF) | _BV(PORF));
 
   /* Lacking any better way to seed rand we use the node id.  A better
      way would be to use the low bit of an ADC to gather some entropy.
@@ -230,4 +233,5 @@ hardware_setup (byte nodetype)
       eeprom_write_byte (&ee_data.nodetype, nodetype);
     }
 
+
 }
index 0983e73..1fbfb36 100644 (file)
@@ -354,7 +354,11 @@ p_busctl_set_debug (byte *msg, size_t msglen)
 static void
 p_busctl_qry_debug (byte *msg, size_t msglen)
 {
-  logmsg_fmt ("flags=%02x", msg[6]);
+  logmsg_fmt ("dbg_flags=%02x rst_flags=%02x%s%s%s%s ", msg[6], msg[7],
+              (msg[7] & 0x08)? " WDRF":"",
+              (msg[7] & 0x04)? " BORF":"",
+              (msg[7] & 0x02)? " EXTRF":"",
+              (msg[7] & 0x01)? " PORF":"");
 }
 
 
index 32d6240..aa9ad10 100644 (file)
   Response format:
 
   byte 6     - Debug flags
-  byte 7..15 - rfu
+  byte 7     - Reset flags at startup.
+  byte 8..15 - rfu
 
 * 0x06 := Query Name
 
index d09ddef..199d0ae 100644 (file)
 enum __attribute__ ((packed)) action_values {
   action_none = 0,
   action_up,
-  action_down
+  action_down,
+  action_up_key,
+  action_down_key
 };
 
 enum __attribute__ ((packed)) motor_state_values {
   motor_state_off = 0,
   motor_state_pre_off,
   motor_state_pre_off2,
+  motor_state_pre_off3,
   motor_state_pre_up,
   motor_state_pre_up2,
   motor_state_up,
@@ -97,25 +100,45 @@ enum __attribute__ ((packed)) motor_state_values {
   motor_state_down_ready
 };
 
-/* Remember the last action time.  */
-static uint16_t schedule_last_tfound;
 
-/* The next action to do.  It's value is changed by the commands
-   and a ticker is switching the motor depending on these values.  */
-static volatile enum action_values next_action;
+\f
+/*
+   Communication between ISR and main loop.
+ */
+
+/* Event flag triggered (surprise) every second.  */
+static volatile byte one_second_event;
 
+/* Event flag trigger by key S2.  */
+static volatile byte key_s2_event;
 
-/* The shutter state byte.  This is directly used for the
-   QueryShutterState response message.  */
-static volatile byte shutter_state;
+/* Event flag trigger by key S3.  */
+static volatile byte key_s3_event;
 
-/* Event flag, triggerd (surprise) every 10 seconds.  */
-static volatile byte ten_seconds_event;
+/* The motor action delay counter and its event flag.  */
+static volatile uint16_t motor_action_delay;
+static volatile uint16_t motor_action_event;
 
-/* Sensor action delay counter and event flag.  */
+/* Sensor action delay counter and its event flag.  */
 static volatile uint16_t sensor_action_delay;
 static volatile byte sensor_action_event;
 
+
+\f
+/*
+   Global state of the main loop.
+ */
+
+/* Remember the last action time.  */
+static uint16_t schedule_last_tfound;
+
+/* The current state of the motor state machine.  */
+static enum motor_state_values motor_state;
+
+/* The shutter state byte.  This is directly used for the
+   QueryShutterState response message.  */
+static byte shutter_state;
+
 /* A structure to control the sensor actions.  This is not used by an
    ISR thus no need for volatile.  */
 static struct
@@ -144,172 +167,195 @@ static void init_eeprom (byte force);
 void
 ticker_bottom (unsigned int clock)
 {
-  static volatile enum action_values current_action;
-  static volatile uint16_t action_delay;
-  static volatile enum motor_state_values state = motor_state_off;
-  enum action_values save_action;
+  if (!(clock % 1000))
+    {
+      one_second_event = 1;
+      wakeup_main = 1;
+    }
 
-  /* Blink the activity LED if the motor is not in off state.  */
-  if (state != motor_state_off)
+  if (!key_s2_event && read_key_s2 ())
     {
-      if (!(clock % 1000))
-        {
-          if ((LED_Collision & _BV(LED_Collision_BIT)))
-            LED_Collision &= ~_BV (LED_Collision_BIT);
-          else
-            LED_Collision |= _BV(LED_Collision_BIT);
-        }
+      key_s2_event = 1;
+      wakeup_main = 1;
     }
 
-  /* Get next action.  */
-  save_action = current_action;
-  if (read_key_s2 ())
-    current_action = current_action != action_down ? action_down : action_none;
-  if (read_key_s3 ())
-    current_action = current_action != action_up? action_up : action_none;
-  if (current_action != save_action || next_action)
+  if (!key_s3_event && read_key_s3 ())
     {
-      if (next_action)
-        {
-          current_action = next_action;
-          next_action = 0;
-        }
-      switch (current_action)
-        {
-        case action_none: state = motor_state_pre_off; break;
-        case action_up:   state = motor_state_pre_up; break;
-        case action_down: state = motor_state_pre_down; break;
-        }
-      /* When a new action has been selected we need to limit the
-         action delay to the minimum value so that we don't stick in
-         the long down or up states.  We also need to make sure that
-         there is an action delay so that we enter the state
-         transition switch. */
-      if (!action_delay)
-        action_delay = 1;
-      else if (action_delay > 200)
-        action_delay = 200;
+      key_s3_event = 1;
+      wakeup_main = 1;
+    }
+
+  if (motor_action_delay && !--motor_action_delay)
+    {
+      motor_action_event = 1;
+      wakeup_main = 1;
+    }
+
+  if (sensor_action_delay && !--sensor_action_delay)
+    {
+      sensor_action_event = 1;
+      wakeup_main = 1;
+    }
+}
+
+
+static void
+send_dbgmsg (const char *string)
+{
+  byte msg[16];
+
+  if (config.debug_flags)
+    {
+      msg[0] = PROTOCOL_EBUS_DBGMSG;
+      msg[1] = config.nodeid_hi;
+      msg[2] = config.nodeid_lo;
+      memset (msg+3, 0, 13);
+      strncpy (msg+3, string, 13);
+      csma_send_message (msg, 16);
     }
+}
+
+
+/* static void */
+/* send_dbgmsg_fmt (const char *format, ...) */
+/* { */
+/*   va_list arg_ptr; */
+/*   char buffer[16]; */
+
+/*   va_start (arg_ptr, format); */
+/*   vsnprintf (buffer, 16, format, arg_ptr); */
+/*   va_end (arg_ptr); */
+/*   send_dbgmsg (buffer); */
+/* } */
+
+
+/* Trigger a new motor action.  */
+static void
+trigger_action (enum action_values action)
+{
+  static enum action_values last_action;
 
-  if (action_delay && !--action_delay)
+ again:
+  switch (action)
     {
-      switch (state)
+    case action_none: motor_state = motor_state_pre_off;  break;
+    case action_up:   motor_state = motor_state_pre_up;   break;
+    case action_down: motor_state = motor_state_pre_down; break;
+
+    case action_up_key:
+      action = last_action == action_up? action_none : action_up;
+      goto again;
+    case action_down_key:
+      action = last_action == action_down? action_none : action_down;
+      goto again;
+
+    default:
+      return;/* Avoid setting a new delay for unknown states.  */
+    }
+
+  last_action = action;
+
+  /* Now force a new transaction using the ticker interrupt.  We set
+     the delay to 1 ms so that the motor_action_event will be
+     triggered within the next millisecond.  There is no need to
+     disable interrupts; the worst what could happen is a double
+     triggered action and that action is anyway a motor off.  Using
+     this indirect method avoids a race between motor_action_delay and
+     motor_action_event.  */
+  motor_action_delay = 1;
+}
+
+
+/* The main state machine for the shutter motors.  The return value is
+   delay to be taken before the next call to this function.  */
+static uint16_t
+motor_action (void)
+{
+  uint16_t newdelay;
+
+  /* Perform the state transitions.  */
+  do
+    {
+      newdelay = 0;
+      switch (motor_state)
         {
         case motor_state_off:
-          LED_Collision &= ~_BV (LED_Collision_BIT); /* Clear LED.  */
-          /* Make sure the motor flags are cleared in the state info.  */
-          shutter_state &= 0b00111111;
           break;
 
-        label_pre_off:
         case motor_state_pre_off:
           MOTOR_on &= ~_BV(MOTOR_on_BIT);    /* Switch motor off. */
-          action_delay = 200; /*ms*/
-          state = motor_state_pre_off2;
+          newdelay = 200; /*ms*/
+          motor_state = motor_state_pre_off2;
           break;
         case motor_state_pre_off2:
           MOTOR_down &= ~_BV(MOTOR_down_BIT);/* Switch direction relay off. */
-          action_delay = 200; /*ms*/
-          state = motor_state_off;
+          newdelay = 200; /*ms*/
+          motor_state = motor_state_pre_off3;
+          break;
+        case motor_state_pre_off3:
+          LED_Collision &= ~_BV (LED_Collision_BIT); /* Clear LED.  */
+          /* Make sure the motor flags are cleared in the state info.  */
+          shutter_state &= 0b00111111;
+          motor_state = motor_state_off;
           break;
 
         case motor_state_pre_up:
           MOTOR_on &= ~_BV(MOTOR_on_BIT);    /* Switch motor off. */
-          action_delay = 200; /*ms*/
-          state = motor_state_pre_up2;
+          newdelay = 200; /*ms*/
+          motor_state = motor_state_pre_up2;
           break;
         case motor_state_pre_up2:
           MOTOR_down &= ~_BV(MOTOR_down_BIT);/* Switch direction relay off. */
-          action_delay = 200; /*ms*/
-          state = motor_state_up;
+          newdelay = 200; /*ms*/
+          motor_state = motor_state_up;
           break;
         case motor_state_up:
           MOTOR_on |= _BV(MOTOR_on_BIT);     /* Switch motor on. */
           shutter_state = 0b11000000;
           /*                |!------- Direction set to up
            *                +-------- Motor running.        */
-          action_delay = 25000; /*ms*/
-          state = motor_state_up_ready;
+          newdelay = 25000; /*ms*/
+          motor_state = motor_state_up_ready;
           break;
         case motor_state_up_ready:
           shutter_state = 0b00100000;
           /*                  | !~~!- Value: 0 = 0% closed
            *                  +------ State in bits 3..0 is valid.  */
-          goto label_pre_off;
+          motor_state = motor_state_pre_off;
+          break;
 
         case motor_state_pre_down:
           MOTOR_on &= ~_BV(MOTOR_on_BIT);    /* Switch motor off. */
-          action_delay = 200; /*ms*/
-          state = motor_state_pre_down2;
+          newdelay = 200; /*ms*/
+          motor_state = motor_state_pre_down2;
           break;
         case motor_state_pre_down2:
           MOTOR_down |= _BV(MOTOR_down_BIT); /* Switch direction relay on. */
-          action_delay = 200; /*ms*/
-          state = motor_state_down;
+          newdelay = 200; /*ms*/
+          motor_state = motor_state_down;
           break;
         case motor_state_down:
           MOTOR_on |= _BV(MOTOR_on_BIT);     /* Switch motor on. */
           shutter_state = 0b10000000;
           /*                |!------- Direction set to down
            *                +-------- Motor running.        */
-          action_delay = 25000; /*ms*/
-          state = motor_state_down_ready;
+          newdelay = 25000; /*ms*/
+          motor_state = motor_state_down_ready;
           break;
         case motor_state_down_ready:
           shutter_state = 0b00101111;
           /*                  | !~~!--- Value: 15 = 100% closed
            *                  +-------- State in bits 3..0 is valid.  */
-          goto label_pre_off;
+          motor_state = motor_state_pre_off;
+          break;
         }
     }
+  while (!newdelay && motor_state != motor_state_off);
 
-  if (!clock)
-    {
-      ten_seconds_event = 1;
-      wakeup_main = 1;
-    }
-
-  if (sensor_action_delay)
-    {
-      if (!--sensor_action_delay)
-        {
-          sensor_action_event = 1;
-          wakeup_main = 1;
-        }
-    }
+  return newdelay;
 }
 
 
-static void
-send_dbgmsg (const char *string)
-{
-  byte msg[16];
-
-  if (config.debug_flags)
-    {
-      msg[0] = PROTOCOL_EBUS_DBGMSG;
-      msg[1] = config.nodeid_hi;
-      msg[2] = config.nodeid_lo;
-      memset (msg+3, 0, 13);
-      strncpy (msg+3, string, 13);
-      csma_send_message (msg, 16);
-    }
-}
-
-
-/* static void */
-/* send_dbgmsg_fmt (const char *format, ...) */
-/* { */
-/*   va_list arg_ptr; */
-/*   char buffer[16]; */
-
-/*   va_start (arg_ptr, format); */
-/*   vsnprintf (buffer, 16, format, arg_ptr); */
-/*   va_end (arg_ptr); */
-/*   send_dbgmsg (buffer); */
-/* } */
-
-
 /* Process scheduled actions.  TIME is the current time.  If
    FORCED_TLOW is not 0 the scheduler will run the last action between
    FORCED_TLOW and TIME regardless on whether it has already been run.
@@ -364,14 +410,15 @@ process_schedule (uint16_t time, uint16_t forced_tlow)
       /* send_dbgmsg_fmt ("act=%u", tfound); */
       if (tfound == SCHEDULE_ACTION_UP)
         {
-          next_action = action_up;
           send_dbgmsg ("sch-act up");
+          trigger_action (action_up);
         }
       else if (tfound == SCHEDULE_ACTION_DOWN)
         {
-          next_action = action_down;
           send_dbgmsg ("sch-act dn");
+          trigger_action (action_down);
         }
+
     }
 }
 
@@ -410,13 +457,13 @@ process_shutter_cmd (byte *msg)
           err = 1;
         else if ((msg[8] & 0xc0) == 0xc0)
           {
-            next_action = action_up;
             send_dbgmsg ("bus-act up");
+            trigger_action (action_up);
           }
         else if ((msg[8] & 0xc0) == 0x80)
           {
-            next_action = action_down;
             send_dbgmsg ("bus-act dn");
+            trigger_action (action_down);
           }
         else
           err = 1;
@@ -737,7 +784,8 @@ process_ebus_busctl (byte *msg)
       msg[4] = config.nodeid_lo;
       msg[5] |= P_BUSCTL_RESPMASK;
       msg[6] = config.debug_flags;
-      memset (msg+7, 0, 9);
+      msg[7] = config.reset_flags;
+      memset (msg+8, 0, 8);
       csma_send_message (msg, MSGSIZE);
       break;
 
@@ -783,6 +831,7 @@ init_eeprom (byte force)
 int
 main (void)
 {
+  static int ten_seconds_counter;
   byte *msg;
 
   hardware_setup (NODETYPE_SHUTTER);
@@ -815,23 +864,64 @@ main (void)
         }
       wakeup_main = 0;
 
+      if (key_s2_event)
+        {
+          send_dbgmsg ("key-act down");
+          trigger_action (action_down_key);
+          key_s2_event = key_s3_event = 0;
+        }
+
+      if (key_s3_event)
+        {
+          send_dbgmsg ("key-act up");
+          trigger_action (action_up_key);
+          key_s3_event = 0;
+        }
+
+      if (motor_action_event)
+        {
+          motor_action_event = 0;
+          motor_action_delay = motor_action ();
+        }
+
       if (sensor_action_event)
         {
           sensor_action_event = 0;
           send_sensor_result ();
         }
 
-      if (ten_seconds_event)
+      if (one_second_event)
         {
-          uint16_t t;
+          one_second_event = 0;
+
+          /*
+             Code to run once a second.
+           */
+
+          /* Blink the activity LED if the motor is not in off state.  */
+          if (motor_state != motor_state_off)
+            {
+              if ((LED_Collision & _BV(LED_Collision_BIT)))
+                LED_Collision &= ~_BV (LED_Collision_BIT);
+              else
+                LED_Collision |= _BV(LED_Collision_BIT);
+            }
 
-          ten_seconds_event = 0;
 
-          t = get_current_time ();
-          if (!(t % 6))
+          if (++ten_seconds_counter == 10)
             {
-              /* Code to run every minute.  */
-              process_schedule (t, 0);
+              /*
+                 Code to run once every 10 seconds.
+               */
+              uint16_t t;
+
+              ten_seconds_counter = 0;
+              t = get_current_time ();
+              if (!(t % 6))
+                {
+                  /* Code to run every minute.  */
+                  process_schedule (t, 0);
+                }
             }
         }