{ public: Timer; ~Timer; int startunsigned int nMilliseconds, TimerType = OneShot; int waitfor; void cancel; TimerState state; TimerType type; unsigned int length; Mutex * pMu
Trang 1{
public:
Timer();
~Timer();
int start(unsigned int nMilliseconds, TimerType = OneShot);
int waitfor();
void cancel();
TimerState state;
TimerType type;
unsigned int length;
Mutex * pMutex;
unsigned int count;
Timer * pNext;
private:
static void interrupt Interrupt();
};
This pointer is initialized each time a software timer is created by the constructor And, thereafter, whenever a timer object is started, its mutex is taken as follows: /******************************************************************
****
*
* Method: start()
*
* Description: Start a software timer, based on the tick from the
* underlying hardware timer
*
* Notes: This version is ready for multitasking
*
* Returns: 0 on success, -1 if the timer is already in use
*
******************************************************************
****/
int
Timer::start(unsigned int nMilliseconds, TimerType timerType)
{
if (state != Idle)
{
return (-1);
Trang 2}
//
// Take the mutex It will be released when the timer expires
//
pMutex->take();
//
// Initialize the software timer
//
state = Active;
type = timerType;
length = nMilliseconds / MS_PER_TICK;
//
// Add this timer to the active timer list
//
timerList.insert(this);
return (0);
} /* start() */
By taking the mutex when the timer is started, we guarantee that no task (not even the one that started this timer) will be able to take it again until the same mutex is released And that won't happen until either the timer expires naturally (via the
interrupt service routine) or the timer is canceled manually (via the cancel
method) So the polling loop inside waitfor can be replaced with
pMutex->take(), as follows:
/******************************************************************
****
*
* Method: waitfor()
*
* Description: Wait for the software timer to finish
*
* Notes: This version is ready for multitasking
*
* Returns: 0 on success, -1 if the timer is not running
*
******************************************************************
****/
int
Timer::waitfor()
Trang 3{
if (state != Active)
{
return (-1);
}
//
// Wait for the timer to expire
//
pMutex->take();
//
// Restart or idle the timer, depending on its type
//
if (type == Periodic)
{
state = Active;
timerList.insert(this);
}
else
{
pMutex->release();
state = Idle;
}
return (0);
} /* waitfor() */
When the timer does eventually expire, the interrupt service routine will release the
mutex and the calling task will awake inside waitfor In the process of waking, the
mutex will already be taken for the next run of the timer The mutex need only be released if the timer is of type OneShot and, because of that, not automatically restarted
9.3 Printing "Hello, World!"
The other part of our example application is a task that prints the text string "Hello, World!" to one of the serial ports at a regular interval Again, the timer driver is used to create the periodicity However, this task also depends on a serial port driver that we haven't seen before The guts of the serial driver will be described in the final two sections of this chapter, but the task that uses it is shown here The only thing you need to know about serial ports to understand this task is that a
Trang 4SerialPort is a C++ class and that the puts method is used to print a string of
characters from that port
#include "timer.h"
#include "serial.h"
/******************************************************************
****
*
* Function: helloWorld()
*
* Description: Send a text message to the serial port periodically
*
* Notes: This outer loop is hardware-independent
*
* Returns: This routine contains an infinite loop
*
******************************************************************
****/
void
helloWorld(void)
{
Timer timer;
SerialPort serial(PORTA, 19200L);
timer.start(10000, Periodic); // Start a periodic 10 s timer
while (1)
{
serial.puts("Hello, World!"); // Output a simple text message
timer.waitfor(); // Wait for the timer to expire
}
} /* helloWorld() */
Though the periodicity has a different length, the general structure of this task is
the same as that of the flashRed function So, the only thing left for us to discuss is
the makeup of the serial port driver We'll start with a description of a generalized serial ports interface and then finish with the specifics of the serial controller found
on the Arcom board
9.4 Working with Serial Ports
Trang 5At the application level, a serial port is simply a bidirectional data channel This channel is usually terminated on each end with a hardware device called a serial communications controller (SCC) Each serial port within the SCC—there are usually at least two serial ports per controller—is connected to the embedded
processor on one side and to a cable (or the connector for one) on the other side At the other end of that cable there is usually a host computer (or some other
embedded system) that has an internal serial communications controller of its own
Of course, the actual purpose of the serial port is application-dependent But the general idea is this: to communicate streams of data between two intelligent
systems or between one such device (the target) and a human operator Typically, the smallest unit of data that can be sent or received over a serial port is an 8-bit character So streams of binary data need to be reorganized into bytes before
transmission This restriction is similar to that of C's stdio library, so it makes
sense to borrow some programming conventions from that interface
In order to support serial communications and emulate a stdio-style interface, I've
defined the SerialPort class as it is shown below This class abstracts the
application's use of the serial port as bidirectional data channel and makes the
interface as similar as possible to what we've all seen before In addition to the
constructor and destructor, the class includes four methods—putchar,[3] puts,
getchar, and gets —for sending characters and strings of characters and receiving the same These routines are defined exactly as they would be in any ANSI
C-compliant version of the header file stdio.h
Here's the actual class definition:
#include "circbuf.h"
#define PORTA 0
#define PORTB 1
class SerialPort
{
public:
SerialPort(int port,
unsigned long baudRate = 19200L,
unsigned int txQueueSize = 64, // Transmit Buffer Size
unsigned int rxQueueSize = 64); // Receive Buffer Size
~SerialPort();
int putchar(int c);
int puts(const char *s);