📄 chapter6.htm
字号:
measurement and the second t3 is a pointer to a Timer that interrupts each millisecond.
Within get_baud() an instance of a Timer1 will be created. The input capture associated
with this Timer1 is assumed to be connected to the serial line. The serial data format is
shown below. The default serial definition is usually n, meaning no parity, 8, meaning
eight data bits, and 1, meaning 1 stop bit. The serial line in its idle condition is high.
The first bit that transmitted is called the start bit and it is labled S in the figure below.
There follows a series of eight data bits each labled 0, 1,...7. These data are transmitted
least significant bit first. After the last data bit there is one additional bit that is held high
for one bit time. This bit is also labled S and it is called the stop bit. There can be either
1 or 2 stop bits depending upon the system set-up.
<p> The beginning of the stop bit is sensed when the input falls from an idle or high
state to a low state. After the eighth bit, the signal is set to high for at least one bit time
corresponding to the stop bit. In high-speed transmission, a new start bit can occur
immediately after completion of the stop bit.
<p> A character is surrounded by start and stop bits. The beginning of the character
can be sensed by the high-to-low transion that marks the beginning of the start bit. The
end of the character can be sensed by a low-to-high transition that signals the beginning
of the stop bit. In total, the time from the first falling edge to the beginning of the stop
bit is nine bit times. Of course, the data bits will have changing values, but it is the last
transition that is necessary to be seen. All printable characters and standard control
characters that can be sent from the keyboard have a value of zero for bit seven.
Therefore, the transition from bit seven to the stop bit will always be a low-to-high
transition and it will always be seen.
<p><img align=middle src="fig6-1.jpgf">
<h4> Figure 6.1 Serial Data Character Format</h4>
<p> Let us now examine the operation of the input capture timer and the MCCI
subsystems. In the MCCI, the baud rate is set by placing a thirteen bit value in
<a href="appendxb.htm#sccr0a">SCCR0A</a>.<a href=appendxb.htm#sccr0>BR</a> field.
This value is found by the equation
<pre><code>
BR = fs / 32 Baud (1)
</code></pre><p>
where BR is the bit field value and Baud is the baud rate in bits per second. The value fs
is the system clock frequency. With the input capture subsystem, the total time for a
single character can be measured. This time is actually the time for nine bits. The time
is expressed in counts of the system clock that has been prescaled. The default prescale
value that is used in this example is 4. Therefore, the measured time is
<pre><code>
Baud = (9/Count) ( fs / 4 )
</code></pre><p>
When this value is substituted into the first expression and cleaned up it is found that
<pre><code>
BR = Count / 72
</code></pre><p>
which is somewhat surprisingly independent of the system clock frequency.
<p> Our code must measure the count time from the beginning of the start bit to the
beginning of the stop bit and divide this value by 72 to find the value needed to be placed
in the <a href="appendxb.htm#sccr0a">SCCR0A</a>.<a href=appendxb.htm#sccr0>BR</a> field. The code below will accomplish this task. The first task is to
instantiate a semaphore that will be used with a delay. Then input capture timer is
instantiated. This timer will be the number specified by the calling program, its event
will be a falling edge, and no interrupt will be used. The program is then stalled until an
event occurs by the statement
<pre><code>
while(!get_status(t))
; /* wait until an event occurs */
</code></pre><p>
This statement will hang on itself until a falling edge is sensed. At that time control is
returned to the next statement in which the measured start time is saved. The edge
sensitivity of the Timer1 is then changed to rising to sense the transition to the stop bit
and an eleven millisecond delay is implemented. Eleven milliseconds was chosen to be
longer than one would expect with a 1200 baud system. The program then waits until
delay releases the specified semaphore. At that time, the value stored in the output
compare register should be the time, relative to the TCNT register, of the beginning of
the stop bit. The difference between the time of the start bit and this measured time is
saved and the instance of the Timer1 and the semaphore are deleted. The measured
value is then divided by 72 and returned to the calling program. At high baud rates, the
value returned becomes relatively small. When the calculated value by the division is
always rounded down, a noticable error can occur, so the code that calculates the return
value rounds the result to the nearest integer value.
<pre><code>
/* This routine returns the value to be placed in SCCR0A.BR to
make the baud rate match that measured by an input capture.
The measured value may not match the book value exactly. */
WORD get_baud(BYTE number, Timer *t3)
{
WORD start_time;
Delay *d;
Semaphore *s;
Timer1 *t;
s=semaphore_();
t=timer1_(number, /* Build input capture--number */
FALLING, /* capture falling edge */
NO); /* no interrupt */
while(!get_status(t))
; /* wait until an event occurs */
start_time=capture_time(t); /* get event time immediately */
new_edge(t,RISING); /* set the sensitivity to rising edge */
d=delay_(t3,s,11); /* t3 must be 1 ms timer to delay 11 ms */
wait_for_semaphore(s); /* wait out the delay */
start_time=capture_time(t)-start_time;
timer1__(t); /* get rid of this timer */
semaphore__(s); /* don't need semaphore any more */
return start_time%72>36?start_time/72+1: start_time/72;
}
<h4> Listing 6.5 get_baud() Bit Rate Measuring Function</h4>
</code></pre><p>
The above function uses several software components. The semaphore, delay,
timer and finally timer1 components are all used together to measure the bit rate of the
serial port. These components all work together smoothly and you should expect no bad
interaction between these components because they have each been encapsulated as
tightly as the C language will allow.
<p> The final portion of the program is to build a program that calls the get_baud()
function to determine the speed of the serial port. This program is shown below in
Listing 6.6. The function listed above is to be compiled with this program, so all of the
necessary header files are #included here. Recall that in the case of the serial
input/output subsystem components, the various interrupt parameters are specified by the
calling program rather than being specifiec within the componet itself. Either approach
is useful. All of the necessray parameters are defined by the calling program for the
serial input/output as shown below. This way, the programmer has complete flexibility
to choose these parameters when the system is designed. The approach used with the
Timer and with the Timer1 components was to specify these similar parameters within
the class itself, and then the programmer did not need to have any worry about the choice
of these values.
<p> Is one approach better than the other? Probably, but it depends more upon the
shop standards set up by the orginization where the coding is being done. Many groups
will deplore the arbitrary choice of things like interrupt vectors and levels by the code.
This approach takes away the flexibility of the programmer to do his job. On the other
hand, many programmers really dislike the need to get into the nature of the
microcontroller being programmed. We can hide this nature inside of the components
that are developed for the system as was done with the timer.
<pre><code>
#include <HC16Y1.H>
#include <scim.h>
#include <mcci.h>
#include <timer.h>
#include <timer1.h>
#include <delay.h>
#include <serio1.h>
#include <semaphor.h>
#include <defines.h>
/* serial port parameters */
#define SERIO_BAUD_19200 19200
#define SERIO_INT_VECTOR 0x40
#define SERIO_IARB 10
#define SERIO_INT_LEVEL 3
#define TIMECOUNT 4000
/* function prototypes */
void printd(unsigned long);
WORD get_baud(BYTE number, Timer *t3);
</code></pre><p>
While discussing philosophy there is another styling thing that is shown in the
following code. A parameter block has been used to pass data to the Timer constructor.
This block is a structure and you will note that it is filled with tp.tick=xxx; type
statements. In this case we use the structure name. A little further down, a similar
parameter block that is called an io_command_block is filled for use with the serial1
component. In that case, the pointer assignment mechanism, oob1->call_complete=xxx;
was used. In both cases, pointers to the parameter blocks were sent to the object when
needed. Which approach is better? Here is another case where both approaches work
equally well, there is no clear winner in regard to code space or execution speed, and
both are equally easy to read and understand. Within a programming team or a group
standard, one and only one of these approaches should be chosen and used. Regardless
of the approach, the idea of object oriented programming is to make the interface
between the various components uniform and easy to follow. The coding approach
shown below works well, but it is in no way a uniform approach to the problem.
<pre><code>
void main(void)
{
Timer *t3;
Timer_parameters tp;
Serial_io *sio;
io_command_block *oob1,ob1;
BYTE pszMessage[]="The selected baud rate is ";
BYTE pszMessage1[]=" bits per second\n\r";
WORD baud;
unsigned long baud_rate;
/* initialize the program */
/* initialize the SIM registers */
SYNCR.X=ON; /* double the clock speed--16.00 mHz */
SYPCR.SWE=OFF; /* disable the watchdog */
CSORBT.DSACK=0; /* use zero wait states for now */
cli(); /* enable system interrupts */
/* set up the timer parameter structure and instantiate a timer */
tp.number=3; /* use output compare number 3 */
tp.tick=TIMECOUNT; /* 1 ms interrupts */
tp.action=0; /* no output action */
tp.OC1_connect=0; /* OC1 not connected to another OC */
tp.connect_data=0; /* therefore no connect data needed */
tp.interrupt=YES; /* need an interrupt here */
t3=timer_(&tp); /* instantiate the timer */
baud=get_baud(1,t3);/* measure the baud rate */
timer__(t3); /*don't need timer any more */
</code></pre><p>
After the system is set-up, the timer is programmed to provide a 1 ms interrupt
and then started. The function shown above, get_baud() is then executed. Upon return
from the get_baud() routine, the timer is no longer needed so it is destroyed. The serial
port is then instantiated with serial1_io_() and the baud value retruned by get_baud() is
used. The messaged "The selected baud rate is " is sent to the screen. The baud rate is
then claculated using Equation 1 above. Here we need to know that the system
frequency, fs, is 16 mHz. This value divided by 32 provides the numerator of 500000
used to calculate the baud_rate. The calculated valued is sent to the screen and then the
message " bits per second\r\n" is sent to the screen. This program was executed many
times at many different standard baud rates between 1200 and 57600. The chip is
incapable of providing a 57600 bit per second rate. Return to Equation 1 and observe that
<pre><code>
16000000 / 32 * 57600 = 8.68
</code></pre><p>
This value will be rounded to 9. In that case, the baud rate will be
<pre><code>
16000000 / 32 * 9 = 55555.555
</code></pre><p>
If you execute the program, this is the baud rate value printed out when requesting a
57600 b/s rate.
<pre><code>
/* set up the serial communications */
sio=serial1_io_(baud, /* set the baud rate */
SERIO_INT_VECTOR, /* interrupt vector */
SERIO_IARB, /* iarb */
SERIO_INT_LEVEL); /* and interrupt level */
/* set up the io blocks */
oob1=&ob1; /* output block */
oob1->call_complete=TRUE; /* have no message going out */
oob1->io_command=OUTPUT; /* oob1 is always output */
oob1->buffer=pszMessage; /* select the message */
oob1->buffer_length= sizeof pszMessage;
serial_io(sio,oob1); /* message to the i/o object */
while(!oob1->call_complete)
; /* wait until transmission is done */
baud_rate=500000ul/baud;
printd(baud_rate); /* send baud rate to screen */
oob1->buffer=pszMessage1;
oob1->buffer_length= sizeof pszMessage1;
serial_io(sio,oob1); /* send out rest of message */
while(!oob1->call_complete)
;
}
</code></pre><p>
The standard printd() function is used to print the numerical value to the screen.
This function shown below is probably the simplest integer to ascii conversion available
for microcontrollers. The controller as well as the compiler used with this function must
support recursion.
<pre><code>
void printd(unsigned long x)
{
if(x/10)
printd(x/10);
putch(x%10+'0');
}
</code></pre><p>
The program above was not only broken into pieces for description, but the order
of functions was switched around. Therefore, the whole program is listed below.
<pre><code>
#include <HC16Y1.H>
#include <scim.h>
#include <mcci.h>
#include <timer.h>
#include <timer1.h>
#include <delay.h>
#include <serio1.h>
#include <semaphor.h>
#include <defines.h>
/* serial port parameters */
#define SERIO_BAUD_19200 19200
#define SERIO_INT_VECTOR 0x40
#define SERIO_IARB 10
#define SERIO_INT_LEVEL 3
#define TIMECOUNT 4000
/* function prototypes */
void printd(unsigned long);
WORD get_baud(BYTE number, Timer *t3);
void main(void)
{
Timer *t3;
Timer_parameters tp;
Serial_io *sio;
io_command_block *oob1,ob1;
BYTE pszMessage[]="The selected baud rate is ";
BYTE pszMessage1[]=" bits per second\n\r";
WORD baud;
unsigned long baud_rate;
/* initialize the program */
/* initialize the SIM registers */
SYNCR.X=ON; /* double the clock speed--16.00 mHz */
SYPCR.SWE=OFF; /* disable the watchdog */
CSORBT.DSACK=0; /* use zero wait states for now */
cli(); /* enable system interrupts */
/* set up the timer parameter structure and instantiate a timer */
tp.number=3; /* use output compare number 3 */
tp.tick=TIMECOUNT; /* 1 ms interrupts */
tp.action=0; /* no output action */
tp.OC1_connect=0; /* OC1 not connected to another OC */
tp.connect_data=0; /* therefore no connect data needed */
tp.interrupt=YES; /* need an interrupt here */
t3=timer_(&tp); /* instantiate the timer */
baud=get_baud(1,t3);/* measure the baud rate */
timer__(t3); /*don't need timer any more */
/* set up the serial communications */
sio=serial1_io_(baud, /* set the baud rate */
SERIO_INT_VECTOR, /* interrupt vector */
SERIO_IARB, /* iarb */
SERIO_INT_LEVEL); /* and interrupt level */
/* set up the io blocks */
oob1=&ob1; /* output block */
oob1->call_complete=TRUE; /* have no message going out */
oob1->io_command=OUTPUT; /* oob1 is always output */
oob1->buffer=pszMessage; /* select the message */
oob1->buffer_length= sizeof pszMessage;
serial_io(sio,oob1); /* message to the i/o object */
while(!oob1->call_complete)
; /* wait until transmission is done */
baud_rate=500000ul/baud;
printd(baud_rate); /* send baud rate to screen */
oob1->buffer=pszMessage1;
oob1->buffer_length= sizeof pszMessage1;
serial_io(sio,oob1); /* send out rest of message */
while(!oob1->call_complete)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -