Файл: Programming Microcontrollers in C, 2-nd edit (Ted Van Sickle, 2001).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 15.06.2025
Просмотров: 4039
Скачиваний: 1
Timer Operations 277
bits, transmit enable and receive enable, that must be set to enable the SCI. The following two lines of code will enable these bits.
SCCR2.TE=1;
SCCR2.RE=1;
These two lines of code must also be added to the initialization portion of the program.
The code to read the data in is as follows:
if (SCCR.RDRF==1) /* read in data if it is there */
{
new_character=SCDR;/*get new byte and reset RDRF*/ while(SCCR.TDRE==0); /* wait until transmit
buffer empty */ SCDR=new_character; /* send out byte and reset
TDRE */
}
This sequence of code does quite a bit more than you might expect. Before this code sequence can be executed, both the RDRF and TDRE flags must be set and reset. The RDRF is set when a character has been received by the SCI, and the TDRE is set when the SCDR is empty and can receive a character to send. Both of these bits are reset by the sequential read of SCSR with the bit set followed by a read for the RDRF or a write for the TDRE to the SCDR register. The above sequence accomplishes all of the proper bit resets, transmits the newly received character back to the sender, and leaves the new character in the location new_character . After receiving the data, the following sequence converts it into a binary number to be processed by the remainder of the program.
if (SCSR.RDRF==1) /* read in data if there */
{
new_character=SCDR;/*get new byte and reset RDRF*/ while(SCSR.TDRE==0); /* wait until transmit
buffer empty */ SCDR=new_character; /* send out byte and reset
TDRE */
/* if a number, process it */ if(new_character>=’0' && new_character <=’9')
278 Chapter 5 Programming Large 8-Bit Systems
new_speed = 10*new_speed + new_character-’0'; else if(new_character==’\r’)
{
/* reject any number out of range */ /* and start over again */
if(new_speed>=1000 && new_speed<=10000) motor_speed=new_speed;
new_speed=0;
}
else
new_speed=0; /* reject everything else */
}
The variable new_speed is initialized to 0 outside of this portion of the program. If the input character is a number (a number is any character that lies between ‘0’and ‘9’), it is converted from a character to a number when the character ‘0’ is subtracted from it. This value is added to ten times the value stored in new_speed , and the result is saved in new_speed . Repeated executions of this code sequence will convert a series of ASCII character numbers to an appropriate unsigned integer value. The entry sequence is terminated when a nonnumber character is received. If this character is a line terminator (a carriage return escape character in this case), the new speed value is put into motor_speed to cause the motor to change to the new value, and new_speed is reset to zero to await for the next input. If any other character is received, the data saved in new_speed is lost, and the whole entry of a new speed into the system must be repeated from the beginning.
In Listing 5-7 there was a lonely line of code
/* input the new motor speed */
That line has been replaced by the above and entered into Listing 5-8. The new motor speed input is placed in the main loop of the applications program, so a test is made each time through the loop to determine if there is a new input from the serial port. This routine also sends out the current speed of the motor once each half second. This output is controlled by the parameters tick1 and in_process. tick1 is set to TRUE each half second, and if in_process is FALSE the motor speed is calculated and sent to the serial port output.
Timer Operations 279
This sequence repeats each half second until in_process is set to TRUE. Whenever a character is read in through the serial port, in_process is set to TRUE to disable the continual output from the system while a new input speed value is being entered. If there is a new character in the input buffer, this character is read in and the RDRF flag is reset. The new character is immediately echoed back to the sending device as part of full duplex operation. If the new character is a digit it is processed, if it is a line feed it is also processed, and any number string is converted to an integer so that it can be used by the computer. The size of the number is tested to be certain that it is within the acceptable speed limits for the motor, and if it passes all of the tests, it is placed in the variable location motor_speed to indicate that a new motor speed is here and should be processed.
#include “hc11e9.h”
#define DIVIDE_8_SHIFT |
3 |
#define COUNT_8 |
8 |
#define COUNT_MAX |
3300 |
#define COUNT_MIN |
1600 |
#define COUNT_ONE_QUARTER |
32 |
#define COUNT_ONE_SECOND |
128 |
#define PERIOD |
0X1000 |
#define TIME_ON |
0x0800 |
#define IMPOSSIBLE |
3500 |
#define TOO_LOW |
100 |
#define RPM_MIN |
1000 |
#define RPM_MAX |
11000 |
#define CR |
0x0d |
#define LF |
0x0a |
/*function prototypes */ @port void IC1_Isr(void); @port void OC2_Isr(void); @port void OC3_Isr(void);
int putchar(char new_character); void dprint (unsigned int c); void do_crlf(void);
long limit(long );
280Chapter 5 Programming Large 8-Bit Systems
/* external variable definitions */ long measured_period,delpc;
WORD time1,time2, motor_period,motor_speed=IMPOSSIBLE; WORD new_speed;
WORD old_motor_speed=TOO_LOW,rpm,mparray[COUNT_8]; long PWM_period=PERIOD, PWM_count=TIME_ON;
int new_character,tick=TRUE,count=0,in_process; int tick1=TRUE,count1=0;
WORD got_new=TRUE; main()
{
/* The initialization portion of the program */
TCTL2.EDG1B=ON;/* capture falling edge only */ OC1M.OC1M7=ON; /* sent OC1 out to PA7 */ OC1M.OC1M5=ON; /* couple OC1 to OC3 */ TMSK1.OC3I=ON; /* enable the OC3 interrupt */ TMSK1.IC1I=ON; /* enable the IC1 interrupt */ OC1D.OC1D5=ON; /* turn on OC3 when OC1 occurs */ TCTL1.OL3=ON; /* toggle OC3 when OC3 occurs */ PACTL.DDRA7=ON;/* make OC1 an output to PA7 */ TOC1=TCNT+PWM_period; /* set OC1 to the period*/ TOC3=TOC1+PWM_count; /* set OC3 time on */
BAUD.SCP=3; |
/* set up the SCI */ |
BAUD.SCR=0; |
/* 9600 baud */ |
SCCR2.TE=ON; |
|
SCCR2.RE=ON; |
|
cli(); |
/* enable the system interrupts */ |
/* the applications portion of the program */
FOREVER
{
if(old_motor_speed!=motor_speed)
{ /* All of the numbers used below are derived in the text */
motor_period=30000000lu/motor_speed; old_motor_speed=motor_speed;
Timer Operations 281
PWM_count= ((motor_speed+12528)/63)*8; PWM_count=limit(PWM_count);
}
if(tick)
{
tick=FALSE; delpc=(3809500lu/motor_period); delpc=delpc*(motor_period-
measured_period)/motor_period; PWM_count -=delpc; PWM_count=limit(PWM_count); rpm=30000000L/measured_period;
}
/* input the new motor speed */
/* Send out the measured RPM to the terminal periodically */
if(tick1&&!in_process)
{
tick1=FALSE; rpm=30000000L/measured_period; dprint(rpm);
do_crlf();
}
if (SCSR.RDRF==ON) /* read in data if it is there */
{
in_process=TRUE;
new_character=SCDR; /* get new byte and save it */
while(SCSR.TDRE==OFF); /* wait until transmit buffer is empty */
SCDR=new_character; /* send out byte and reset TDRE */
/* got a number, process it */ if(new_character>=’0' && new_character <=’9') new_speed = 10*new_speed + new_character-‘0’; else if(new_character==’\r’)
282 Chapter 5 Programming Large 8-Bit Systems
{
do_crlf();
/*reject any number out of range */ /* and start over again */
if(new_speed>=RPM_MIN && new_speed <=RPM_MAX)
{
got_new=TRUE; in_process=FALSE; motor_speed=new_speed; new_speed=0;
}
}
else
new_speed=0; /* reject everything else */
}
}
}
/* functions of the main program */
/* the range of acceptable PWM_count is 1600 to 3300 */
long limit(long x)
{
if(x<COUNT_MIN) return COUNT_MIN; else if(x>COUNT_MAX) return COUNT_MAX; else
return x;
}
/* send out a single carriage return-line feed sequence */
void do_crlf(void)
{
Timer Operations 283
while(SCSR.TDRE==OFF);
SCDR=CR;
while(SCSR.TDRE==OFF);
SCDR=LF;
}
/* send a single character to the serial port */
int putchar(char new_character)
{
while(SCSR.TDRE==OFF);
/* wait until transmit buffer empty */ SCDR=new_character;
/* send out byte and reset TDRE */
}
/* convert an integer and send it to the serial port as a string */
void dprint (unsigned int c)
{
if ( c/10) /* recursively determines if */ dprint(c/10); /* a zero has been reached and then */ putchar(c%10+’0'); /* sends out the characters */
}
/* The asynchronous service portion of the program */
@port void IC1_Isr( void)/*the motor speed measurement*/
{
static int i; int j; time2=TIC1;
TFLG1=IC1F; /* reset IC1 interrupt flag */ mparray[i]=time2-time1;
if (++i==COUNT_8)
{
i=0;
284 Chapter 5 Programming Large 8-Bit Systems
measured_period=0; for(j=0;j<COUNT_8;j++)
measured_period += mparray[j]; measured_period >>= DIVIDE_8_SHIFT;
}
time1=time2;
TOC2=time2+5*measured_period/8; /*debounce time Is 5/8 revolution */
TMSK1.IC1I=OFF; /* disable IC1 interrupt */ TMSK1.OC2I=ON; /* enable OC2 interrupt */
}
@port void OC2_Isr(void) /* the debounce isr */
{
TFLG1=IC1F|OC2F; |
/* reset interrupt flags */ |
TMSK1.OC2I=OFF; |
/* disable OC2 interrupt */ |
TMSK1.IC1I=ON; |
/* enable IC1 interrupt */ |
}
@port void OC3_Isr( void) /* the PWM isr */
{
TFLG1=OC1F; |
/* |
reset OC1 |
interrupt |
flag |
*/ |
TOC1+=PWM_period; |
|||||
OC1D.OC1D7 ^=ON; |
|||||
TFLG1=OC3F; |
/* |
reset OC3 |
interrupt |
flag |
*/ |
TOC3=TOC1+PWM_count; if(++count>=COUNT_ONE_QUARTER)
{
count=0; |
/*enter speed control about */ |
tick=TRUE; |
/*each quarter of a second */ |
}
if(++count1==COUNT_ONE_SECOND)
{
count1=0;
tick1=TRUE;
}
}
Listing 5-8: The Completed Application
Summary 285
There are three input/output routines that have been written for this program. These routines, putchar(), dprint(), and do_crlf(), can be used with other systems with a serial input/ output system. The Cosmic compiler does provide the usual I/O routines like printf(), gets(), puts(), etc. It does not provide a basic putchar() and getchar() which is used by all of these library routines. The reason that these routines are not provided by the compiler is the wide variety of what the programmer will want to implement with the built-in SCI ports on the MC68HC11 family. The putchar() shown above will work in most instances. The dprint() routine is a recursive routine that converts an integer into an ASCII string and sends it to the SCI port.
There is one final modification to the program. In the last lines of the PWM timer routine, count1 and tick1 are processed to set tick1 to be TRUE each second. This flag is then used to control the writing of the motor speeds to the terminal screen.
Summary
There has been no attempt to work all of the peripherals on the MC68HC11. The various peripherals are similar to those on the other parts that we have discussed in other chapters or will discuss later. We have seen several timer applications both in the MC68HC11 and in the MC68HC05. We will see other timer applications in the following chapters.
We have seen detailed use of the output compare timer subsystem to make a pulse width modulation digital-to-analog converter system. Depending on the program, the system allowed excellent performance in either short on times or maximum on times, but not both without the addition of a significant amount of code. We will see a system in the next chapter that provides excellent performance for both minimum and maximum on times. This performance is not a limitation of the MC68HC11, merely a limitation of the programs presented so far.
The input capture subsystem has been used to measure motor speed in a simple DC motor controller. This system used a primitive reed switch to measure the rotation of the motor shaft, and the performance of the switch was poor. A debouncing system was developed that prevented input captures to occur for a specified time after the first input was detected. This approach uses an output
286 Chapter 5 Programming Large 8-Bit Systems
compare channel, but it does not tie up the microcontroller to wait out any delay times during the debounce period. This program is not too removed from many of those encountered in the real world.
The organization of the program is similar to how most applications can be programmed, and the way in which the program was developed showed how most problems should be approached. The problem was broken down into a set of small operations that could each be handled easily. These different parts of the program were developed, tested, and debugged separately. This approach keeps the development of the individual parts of the program manageable, and debugging is not too difficult. If the whole program were written and then debugging started, it would have been nearly impossible to separate out the effects of one part of the program on the others. The main interrupt service routines were written first and tested as well as possible by themselves. With these important functions behind us, it was easy to attack the applications portion of the program in which the closed loop system was implemented along with the management of the input/output through the serial port of the device.
The MC68HC11 is a powerful enough computer that it is possible to make an ANSI compliant compiler. Parameters can be passed to functions on the stack, and re-entrant or recursive functions can be written for this part as was demonstrated with the dprint() function. Remember, it is the microcontroller that limited the ANSI compliance with the MC68HC05—not the compiler.