📄 beep.c
字号:
#include "includes.h"
/*
* Beeper management task. This is included as a case study in the
* construction of GPS Builder-2 queue server tasks. It provides an
* example framework which can be used by the GPS software designer to
* create more meaningful time-critical tasks.
*
* Let's begin with the problem to be solved. On occasion, the GPS
* designer will want to emit a beep of a specified frequency and
* duration. This may signal a warning, for instance. Beeps are
* generated in a single-tasking environment as follows:
*
* ...
* sound(440); Begin generating a 440 Hz tone
* delay(100); Busy-wait 100 milliseconds
* nosound(); Turn off the tone
* ...
*
* In a multitasking environment, however, you rarely want to busy-wait
* for more than about 10 microseconds. Instead, you want to suspend
* for a specified time interval, yielding the processor to all the
* other tasks that are ready to execute. One way to do this would be:
*
* ...
* sound(440); Start a 440 Hz tone
* Suspend(1); Suspend for 1 TIC (about 100 ms)
* nosound(); Wake up and turn off the tone
* ... Continue executing
*
* This is perfectly adequate for some multitasking applications, but
* there are some very important limitations:
*
* 1) If the beeping task has a low priority, the suspend
* interval may vary somewhat when higher priority tasks
* preempt the processor. Result: beeps of irregular
* duration.
*
* 2) Beep starts and stops are not synchronized with any
* sort of time reference.
*
* 3) Two concurrent tasks may try to issue overlapping beeps.
* Since hardware control library routines are seldom if
* ever reentrant, the sound() and nosound() routines may
* get confused by concurrent callers.
*
* 4) You may not want to waste time suspending while the beep
* occurs. You may instead want to continue executing in
* order to accomplish some time-critical processing.
*
* There are a number of methods to solve these problem. Normally you
* will select the simplest method that does the job. You can make
* rules about which tasks are allowed to beep and when. Or you can
* create some sort of interlock/semaphore system so that beeping tasks
* either wait for access to the PC speaker or decide not to beep at
* all.
*
* But the fully general way to solve the problems is to implement a
* "queue server". This means that there is a single task which
* generates beeps according to requests which are placed into a queue
* by other tasks. Here's how it works.
*
* You create a moderately high priority task called TBeep. The
* priority should be high because 1) it doesn't require much processor
* time, and 2) it needs to respond in real time. Whenever TBeep
* activates, it checks a queue which may (or may not) contain beep
* requests expressed as frequency & duration. If there are no
* requests, TBeep simply suspends (goes to sleep) for some long time
* interval.
*
* If TBeep finds a beep request in the queue, it removes the request
* and initiates the beep. It will then suspend awaiting the end of the
* beep. Upon reactivation it can process out more requests. When the
* request queue is empty, TBeep can go back to sleep.
* Since the human ear is very sensitive to the duration of short beeps,
* we approach beeping as a time-critical task. The only routine
* in GPS Builder which executes on a regular fast cycle is GPISR, the
* GP2021 interrupt handler. So we have put some logic in GPISR to start
* and stop beeps. We make the judgement that beeping is a lower
* priority activity than servicing the correlator, so the beeping logic
* is placed near the end of the GPISR interrupt routine (rather than
* at the beginning).
*
* The grand strategy is to have the queue server TBeep take beep requests
* from many possible concurrent sources and feed them serially to a
* closely timed beep starter/stopper in the GPISR interrupt routine.
*
* Programming this can be a little bit tricky. You need to arrange
* things so that requestor tasks can add to the request queue in a way
* that doesn't interfere with TBeep's taking out requests concurrently.
* There are numerous ways for such interferences to occur. Understanding
* and preventing such interferences is the basic skill of the real-time
* programmer.
*
* The easiest way to prevent interference between tasks is to write
* "reentrant" routines. Such routines can be called concurrently by
* several callers. At one level of sophistication, you can make a
* procedure reentrant by following three simple rules:
*
* 1) Use only local variables in procedures.
*
* 2) Don't interface with hardware.
*
* 3) Don't call any non-reentrant subroutines.
*
* When you need to use non-local variables, or interface with hardware,
* or call some non-reentrant subroutine, you may need to temporarily
* suppress task switching. (Sometimes it is possible to assure that
* interference between concurrent tasks will not occur or will not matter,
* but as a general rule it is safer to program conservatively.)
*
* In GPS Builde-2, task switching is suppressed by performing a
* PROTECT++ just before you start to do something non-reentrant. When
* you are reentrant again, you do PROTECT-- to cancel the protection.
*
* Even when you are PROTECTed, you must take care to access and update
* shared data structures indivisibly. If you are reading a satellite's
* ephemeris data, for example, you want to PROTECT++ yourself while you
* are doing it. This keeps some other task from updating the entry
* while you are using it, a condition that will often crashes. Since
* you shouldn't suppress task switching very long, it's sometimes a good
* idea to make your own local copy of a shared data structure while you
* are PROTECTed, then to munch on the data at your leisure in the normal
* unprotected mode.
*
* As a final note, you will often want to polish up a queue server
* task by providing a companion request-making subroutine. Such a
* routine can handle the details of reentrancy, interlocks, protection,
* indivisible data structure accesses, and intertask communication.
* Procedure Beep() below is a request-making subroutine.
*
* See routine cmdproc in module MAIN. The WN nnnn command initiates
* a series of beeps through this queue server.
*
* The following global variables are shared with GPISR. The interprocess
* communication protocol is as follows:
*
* 1) BeepFrq and BeepDur both have default initializations of zero.
*
* 2) If BeepDur is zero, GPISR is ready to start the next beep.
* TBeep first sets BeepFrq and then BeepDur to the desired values.
*
* 3) At the next 505-us correlator interrupt, GPISR will see a
* nonzero value in BeepDur. If BeepFrq is nonzero, GPISR will
* start a beep and then zero BeepFrq. (If BeepFrq is zero, GPISR
* knows that it has already started a beep. In that case it will
* simply downcount BeepDur to zero, then shut off the beep.)
*
* 4) TBeep will reactivate every TIC during the beep and monitor
* BeepDur. When it has been downcounted to zero, TBeep is then
* ready to service another beep request.
*/
volatile unsigned BeepFrq,BeepDur; /* Borland inits global vars to zero. */
#define BeepQEntries 5 /* Define how many beeps can be enqueued. */
typedef struct /* Define a beep queue entry. */
{
unsigned Frq; /* Frequency in Hz. */
unsigned Dur; /* Duration in milliseconds. */
} BeepQEntry;
/*
* The actual beep request queue. NextInBQ is the index of the next
* entry available for making a beep request. CurrOutBQ is the index
* of the next beep request which is to be processed out. NInBQ is the
* number of requests pending in the beep request queue. Since these
* variables are being accessed by concurrent tasks, care must be taken
* to avoid interprocess interference.
*/
BeepQEntry BeepQ[BeepQEntries];
volatile unsigned NextInBQ,CurrOutBQ,NInBQ;
/****************************************************************************
* Function: void TBeep(void)
*
* Take beep requests out of the beep request queue, initiate beeps by
* communicating with the correlator ISR, then suspend awaiting more beep
* requests.
*
* Input: None.
*
* Output: None.
*
* Return Value: None.
****************************************************************************/
void TBeep(void)
{
unsigned FreqHz,DurMS;
char buff[16];
/* Do forever: take beep requests out of the request queue, generate
beeps, then go to sleep for a while. The request-making subroutine
will activate us whenever new requests are placed in the queue. */
while(TRUE)
{
while(NInBQ)
{
/* If a beep is in progress, wait for the beep to complete.
(Don't busy-wait. Instead, suspend for 1/10 second intervals
between status checks.) */
while(BeepDur)
Suspend(1);
PROTECT++;
FreqHz = BeepQ[CurrOutBQ].Frq;
DurMS = BeepQ[CurrOutBQ].Dur;
CurrOutBQ++;
if(CurrOutBQ >= BeepQEntries)
CurrOutBQ = 0;
NInBQ--;
PROTECT--;
/* Initiate the beep by communicating with GPISR. Translate the
beep duration into 505-us counts (also avoid arithmetic
problems by saying that 1 ms is approximately 2 505-us
intervals). */
BeepFrq = FreqHz;
BeepDur = DurMS*2;
sprintf(buff,"%5uHz/%4ums",FreqHz,DurMS);
TCBComment (buff);
/* Wait for the beep to complete. Again, don't busy-wait. */
while(BeepDur)
Suspend(1);
}
/* Suspend for a longer interval awaiting more beep requests. The
grizzled real-time veteran will skeptically inquire about what
happens if a higher-priority task executes and activates TBeep
between the time TBeep decides to suspend and when the suspend
is finally accomplished. In GPS Builder, activations are
cumulative and may be pending. So if an activation happens at
this point, the Suspend routine will see the pending reactivation
and continue TBeep instead of actually suspending. */
Suspend(300); /* Suspend 30 sec, or until reactivated by Beep(). */
}
/* Note: GPS Builder tasks must never attempt to terminate. */
}
/****************************************************************************
* Function: void Beep(unsigned FreqHz,unsigned DurMS)
*
* This is TBeep's request-generating routine. The application task calls it
* with a specified beep frequency and duration. After the beep request is
* enqueued, we return to the caller.
*
* Input: FreqHz - Beep frequency, Hz.
* DurMS - Beep duration, in milliseconds.
*
* Output: None.
*
* Return Value: None.
****************************************************************************/
void Beep(unsigned FreqHz,unsigned DurMS)
{
/* Ignore requests in excess of the beep request queue capacity. */
if(NInBQ >= BeepQEntries)
return;
PROTECT++; /* To indivisibly access the beep request queue. */
BeepQ[NextInBQ].Frq = FreqHz;
BeepQ[NextInBQ].Dur = DurMS;
NextInBQ++;
if(NextInBQ >= BeepQEntries)
NextInBQ = 0;
NInBQ++;
PROTECT--;
Activate("TBeep");
return;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -