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.
Our hardware actually looked like this, but the functionality was much the same.
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:
Post a Comment