Файл: Programming Microcontrollers in C, 2-nd edit (Ted Van Sickle, 2001).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 15.06.2025
Просмотров: 4079
Скачиваний: 1
Pulse Width Modulator System 201
The assembly code created by the compiler for this approach is in the address range $11d to $130. This code uses 21 bytes of memory. Careful examination of this code will show that the instruc tion at $122 is unneeded, so the code could have been completed in 17 bytes.
The assembly version of the same routine merely adds the two unsigned numbers, and then rotates the result right 1 bit to accom plish the divide by 2. The 1-bit rotate accomplishes the same thing as a right shift by 1 bit with the exception that the carry bit is shifted into the most significant bit of the result. The only way the carry bit could be set is by the most significant bits of both addends being 1. The assembly version of the code resides in $132 to $13c and re quires 12 bytes of code.
Here is a case where judicious choice of assembly code will provide a significant improvement in the amount of code needed to execute a specific program. The main reason that the assembly ver sion is shorter is that the carry bit in the condition code register is available to the programmer from assembly. This bit is completely hidden from the programmer in any high-level language. Therefore, tricks like rotating a bit from the carry into a register are not avail able in the high-level language.
EXERCISE
1.Write a routine to average readings from the ADC on a microcontroller, but weight the current reading three times that of the past average.
Pulse Width Modulator System
In this section on programming of the timers, two approaches to the generation of a pulse width modulation (PWM) signal will be discussed. These approaches both use the output compare system of the 16-bit timer.
What is a PWM system? A PWM signal is a periodic signal where the signal is set high for a calculated duty factor, and then the output goes low for the remainder of the period. If you measure a PWM signal with an averaging voltmeter it will have a value equal to the peak voltage times the duty factor of the PWM. Here the duty factor is defined as the ratio of the on time to repetition period of the signal.
202 Chapter 4 Small 8-Bit Systems
Thus, we have created a simple digital-to-analog converter. Many control applications need analog control signals for parts of the sys tem. PWM systems on microcontrollers provide an excellent means for providing these analog voltages, so long as the limitations we will discuss below are acceptable to the total system design.
The built-in PWMs in the M68HC05Bx family of devices pro vide you with one of two pulse periods: the programmable timer clock frequency divided by 256 or 4096. These frequencies are fixed, and the interval is divided into 256 parts. Therefore, the analog fre quency ranges—and the accuracy of the analog reproduction—is limited to one part in 256 over the range of the output.
When using the output compare system to create a PWM, it is possible to achieve a finer analog resolution at slower frequencies, or perhaps a faster signal with poorer accuracy. A PWM created from an output compare system is somewhat more flexible than the usual built-in PWM. With the PWM systems built around the output com pare system, the computer is responding to frequent interrupts. When an interrupt occurs, the system status is pushed onto the stack and the address of the interrupt service routine is placed into the program counter. It takes approximately 10 clock cycles to respond to an in terrupt after the completion of the executing instruction. Return from an interrupt requires 9 clock cycles. Therefore, it is not possible to accomplish the full goal of a PWM with an output compare system. The goal in this case is to achieve full on at the one extreme input level and full off at the other. The times required to process an inter rupt will limit the performance at one extreme, the other or perhaps both when a PWM is implemented with an output compare system.
A first example is shown in the following listing. This example shows the interrupt service routine along with the necessary defines only. It is assumed that the output compare interrupt bit along with the interrupt bit are properly set to allow the output compare 2 to interrupt the processor. Also shown in this example is code to control a second output compare—1—to provide the system with a fixed time base.
In operation, the applications portion of the program must place a pair of complementary numbers, pwm_number and off_count, in place. The sum of these two numbers must equal a constant that establishes the period for the PWM signal. For the M68HC05B4, the
Pulse Width Modulator System 203
period will be 2 microseconds times the total count value. Prior to starting use of the PWM, the bit flag.ON should be reset and the value for count, which is the main time base period count, should be set.
/* bits in flag */
#define ON 0
union both time_cnt;
unsigned long off_count,pwm_number,count; bits flag;
void __TIMER_OC(void)
{
if( TSR.OCF2==1) /* accesses the TSR */
{
if(flag.ON==0)
{
flag.ON=1;
TCR.OLVL2=0; /* Timer Compare 2 off*/ time_cnt.b.hi=OCHI2;/* get start time */ time_cnt.b.lo=OCLO2; /* reset the OCF */ time_cnt.l +=pwm_number; OCHI2=time_cnt.b.hi; OCLO2=time_cnt.b.lo;
return;
}
else
{
flag.ON=0;
TCR.OLVL2=1;/* Timer Compare 2 to on */ time_cnt.b.hi=OCHI2;/* get start time */ time_cnt.b.lo=OCLO2; /* reset the OCF */ time_cnt.l +=off_count; OCHI2=time_cnt.b.hi; OCLO2=time_cnt.b.lo;
return;
204 Chapter 4 Small 8-Bit Systems
}
}
else /* must be that OCF1==1 rather than OCF2
*/
{
time_cnt.b.hi=OCHI1; /* output compare */ time_cnt.b.lo=OCLO1; /* get lo byte */ time_cnt.l +=count; /* time for interrupt */ OCHI1=time_cnt.b.hi;
OCLO1=time_cnt.b.lo;/* reset the OCF */ ms_10++;
return;
}
}
Listing 4-6: ISR For PWM System
Since the interrupt bits are set to allow output compare interrupts, an interrupt will occur. The interrupt service routine will be entered, and the instruction sequence
if( TSR.OCF2==1) /* accesses the TSR */
{
.
.
.
accomplishes two things. First, it determines if the output compare 2 caused the interrupt, and, secondly, it accesses the TSR and arms the reset of the OCFX when the proper lower byte of the output compare register is read later.
For the moment, assume that OCF2 bit is set: the interrupt was caused by a compare in output compare register 2. In this case a test is made to determine if flag.ON is reset. Since the initialization rou tine reset this bit, it will be 0. Therefore, the code sequence
flag.ON=1;
TCR.OLVL2=0; /* Timer Compare 2 to turn off*/ time_cnt.b.hi=OCHI2; /* get the start time */ time_cnt.b.lo=OCLO2; /* reset the OCF */ time_cnt.l +=pwm_number;
Pulse Width Modulator System 205
OCHI2=time_cnt.b.hi; OCLO2=time_cnt.b.lo; return;
will be executed. This code is the “on” code for the PWM system. The first instruction in this sequence will set the bit flag.ON, so that this code will not be executed in the next execution of the inter rupt service routine. The bit TCR.OLVL2 is the state that will be transferred to TCMP2 pin when an output compare occurs. TCR.OLVL2 is then set to 0 so that TCMP2 will be set low when the next output compare occurs. The code sequence that follows gets the contents of the output compare register 2, increments this value by pwm_number, and places this new value back into output compare register 2. Thus, the time of the output compare is established and the interrupt service routine is exited.
Eventually, the next output compare 2 interrupt will occur. Since the bit flag.ON has been set, the following code will be executed. This code is the “off” code for the PWM system. The first business to take care of is to reset the bit flag.ON so that the on portion of PWM code will be executed the next time into the ISR. TCR.OLVO2 is set so that TCMP2 will go on when the next output compare occurs.
flag.ON=0;
TCR.OLVL2=1; /* Timer Compare 2 to turn on */ time_cnt.b.hi=OCHI2; /* get the start time */ time_cnt.b.lo=OCLO2; /* reset the OCF */ time_cnt.l +=off_count; /*complete the cycle*/ OCHI2=time_cnt.b.hi;
OCLO2=time_cnt.b.lo; return;
The contents of OCR2 are handled as above, but this time their value is incremented by the contents of off_count. The main line code is then re-entered by the use of the return instruction.
There is another approach to generation of PWM signals with out put compare systems that will materially aid this problem. However, this approach requires two output compare systems. One output com pare system is used to create the time base and the other is used to generate the on or off time. Let’s examine the following interrupt service routine. Output compare 1 is used here to generate the PWM
206 Chapter 4 Small 8-Bit Systems
period. The way that this routine works, it is possible for an interrupt to occur as a result of a compare on output compare 2. This interrupt is to be ignored, and the first five lines of the code below tests for this condition and clears interrupt when it occurs. This interrupt occurs at the end of the on time. Control of the program must be out of this portion of the ISR and in the main line code before the next interrupt caused by OCF1 occurs. The time required for the first five lines of code here is about 18 microseconds, which is the minimum on time.
void __TIMER_OC(void)
{
if(TSR.OCF2==1)
{
ac=OCLO2; /* if interrupt is caused by out
put*/
return; /* compare 2 reset it and return */
}
time_cnt.b.hi=TCHI; /* get in the TCR */ time_cnt.b.lo=TCLO; /* get lo byte */ time_cnt.l +=count; /* bump the time counter
for next*/ OCHI1=time_cnt.b.hi;/* interrupt */ OCLO1=time_cnt.b.lo;/* reset the OCF */ if(flag.PWM==1)
{
TCR.OLVL2=1;/* Timer Compare 2 to set output */ TCR.FOLV2=1;/* turn on Timer compare 2 output */ TCR.OLVL2=0;/* Timer Compare 2 to reset output*/ time_cnt.b.hi=TCHI; /* get the start time */ time_cnt.b.lo=TCLO;
time_cnt.l +=pwm_number; OCHI2=time_cnt.b.hi; OCLO2=time_cnt.b.lo;
}
}
Listing 4-7: Alternate PWM ISR
The next sequence is the code required to establish the time base. This time base operates on output compare 1. This routine is similar to those discussed above. A bit in the bit field flag is tested to deter
Other Program Items 207
mine if the PWM signal is to be turned on. If flag.PWM is set, the PWM code will be executed. When this routine is entered, TCMP2 will be reset to zero. The first instruction sets TCR.OLVL2 so that TCMP2 will be on at the next output compare. The next instruction, TCR.FOVL2=1 , sets the force overflow bit for TCMP2 which will cause this bit to be turned on since TCR.OLVL2 is set. This time is the beginning of the on period of the PWM signal.
The next instruction
TCR.OLVO2=0;
is so that TCMP2 will go off or be reset at the next output compare 2. Control is then passed to the main line of code. When TCMP2 oc curs, the output will return to 0 and an interrupt will occur. This interrupt is detected by the first lines of the interrupt service routine.
One could write a code sequence that would generate the mirror image signal to that above. That is, the off time would be controlled by TCMP2 and the on time would be the difference between the time of TCMP2 and the main time base.
Other Program Items
Several other small programs come to mind that are very useful in programming microcontrollers. With these small parts, it is usu ally desirable to avoid library functions and, if possible, use a bag of tricks to arrive at the desired results. For example, dealing with num bers for either input or output offers a good place to exercise some experience over expedience. One case where it is often important to minimize code space is in generating binary coded decimal (BCD) numbers from integer numbers to output from a computer. Perhaps the most direct approach to accomplish this conversion is as follows:
/* convert a binary number less than 100 to BCD */
unsigned char convert_bcd(unsigned char n)
{
unsigned int result; if(n>99) return 0xff; result=n/10<<4; result += n%10;
208 Chapter 4 Small 8-Bit Systems
return result;
}
Listing 4-8: First BCD Conversion
This small function takes an 8-bit character n that is less than 99 and converts the number into one 8-bit result. The upper nibble is the number of tens in the number and the lower nibble is the number of units. A hexadecimal number 0xff is returned if the number is greater than 99. Note that the calculation requires an integer divide opera tion and a modulus operation which is equivalent to a divide operation. The code to execute the divide and modulus operations must be asso ciated with this function to complete its task.
Another approach is shown below. This approach avoids any exter nal function calls and actually requires less total code than the version in Listing 4-8, even though the C code is somewhat longer. In this case, the while loop essentially divides the input number by 10 and places the result in the most significant nibble of the result.After the tens have been removed from the number, all that is left is the units. This value—the number of units—is ORed on the result before it is returned.
/* convert a binary number less than 100 to BCD */
unsigned char convert_bcd(unsigned char n)
{
unsigned int result;
if(n>99) return 0xff; result=0; while((n-10)>=0)
result+=0x10;
n+=10; result += n;
return result;
}
Listing 4-9: Second BCD Conversion
The function shown below was originally written for use on a M68HC05, but was later used on a M68HC11 as well. A two-digit number must be sent to a seven-segment LED display. The number to be shown is contained in the memory locations for tens and units.