Thursday, October 21, 2010

Coming up for air

Well that was fun.

Today we had no lecture, instead the entire day was devoted to a team project.  Specifically we designed and implemented the software for a scuba diving computer.  Something that would look like this.

dive computer

Our hardware actually looked like this, but the functionality was much the same.

board

We worked in teams of 4 to develop the software required to do the following.

1 - Show ascent-rate, depth, oxygen remaining, elapsed dive time and time to surface on the display.

2 - Sound one of three alarms.

  • High priority alarm if there is not enough oxygen in the tanks to reach the surface.
  • Medium priority alarm if the rate of ascent is greater than 15 meters per minute (prevents getting the bends).
  • Low priority alarm if your depth is greater than 40 meters.

3 – Toggle the display between showing the elapsed time, and the time to surface every 2 seconds.

This is why I love being an engineer; you get to build really cool things.  As an aside our team was the only one to finish the project.  We finished it 5 minutes before the deadline.  Whew…

To complete the project our design used 8 tasks.

  • LED driver task
  • Pushbutton driver task
  • Speaker driver task
  • Analog to digital converter driver task
  • Calculation task
  • Display task
  • Alarm task
  • Watchdog monitoring task

As a point of interest I have included below some of the code that I worked on today which did the calculations for the displayed values.  Probably not the most elegant code since we were under a time constraint, but it shows the proper use of several forms of inter-task communication.  Enjoy.

/***********************************************************
DIVE COMPUTER CALCULATION TASK
***********************************************************/
void AppTaskCalc(void * pdata)
{
   INT16S         rate_of_ascent_in_mpm = 0;
   INT16U         elapsed_time_in_s = 0;
   INT32S         depth_in_mm = 0;
   INT32S         air_supply_in_cl = 0;
   OS_FLAGS       flags;
   INT8U          err;
   INT16U         sample;
   INT16U         tick_counter = 0;
   INT16U         air_to_surface_in_cl;
   INT32U         alarm_curr = GF_ALARM_OFF;
   INT8U          at_surface = 1;

   static display_data_t display_data = {0,0,0,0,0}; 

   while(1)
   {
      flags = OSFlagPend(gh_sys_flags, GF_TIME_CALC |
              GF_BUTTON_AIR, OS_FLAG_WAIT_SET_ANY |
              OS_FLAG_CONSUME, 0, &err);
      assert(OS_NO_ERR == err);   
     
      if (flags & GF_TIME_CALC)
      {
         tick_counter++;
         // Pend on the A2D mailbox
         sample = (*(INT16U *) OSMboxPend(ghMboxAdc,
                                          10, &err));
         assert(OS_NO_ERR == err || OS_TIMEOUT == err);
        
         // Calculate the rate of ascent
         rate_of_ascent_in_mpm = sample - 511;
         rate_of_ascent_in_mpm /= 13;
         depth_in_mm -= depth_change_in_mm(
                                  rate_of_ascent_in_mpm);
         if(depth_in_mm <= 0)
         {
            depth_in_mm = 0;
            at_surface = 1;
         }

         // Calculate the elapsed time if depth is not 0
         if (depth_in_mm != 0)
         {
            if (at_surface == 1)
            {
               at_surface = 0;
               elapsed_time_in_s = 0;
            }
            if(tick_counter & 0x01)
            {
               // Every other .5s tick increment
               //   the elapsed time
               elapsed_time_in_s++;
            }
         }

         if(at_surface == 0)
         {
            air_supply_in_cl -= gas_rate_in_cl(
                                   depth_in_mm / 1000);
            if(air_supply_in_cl < 0)
            {
               air_supply_in_cl = 0;
            }
         }
         air_to_surface_in_cl = gas_to_surface_in_l(
                                   depth_in_mm / 1000);
         // Update display_data struct
         if(at_surface == 1)
         {
            display_data.rate_of_ascent_in_mpm = 0;
         }
         else
         {
            display_data.rate_of_ascent_in_mpm =
                                      rate_of_ascent_in_mpm;
         }
         display_data.depth_in_m = depth_in_mm / 1000;
         display_data.air_supply_in_cl = air_supply_in_cl;
         display_data.elapsed_time_in_s = elapsed_time_in_s;
        
         // Maximum ascent rate is 0.25m / s
         display_data.time_to_surface_in_s =
                          display_data.depth_in_m * 4;

         err = OSMboxPost(ghMboxCalc, &display_data);
         assert(OS_NO_ERR == err);
        
         OSFlagPost(gh_sys_flags, GF_MBOX_DISP,
                                        OS_FLAG_SET, &err);
         assert(OS_NO_ERR == err);
        
         // Set current alarm state.
         if(at_surface == 1)
         {
            alarm_curr = GF_ALARM_OFF;
         }
         else if (air_supply_in_cl < air_to_surface_in_cl)
         {
            alarm_curr = GF_ALARM_HI;
         }
         else if (15 < rate_of_ascent_in_mpm)
         {
            alarm_curr = GF_ALARM_MID;
         }
         else if (40 < (depth_in_mm / 1000))
         {
            alarm_curr = GF_ALARM_LOW;
         }
         else
         {
            alarm_curr = GF_ALARM_OFF;
         }

         // Signal change of speaker priority to the alarm
         OSFlagPost(gh_sys_flags, alarm_curr,
                                      OS_FLAG_SET, &err);
         assert(OS_NO_ERR == err);
      }
     
      if (flags & GF_BUTTON_AIR)
      {
         // Increment the air count at 5L per semaphore
         do
         {
            // Wait for a signal from the button driver
            OSSemPend(ghSemB1, 10, &err);
            assert(OS_NO_ERR == err || OS_TIMEOUT == err);
            if ((OS_NO_ERR == err) && (at_surface == 1))
            {
               if (air_supply_in_cl <= 199500)
               {
                  air_supply_in_cl += 500;
               }
               else if (air_supply_in_cl < 200000)
               {
                  air_supply_in_cl = 200000;
               }
            }
         }while (OS_NO_ERR == err);
      }
      // tell watchdog we are alive
      OSFlagPost(gh_sys_flags, GF_ALIVE_CALC,
                                       OS_FLAG_SET, &err);
      assert(OS_NO_ERR == err);     
   }
}

No comments: