Software Development For Microprocessor Control Systems
Software Development For Microprocessor Control Systems
by
A Thesis submitted in partial fulfilment of the requirements for the degree of Master of Engineering
June 1998
Abstract
This thesis concerns the development and application of a user-friendly microprocessor-based digital controller. Microprocessor programming is a specialized, time-consuming and laborintensive step in the design process for digital control systems. The user-friendly control system software will allow control engineers to execute real-time microprocessor control algorithms on a system in a cost-effective manner without having to deal with any microprocessor software coding. This will result in shorter design and prototyping cycle times for microprocessor-based control systems. A Windows-based software program has been developed that will allow the user to select a controller from a finite list of controller designs and to specify the control parameters for the particular controller. The program will generate, compile and download the complete control algorithm to a microcontroller on which the control program will be executed. Controller performance data can be monitored in real-time while further data analysis is possible with offline plotting and saving features. The controller software was tested on control applications such as DC motor speed control and multivariable temperature control. The results from these tests were found to be in agreement with corresponding numerical models and verified the correct functionality of the controller software.
iii
Acknowledgements
I would like to thank some of the people who helped me to make this thesis possible. First, I would like to thank my supervisors, Associate Professor Paul Austin, Dr. Bruce Macdonald and Dr. Sing Kiong Nguang for their contribution to this thesis in terms of ideas, comments and technical advice. Their friendliness and professional working approach was invaluable to the successful completion of this thesis. It was a pleasure working with them. I would also like to thank Sunita Bhide, Leonid Ostrovsky and Slavek Przepiorski for assisting me with some of the necessary hardware. Finally, I would like to thank my parents for their love and support and for giving me the opportunity for tertiary education. Their interest and involvement in my work was a big inspiration for me during all my years of studying.
Contents
Abstract . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
v
2.3 The PID Controller. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.3.1 Numerical Approximation of the PID Controller . . . . . . . . . . . 22
2.3.2 Digital PID Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
vii
viii
5.6 Clearing Timer Flags. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 5.7 Sample Frequency Regulation . . . . . . . . . . . . . . . . . . . . . . . . . 89 5.8 Compiling and Linking Source Code. . . . . . . . . . . . . . . . . . . . 91
ix
References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .151
Appendix A ANSI/IEEE Standard 754 Floating-Point Representation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 Appendix B S-record Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 Appendix C Ziegler-Nichols Tuning of PID Controllers . . . . . . . . . . . . . . . . 163 Appendix D Circuit Schematics and PCB Layouts . . . . . . . . . . . . . . . . . . . . . . 167 Appendix E Application Example Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 Appendix F The Windows-Based Software Source Code . . . . . . . . . . . . . . . .175 Appendix G The Microcontroller Software Source Code . . . . . . . . . . . . . . . . 319
xi
Glossary of Terms
A/D ADCTL ANSI ASCII BUFFALO CFF CMOS COFF CRA CRB CSEL DIP DRA DRB DSP EBLP EEPROM GUI I/O IEEE ISR MCU MIMO MIPS MSB OTPROM PA PB PC PCB PI Analog to Digital Analog to Digital Control Status American National Standards Institute American Standard Code for Information Interchange Bit User Fast Friendly Aid to Logical Operations Conversions Complete Flag Complementary Metal Oxide Semiconductor Common Object File Format Control Register A Control Register B Clock Select Dual In-line Package Data Register A Data Register B Digital Signal Processor Evaluation Board Low Power Electrically Erasable Programmable Read Only Memory Graphical User Interface Input/Output Institute of Electrical and Electronics Engineers, Inc. Interrupt Service Routine Microcontroller Unit Multi-Input-Multi-Output Million Instructions Per Second Most Significant Bit One Time Programmable Read Only Memory Port A Port B Personal Computer Printed Circuit Board Proportional + Integral
xii
PIA PID PLCC PWM RAD RAM RE RIDE ROM S/N SCI SISO T TC TDRE TE TIC TOC TOF UART VRH VRL
Peripheral Interface Adapter Proportional + Integral + Derivative Plastic Leadless Chip Carrier Pulse Width Modulation Rapid Application Development Random Access Memory Receive Enable Real-time Integrated Development Environment Read Only Memory Signal/Noise Serial Communications Interface Single-Input-Single-Output Sample Period (seconds) Transmit Complete Transmit Data Register Empty Transmit Enable Timer Input Compare Timer Output Compare Timer Overflow Universal Asynchronous Receiver Transmitter Voltage Reference High Voltage Reference Low
xiii
Chapter 1
Introduction
1.1 Background
Feedback control is having, and will have in the future, a significant impact on all aspects of modern society. In recent years significant progress has been made in microcomputer control of dynamic systems as the price and performance of digital computers has improved dramatically. Many automatic control functions such as decision-making, self-tuning and adaptive control are now possible with the use of digital controllers. Digital controllers are less sensitive to noise and disturbance, more reliable, more flexible and less susceptible to aging and environmental variations than their analog counterparts [1]. The chemical and process industries were one the first users of computer-based control systems mainly because of the relatively inert dynamic characteristics of most chemical processes [2]. Recent advances in microprocessor technology made it possible to implement many new digital control applications such as aircraft automatic pilot, automotive antilock braking systems, radar positioning control and
Chapter 1 - Introduction
power plant turbine speed control [3]. Modern digital controllers are also used for less obvious control applications such as the control of water height and spin speed in modern washing machines and the control of blood flow in artificial hearts [4].
Figure 1.1 The flight deck for the new Boeing 717 features the latest in digital control technology. The dual Flight Management System and the Category IIIa standard automatic landing capability integrates navigation, guidance, and performance data functions. The Flight Management System provides accurate engine thrust settings and flight-path guidance during all phases of flight from takeoff to final approach and landing. The system can predict the speeds and altitudes that will result in the best fuel consumption and command the airplane to follow the most economical flight path. (Courtesy of The Boeing Company)
The basic structure of a digital control system can be illustrated through an example of an automatic aircraft landing system as illustrated in Figure 1.2. The system can be divided into four basic parts: the aircraft, the radar unit, the controller unit and the transmitter unit. The radar unit will measure variables such as lateral position, vertical position and range. The measured data will be sent to the controller unit and the appropriate pitch and bank commands will be calculated and transmitted back to the on-board automatic flight control system. The on-board automatic flight control system will then do the necessary adjustments to the flight controls to keep the aircraft on the correct glide slope and extended runway centerline.
Chapter 1 Introduction
It is necessary to know the mathematical relationship between all the parts of the system before any control algorithm can be programmed into the aircraft controller. This mathematical relationship is referred to as the system mathematical model. The dynamic response of most aircraft can be modeled in the form of a ninth-order nonlinear differential equation [5]. Once the systems mathematical model is known, a control system can be designed that will keep the aircraft stable on its proper descent path.
Controller Computer Lateral position Range Lateral digital compensator Position command Vertical digital compensator Transmitter
Vertical position
The task of the control engineer is to design and specify the most effective processing algorithm to be accomplished in the digital controller. There is normally a compromise between the swiftness of the aircraft response and its position stability. The processing algorithm in the controller computer has to correct any error in the aircrafts position as rapidly and effectively as possible while the systems sensitivity to external disturbances such as wind and radar noise must kept to a minimum.
Chapter 1 - Introduction
Figure 1.3 The Quattro62 Multiple Processor DSP Board is based on Texas Instruments TMS320C6201 DSP and is capable of sustained total computational throughput of 6400 MIPS. (Source: Innovative Integration)
Chapter 1 Introduction
VisSim/DSP
VisSim from Visual Solutions, Inc. provides a fully integrated Windows-based control system design environment for the modeling and simulation of a wide variety of control systems. VisSim incorporates a visual block diagram user interface that offers a simple method for constructing, modifying and analyzing complex system models. VisSim/DSP is a completely integrated Windows program for the rapid prototyping of control systems targeted for DSPs and embedded systems. VisSim/DSP includes integrated modules for automatic C code generation, downloading and real-time DSP validation & optimization.
Chapter 1 - Introduction
Hypersignal RIDE
Hyperception's Real-time Integrated Development Environment (RIDE) is a visual graphical environment for the design, implementation, and analysis of real-time DSP algorithms. Hypersignal RIDE supports numerous industry-standard plug-in DSP boards. The system can also export DSP Object code exported to a standard Common Object File Format (COFF) object file for embedded DSP applications. The user interface of Hypersignal RIDE is the same for both simulated and real-time DSP block functions, which allows convenient conversions between design simulations and real-time implementations. Hypersignal RIDE can also generate ANSI C source code, which may then be crosscompiled for use in other environments.
Chapter 1 Introduction
Chapter 1 - Introduction
board is a version of PMAC technology provided through Delta Tau Data Systems and will be used in conjunction with the I/O electronics unit.
Chapter 1 Introduction
Figure 1.7 shows the basic layout of the M68HC11 based control system. The type of controller and its control parameters will be specified by the user on the PC. The software on the PC will then generate, compile and download the control algorithm to the M68HC11 microcontroller. The microcontroller will execute the control algorithm on a plant while the PC will only monitor the system performance. The data received from the microcontroller can be plotted or stored on disk for further analysis.
Chapter 1 - Introduction
Control Algorithm
Monitor Signals
Control Process
Microcontroller
Control Signals
Process
Feedback Signals
Figure 1.7
10
Chapter 1 Introduction
11
Chapter 1 - Introduction
12
Chapter 2
where e(k ) and u (k ) are the respective input and output numbers of the computer at time t. The values e(k n ) and u (k n ) are the respective input and output numbers of the computer at time t nT . The z-Transform method will transform a system whose operation is described by a set of differential equations in the Laplace domain to a system whose operation is described by a set of difference equations in the z-domain. The z-Transform is
13
therefore a useful transformation to find a convenient way of representing the operation of digital control systems.
T (t ) = (t kT )
k =0
(2.2)
(2.3)
(2.4)
14
Equation 2.4 represents an infinite series that involves factors of e sT and its powers. The transformation between s an z can be defined as z = e Ts Equation 2.5 can also be written in terms of z as s= 1 ln z T (2.6) (2.5)
Equation 2.5 and 2.6 is defined as the z-transformation. The z-transform of r(t) can therefore be written as. R(z ) = r (kT )z k
k =0
(2.7)
15
The differential equation for the second-order RLC filter is d2y dy 2 2 + 2 +0 y = 0 x 2 dt dt where R 2L 1 2 0 = LC (2.8)
(2.9)
16
The second-order transfer function of the RLC circuit can be obtained by taking the Laplace transform of Equation 2.8. Y (s ) 02 = 2 2 X (s ) s + 2s + 0
(2.10)
By using the backward difference rule as shown by Bozic (1979), the discrete-time form of Equation 2.10 can be written as y * ( t ) = a0 x * ( t ) + b1 y * ( t T ) + b2 y * ( t 2T ) The solution for the parameter gains using backward difference rule is a0 = 1 b11 = 2e T cos 1T b2 = e
2 T
(2.11)
(2.12)
where T is the sampling period. The digital equivalent of the RLC filter illustrated in Figure 2.2 can thereforre be implemented by the digital filter structure illustrated in Figure 2.3.
x(t)
x*(t)
a0
+ + +
y*(t)
b2
b1
y*(t-2T)
z 1
y*(t-T)
z 1
17
Y (z ) = H (z ) = X (x )
b z
n m=0
n=0 M
(2.13)
m
This digital transfer function is only valid for zero initial conditions, which are satisfied for the cases considered in this text [8]. By taking a 0 = 1 , Equation 2.13 can be expressed as Y ( z ) = bn z n X ( z ) a m z n Y ( z )
n=0 m =1 N M
(2.14)
(2.15)
which is the difference equation that realizes H(z). Difference Equation 2.15 can also be directly implemented in any computational algorithm to realize a digital filter [8]. Two basic microcomputer operations are required to implement the difference equation. The first operation is data storage. Past samples of the filter input and output are normally used in the computation of the output y*(t). The second operation involves arithmetic operations such as add and multiply.
18
z 1
z 1
z 1
b0 a0 + +
b1 a0 + +
b2 a0 + +
bn a0
+ +
y * (t )
+ + a1 a0 a2 a0
+ + an a0
z 1
y * (t T )
z 1
y * (t 2T )
z 1
y * (t nT )
19
D( z ) =
(2.16)
(2.17)
x * (t )
D1 ( z )
D2 ( z )
D p (z )
y * (t )
Physical Realizability Considerations The digital filter transfer function, H(z) can only be realized if H(z) is physical realizable [1] . This implies that no output signal of the digital filter will appear before an input signal is applied. For the transfer function in Equation 2.13 to be physically realizable, the highest power of the denominator must be greater or equal than the highest power of the numerator. This implies that n m . Also, for the difference Equation 2.15 to be physical realizable, a 0 may not be zero.
20
Kp
E (s )
KI s
U( s )
KDs
Figure 2.6 The continuous-time PID controller
21
(2.20)
where T is the sampling period of the controller. Approximation of the Integral Part The right-side rectangular technique can be used to approximate the integral part of the PID controller. Figure 2.7 illustrates the use of this technique.
e(t )
u I (t ) = K I e(t )dt
0
(2.21)
(2.22)
22
(2.23)
(2.24)
The numerical representation of the PID controller can therefore be written as: u( nT ) = K P u P ( nT ) + K I u I ( nT ) + K D u D ( nT ) where: u P ( nT ) = e( nT ) u I ( nT ) = u I ( nT T ) + Te( nT ) u D ( nT ) = e( nT ) e( nT T ) T (2.26) (2.25)
Derivation of a Discrete-data Mapping Function An alternative sampled-data mapping function can now be derived which will make manual transformations between the s-plane and the z-plane easier. The equivalent Laplace domain approximation for Equation 2.26 is: Y ( s ) e sT Y ( s ) + y( 0+ ) T
sY ( s )
(2.27)
(2.28)
Equation 2.5 defines the mapping function of the standard z-transform as z = e sT (2.29)
23
By substituting e sT with z in Equation 2.28, the sampled-data mapping function for the backward difference approximation can be written as 1 z 1 T
s=
(2.30)
This mapping function will make manual transformations between the s-plane and the z-plane easier. The major disadvantage of this mapping function lies in its frequency response contour [5]. Small values for T will however improve this approximation.
(2.31)
This transfer function can be directly implemented by the digital filter structure illustrated in Figure 2.8. Arithmetic operations for the proportional, integral and differential terms are kept separately until the terms are summed at the output. KP
e*(t)
+
z
+
1
KIT 2
+ +
u*(t)
z 1
KD T +
24
An alternative method to implement the digital PID controller is to find the secondorder transfer function of Equation 2.31. K P ( z 1)( z ) + K IT (z + 1)( z ) + K D ( z 1)( z 1) 2 T ( z 1)( z )
D( z ) =
KP z 2 z +
K IT 2 K z + z + D z 2 2z + 1 2 T z2 z
)
(2.32)
KT K 2 K T 2K D K z + D K P + I + D z + KP + I 2 2 T T T = 2 z z = b0 + b1 z 1 + b2 z 2 a0 + a1 z 1 + a2 z 2
b0 = K P +
b1 = K P + b2 = KD T
a0 = 1 a1 = 1 a2 = 0 Consequently, a digital PID controller can be implemented by any second-order direct digital filter structure as discussed in section 2.2.1.
25
u1 u2
The transfer function of the system illustrated in Figure 2.9 can be described as B11 u1 A11 u = B 2 21 A21 B12 A12 e1 B22 e2 A22
(2.34)
(2.35)
26
Rewriting Equation 2.34 gives u1 = u2 = B11 B e1 + 12 e2 A11 A12 B21 B e1 + 22 e2 A21 A22
(2.36)
By getting a common denominator, Equation 2.36 can be written as A11 A12 u1 = B11 A12 e1 + A11 B12 e 2 A21 A22u2 = B21 A22 e1 + A21B22e2 Let A11 A12 = P1 = 1 + p1 1 z 1 + p1 2 z 2 + p1 3 z 3 + p1 4 z 4 B11 A12 = Q1 = q1 0 + q1 1 z 1 + q1 2 z 2 + q1 3 z 3 + q1 4 z 4 A11 B12 = R1 = r1 0 + r1 1 z 1 + r1 2 z 2 + r1 3 z 3 + r1 4 z 4 A21 A22 = P2 = 1 + p 2 1 z 1 + p 2 2 z 2 + p 2 3 z 3 + p 2 4 z 4 B21 A22 = Q2 = q 2 0 + q 2 1 z 1 + q 2 2 z 2 + q 2 3 z 3 + q 2 4 z 4 A21 B22 = R 2 = r2 0 + r2 1 z 1 + r2 2 z 2 + r2 3 z 3 + r2 4 z 4 so that Equation 2.37 can be written as P1 0 0 u1 Q1 = P2 u 2 Q 2 R1 e1 R2 e 2 (2.38)
(2.37)
(2.39)
27
1 0
p1 1 0
p1 2 0
p1 3 0
p1 4 0
0 1
0 p2 1
0 p2 2
0 p2 3
u1 * (t ) u * (t T ) 1 u1 * (t 2T ) u1 * (t 3T ) 0 u1 * (t 4T ) p2 4 u 2 * (t ) u * (t T ) 2 u 2 * (t 2T ) u * (t 3T ) 2 u 2 * (t 4T ) e1 * (t ) e * (t T ) 1 e1 * (t 2T ) e1 * (t 3T ) r1 4 e1 * (t 4T ) r2 4 e 2 * (t ) e * (t T ) 2 e 2 * (t 2T ) e * (t 3T ) 2 e 2 * (t 4T )
q1 0 = q 2 0
q1 1 q2 1
q1 2 q2 2
q1 3 q2 3
q1 4 q2 4
r1 0 r2 0
r1 1 r2 1
r1 2 r2 2
r1 3 r2 3
(2.40)
28
u1 * (t ) q1 0 u * (t ) = q 2 20
q1 1 q2 1
q1 2 q2 2
q1 3 q2 3
q1 4 q2 4
r1 0 r2 0
r1 1 r2 1
r1 2 r2 2
r1 3 r2 3
p1 1 0
p1 2 0
p1 3 0
p1 4 0
0 p2 1
0 p2 2
0 p2 3
u1 * (t T ) u * (t 2T ) 1 u1 * (t 3T ) 0 u1 * (t 4T ) p2 3 u 2 * (t T ) u 2 * (t 2T ) u * (t 3T ) 2 u 2 * (t 4T )
(2.41)
Consequently, Equation 2.41 can be directly implemented in any computational algorithm to realize the transfer function of the second-order double-input-doubleoutput digital filter described in Equation 2.34.
29
30
Chapter 3
Hardware Overview
3.1 Introduction
This chapter contains a brief overview and discussion of the hardware needed by the software in this project. The M68EBLP11 Evaluation Board Low Power (EBLP), which incorporates an MC68B11E9 resident microcontroller, was used as the development platform in this project. The monitoring and debugging program, BUFFALO (Bit User Fast Friendly Aid to Logical Operations), which resides in the microcontroller ROM, is used to load S-record code into the microcontroller memory. The M68EBLP11 evaluation board was operated in expanded mode due to insufficient on-chip RAM. A custom-designed memory expansion board was added to the M68EBLP11 evaluation board to form the complete development platform for the software system. The schematic and PCB diagrams for this memory expansion board is listed in Appendix D.
31
32
It is important to note the only the microcontroller features that are significant for this project were emphasized in this chapter. Various texts, such as the M68HC11 Reference Manual are available which will provide a detailed description of the M68HC11 family of microcontrollers.
Figure 3.1 MC68HC11E9 52 pin PLCC and 65 pin SDIP pin assignments.
(Reproduced from the M68HC11 reference manual)
Programming a microcontroller is different than programming a large PC. Although most programming is done in a high level language, some control bits in the microcontroller still has to be adjusted using low level assembly language to ensure proper operation of the device. It is absolutely necessary for the programmer to fully understand the microcontroller when writing code for it. Aspects such as I/O, time management and memory management are handled by the operating system on larger machines. There is no such operating system present on a M68HC11 microcontroller.
33
The C programming language was used for writing programs for the M68HC11. The reason for choosing C over assembly language for writing programs on the M68HC11, is that it is faster and easier to construct larger programs in C than in assembly language. C is also portable to other microprocessors. This means that by using a different C compiler, the same C program can be used on a different microprocessor. C compilers for the 68HC11 are widely available. The Archimedes ANSI C 68HC11 Compiler was used to compile the C programs for the 68HC11 in this project.
34
(3.1)
where n is the number of bits. The 68HC11 microcontroller support only 8-bit A/D conversion. The maximum resolution for the 68HC11 A/D converter can therefore be calculated as follow: Resolution = Analog input range 5.12 V = = 20 mV/bit 2n 28
(3.2)
A/D Registers and memory map locations for Port E. Table 3-1 shows all the address locations associated with Port E. The OPTION register will control the A/D conversion process. Only the ADPU bit and the CSEL (Clock Select) bit in the OPTION register will affect the A/D conversion. The ADPU bit must be set before A/D conversion can take place and the CSEL bit should remain clear for microcontroller clock speeds above 750 kHz.
35
The contents of the A/D control-status (ADCTL) register will control all the A/D conversions. A/D conversions are initiated by writing to the CFF (conversions complete flag) in the ADCTL register. The CFF flag will set every time an A/D conversion is complete. The SCAN bit will determine whether the A/D inputs are continuously scanned for updated values. If the SCAN bit is 0, A/D conversion will take place and the CFF flag will be set. If the SCAN bit is 1, A/D conversion will continually take place and the newer result will overwrite previous results. The MULT bit in the ADCTL register will determine whether A/D conversion will be performed in single channel mode or multiple channel mode. If the MULT bit is clear, only the analog input on a single channel will be converted. Four successive conversions will be performed, and the four results will be written in the four A/D result (ADR) registers. If the MULT bit is set, A/D conversion will be performed on each channel in a four-channel group. Table 3-2 shows the A/D channel assignments available to the user. The CD, CC, CB and CA bits are set in the ADCTL register.
36
CD 0 0 0 0 0 0 0 0
CC 0 0 0 0 1 1 1 1
CB 0 0 1 1 0 0 1 1
CA 0 1 0 1 0 1 0 1
A/D Channel PE0 PE1 PE2 PE3 PE4 PE5 PE6 PE7
ADRx result for MULT=1 ADR1 ADR2 ADR3 ADR4 ADR1 ADR2 ADR3 ADR4
The bit contents and the memory locations of these registers are listed in Table 3-3.
37
TxD
The contents of the BAUD register will determine the data transmission speed for both data transmission and reception. If the crystal frequency is 8 MHz, the SCP0 and SCP1 registers must be set and all the other bits in the BAUD register should be clear to ensure a data transmission speed 9600 bits per second. The Motorola reference manual can be consulted for other data transfer rates. In the SCCR1 register only the M bit and the T8 bit is used by the SCI. These bits are normally left alone. The M bit determines the length of the character to be transmitted. If the M bit is clear, the character transmitted will consist of a START bit, eight data bits and one STOP bit. The T8 bit will determine the number of STOP bits which will be used for data transmission. The default is one STOP bit which correspond to T8=0. Bits 0 and 1 of Port D are associated with the SCI. Bit 0 is the Rx data line which receive serial data and bit 1 is the Tx data line which transmit serial data. The TE (Transmit Enable) and RE (Receive Enable) bits in the SCCR2 register will determine whether bits 0 and 1 of Port D are used for serial communication or simple I/O. Serial communication will be enabled if the TE and RE bits in the SCCR2 register are set. The SCI status register, SCSR2, contains two bits that are associated to the SCI. The TDRE (Transmit Data Register Empty) bit will be set whenever the transmit data register is empty, which will inform the microprocessor of the status. If the transmit data register is full, TDRE will be set to 0 and data may not be send to the transmit
38
data register until the TDRE bit is 1 again. The TC (Transmit Complete) bit will be set to 1 if no data is being transmitted by the SCI. The SCI Data Register, SCDR will contain the data transmitted or received through the SCI. In the case of data reception, characters will arrive one bit at a time through PD0 and are shifted into the SCDR where the complete received byte can be read. In the case of data transmission, a character is placed into the SCDR, and it will be send through PD1 one bit at a time. It is also important to note that the SCDR is write-only for characters transmitted and read only for characters received through the SCI. Register name BAUD SCCR1 SCCR2 SCSR2 SCDR Memory Address $102B $102C $102D $102E $102F
Bit 2
Bit 1
Table 3-3 Registers associated with asynchronous serial communication on the 68HC11.
3.2.3
Timing is an essential part for most digital control applications and it is therefore important to fully understand the timing system of the 68HC11 when creating control applications for it. Timing is determined by a crystal oscillator, which normally oscillates at 8 MHz for most 68HC11 applications. The E clock determines the time of each instruction cycle on the 68HC11 is one quarter of the crystal frequency. The E clock is therefore 2 MHz if the crystal frequency is 8 MHz. The output of the E clock is connected to a 16-bit counter. The output of the 16-bit counter is connected to the TCNT register at memory locations $100E and $100F. Memory location $100E forms the upper 8 bits of the counter and memory location $100F forms the lower 8 bits of the counter. The resolution (the smallest time interval) of the TCNT counter is the inverse of the E clock frequency, which is 0.5 s. Table 3-3 shows the registers associated with the main timer system. The timer interrupt subsystems are discussed in section 3.2.4. The TOF (timer overflow) bit in
39
the TFLG2 register will set every time the 16-bit TCNT counter overflows. The TCNT will overflow every 32.77 ms if the resolution of the TCNT counter is 0.5 s. The TOF bit will therefore set every 32.77 ms. The TOI (Timer Overflow Interrupt) bit in the TMSK2 register will determine whether a timer overflow interrupt will occur every time the timer overflows. Interrupts are discussed in section 3.2.4 Register name TMSK2 TFLG2 Memory Address $1024 $1025 Bit 7 TOI TOF Bit 6 RTII RTIF Bit 5 PAOVI PAOVF Bit 4 PAII PAIF Bit 3 Bit 2 Bit 1 Bit 0 0 0 0 0 PR1 0 PR0 0
Table 3-3 Registers associated with the main timing system on the 68HC11.
Input Capture and Output Compare registers The input capture system is very useful for measuring periodic data such as rotation speed. The input capture registers (TIC1, TIC2, and TIC3) are 16-bit read-only registers and are connected to pins PA0, PA1 and PA2 respectively. When a signal edge occurs on one of these input pins, the contents of the timer counter TCNT will be copied into the corresponding register and the corresponding flag in the TFLG1 register will set. The ICxI bits in the TMSK1 register will determine whether the corresponding input capture interrupt is enabled. The EDGxA and EDGxB bits in the TCTL2 register will determine how the input signal on pins PA0, PA1 and PA2 will affect the input capture registers. EDGxB 0 0 1 1 EDGxA 0 1 0 1 Configuration Capture disabled Capture rising edges only Capture falling edged only Capture on rising and falling edges
The output compare system is useful for generating complex timing waveforms, driving stepper motors or for creating accurate PWM waveforms. The output compare system consists of five 16-bit registers. They are labeled TOC1 to TOC5. Whenever
40
the value in the TCNT counter equals the value in one of the output compare registers, a flag for the corresponding output compare register will be set. The corresponding interrupt service routine will be called if the flag in the TMSK1 register associated with the output compare are set. Output pins PA7 to PA3 can be controlled by the five output compare registers. The contents of the TCTL1 register will determine how the output pins PA3 to PA6 will be affected by TOC2 to TOC5 respectively. Output compare register 1 (TOC1) is different from the other four output compare registers and can be set to affect all of the output pins (PA3 to PA7).
41
Pseudo Interrupt Vector Serial communications interface (SCI) Serial peripheral interface (SPI) Pulse accumulator input edge Pulse accumulator overflow Clock counter (TCNT) overflow Timer output compare 5 (TOC5) Timer output compare 4 (TOC4) Timer output compare 3 (TOC3) Timer output compare 2 (TOC2) Timer output compare 1 (TOC1) Timer input capture 3 (TIC3) Timer input capture 2 (TIC2) Timer input capture 1 (TIC1) Real Time Interrupt IRQ XIRQ Software interrupt (SWI) Illegal Op code Computer operating properly Clock monitor
Memory Address Field $00C4-$00C6 $00C7-$00C9 $00CA-$00CC $00CD-$00CF $00D0-$00D2 $00D3-$00D5 $00D6-$00D8 $00D9-$00DB $00DC-$00DE $00DF-$00E1 $00E2-$00E4 $00E5-$00E7 $00E8-$00EA $00EB-$00ED $00EE-$00F0 $00F1-$00F3 $00F4-$00F6 $00F7-$00F9 $00FA-$00FC $00FD-$00FF
Table 3-5 Pseudo vector table for the 68HC11 (Reproduced from Greenfield, 1992)
42
43
Only the B part of the MC6821 PIA will be considered since the A and B part of the PIA is almost identical. The data register is essentially a buffer between the PIA I/O pins and the system data bus [11]. The direction of each bit in the data register is determined by the data direction register configuration. A 0 on a bit in the data direction register will make the corresponding data register bit an input, and vice versa for a 1 on the data direction register bit. The data register and the data direction register share the same address at 0xA102. Bit 2 in the control register (CRB2) will determine whether the data register or the data direction register will be activated when memory location 0xA102 is addressed. If CRB2=1 then memory address 0xA102 applies to the data register. The B section peripheral data lines (PB0-PB7) of can be programmed to act as inputs or outputs. For the purpose of this project, PB0-PB7 are programmed to act as output lines. The C code segment below will initialize the MC6821 to use PB0-PB7 as output lines.
#define PIAPB (*(volatile unsigned char*)(0xA102)) (unsigned char ) 0xA102; (unsigned char ) 0xA103; *(unsigned char ) 0xA103=0x00; /*Clear CRB*/ *(unsigned char *) 0xA102=0xFF; /*Set bits in DDRB*/ *(unsigned char *) 0xA103=0x04; /*Set DDRB bit for output*/
44
Chapter 4
4.1 Introduction
A Windows95 based digital control program was developed, which provides a userfriendly interface for implementing a wide variety of digital controller designs on the M68HC11 microcontroller. The Windows program, which is called HC11Control, will allow the user to select a digital controller design and to specify the control parameters for the particular controller. HC11Control will compile and download the complete control algorithm to the M68HC11 evaluation board on which the control program will be executed. The input and output ports can be saturated without incorporating any control wind-up. HC11Control can also monitor, record, plot and save the control system performance data for further analysis. It was decided to develop the GUI program to run under Windows 95, while C++ was selected as the programming language to be used for this project. The reason for
45
choosing the Windows 95 operating system is that Windows 95 is intended to operate PCs for many years to come [13]. Windows 95 is also a 32-bit operating system that makes windows programming easier. The C++ programming language was selected because it combines the elements of high-level programming languages with the functionalism of assembly language [13]. Consequently, C and C++ are one of the most powerful and most popular programming languages in use today. Borland C++Builder was used as the development environment for the Windowsbased software. Borland C++Builder is an object-oriented, visual programming environment for Rapid Application Development (RAD) for Microsoft Windows 95 and Windows NT. Using the Borland C++ rapid application development environment, application development time can significantly be reduced by reusing various predefined software components. The software structure for the Windows-based program is completely event-driven. The program exists out of a number of functions, which is called by the appropriate events. These event-driven functions are called event handlers. Each event handler will perform a specific task, such as downloading control parameters, monitor incoming serial data, etc. The Windows-based program will contain the predefined control algorithms for each controller type. The predefined control algorithms for the different types of controllers in the is already compiled and linked by the ANSI C 68HC11 Compiler, Linker and Burner from Archimedes Software Inc. The microcontroller control algorithms is stored in the S-record format in HC11Control. The user interface consists of four integrated modules. The four modules are: Test Input/Output Single-Input-Single-Output Digital Controller Double-Input-Double-Output Digital Controller PID Controller
The PID Controller uses the same software structure as the Single-Input-SingleOutput Digital Controller. The three PID controller parameters, K P , K I and K D are converted to the a and b vectors as described in section 2.3.2 before downloading the controller parameters to the microcontroller.
46
Serial communication with the microcontroller forms an essential part of the software system. Serial communications in Windows 95 is also significantly different from serial communications in the older 16-bit versions of Windows. The ZComm component for C++Builder provided by ZBuilder Software was used to create the asynchronous serial communication between the PC and the microcontroller. Only the essential concepts behind the Windows-based software are covered in this chapter. Software aspects such as the user interface and error handling will not be discussed. The complete program source code for the Windows-based software is listed in Appendix F. The following software concepts will be discussed in this chapter: Adding control parameters to the microcontroller code. Downloading the control algorithm to the microcontroller. Starting the control program on the microcontroller. Decoding the incoming controller performance data from the microcontroller. Monitor, record, plot and save the controller performance data to disk.
47
48
10
11
Convert entered control parameters to hexadecimal format
09
0A
0B
090A0B
S1060000090A0B
Calculate the checksum and add the checksum byte to the string
S1060000090A0BDB
S940000B5 . . .
49
The following program segment shows an example which will convert the value entered in the DigitalSetpointEdit textbox to an 8-bit unsigned character, which is placed in variable[0].
char variable[10]; char *psetpoint; int setpoint; psetpoint=DigitalSetpointEdit->Text.c_str(); setpoint=atoi(psetpoint); variable[0]=setpoint;
The next program segment is an example that will convert a real number entered in DigitalA0Edit text box to a ANSI/IEEE 754 floating point value stored in A0. The four bytes which represent the 32-bit floating point number will then be stored in variable[1], variable[2], variable[3] and variable[4]. The variable array will later be used to construct the S-record line, which will be added to the predefined microcontroller control algorithm.
char variable[10]; char *pstrA0; float A0; unsigned int adrA0; float *pA0; unsigned char A0_byte0; unsigned char A0_byte1; unsigned char A0_byte2; unsigned char A0_byte3; AnsiString ansiA0=DigitalA0Edit->Text; pstrA0=ansiA0.c_str(); A0=atof(pstrA0); pA0=&A0; adrA0=(int)pA0; (char *) adrA0; (char *) (adrA0+1); (char *) (adrA0+2); (char *) (adrA0+3); A0_byte3=*(char *) adrA0; A0_byte2=*(char *) (adrA0+1); A0_byte1=*(char *) (adrA0+2); A0_byte0=*(char *) (adrA0+3); variable[1]=A0_byte0; variable[2]=A0_byte1;
50
variable[3]=A0_byte2; variable[4]=A0_byte3;
It is important to note that the pointer, pA0 is a pointer to a float type. If the current value of pA0 is 2000 for example, the result of the expression pA0+1 will be 2005 and not 2001. Each time pA0 is incremented, it points to the next floating-point number and not to the next byte in the floating-point number. It is necessary though, to point to the next byte in the 4-byte floating-point number. One way to get around this problem is to declare an integer variable, adrA0, which acts as a substitute pointer to the floating-point number. The expression adrA0+1 will then point to the next byte of the floating-point number.
51
52
No
Yes
No
Yes Initialize the microcontroller to receive program data from the serial communication interface. End the download procedure
No
Yes
No
Yes Enable the user to start the control algorithm on the microcontroller End the download procedure
53
54
55
The consecution of the data may start and end at any point, but the data always have to be in the same order, for example: the following set of data is also valid for the single-input-single-output system:
Input from plant 253 Output to plant 251 Sample Frequency 252
A set of data will be received at every sample period. The sample frequency data will be used to calculate the period between each sample. The controller input and output values can therefore be represented as a function of time. The following code segment will decode the input serial data from for the doubleinput-double-output system into meaningful data. All the input data will be stored in the Databuffer array. The data in the Databuffer array will then be decoded in to the time, analoginput, freqinput, _8bitoutput and PWMoutput arrays respectively.
unsigned int len=strlen((char *)Databuffer); for (int scandata=0;scandata<10;scandata++) { if (Databuffer[scandata]==251) { for (unsigned int count=1+scandata;count<len;count=count+10) { samplefrequency=Databuffer[count]; float period=(float) 1/Databuffer[count]; if (timeindex==0) { time[timeindex]=0; } else { time[timeindex]=time[timeindex-1]+period; } timedisplay=time[timeindex]; timeindex++; } } else if (Databuffer[scandata]==252) {
56
for (unsigned int count=1+scandata;count<len;count=count+10) { analoginputdisplay=Databuffer[count]; analoginput[analoginputindex]=Databuffer[count]; analoginputindex++; } } else if (Databuffer[scandata]==253) { for (unsigned int count=1+scandata;count<len;count=count+10) { freqinputdisplay=Databuffer[count]; freqinput[freqinputindex]=Databuffer[count]; freqinputindex++; } } else if (Databuffer[scandata]==254) { for (unsigned int count=1+scandata;count<len;count=count+10) { _8bitoutputdisplay=Databuffer[count]; _8bitoutput[_8bitoutputindex]=Databuffer[count]; _8bitoutputindex++; } } else if (Databuffer[scandata]==255) { for (unsigned int count=1+scandata;count<len;count=count+10) { PWMoutputdisplay=Databuffer[count]; PWMoutput[PWMoutputindex]=Databuffer[count]; PWMoutputindex++; } }
57
4.6 Monitoring, Recording, Plotting and Saving the Controller Performance Data.
After the control algorithm has been started on the microcontroller, the user will be able to monitor, record, plot and save the controller performance data. The Borland C++ Builder Timer component will be used to display the incoming data from the microcontroller at regular intervals. The Timer component will call the DisplayTimer event-handler, which will display the latest valid incoming data from the microcontroller. The recording process works on the principle of filling an array sequentially with the incoming data from the microcontroller. The MonitorCommDataAvailable eventhandler, which was discussed in section 4.5, will continuously fill the data arrays with incoming serial data. The array index is a 16-bit number, which will overflow to zero every 65536 counts. The data in the array will therefore be overwritten after every 65536 data entries. If the user samples incoming serial data at a rate of 50Hz, then it is possible to record approximately 22 minutes of controller performance data. It is important to note that the recording of controller performance data can utilize a lot of memory. The stack is of a limited size and cannot be changed as the program runs. It is therefore necessary to use dynamic memory allocation for the data arrays since these arrays are of a substantial size. Dynamic allocation means that the memory utilized by large data arrays is allocated from the heap. The heap amounts to all the free physical RAM plus all the free hard disk space on the PC. In C++, memory is allocated dynamically by using the new[] operator. The line of code below illustrates how an array can by allocated dynamically.
double* analoginput = new double[65536];
All memory allocated with the new[] operator must be released by using the delete[] operator before closing the program. The line of code below illustrates the use of the delete[] operator.
delete[] analoginput;
58
The data array index will simply be reset to zero if the user activates the recording process. The data array will then be sequentially filled from zero. When the user stops the recording process, the array index number will be saved. All the data in the array up to the point of the saved index number will be copied into a new data array, which represents the recorded data. The recorded data can be saved in the form of a normal text file. This data can be loaded by any data processing or spreadsheet program for further analyzing purposes. The SaveButtonClick event-handler will determine whether an existing file will. If a text file is to be overwritten, the FileOverwriteDiaolgBox will be called and will prompt the user whether the save procedure may continue or not. Recorded data, such as plant feedback and control effort can be plotted versus time for quick control system analysis. The TXYPlot component was used for plotting the controller performance data from the provided data arrays. TXYPlot is a Borland C++ Builder VCL plotting component for graphing the recorded data. It is capable of displaying an arbitrary number of plots simultaneously, each with its own x and y data.
59
60
Chapter 5
5.1 Introduction
The microcontroller-based software will handle all interfacing to peripheral equipment and will execute the control algorithms for the different digital controller designs, which is discussed in Chapter 2. The plant- and controller states and other essential controller data, such as sampling frequency will be transmitted back to the host PC for monitoring purposes. The essential concepts behind the microcontroller-based software are discussed in this chapter. This software system consists of three independent modules, which falls into the following categories: Test Input/Output Single-Input-Single-Output (SISO) Digital Controller Multi-Input-Multi-Output (MIMO) Digital Controller
61
The MIMO Digital Controller is also capable of performing single-input-single-output (SISO) control. The SISO Digital Controller is however faster and easier to implement and is therefore not redundant. It is possible to embed the different program modules into one single program. The three categories of programs were kept separate for simplicity reasons. An embedded program will also be much bigger which will lead to more required microcontroller memory and longer download times from the PC. The major disadvantage of this individual program module scheme is that any software changes in one of the modules will affect only that specific module. It will therefore be more time-consuming to make any global changes to a specific category of programs.
62
Microcontroller Output
Step size Step delay time Output Lower Limit Time Figure 5.1 Output signal configuration for the Test Input/Output category of programs.
The user can choose between four different input/output combinations as listed in Table 5-1. Section 5.5 provides a detailed description of the microcontrollers interface to peripheral equipment while the complete program source code can be found in Appendix G.
63
I/O Combination 1 2 3 4
Associated Input Analog voltage input on pin PE5 Frequency input on pin PA0 Frequency input on pin PA0 Analog voltage input on pin PE5
Associated Output 8-Bit parallel output on pins PB0-PB7 on the MC6821 PIA 8-Bit parallel output on pins PB0-PB7 on the MC6821 PIA PWM output on pin PA5 PWM output on pin PA5
Table 5-1. Input/Output port configuration for the four Test Input/Output program
The interrupt service routines for frequency input measurement and PWM signal generation are discussed in detail in section 5.5.2 and section 5.5.4 respectively.
64
Input/Output combination = 1?
Yes
No
Initialize pins PB0-PB7 on the M6821 Peripheral Interface Adapter to serve as an 8-bit output port. Yes
Input/Output combination = 2?
No
Initialize the input capture 3 (IC3) PA0 pin to set the IC3F flag of falling edges
Initialize pins PB0-PB7 on the M6821 Peripheral Interface Adapter to serve as an 8-bit output port. Yes
Input/Output combination = 3?
No
Initialize the input capture 3 (IC3) PA0 pin to set the IC3F flag of falling edges
1A
1B
1C
65
1A
1B
1C
No
Input/Output combination = 1 or 4 ?
Yes
Read the latest frequencyinput period from memory, which was calculated by the frequency- input interrupt service routine.
2C
2A
2B
66
2C
2A
2B
Input/Output combination = 1 or 2 ?
Yes
No
Send Output the peripheral interface adapter
Calculate the desired PWM on-time and write this value to memory.
Send Input, Output and Sample Frequency values to the Serial Communication Interface (SCI).
No
Yes
67
Basic signal conversion can also be performed by the Digital Controller program module, such as: Analog to PWM conversion Analog to Digital conversion Frequency to PWM conversion Frequency to Digital conversion
Microcontroller
Serial Communication Interface
Serial data to PC
Saturation Input
Saturation
4
b 0 + b1 z +
+ b2 z
+ b3 z
+ b4 z
Output
a 0 + a1 z 1 + a 2 z 2 + a 3 z 3 + a 4 z 4
Setpoint
Figure 5.3 Schematic layout of the microcontroller-based digital controller software system
68
The Input/Output configuration of the SISO Digital Controller is the same the Test Input/Output section. The user can choose between four different input/output combinations as listed in Table 5-1. The software structure for the SISO Digital Controller module is illustrated in the flow diagrams shown in Figure 5.4. The complete program source code is listed in Appendix G.
Begin
Input/Output combination = 1?
Yes
No
Initialize pins PB0-PB7 on the M6821 Peripheral Interface Adapter to serve as an 8-bit output port. Yes
Input/Output combination = 2?
No
Initialize the input capture 3 (IC3) PA0 pin to set the IC3F flag of falling edges
1A
1B
1C
69
1A
1B
1C
Initialize pins PB0-PB7 on the M6821 Peripheral Interface Adapter to serve as an 8-bit output port. Yes
Input/Output combination = 3?
No
Initialize the input capture 3 (IC3) PA0 pin to set the IC3F flag of falling edges
Save sample start-time and calculate the desired sample end-time, which corresponds to the desired sample frequency. 4
Input/Output combination = 1 or 4?
No
Yes
Read the latest frequencyinput period from memory, which was calculated by the frequency- input interrupt service routine.
2A
2B
70
2A
2B
No Input < Input Lower Limit? Input > Input Upper Limit?
No
Yes
Yes
No
Yes
3A
71
3A
No
No
Save the current input and output values for the next difference equation calculations
Input/Output combination = 1 or 2?
Yes
No
Calculate the PWM ontime and write this value to memory. Send Output the peripheral interface adapter
Send Input, Output and Sample Frequency values to the Serial Communication Interface (SCI).
No
4 Yes Calculate the real sample period and save this value
72
1 + a1 12 z + a 2 12 z 1 2 b0 xy + b1 xy z + b2 xy z 1 + a1 21 z 1 + a 2 21 z 2 b0 12 + b1 12 z
1
+ b2 12 z
Output 1
Saturation Input 2
Saturation Output 2
The software structure for the MIMO Digital Controller module is illustrated in the flow diagram shown in Figure 5.6. The complete program source code is listed in Appendix G.
73
Initialize pins PB0-PB7 on the M6821 Peripheral Interface Adapter to serve as an 8-bit output port.
Initialize the input capture 3 (IC3) PA0 pin to set the IC3F flag of falling edges
Initialize pins PB0-PB7 on the M6821 Peripheral Interface Adapter to serve as an 8-bit output port.
Save sample start-time and calculate the desired sample end-time, which corresponds to the desired sample frequency.
Read the latest frequencyinput period from memory, which was calculated by the frequency- input interrupt service routine.
1A
74
No
No
Yes
Yes
No
No
No
Yes Frequency Error = Frequency Setpoint Frequency Input Frequency Error = Frequency Setpoint + Frequency Input
2A
75
No
Yes Analog Error = Analog Setpoint Analog Input Analog Error = Analog Setpoint + Analog Input
No
No
Yes
Yes
No
No
Yes 3A
Yes 3B 3C
76
3A
3B
3C
Save the current input and output values for the next difference equation calculations
Send Input, Output and Sample Frequency values to the Serial Communication Interface (SCI).
No
4 Yes Calculate the real sample period and save this value
77
Table 5-2 Input/Output port characteristics for the microcontroller software system.
78
conversion result will be stored in the four A/D result registers, ADR1 ADR3. The contents of the four A/D result registers can expected to be nearly identical. It is therefore only necessary to read one of the four A/D result registers. The following program segment shows the functions associated with A/D conversion.
/************************************************/ /* Function to initialize the analog-to-digital */ /* converter system */ /************************************************/ void initadc(void) { OPTION=0x80; /* Set bit 7 in OPTION high for A/D conversion */ } unsigned char adc(void) { /****************************************/ /* Analog to Digital conversion routine */ /****************************************/ unsigned char temp; ADCTL=0x5; /* Set analog inputs on PE5 (pin 46) */ while (ADCTL<128); /* Wait for input */ temp=ADR1; return temp; }
79
system to measure waveform periods up to 8 seconds with a bus speed of 2 MHz and a prescale factor of one. It is necessary to decide whether or not to count an overflow when an input capture occurs very close to a timer overflow. The software will handle this decision by looking at the MSB of the captured value. It is assumed that the timer-overflow interrupt service routine will be completed before the MSB of the free-running counter sets again. (The MSB of the counter will be set after 32768 TCNT counts, which correspond to 16.384 milliseconds.) If the timer overflow (TOF) bit and the input capture flag (IC3F) are both set and the captured value has a one in its MSB, then the capture occurred before the overflow. Conversely, if the TOF bit and IC3F are both set and the captured value has a zero in its MSB, then the capture occurred after the overflow and the case can be treated accordingly. It is also assumed that if an input capture and a timer-overflow happen in the vicinity of each other, the input capture will be serviced before the overflow. This assumption is confirmed by the interrupt priority resolution of the M68HC11, in which the input captures are handled prior to the timer overflow. The only way a timer overflow can be serviced before an input capture is if the timer overflow happened long enough before the input capture for the stacking and vector selection to be completed before any input capture is detected. The input capture interrupt service routine will check if a timer overflow occurred just after a leading edge or just before a trailing edge of a measurement period. If such an overflow is detected, the contents of the MSB of the captured value will then determine whether to include or exclude the overflow. The inittic3 function will initialize the microcontroller to react on a leading edge in the pulse input on pin PA0. The inputcapture function is the actual interrupt service routine, which will be activated when a leading edge on the input is detected. The inittimerinterrupt function will initialize the clock counter (TCNT) overflow interrupt and the timeroverflow function is the interrupt service routine which will be activated every time the TCNT clock counter overflows, which is every 32.768 ms.
80
Yes
No
No
Yes
Calculate the difference between the start-time of the current interrupt service route and the start-time of the previous interrupt service routine.
Save the start-time of this interrupt service routine for the calculations in the next input capture service routine.
81
It is not necessary to use the lower 8-bits of the TCNT timer. The resolution of the upper 8-bits of the TCNT counter is 2560.5 s = 0.000128 seconds which is still high enough to provide accurate timing. Because the 68HC11 is an 8-bit microcontroller, processing speed will significantly increase by leaving the lower 8bits of the counter out of the calculations. The period variable, which represents all the TCNT counts between two consecutive pulses, have to be a 32-bit long integer if the program uses all the 16 bits of the TCNT counter. Unknown problems with the compiler were also experienced when performing arithmetic operations with 32-bit long integers. The solution for this problem was to use only the upper 8-bits of the TIC3 register in the arithmetic operations. The upper 8-bits of the TIC3 register was defined as TIC3UP in the hc11e9.h header file. The following program segment shows the functions associated with the frequency measurement system. The global variables are declared as shown in the complete program listing in Appendix G. The hc11e9.h header file must also be included for register name definitions.
/*******************************************************/ /* Routine to initialize the timer overflow interrupt */ /*******************************************************/ void inittimerinterrupt(void) { TMSK2.bit7=1; /* Enable the timer overflow interrupt */ asm { PSHA /* Save the contents of accumulator A LDAA #0x80 /* Load accumulator A with the mask data STAA 0x1025 /* Reset TOF Flag by writing 1 to it PULA /* Retrieve the saved contents of accumulator A } /* Memory locations D0-D2 is a 3 byte pseudo interrupt vector for the timer overflow */ (char *) 0x00D0; /* Place jump instruction (op code 7E) in 00D0 */ *(char *) 0x00D0=0x7E; (int * ) 0x00D1; /* Place starting address of interrupt routine in D1&D2 */ *(int *) 0x00D1 = (int)timeroverflow; }
*/ */ */ */
82
/***************************************************/ /* Routine to initialize the input capture 3 (IC3) */ /* interrupt service routine. */ /***************************************************/ void initic3(void) { /* Set input capture in TIC3(PA0) on rising edges only */ /* (For falling edges, TCTL2=0x02) */ TCTL2=0x01; TMSK1.bit0=1; asm { PSHA LDAA #0x01 STAA 0x1023 PULA } /* Enable the interrupt mask for IC3 (PA0) */
/* Save the contents of accumulator A /* Load accumulator A with the mask data /* Reset input flag IC3F by writing 1 to it
*/ */ */
/* Memory locations E2-E4 is a 3 byte */ /* pseudo interrupt vector for IC3(PA0) */ /* Place jump instruction (op code 7E) in 00E2 */ (char *) 0x00E2; *(char *) 0x00E2 = 0x7E; /* Place starting address of interrupt routine in E3&E4 */ (int * ) 0x00E3; *(int *) 0x00E3 = (int)inputcapture; } /***************************************************/ /* TCNT counter overflow interrupt service routine */ /***************************************************/ void timeroverflow(void) { timeroverflows++; /* Timeroverflow counter for inputcapture */ sampletimeroverflows++; /* Counter for sample period regulation */ asm { PSHA LDAA #0x80 STAA 0x1025 PULA RTI } } /* Save the contents of accumulator A /* Load accumulator A with the mask data */ */
/* Reset the timer overflow flag (TOF) */ /* Retrieve the saved contents of accumulator A */ /* Return from Interrupt Service Routine */
83
/***************************************************/ /* Frequency Measurement Interrupt Service Routine */ /***************************************************/ void inputcapture(void) { asm /* Reset input flag IC3F by writing 1 to it */ { PSHA LDAA #0x01 STAA 0x1023 PULA } /* Save the contents of accumulator A /* Load accumulator A with the mask data */ */
if (TFLG2BYTE>=0x80 && TIC3UP<=0x80) { timeroverflows++; asm { PSHA /* LDAA #0x80 /* STAA 0x1025 /* PULA /* } }
Save the contents of accumulator A Load accumulator A with the mask data Reset IC3F flag by writing 1 to it Retrieve the saved contents of accumulator A
*/ */ */ */
overflow=(unsigned int) timeroverflows*0x100; /* Copy upper 8-bits of the TIC3 register into tic3upint */ tic3upint=(unsigned int)TIC3UP; /* The overflow variable is the number of the upper 8-bit counts of the TNCT counter that correspond to the amount of TNCT overflows recorded in the timeroverflows variable */ period=(unsigned int) (overflow+tic3upint-time1); /* The period variable is the total number of TCNT upper 8-bit counts between this interrupt service routine and the previous one.*/ time1=(unsigned int)TIC3UP; timeroverflows=0; asm RTI; } /* Save the endtime of this ISR */
84
Assigning an 8-bit value to PIAPORTB can therefore access Port B on the MC6821 PIA.
85
create the PWM waveform. pwmperiod and pwmtimeon are 16-bit unsigned integers which will determine the period and on-time of the PWM waveform. The value of pwmtimeon must be smaller than pwmperiod. The code segment below shows the initpwm and oc3interrupt functions. The global variables are declared as shown in the complete program listing in Appendix G. The hc11e9.h header file must also be included for register name definitions.
/************************************************/ /* Routine to initialize the microcontroller to */ /* generate a PWM signal on OC3 (PA5) */ /************************************************/ void initpwm(void) { /* Memory locations D9-DB is a 3 byte pseudo */ /* interrupt vector for TOC3(PA5) */ (char *) 0x00D9; /* Place jump instruction (op code 7E) in 00D9 */ *(char *) 0x00D9 = 0x7E; /* Place starting address of interrupt routine in DA&DB */ (int * ) 0x00DA; *(int *) 0x00DA = (int)oc3interrupt; OC1M.OC1M5=1; TMSK1.OC3I=1; OC1D.OC1D5=1; TCTL1.OL3=1; TOC1=TCNT+50000; TOC3=TOC1+1; } /* couple OC1 to OC3 */ /* /* /* /* /* enable the OC3 interrupt */ turn on OC3 when OC1 occurs */ toggle OC3 when OC3 occurs */ initialize OC1 */ initialize OC3 */
/*****************************************************/ /* OC3 Interrupt Service Routine. This routine will */ /* generate a PWM signal on OC3 (PA5) */ /*****************************************************/ void oc3interrupt(void) { asm { PSHA /* Save the contents of accumulator A LDAA #0x20 /* Load accumulator A with the mask data STAA 0x1023 /* Reset OC3F flag by writing 1 to it
*/ */ */
86
PULA /* Retrieve the saved contents of accumulator A */ } toc1=TOC1+pwmperiod; TOC1=toc1; toc3=toc1+pwmtimeon; TOC3=toc3; asm RTI } /* Return from Interrupt Service Routine */
The desired PWM on-time and PWM period can be changed in the main program loop by changing the pwmtimeon and pwmperiod variables. These variables are 16-bit integers. There is a possibility that the OC3 interrupt service routine can be called while changing the pwmtimeon or pwmperiod variable so that only one of the two bytes in the pwmtimeon or pwmperiod variable are updated. Consequently, the 16-bit variable will be corrupt while executing the 0C3 interrupt service routine. It is therefore important to disable the system interrupts while any changes are made to the pwmtimeon and pwmperiod variables.
87
The reason why this is the wrong way of clearing timer flags is that the Archimedes ANSI C compiler generates a bit set (BSET) instruction from the bit-field feature. The BSET instruction is a read-modify-write instruction that reads the operand, performs an OR operation with a mask having ones in the bits to be set, and writes the resulting value back to the operand address. Using the BSET instruction on the TFLG1 and TFLG2 registers will clear all flags that are set at the time the operand is read. The correct way of clearing the IC3F flag in C is illustrated in the following code segment.
asm { PSHA LDAA #0x01 STAA 0x1023 PULA }
/* /* /* /*
Save the contents of accumulator A Load accumulator A with the mask data Reset IC3F by writing 1 to it Retrieve the saved state of accumulator A
*/ */ */ */
88
89
The actual sampling period will by slightly longer than the desired sampling period due to the calculation of the actual sample period after the sample period has elapsed. The calculation of the actual sample period was found to be 48 time-steps long, which correspond to 24 microseconds on the M68HC11 with a bus speed of 2 MHz. The actual sample period will only be significantly larger than the desired sampling period when the desired sample period cannot be met due to insufficient processing speed of the main program loop. In such a case, the program will not enter a wait state at the two while loops, but will jump immediately to the next instruction, which is the calculation of the actual sample period. The timeroverflow interrupt service routine used for the sample frequency regulation is the same interrupt service routine used for measuring the input frequency on pin PA0.
90
Start11.c
Lustart.h
Non_bank.sgm
Program.c
Hc11e9.h
Compiler
Compiler
Ansi.lib
Start11.o
Program.prm
Program.o
Linker
Program.abs
Burner
Program.s19
Figure 5.8 The process for creating an executable S-record file from the C source code.
91
The following files are needed for compiling and linking a C program. The contents of Start11.c, Lustart.h, Hc11e9.h and Non_bank.sgm are listed in Appendix G. Start11.c Program.c Program.prm Lustart.h Hc11e9.h Non_bank.sgm Ansi.lib Default startup code for the 68HC11. Main control algorithm source code. Linker parameter file. Header file which defines the startup description. Header file for the 68HC11 register definitions. HC11 Small and medium memory model. ANSI C libraries.
92
Chapter 6
6.1 Introduction
The Windows-based software, which is called HC11Control, will provide the control engineer with a user-friendly interface for implementing a controller design on the M68HC11 microcontroller. This chapter will discuss HC11Control from a users point of view. No microcontroller programming knowledge is required by the user. HC11Control will automatically generate and download the control algorithm to a microcontroller from the specified control parameters. It is still the responsibility of the user to determine and specify the correct control parameters for a specific control application. HC11Control is started the same way as any other Windows-based application. Figure 6.1 shows the startup window of HC11Control.
93
The startup window provides a choice between four different program modules: Test Input/Output. Single-Input-Single-Output (SISO) Digital Controller. Multi-Input-Multi-Output (MIMO) Digital Controller. PID Controller.
Each module will provide the user with a controller panel, which incorporates all the appropriate controls and edit boxes unique to that particular controller design.
94
Two input ports and two output ports are available for the user. The user can configure the output signal to the plant as illustrated in Figure 6.3. The corresponding input signal from the plant can then be monitored, recorded, plotted and saved.
Microcontroller Output
Time
Figure 6.3 Output signal configuration for the Test Input/Output category of programs.
95
b 0 + b1 z +
+ b2 z
+ b3 z
+ b4 z
Output
a 0 + a1 z 1 + a 2 z 2 + a 3 z 3 + a 4 z 4
Setpoint
The input and output ports are saturated with the Upper Limit and Lower Limit values in HC11Control. The summing junction can be toggled between positive and negative, which adds more flexibility to the controller.
96
Controller
Analog Setpoint Saturation
Analog Input
8-Bit Output
e1 e2
Frequency Input Saturation
b0 11 + b1 11 z 1 + b2 11 z 2 1 2 1 + a1 11 z + a 2 11 z b0 21 + b1 21 z 1 + b2 21 z 2 1 2 1 + a1 21 z + a 2 21 z b0 12 + b1 12 z 1 + b2 12 z 2 1 + a1 12 z 1 + a 2 12 z 2 b0 xy + b1 xy z 1 + b2 xy z 2 1 + a1 21 z 1 + a 2 21 z 2
u1 u2
Saturation PWM Output
Frequency Setpoint
The multivariable digital filter transfer function can be described as follow: B11 u1 A11 u = B 2 21 A21 where B xy Axy b0 xy + b1 xy z 1 + b2 xy z 2 1 + a1 xy z
1
(6.1)
+ a 2 xy z
(6.2)
97
The six parameter vectors P1 , Q1 , R1 , P2 , Q2 and R2 are defined as P1 = A11 A12 = 1 + p1 1 z 1 + p1 2 z 2 + p1 3 z 3 + p1 4 z 4 Q1 = B11 A12 = q1 0 + q1 1 z 1 + q1 2 z 2 + q1 3 z 3 + q1 4 z 4 R1 = A11 B12 = r1 0 + r1 1 z 1 + r1 2 z 2 + r1 3 z 3 + r1 4 z 4 P2 = A21 A22 = 1 + p 2 1 z 1 + p 2 2 z 2 + p 2 3 z 3 + p 2 4 z 4 Q2 = B21 A22 = q 2 0 + q 2 1 z 1 + q 2 2 z 2 + q 2 3 z 3 + q 2 4 z 4 R2 = A21 B22 = r2 0 + r2 1 z 1 + r2 2 z 2 + r2 3 z 3 + r2 4 z 4 It is the responsibility of the user to calculate the six parameter vectors P1 , Q1 , R1 , P2 , Q2 and R2 from the transfer function shown in Equation 6.1. The vectors P1 , Q1 , R1 , P2 , Q2 and R2 vectors can then be directly implemented on the double-inputdouble-output controller as illustrated in Figure 6.7. A more complete description of the double-input-double-output system can be found in section 2.4. (6.3)
98
PID Controller
Proportional Term
KP
K IT z +1 2 z 1
Derivative Term
+ + +
Saturation Output
Setpoint
K D z 1 T z
Figure 6.8 Schematic layout of the digital PID controller.
The input and output saturation are specified by the Upper Limit and Lower Limit values for the input and output respectively. The input summing junction can be toggled between positive and negative with the Feedback Mode option. The three PID parameters, K P , K I and K D can be any floating-point values, while the sample period, T is determined by the Sample Frequency value.
99
100
Record the controller Save the recorded controller performance performance data data
101
A reset action on the microcontroller will force the microcontroller unit to undertake a set of initial conditions and begin executing instructions from a predetermined starting address and will start downloading the control algorithm from the PC. After the downloading procedure is finished, the user will be prompted whether the downloading procedure was successful or not as shown in Figure 6.13.
Figure 6.11
Figure 6.12 Window to prompt the user that the downloading procedure cannot continue due to a running program on the microcontroller
102
Download error
Download successful
103
Figure 6.14 Window to prompt the user that a program on the microcontroller is currently being executed and that the new program cannot be started.
Figure 6.15 Window to prompt the user to reset the microcontroller before starting to execute the new control algorithm.
104
If data is recorded at a sample rate of 50Hz, the maximum allowed recording time will be 1310 seconds, which is approximately 22 minutes. If the Record data when program starts option under the Options menu is checked, the data recording procedure will start immediately after the control algorithm has been started. The stop button on the HC11Control toolbar, which allows the user to stop the recording process, will be enabled while controller data is being recorded.
105
The value-boxes at the left side and at the bottom of each plot display the cursor position on the plot. The plot can be drag-zoomed by using the alt key and the mouse button simultaneously or can be zoomed the conventional way by using the on-form
106
buttons. The ctrl key and the mouse button can be used to select a box in which the plot will be zoomed. The plot can be dragged or scrolled by using the on-form buttons or by dragging the mouse over the plot while keeping the mouse button down. The save button on the HC11Control toolbar will allow the user to save the recorded controller performance data in standard ASCII format. Each parameter data series will be saved in a separate column. Any standard data analysis or spreadsheet program will be able to retrieve the saved data.
107
108
Chapter 7
7.1 Introduction
Servomotors form an important part of many modern control systems where lateral or rotational motion of a system is to be controlled. Various techniques for DC motor speed control have been developed. One of the most common methods for measuring rotational motion is by measuring the time duration for each complete revolution. The power supplied to the DC motor can be varied with a variable analog input signal or by using a Pulse Width Modulation (PWM) supply as demonstrated in this Chapter. The functionality of the Windows-based software system was demonstrated by modeling and controlling the speed of a permanent magnet DC motor. Some images of this application example can be found in Appendix E
109
1k2
2N3904 TIP31
Figure 7.1 DC Motor speed control with the PWM Darlington Circuit
110
5 V
5 k 6
LED
Aluminum disk
111
Figure 7.4 HC11Control setup for generating a PWM ramp signal on PA5 and to measure a frequency input on PA0
The linearity between the PWM input and the speed output of the DC motor was tested by using the Test Input/Output section in the HC11Control program. The Test Input/Output section is useful for testing the interface between the controller and plant
112
and to establish the steady-state input/output characteristics of the plant. The Test Input/Output section can also be used to produce a known step input to the plant and to measure the corresponding plant response to the step input. At first, HC11Control was set up as shown in Figure 7.4 to generate a linear output signal (also known as a ramp signal) to the DC motor. The corresponding rotational speed of the DC motor was recorded in HC11Control and saved to disk. Figure 7.5 shows the recorded result.
Figure 7.5 Steady state Input/Output characteristics of the DC motor when subjected to a PWM input. The percentage pulse width is the PWM value2.56.
It can be seen from Figure 7.5 that the relation between the motor speed and the PWM input is not linear. This non-linear characteristic between the motor speed and the PWM input is to be expected mainly because of higher motor friction at high speed. It is important to note that the PWM input is proportional to the power supplied to the DC motor. The relationship between the PWM input and the motor torque at a constant motor speed can be expected to be linear. The open-loop transfer function for a step input between 8-bit PWM values of 40 and 90 was obtained by setting the HC11Control program as in Figure 7.6. The program setup shown in Figure 7.6 will generate a PWM step input between 8-bit values of 40 and 90 at a 5-second interval. This means that the pulse width will toggle between 15.6% and 35.2% every 5 seconds. (A 100% pulse width, which means that the PWM signal will remain high, correspond to a PWM value of 256.)
113
Figure 7.6 HC11Control setup for generating a PWM step between 8-bit values of 40 and 90 and to measure a frequency input on PA0
Figure 7.7 shows the recorded results obtained from HC11Control. This result represents the open-loop step response of the DC motor.
32
27
63.2 % Line
22 Time Constant
17
12
7 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 1.1 1.2
Time (seconds) Figure 7.7 DC Motor step response for a PWM step between 8-bit values of 40 and 90.
114
The transfer function of the DC motor speed can be modeled as a first-order transfer function. Thus, the transfer function of the DC motor can be written as G (s ) = Kb s +b
(7.1)
where K is the DC motor steady-state gain constant and b is the inverse of the motor time-constant. The value of K can be obtained by calculating the average gradient of the PWM input versus the motor speed in Figure 7.7. The average gradient between a PWM input of 40 and 90 were calculated as follow K= Motor Speed 34 6 = = 0.56 PWM Input 90 40
(7.2)
The DC motor time-constant is the time required for the motor speed to reach 63.2% of its final value. The motor time-constant is calculated as shown in Figure 7.7. The final value is 35Hz and the starting value is 6Hz. 63.2% of this range is 24Hz. The time to reach 24 Hz is 0.2 seconds. The small delay of 0.04 seconds is not integrated into the DC motor model, but is kept separately as shown in Figure 7.8. The b parameter is then calculated. b= 1 = 1 =5 0 .2
(7.3)
Step Input
-28 Offset
output.mat To File
Figure 7.8 Simulink model for the DC motor open-loop step response
115
The DC motor transfer function can therefore be written as G (s ) = Kb 0.56 5 2 .8 = = s+b s+5 s+5
(7.4)
This transfer function was verified with a Simulink model shown in Figure 7.8. The comparison between Simulink model and the actual motor step response is shown in Figure 7.9.
Time (seconds) Figure 7.9 DC Motor step response for a PWM step between 8-bit values of 40 and 90.
116
(7.5)
117
Step Input Input Graph + 2.16-1.84z-1 1-z-1 Digital Controller -28 Offset Saturation1 Quantizer1 + + Saturation2 2.8 s+5 DC Motor Delay Output Graph
Saturation3
Quantizer2
Zero-Order Hold
Figure 7.11
118
The control algorithm was downloaded from the PC to the microcontroller and the control program was started. The record data when control program starts option in the Options menu must be checked to ensure that incoming data from the microcontroller will be recorded by HC11Control as soon as the control algorithm starts. Figure 7.12 and Figure 7.13 compares the control system step response between the Simulink model and the data logged from HC11Control. The overall comparison is good. The only area of significant difference between the Simulink model and the real system is in the first 0.1 seconds. This difference may be a result of different initial conditions between the two systems or due to the starting friction of the DC motor, which is not modeled in the Simulink model. The DC motor speed in the Simulink model is also approximated as a linear model, which is slightly different from the real DC motor. The constant difference in controller output as shown in Figure 7.13 is a result of the linear approximation of the DC motor speed in the Simulink model. The graph shown in Figure 7.5 shows the decrease in the motor-speed gradient as the PWM output increases. The result shown in Figure 7.13 indicates that the steady state gain of the real DC motor is still higher at an 8-bit PWM output value of around 70 than the linear approximation in the Simulink model.
25 20 15
Time
Figure 7.12 DC motor speed step response when controlled with a PID controller.
119
80
Controller Output
70
60
50
120
Chapter 8
8.1 Introduction
Low-pass filtering is often used in low-frequency measurement systems where high frequency noise in the measurement signal has to be filtered out before the measurement signal can be processed by a computer system. PID controllers in particular require extensive noise filtering mainly because of the noise- amplifying characteristics of the differential term in the PID controller. This example will demonstrate the application of a fourth-order low-pass digital Chebyshev type I filter with a cutoff frequency of 5Hz. The Chebyshev type I filter is equiripple in the passband and monotonic in the stopband and provides a relative steep rolloff characteristic for the specific filter order [16]. The cutoff frequency is the frequency at which the filters magnitude response is equal to 3dB [3].
121
Figure 8.1 illustrates a typical measurement system that is used by numerous control applications. Most processes and their sensors are analog in nature. The sensor usually produces a relatively low output voltage and picks up high-frequency electrical noise from nearby electrical lines and equipment. The low voltage output characteristics of the sensor will normally result in low signal/noise (S/N) ratios. The low-pass filter will act as a signal conditioner by eliminating the unwanted noise in the measured signal.
Plant
x
Noise
Sensor
mV
Amplifier
Low-pass Filter
Computer System
Figure 8.1 A measuring system composed of several system elements including a low-pass filter. The signal is shown as it leaves each element.
122
Microcontroller
Serial Communication Interface
Serial data to PC
Saturation Input
Saturation
4
b 0 + b1 z +
+ b2 z
+ b3 z
+ b4 z
Output
a 0 + a1 z 1 + a 2 z 2 + a 3 z 3 + a 4 z 4
Setpoint
Figure 8.2
The cheby1 function in the Matlab Signal and Systems Toolbox was used to find the a and b vectors for the Chebyshev type I digital filter. For data sampled at 50Hz, with 3 dB of ripple in the passband, and a cutoff frequency of 5Hz, the a and b vectors of the fourth-order low-pass Chebyshev type I digital filter are calculated in Matlab as follow:
order=4; passband_ripple=3; cutoff_frequency=5; sample_frequency=50; pi=3.141592654; [b,a] = cheby1(order,passband_ripple, (2*pi*cutoff_frequency)/(pi*sample_frequency));
123
The Matlab cheby1 function yields the following a and b vectors for the fourth-order low-pass Chebyshev type I digital filter for data sampled at 50Hz, with 3 dB of ripple in the passband, and a cutoff frequency of 5Hz. a0 = 1 a1 = 3.2692 a2 = 4.3403 a 3 = 2.7419 a4 = 0.6946 b0 = 0.0011 b1 = 0.0042 b2 = 0.0063 b3 = 0.0042 b4 = 0.0011
The frequency response of this filter is shown in Figure 8.3. Note that the unit of the frequency axis is radians per second and not Hz.
Gain dB
-50
-100 0 10
10
10
10
Frequency (rad/sec)
Phase deg
10
10
10
Frequency (rad/sec) Figure 8.3 Frequency response of a fourth-order low-pass Chebyshev type I digital filter
124
The a and b vectors for the fourth-order low-pass Chebyshev type I digital filter are entered into the HC11Control program as shown in Figure 8.4.
Figure 8.4 HC11Control setup for implementing the 5Hz cut-off Chebyshev type I digital filter.
The low-pass digital filter was tested by connecting the microcontroller analog input to a function generator. The function generator was configured to produce a frequency sweep between 0.01 and 10 Hz. Figure 8.5 shows the results obtained from this test. This digital filter performed as expected and corresponds well with the bode diagram shown in Figure 8.3. This filter was also tested with a very noisy 2Hz sine wave. A Gaussian noise generator was used to add noise to the sine wave. Figure 8.6 illustrates the noisefiltering abilities of this digital filter.
125
130
110
90
70
50 0 1 2 3 4 5 6 7
Frequency (Hz)
Figure 8.5 Frequency response results obtained from HC11Control for the 5Hz cutoff frequency Chebyshev type I digital filter
8-Bit value
Tim e (seconds )
Figure 8.6 Noise-filtering with the low-pass Chebyshev type I digital filter
126
Chapter 9
9.1 Introduction
This example will demonstrate temperature control of an open system in which the system mass flow rate and the heat input rate can be regulated by the microcontroller. The Multi-Input-Multi-Output section in HC11Control will be used for implementing a multivariable controller on the system. The controller will be configured read a single temperature input and to generate two independent outputs. The two controller outputs will regulate a heating coil and a DC fan respectively. The image of this application example can be found in Appendix E
127
Two system modeling approaches will be discussed in this example: i. Analytical System modeling ii. Experimental System Modeling. The analytical modeling approach will be based on the principle of conservation of energy, which is known as the first law of thermodynamics. A state-space model will be derived that will contain all the different system parameters such as the system volume, airflow rate, heat input rate, thermal conductivity etc. Accurate system parameters are sometimes difficult to obtain. Experimental system modeling is often the most effective way of obtaining an accurate system model. The interface between the microcontroller and the plant will be discussed before any experimental modeling will be discussed. Once the interface is established, the systems transient response data can be obtained from step inputs. The systems transient response data is relatively easy to obtain and is sufficient for modeling firstorder systems experimentally. A more complex multivariable system can be identified by using offline least squares techniques from the system input and output data. The least squares technique will only yield a useful solution if either a white noise random sequence or a pseudo random binary sequence (PRBS) is applied to the plant input [17]. The system will be modeled in Simulink as a SISO system with a heat input and constant airflow. The PI controller parameters for the SISO heating process will be verified with the Simulink model. The same PI controller parameters will then be implemented on the fan, which will be used for the cooling process. Consequently, the system will be controlled by two independent PI algorithms: One PI algorithm for heating and one PI algorithm for cooling.
128
& Q i
& he , m
& hi , m
& = Heat Loss (W ) Q c & = Heat Input (W ) Q i &= Mass Flow Rate (kg s ) m hi = Specific Input Enthalpy ( J kg )
The system described in Figure 9.1 can be modeled analytically by using the first law of thermodynamics. The first law is based on the principle of conservation of energy, which is a basic law of physics [18]. It is assumed that the system contains a fixed mass of air, V and that the gas constant c p is constant for all the temperatures considered. It is also assumed that the energy effect of the difference in the input and output heights are negligible. Thus the first law of thermodynamics give: Heat Input Rate Heat Output Rate = Heat Accumulation Rate dh &+ m &+m & & [Q h ] [Q h ] = V dt
i i c e e
(9.1)
129
Given that c p is constant for all the temperatures considered, we can write h = c pT where c p = Constant pressure specific heat ( J kg K) T = Temperature (K) The heat loss across the walls occurs by means of conduction and can be defined as & = Te Ti Q c L kA where L = Wall Thickness (m) k = Thermal Conductivity (W/m K) A = Wall Surface Area ( m 2 ) Ti = Inlet air temperature (K ) Te = Outlet air temperature ( K ) Substituting Equations 9.2 and 9.3 into Equation 9.1 gives T T &+m & c T ] [Q L kA
e i p i
(9.2)
(9.3)
dT & c p Te = Vc p e +m dt
(9.4)
Rearranging Equation 9.4 gives the system state-space representation in the form &= Ax + Bu . x kA & cp m dTe T kA L T + 1 &+m & = c p Ti + i e Q i dt Vc p L Vc p
(9.5)
130
(9.6)
5V 5V 10K
LM35
LM324 +
1K 0-5V
The 0-5V output form the temperature measurement circuit was connected directly to the M68HC11 analog input (pin PE5). The A/D input can be permanently damaged by applying a negative voltage to the input pin, especially if the negative voltage is from a low impedance source [19]. The 1K resistor is included to protect the A/D converter input against excessive input currents.
131
1 1 2 8
16
5k6 10 F
2 15
ICL7660
7 6
+
3 4 14
5k6 DAC0808
13 12 11 10 9
10F
3 4
5V
5k6
5 6 7 8
5k 9V
8
L272
7
2 1 4
5k
Figure 9.3 8-Bit Digital-to-Analog (D/A) converter for fan speed control
132
12 V
1k2
2N3904 TIP31
Heating Coil
Figure 9.4 Heating coil power supply regulation with the PWM Darlington Circuit
133
Air Out
Air In Fan
Temperature Sensor
Heating Coil
The heating coil and the fan were modeled as two separate systems. It is assumed for this application that When the system temperature is lower than the desired system temperature, the fan will be running at minimum speed while the heating coil will be controlled by the microcontroller. When the system temperature is higher than the desired system temperature, power supply to the heating coil will be disabled while the fan will be controlled by the microcontroller.
134
The heater transfer function was determined by subjecting the heating element to a step input while the fan speed was kept at its minimum speed. HC11Control was configured as shown in Figure 9.6. The result obtained from a step in the heating power is shown in Figure 9.7. The y-axis in Figure 9.7 shows the 8-bit analog input. The corresponding temperature in C is the 8-bit value times 0.178 as shown in Equation 9.6
Figure 9.6 HC11Control setup for generating a PWM step on the heating coil
Figure 9.7 Open-loop temperature step response for the open system
135
(9.7)
where K is the steady-state gain constant and b is the inverse of the process timeconstant. The steady-state gain for the heating process can be calculated as follow: K= = Final Temperature Initial Temperature PWM Step Size 0.178 (159 113) = 0.0546 150
(9.8)
The time-constant for the heating process can be determined from Figure 9.7 as approximately 111 seconds. The b parameter can be calculated as follow. 1 1 = = 0.00901 Process Time Constant 111
b=
(9.10)
The heating process transfer function can therefore be expressed as Kb 0.0546 0.00901 0.00049 = = s+b s + 0.00901 s + 0.00901
G (s ) =
(9.11)
20.5 Ambient Temperature Open System Quantizer1 0.00049 Step Input Saturation1 s+0.00901 Delay + + Sum
output.mat To File
Graph
Figure 9.8 Simulink model for the heating process open-loop step response
136
The transfer function for the heating process was verified with the Simulink model as shown in Figure 9.8. The comparison between the Simulink model and the actual heating process is shown in Figure 9.9.
Temperature (C)
Time (Seconds)
Figure 9.9 Open loop step response for the heating process
137
(9.12)
138
Once the values of the a and b vectors are known, the multivariable P,Q and R vectors can be calculated as described in section 6.4. Equation 6.3 yields the following P, Q and R vectors for a PI control action on the heating process. P1 = 1 Q1 = 0 R1 = 0 P2 = 1 z 1 Q2 = 2.002 1.998 z 1 R2 = 0 (9.13)
Figure 9.10 HC11Control setup for implementing a PI control scheme on the heating coil and keeping the fan speed constant.
139
Figure 9.11 shows the closed-loop system temperature step response. The y-axis shows the 8-bit analog input form the temperature sensor. The corresponding temperature in C is the 8-bit value times 0.178 as shown in Equation 9.6. Figure 9.12 shows the control effort, which corresponds to the PWM current supplied to the heating coil. The y-axis in Figure 9.12 shows the 8-bit PWM value, which range from 0% to 100% heating power.
Figure 9.12 Closed-loop control effort step response for a PI control action
The results shown in Figure 9.11 and Figure 9.12 was verified with the Simulink model shown in Figure 9.13. Figure 9.14 and 9.15 shows the comparison between the results from the Simulink model and the experiment. The comparison between the experimental data and the Simulink model is good. This result is a good indication that the microcontroller-based controller performs as expected.
140
Open System Control Effort Quantizer1 0.00052 Saturation1 s+0.00901 Delay + + Sum
Temperature Out
Temperature step response for a closed-loop PI control action 28 27 26 Temperature (C) 25 24 23 22 21 20 19 18 0 100 200 300 400 500 Time (Seconds) 600 700 800 Simulink Model Experiment
Figure 9.14 Temperature step response comparison between the Simulink model and the experiment for a PI control action on the heating process .
141
Control effort step response for a closed-loop PI control action 200 8-Bit PWM output value
150
100
50
0 0 100 200 300 400 500 Time (seconds) 600 700 800
Figure 9.15 Control effort step response comparison between the Simulink model and the experiment for a PI control action on the heating process .
142
The a and b vectors for the heating process will remain unchanged and will be the same as in section 9.5.1. The a vector for the cooling process will be the same as the a vector for the heating process while the b vector of the cooling process will be the negative of the b vector for the heating process. As in section 9.5.1, the PI control parameters for the heating process are expressed as follow: K IT KD 0.08 0.05 = 2.002 + =2+ 2 2 T K T 2K D 0.08 0.05 = 2 + = 1.998 b1 21 = K P + I 2 2 T K 0 =0 b2 21 = D = 0.05 T a 0 21 = 1 b0 21 = K P + a1 21 = 1 a 2 21 = 0
(9.14)
The PI control parameters for the cooling process are expressed as follow: KIT KD 0.08 0.05 = 2 = 2.002 2 2 T K T 2K D 0.08 0.05 = 2 = 1.998 b111 = K P I + 2 2 T K 0 =0 b2 11 = D = 0.05 T a 0 11 = 1 b011 = K P a111 = 1 a 2 11 = 0 The convention for the subscripts of the a and b vectors are discussed in section 6.4. Equation 6.3 yields the following multivariable P, Q and R vectors from the a and b vectors.
(9.15)
143
Q1 = 2.002 + 1.998z 1 R1 = 0 P2 = 1 z 1 Q2 = 2.002 1.998 z 1 R2 = 0 These P, Q and R vectors can be directly implemented in HC11Control as shown in Figure 9.16. The 8-bit output value, which corresponds to the fan speed, is limited between 8-bit values of 100 and 250. This will ensure that the fan will keep running at all times. (9.16)
Figure 9.16 HC11Control setup for implementing a multivariable temperature controller in which the 8-bit output and the PWM output are controlled with two independent PI routines.
144
The results for the multivariable control scheme are shown in Figures 9.17, 9.18 and 9.19.
Figure 9.17 Temperature step response for the multivariable control scheme
Figure 9.18 Heating power step response for the multivariable control scheme
Figure 9.19 Fan speed step response for the multivariable control scheme
145
Figure 9.17 shows the temperature step response of the system, which is more damped than the temperature step response in section 9.5.1. The y-axis in Figure 9.17 shows the 8-bit analog input. The corresponding temperature in C is the 8-bit value times 0.178 as shown in Equation 9.6 Figure 9.18 shows the heating control effort to the plant while Figure 9.19 shows the cooling control effort to the plant. It can be seen from Figures 9.18 and 9.19 that there is a tradeoff between the heating power and the fan speed to keep the system on the desired temperature. The balance between the heating power and the fan speed is marginally stable. It is possible however to alter the control algorithm to minimize the power consumption while keeping system stable on the desired temperature.
146
Chapter 10
10.1 Conclusions
The user-friendly microcontroller-based controller software has been developed in this thesis to such an extent so that control engineers can implement a wide range of SISO and MIMO discrete-time controller designs based on the M68HC11 microcontroller. Control engineers can rapidly implement a prototype controller design without having to deal with any microcontroller software coding. The controller can be implemented in a cost-effective manner by using widely available resources. The only equipment needed for implementing the user-friendly digital controller are: A PC with a Windows 95 environment M68EBLP11 Evaluation Board M68HC11 Memory Expansion Board
147
Controller performance data can be monitored in real-time while the control algorithm is being executed on the microcontroller. An offline plotting feature will allow the user of the control software to evaluate the system performance graphically. The controller performance data can also be saved to disk for further analysis. The functionality of the controller software was verified by testing the system on control applications such as DC motor speed control and multivariable temperature control. These control system applications were also numerically modeled in Simulink. The comparison between the experimental results and the results obtained from the Simulink models were found to be in agreement and verified the correct functionality of the controller software that was developed in this thesis.
148
10.2 Recommendations
This thesis provides a powerful foundation for further software development for rapid prototyping of embedded control systems. Higher-order controllers The SISO and MIMO controllers are limited to fourth-order direct digital filter structures in this thesis. Higher-order digital filters are however possible to implement by altering the software system slightly. The main constraint was the lack of processing speed on the M68HC11 microcontroller, which can be overcome by using a more powerful processor. Alternative Hardware There are many types of microcontrollers on the market, and a variety of manufacturers to choose from. Microcontrollers are cost-effective and sufficient for numerous medium performance control applications. Other devices such as the 16-bit M68HC12 microcontroller or the PC32 DSP card can also be used as an alternative to the 68HC11 microcontroller. The M68HC12 is Motorolas latest developed 16-bit microcontroller. The M68HC12 is completely source code compatible with the M68HC11 family of 8-bit microcontrollers. Current 68HC11 based applications can therefore easily be upgraded to 16-bit performance with increased functionality and more on-chip memory. The primary difference between a standard microcontroller and a DSP is in the instruction sets and the speed of the particular instructions [1]. The instruction sets for DSPs are rich in math capabilities and most DSPs have a build in multiplieraccumulator (MAC) which is dedicated to multiplying instructions and fast floatingpoint arithmetic. A DSP-class processor is therefore the correct choice for demanding control applications. DSPs can provide filter characteristics with very sharp cutoff rates that are impossible to obtain with conventional microprocessors [1].
149
The PC32 from Innovative Integration is an example of a low cost 32-bit DSP card with hardware floating-point. The PC32 DSP is also coupled with analog and digital peripherals for plant interfacing. An ANSI C compiler is also available for DSP code generation. A wider variety of controller Input/Output ports. Plant interfacing is limited to two input ports and two output ports that are configured as follow: Input ports Analog input Frequency input Output ports PWM output 8-Bit Parallel output
It is possible to implement additional input and output ports by altering the microcontroller and the Windows software slightly. This will result in more flexibility in controller/plant interface and will allow more input and output possibilities for multivariable control applications. More controller features Many other software features that can be added to the current software. A lag term can be incorporated in the control algorithm, which will allow the user to implement a controller delay on a system. The system can be modified to interact with other software packages such as MATLAB for real-time plotting and data analysis. Serial communication between the PC and the microcontroller can be improved to optimize the controller performance while monitoring the controller performance data.
150
References
[1] Kuo, B.C. (1992) Digital Control Systems Second Edition. Saunders College Publishing, London. [2] Stoecker, W.F. and Stoecker, P.A. (1989) Microcomputer Control of Thermal and Mechanical Systems. Van Nostrand Reinhold, New York, pp 3-4. Dorf, R.C and Bishop, R.H. (1995) Modern Control Systems. AddisonWesley, New York, pp 240-242. Liu, Yu-Gheng and Gibson, A. (1986) Microcomputer Systems: The 8086/8088 Family 2nd edition. Prentice Hall, New Jersey, pp 2. [5] Phillips, C.L. and Nagle, H.T. (1995) Digital Control System Analysis and Design Third Edition. Prentice Hall, New Jersey. Franklin, G.F.,Powel, J.D. and Workman, M.L. (1990) Digital Control of Dynamic Systems. Addison-Wesley, New York, pp 641-666. Greenfield, J.D. (1992) The 68HC11 Microcontroller. Saunders College Publishing, London, pp 303-306. Bozic, S.M. (1979) Digital and Kalman filtering. Edward Arnorld, London. Kuo, B.C. (1991) Automatic Control Systems 6th edition. Prentice Hall, New Jersey. Lefkon D. and Payne B. (1998) Making embedded systems year 2000 compliant. IEEE Spectrum. 35 , 74-79. Beards, P.H. (1991) Analog and Digital Electronics. Prentice Hall, New York, pp 564-567.
[3]
[4]
[6]
[7]
[8] [9]
[10]
[11]
151
[12]
Driscoll, F.D., Coughlin, R.F. and Villanucci, R.S. (1994) Data Acquisition and Process Control with the M68HC11 Microcontroller. Prentice Hall, New Jersey, pp 131.
[13]
Schildt, H. (1997) Borland C++: The Complete Reference. Osborne McGraw-Hill, New York.
[14]
Van Sickle, T. (1994) Programming Microcontrollers in C. HighText, CA, pp 228-231. Beckwith, T.G., Marangoni, R.D. and Lienhard, J.H. (1993) Mechanical Measurements Fifth Edition. Addison-Wesley, New York, pp 179-204.
[15]
[16]
The Student Edition of Matlab Users Guide, Prentice Hall, Englewood Cliffs, NJ, pp 273-274. Austin, P.C. (1998) Lecture Handout, 660.704 FC Advanced Control Systems, The University of Auckland.
[17]
Mills, A. F. (1995) Basic Heat and Mass Transfer. IRWIN, Chicago, pp 4. Motorola M68HC11 Reference Manual, Motorola, Phoenix, AZ, 1990 Gatland, B. (1998) A better tuning method?. Automation & Control. March 98, 30-31.
152
Appendix A
A1. Introduction
There are several ways to represent real numbers on computers. The Floating-point representation is the most common representation for real numbers on Intel-based PCs, Macintoshes, Unix platforms and Motorola microcontrollers. Floating-point representation was standardized by the Institute for Electrical and Electronic Engineers (IEEE) and the American National Standards Institute (ANSI) on 26 July 1985. The four floating-point formats specified by ANSI/IEEE 754 are: Basic single-precision Basic double-precision Extended single-precision Extended double-precision
153
31
30 - 23
22 - 0
1 bit sign
63
62 - 52
51 - 0
1 bit sign
The Sign Bit A Zero in the sign bit denotes a positive number and a one denotes a negative number. The Exponent The exponent is stored in biased form (also know as the excess-n form). The bias is 127 for the basic single precision ANSI/IEEE 754 floating point number. For example, to represent an exponent of 0, the 8-bit exponent will contain the number 127. To represent 120, the exponent will contain the number 7. And so forth. The exponent bias is 1023 for ANSI/IEEE 754 double precision floating point numbers.
154
The Mantissa The mantissa (also known as the significand) represents the precision bits of the number. Floating points are stores in normalized form. The normalized form will put the radix point after the first non-zero digit. For example, twelve is represented as 1.2 10 1 and not as 12 10 0 . Since the only non-zero digit in binary format is one, the leading one can be removed. The mantissa has therefore effectively 24 bits of resolution.
Normalized Single Precision Minimum Single Precision Maximum Double Precision Minimum Double Precision Maximum 2 149 (1 2 23 ) 2 126 2 1074 1 2 52 2 1022
Denormalized 2 126
(1 2 23 ) 2 127
2 1022
1 2 52 21023
155
denormalized numbers are represented as ( 1) 0.m 2 1022 . Infinity The values infinity are represented with an exponent of all ones and a mantissa of all zeros. The sign bit distinguishes between positive and negative infinity. All arithmetic operations with infinite values are defined in IEEE. Indeterminate An indeterminate value is used to represent results from arithmetic operations from which the result is indeterminate, such as infinity-infinity or infinity 0. An indeterminate value is represented by an exponent of all ones, a mantissa with a leading one followed by all zeros, and a sign bit of one. Not a Number The value NaN is used to represent a floating-pint value that is an error of some form. The NaN value is represented with an exponent field of all ones and a sign bit of zero.
156
157
158
Appendix B
S-record Information
B1. Introduction
All the parameters entered by the user in the Graphical User Interface (GUI) program on the PC will be transformed to a s-record line. That line will be added to the predefined control algorithm, which is already in s-record format. The complete Srecord file is then ready to be downloaded to the microcontroller. It is therefore necessary for the reader to have some background on the S-record format. An S-record file is an ASCII text file that consists of a number of character strings in hexadecimal format. Each character string starts with the letter S and consists of the following data fields: record type, record length, memory address, data and checksum. The record length and checksum fields ensure accuracy of transmission between the different hardware devices. Each byte of data is encoded as a two-character hexadecimal number. All the programs for the microcontroller are stored and downloaded in S-record format.
159
Number of characters
Contents S-record type: S0-S9 Number of character pairs in the character string, excluding type and record length pairs. The 2-4 byte starting address at which the code/data field is to be loaded into memory. The executable program code and data in machine code, which will be loaded into the memory of the microcontroller. The checksum is the least significant byte of the complement of the sum of all the bytes in the record length, code/data s-record line.
4, 6, or 8 0-n
Checksum
S-record types There are eight types of s-records. Only three S-record types namely S0, S1 and S9 are significant for using with the 68HC11 microcontroller. S0 type The S0 type is the header for each block of character strings of S-records. The address is normally zeros and the code/data field may contain any information regarding the following block of S-record lines. S1 type The S1 type contains a 2-byte memory address followed by the code/data which is to be loaded into the microcontroller memory. S9 type The S9 line terminates the S-record file. There is no code/data field in the S9 type.
160
The first line is a S0 type S-record and consists of the following character pairs: S0 06 The line is a type S0 S-record. This character pair / byte is the record length. Hexadecimal 06 indicates that six character pairs / bytes will follow. The four character address field which is zeros.
00 00 44 48 52 1B
The data field, ASCII H, D and R. The data field can contain any descriptive information regarding the block of S-record data which follows. The last character pair is the checksum of this S0 record. This checksum can be verified as follow: Checksum = FF- (06 + 48 + 44 + 52) = FF-E4 = 1B
The second line and the following eleven S-record lines can be explained as follow: S1 This means that this line is a type S1 record.
161
23
Hexadecimal 23 (decimal 35) indicates that 35 character pairs / bytes of data will follow B600 is the 2-byte memory starting-address at which the code/data field will be loaded into.
B6 00
The next 35 character pairs are the actual code/data, which will be loaded into the memory. The last character pair namely 3F is the checksum of the first S1 type Srecord. The last S-record line is a S9 type S-record and can be explained as follow: S9 03 The S9 indicates that the line is an S9 type S-record. Hexadecimal 03 (decimal 3) indicates that 3 character pairs / bytes of data will follow These two character pairs is the 2-byte address of the instruction to which the control is passed. This is the checksum of the S9 record.
B6 00 46
162
Appendix C
Sophisticated modeling and design methods are available to develop a controller that will compensate a system to meet transient and steady-state specifications. These methods require complete mathematical models of the process to be controlled in the form of state equations or transfer functions. It is often difficult to obtain accurate system parameters, which make PID controller tuning rather difficult. Fifty years ago a breakthrough came with the Ziegler-Nichols method[20]. Ziegler and Nichols recognized that most process systems have the general S-shaped step response shown in Figure C1. This S-shaped curve is known as the process reaction curve and can be generated experimentally or obtained from a dynamic simulation of the process [6].
163
(C.1)
which is simply a first-order system plus a transportation lag. Ziegler and Nichols provided two methods for tuning the PID controller. In the first method, the controller parameters are selected so that the plant output decays to a quarter of its value after one period of oscillation as illustrated in Figure C2. A quarter decay corresponds to a damping ratio of 0.21, which is a good compromise between quick response and adequate stability. The PID controller parameters suggested by Ziegler and Nichols are shown in Table C-1. y(t )
1 0.25
Period t
Figure C2. Quarter decay ratio
164
Type of Controller P
Optimum Gain KP = KP = KP =
K d
0.9 0.27 , KI = 2 K d K d 1.2 0.6 0.6 , KD = , KI = 2 K K d K d
PI
PID
Table C-1. Ziegler-Nichols PID controller tuning for a decay ratio of 0.25
In the second method for tuning the PID controller, the criteria for adjusting the controller parameters are based on evaluating the system at the limit of stability. The proportional gain is increased until the system produces continuous oscillations. The corresponding gain K u , which is known as the ultimate gain and the period of oscillation Pu , which is known as the ultimate period can be determined as illustrated in Figure C3.
+ -
Ku
Process Pu
The PID controller parameters suggested by Ziegler and Nichols, in terms of K u and Pu , are shown in Table C-2. The final tuning of the controller parameters can be done manually to obtain the most desirable control system response.
165
Type of Controller P
PI
PID
Ku , K D = 0.075K u Pu Pu
Table C-2. Ziegler-Nichols PID controller tuning based on the stability boundary parameters.
166
Appendix D
The only memory available on the M68EBLP11 Evaluation Board, is the internal memory on the MC68B11E9 microcontroller, which is 322 bytes of internal RAM and 512 bytes of internal EEPROM (Electrically Erasable Programmable Read Only Memory). For the purpose of this project, the M68EBLP11 is being operated in expansion mode in conjunction with a 32Kb custom-built memory expansion board. The addressable RAM of the memory expansion board is 0x2000 to 0x9FFF.This section contains the schematic and PCB layout of the memory expansion board.
167
168
Figure D2 PCB layout of the 68HC11 Memory Expansion Board (Top View)
169
Figure D3 PCB layout of the 68HC11 Memory Expansion Board (Bottom View)
170
This section contains images from the DC motor speed control and multivariable temperature control application examples.
171
Figure E1.
The M68EBLP11 evaluation board, the memory expansion board, the DC Motor and the inteface electronics for the DC motor speed control application example.
Figure E2.
172
Figure E3.
The M68EBLP11 evaluation board, the memory expansion board, the heating system and the inteface electronics for the multivariable temperature control application example.
173
174
Appendix F
HC11Control was developed in the Borland C++ Builder development environment This section contains all the necessary Windows-based software source code for HC11Control. The different software modules will be listed as follow: Comsettings.cpp Comsettings.h Download.cpp Download.h Download thread.cpp Download thread.h Downloadstarterror.cpp Downloadstarterror.h InsufficientDataFormFile.cpp InsufficientDataFormFile.h Communication settings form sourcce code Communication settings form header file Download form source code Download form header file Download thread source code Download thread header file Downloadstarterror form source code Downloadstarterror form header file InsufficientDataForm source code InsufficientDataForm header file
175
Main.cpp Main.h Mainmenu.cpp Mainmenu.h Max250min1.cpp Max250min1.h Max255.cpp Max255.h Min40.cpp Min40.h Plot1.cpp Plot1.h Plot2.cpp Plot2.h Plot3.cpp Plot3.h Plot4.cpp Plot4.h Runcontrolprogram.cpp Runcontrolprogram.h Savedata.cpp Savedata.h
Main form source code Main form header file Startup menu source code Startup menu header file Error-message form source code Error-message form header file Error-message form source code Error-message form header file Error-message form source code Error-message form header file Plotform1 source code Plotform1 header file Plotform2 source code Plotform2 header file Plotform3 source code Plotform3 header file Plotform4 source code Plotform4 header file Runform source code Runform header file Savedata form source code Savedata form header file
176
177
} //-------------------------------------------------------------------void __fastcall TCommunicationSettingsForm::RadioButton2Click( TObject *Sender) { MonitorForm->DigitalComm->Port="COM2"; MonitorForm->TestDACADCComm->Port="COM2"; MonitorForm->PIDMonitorComm->Port="COM2"; MonitorForm->MultiComm->Port="COM2"; Download1->CommReceive->Port="COM2"; Download1->ZComm1->Port="COM2"; RunProgramForm->CommReceive->Port="COM2"; RunProgramForm->RunComm->Port="COM2"; } //-------------------------------------------------------------------void __fastcall TCommunicationSettingsForm::RadioButton3Click( TObject *Sender) { MonitorForm->DigitalComm->Port="COM3"; MonitorForm->TestDACADCComm->Port="COM3"; MonitorForm->PIDMonitorComm->Port="COM3"; MonitorForm->MultiComm->Port="COM3"; Download1->CommReceive->Port="COM3"; Download1->ZComm1->Port="COM3"; RunProgramForm->CommReceive->Port="COM3"; RunProgramForm->RunComm->Port="COM3"; } //--------------------------------------------------------------------
178
179
//------------------------------------------------------------------#pragma link "zcomm" #pragma resource "*.dfm" TDownload1 *Download1; TDownloadThread *DownloadThread; void delay(int ms); void delay(int ms) { DWORD time1 = GetTickCount(); while(GetTickCount()<time1+ms); } //------------------------------------------------------------------__fastcall TDownload1::TDownload1(TComponent* Owner) : TForm(Owner) { } //------------------------------------------------------------------void __fastcall TDownload1::CancelClick(TObject *Sender) { // This event handler and will close the download form. Download1->Close(); } //------------------------------------------------------------------//------------------------------------------------------------------void __fastcall TDownload1::CommReceiveDataAvailable(TObject *Sender) // // // // // // This event handler monitors the characters received from the microcontroller and will activate the download procedure when the characters which correspond to a reset action is received from the microcontroller. If any other characters is received from the microcontroller, the program will call the ErrorDownloadStartMessage form in which the user will be prompted
180
// // // // // //
that a running program on the microcontroller has to be stopped before the download procedure can continue. It was found that the download procedure halts all other event handlers and functions in the program. The solution was to program the download procedure in such a way so that it will run in a separate thread from the main program.
{ unsigned char *buffer,data[10]; buffer=data; // Clear the input buffer by writing zero's to it for (int n=0;n<=10;n++) {data[n]=0;} CommReceive->ReadComm(buffer,4); // // // // // The next line can also be: "if (data[0]==13 && data[1]==10 && data[2]==66 && data[3]==85)" A reset action on the microcontroller will send the abovementioned characters in the specefic order to the PC. This program will continue if the first character received is 13.
if (data[0]==13) // if reset is detected { BlinkTimer->Enabled=false; Download1->CommReceive->CloseConnection(); delay(10); DownloadThread = new TDownloadThread(false); //Start the download //seperate thread. DownloadThread->Priority=tpHighest; } else { // // // // // // The next six lines of program code will disable serial communication between the PC and the microcontroller and will activate the ErrorDownloadStartMessage form in which the user will be prompted that a running program on the microcontroller has to be stopped before the download procedure can continue.
BlinkTimer->Enabled=false; Download1->CommReceive->CloseConnection(); Download1->Close(); ErrorDownloadStartMessage->Label3->Caption= "downloding the new program."; ErrorDownloadStartMessage->ShowModal(); } } //------------------------------------------------------------------void __fastcall TDownload1::ZComm1DataAvailable(TObject *Sender) // This procedure monitors the characters received from the // microcontroller while a different thread is downloading the
181
// control algorithm to the microcontroller. The thread which // downloads the control algorihm to the microcontroller is listed // in the "Download thrad.cpp" unit. If the download procedure is // successfull, a 'done' message will be received from the // microcontroller. //------------------------------------------------------------------{ unsigned char *buffer,data[20]; buffer=data; // Clear the input buffer by writing zero's to it for (int n=0;n<=20;n++) { data[n]=0; } ZComm1->ReadComm(buffer,8); int len=strlen(data); if (len>20) len=20; //Limit the input buffer length to 20 characters. for (int tel=0;tel<len;tel++) { if (data[tel]=='d' && data[tel+1]=='o') // Monitor the output of
{ Download1->ErrorTimer->Enabled=false; // Disable the ErrorTimer Download1->Messagebox->Left=130; Download1->Messagebox->Caption="Download Successful"; Download1->ZComm1->CloseConnection(); delete DownloadThread;// Deallocate DownloadThread in the memory OK->Visible=true; OK->SetFocus(); Cancel->Visible=false; // if the Digital Control program was selected if (MonitorForm->DigitalController1->Checked) { MonitorForm->DigitalSaveButton->Enabled=false; MonitorForm->DigitalRunButton->Enabled=true; MonitorForm->DigitalDisconnectButton->Enabled=false; MonitorForm->DigitalMonitorButton->Enabled=false; MonitorForm->DigitalPlotButton->Enabled=false; } else if (MonitorForm->PIDController1->Checked) { MonitorForm->SaveButton->Enabled=false; MonitorForm->PIDRunButton->Enabled=true; MonitorForm->PIDDisconnectButton->Enabled=false; MonitorForm->PIDMonitorButton->Enabled=false; MonitorForm->PIDPlotButton->Enabled=false; } else if (MonitorForm->TestDACADC1->Checked) { MonitorForm->TestDACADCSaveButton->Enabled=false; MonitorForm->TestDACADCRunButton->Enabled=true; MonitorForm->TestDACADCDisconnectButton->Enabled=false;
182
MonitorForm->TestDACADCMonitorButton->Enabled=false; MonitorForm->TestDACADCPlotButton->Enabled=false; } else if (MonitorForm->MultiController1->Checked) { MonitorForm->MultiSaveButton->Enabled=false; MonitorForm->MultiRunButton->Enabled=true; MonitorForm->MultiDisconnectButton->Enabled=false; MonitorForm->MultiMonitorButton->Enabled=false; MonitorForm->MultiPlotButton->Enabled=false; } } } } //-------------------------------------------------------------------
void __fastcall TDownload1::ErrorTimerTimer(TObject *Sender) //------------------------------------------------------------------// The ErrorTimer will be enabled in the EndDownload class member // function in the "Download thread.cpp" unit when the downloading // procedure to the microcontroller is finished. The interval of the // ErrorTimer is 2 seconds. The ZComm1DataAvailable component will // monitor for a 'done' message and will disable the ErrorTimer if // a 'done' message was received from the microcontroller. If no // 'done' message is recieved from the microcontroller within 2 // seconds after completion of the download procedure, the // ErrorTimerTimer event handler will be called which will prompt // the user that a download error occured. //------------------------------------------------------------------{ Download1->Messagebox->Left=170; Download1->Messagebox->Caption="Download Error"; Download1->ZComm1->CloseConnection(); delete DownloadThread; // Free the memory allocated by DownloadThread OK->Visible=true; Cancel->Visible=false; ErrorTimer->Enabled=false; } //------------------------------------------------------------------void __fastcall TDownload1::ViewHideClick(TObject *Sender) // This event hander will display the complete s-record code which
183
// is to be downloaded to the microcontroller { if (Download1->Memo1->Visible==false) { Download1->Height=458; Download1->ViewHide->Caption="Hide Code"; Memo1->Visible=true; } else { Download1->Height=171; ViewHide->Caption="View Code"; Memo1->Visible=false; } } //------------------------------------------------------------------void __fastcall TDownload1::FormActivate(TObject *Sender) { // This event handler initialise all the buttons and controls // for the Download form and enable the serial communication // between the PC and the microcontroller. Cancel->Enabled=true; OK->Visible=false; Cancel->Visible=true; Messagebox->Left=120; Messagebox->Caption="Reset The Microcontroller"; BlinkTimer->Enabled=true; ProgressBar1->Position=0; Download1->Height=171; ViewHide->Caption="View Code"; Download1->CommReceive->OpenConnection(); Download1->Height=176; Download1->Memo1->Visible=false; } //------------------------------------------------------------------void __fastcall TDownload1::FormClose(TObject *Sender, TCloseAction &Action) // // // // This event handler will check whether serial communication between the PC and the microcontroller is enabled when the Download form closes and will disconnect the serail port before the program closes.
184
//------------------------------------------------------------------void __fastcall TDownload1::BlinkTimerTimer(TObject *Sender) // This event handler will display a blinking message in which the // user will be prompted to reset the microcmontroller. { if (Download1->Messagebox->Caption=="") { Messagebox->Caption="Reset The Microcontroller"; } else Messagebox->Caption=""; } //-------------------------------------------------------------------
185
186
for (tel=0;tel<len;tel++)
187
{ if(!(text[tel]==10)&&!(text[tel]==13)) // Ignore characters 10&13 { Synchronize(SendCharacter); // Send the character to the } //delayt(5); // Delay for 5 ms between each character. } Synchronize(EndDownload); } //------------------------------------------------------------------void __fastcall TDownloadThread::Downloading() // This class member function will initialise the microcontroller // to receive s-record data from the serial communication interface. { Download1->Messagebox->Left=104; Download1->Messagebox->Caption="Downloading Control Algorithm"; Download1->Cancel->Enabled=false; Download1->ZComm1->OpenConnection(); char *ptext[20000]; *ptext=Download1->Memo1->Lines->GetText(); strcpy(text,*ptext); //Copy the characters to be downloaded to the len=strlen(text); // The next lines will perform a "load t" instruction. The "load t" // instruction will initiate the microcontroller to receive the // control algorithm which will follow unsigned char c=13; // 13 is the keyboard code for the Enter key Download1->ZComm1->WriteCommByte(c); delayt(500); char loadt[]="load t"; for (int cnt=0;cnt<=5;cnt++) { Download1->ZComm1->WriteCommByte(loadt[cnt]); delayt(50); } c=13; Download1->ZComm1->WriteCommByte(c); //End of "load t" instruction Download1->ProgressBar1->Min=0; Download1->ProgressBar1->Max=len; Download1->ProgressBar1->Step=1; } //-----------------------------------------------------------------void __fastcall TDownloadThread::SendCharacter()
188
// This class member function will send one character at a time from // the s-record file to the microcontroller. { Download1->ProgressBar1->Position=tel; Download1->ZComm1->WriteCommByte(text[tel]); } //-----------------------------------------------------------------void __fastcall TDownloadThread::EndDownload() // // // // { Download1->ErrorTimer->Enabled=true; // Enable the ErrorTimer. Download1->Messagebox->Left=192; Download1->Messagebox->Caption="Done"; } //-----------------------------------------------------------------This class member function will Enable the ErrorTimer and prompt the user that the downloading procedure is complete. A detail description of the ErrorTimer is listed at the ErrorTimerTimer event handler in the "download.cpp" unit.
189
190
191
192
193
194
195
double* Multianaloginput = new double[65536]; double* Multianaloginputrecorded = new double[65536]; unsigned char Multianaloginputdisplay; unsigned short int Multifreqinputindex=0; double* Multifreqinput = new double[65536]; double* Multifreqinputrecorded = new double[65536]; unsigned char Multifreqinputdisplay; unsigned short int Multi8bitoutputindex=0; double* Multi8bitoutput = new double[65536]; double* Multi8bitoutputrecorded = new double[65536]; unsigned char Multi8bitoutputdisplay; unsigned short int MultiPWMoutputindex=0; double* MultiPWMoutput = new double[65536]; double* MultiPWMoutputrecorded = new double[65536]; unsigned char MultiPWMoutputdisplay; unsigned char Multibuffercount=0; //-------------------------------------------------------------------// Digital Section global variables //-------------------------------------------------------------------unsigned char Digitaldata1[300],Digitaldata2[300], Digitaldata3[300],Digitaldata4[300],Digitaldata5[1800]; unsigned short int Digitaltimeindex=0,Digitaltimeindexstop=0; double Digitaltime[65536],Digitaltimerecorded[65536]; unsigned char Digitalsamplefrequency; unsigned int Digitaltimedisplay; unsigned short int Digitalinputindex=0; double Digitalinput[65536],Digitalinputrecorded[65536]; unsigned char Digitalinputdisplay; unsigned short int Digitaloutputindex=0; double Digitaloutput[65536],Digitaloutputrecorded[65536]; unsigned char Digitaloutputdisplay; unsigned char Digitalbuffercount=0; unsigned char digitalinoutcombination=1; //-------------------------------------------------------------------// PID Section global variables //-------------------------------------------------------------------unsigned char PIDdata1[300],PIDdata2[300], PIDdata3[300],PIDdata4[300],PIDdata5[1800]; unsigned short int PIDtimeindex=0,PIDtimeindexstop=0; double PIDtime[65536],PIDtimerecorded[65536]; unsigned char PIDsamplefrequency; unsigned int PIDtimedisplay;
196
unsigned short int PIDinputindex=0; double PIDinput[65536],PIDinputrecorded[65536]; unsigned char PIDinputdisplay; unsigned short int PIDoutputindex=0; double PIDoutput[65536],PIDoutputrecorded[65536]; unsigned char PIDoutputdisplay; unsigned char PIDbuffercount=0; unsigned char pidinoutcombination=1; //-------------------------------------------------------------------// TestDACADC Section global variables //-------------------------------------------------------------------unsigned char TestDACADCdata1[300],TestDACADCdata2[300], TestDACADCdata3[300],TestDACADCdata4[300], TestDACADCdata5[1800]; unsigned short int TestDACADCtimeindex=0,TestDACADCtimeindexstop=0; double TestDACADCtime[65536],TestDACADCtimerecorded[65536]; unsigned char TestDACADCsamplefrequency; unsigned int TestDACADCtimedisplay; unsigned short int TestDACADCinputindex=0; double TestDACADCinput[65536],TestDACADCinputrecorded[65536]; unsigned char TestDACADCinputdisplay; unsigned short int TestDACADCoutputindex=0; double TestDACADCoutput[65536],TestDACADCoutputrecorded[65536]; unsigned char TestDACADCoutputdisplay; unsigned char TestDACADCbuffercount=0; unsigned char testdacinoutcombination=1; //-------------------------------------------------------------------__fastcall TMonitorForm::TMonitorForm(TComponent* Owner) : TForm(Owner) { } //-------------------------------------------------------------------void __fastcall TMonitorForm::FormClose(TObject *Sender, TCloseAction &Action) // This event handler will check whether serial communication between // the PC and the microcontroller is enabled when the program closes // and will disconnect the serail port before the program closes. { if (PIDMonitorComm->Connected==true) PIDMonitorComm->CloseConnection(); if (TestDACADCComm->Connected==true) TestDACADCComm->CloseConnection(); delete[] Multitime;
197
delete[] Multitimerecorded; delete[] Multianaloginput; delete[] Multianaloginputrecorded; delete[] Multifreqinput; delete[] Multifreqinputrecorded; delete[] Multi8bitoutput; delete[] Multi8bitoutputrecorded; delete[] MultiPWMoutput; delete[] MultiPWMoutputrecorded; } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDController1Click(TObject *Sender) { // This event handler will enable and display the PID controller // panel on which all the controls and editboxes for the PID // controller are displayed. MonitorForm->Caption="HC11Control - PID Controller"; PIDController1->Checked=true; MonitorForm->PIDPanel->Visible=true; MonitorForm->PIDPanel->Enabled=true; MonitorForm->N68HC11Programs1->Enabled=false; MonitorForm->New1->Enabled=true; // Load microcontroller code from s-record file AnsiString s19filename="digital.s19"; MonitorForm->DigitalCode->Lines->LoadFromFile(s19filename); } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADC1Click(TObject *Sender) { MonitorForm->Caption="HC11Control - Test Input/Output"; TestDACADC1->Checked=true; MonitorForm->TestDACADCPanel->Visible=true; MonitorForm->TestDACADCPanel->Enabled=true; MonitorForm->N68HC11Programs1->Enabled=false; MonitorForm->New1->Enabled=true; // Load microcontroller code from s-record file AnsiString s19filename="testio.s19"; MonitorForm->TestDACADCCode->Lines->LoadFromFile(s19filename); } //-------------------------------------------------------------------void __fastcall TMonitorForm::New1Click(TObject *Sender) // The New1Click event handler will reset all current control // applications. { DigitalController1->Checked=false; MultiController1->Checked=false; PIDController1->Checked=false; TestDACADC1->Checked=false; MonitorForm->New1->Enabled=false;
198
MonitorForm->N68HC11Programs1->Enabled=true; MonitorForm->PIDPanel->Visible=false; MonitorForm->PIDPanel->Enabled=false; MonitorForm->TestDACADCPanel->Visible=false; MonitorForm->TestDACADCPanel->Enabled=false; MonitorForm->DigitalPanel->Visible=false; MonitorForm->DigitalPanel->Enabled=false; MonitorForm->MultiPanel->Visible=false; MonitorForm->MultiPanel->Enabled=false; MenuForm->ShowModal(); } //-------------------------------------------------------------------void __fastcall TMonitorForm::Exit1Click(TObject *Sender) { MonitorForm->Close(); // Close the program } //-------------------------------------------------------------------void __fastcall TMonitorForm::Communicationsettings1Click( TObject *Sender) // This event handler will call the form on which the user will // specify the communication port. { CommunicationSettingsForm->ShowModal(); } //-------------------------------------------------------------------void __fastcall TMonitorForm:: Recorddatawhencontrolprogramstarts1Click(TObject *Sender) // This eventhandler will set or reset the flag which will // determine whether incoming data from the microcontroller // will be logged when the program on the microcontroller starts. // If Recorddatawhencontrolprogramstarts1->Checked=true then // incoming data will be logged as soon as the program on // the microcontroller starts. { if (Recorddatawhencontrolprogramstarts1->Checked) { Recorddatawhencontrolprogramstarts1->Checked=false; } else { Recorddatawhencontrolprogramstarts1->Checked=true; } } //-------------------------------------------------------------------void __fastcall TMonitorForm::SaveButtonClick(TObject *Sender)
199
{ // // // // // This event handler will determine whether an existing file will be overwritten and will be used by the PID and TestDACADC saection. If a file is to be overwritten, the FileOverwriteDiaolgBox will be called and will prompt the user whether the save procedure may continue or not.
SaveDialog->Title = "Save As"; if (SaveDialog->Execute()) { char *filename=SaveDialog->FileName.c_str(); ifstream checkfile(filename); if(checkfile) { checkfile.close(); FileOverwriteDialogBox->Label3->Caption=filename; FileOverwriteDialogBox->ShowModal(); } else { SaveNowHiddenButtonClick(Sender); } } } //-------------------------------------------------------------------void __fastcall TMonitorForm::SaveNowHiddenButtonClick( TObject *Sender) // // // // // This event handler will be called by the SaveButtonClick() event handler if FileName does not exist in the specified directory and by the YesButtonClick event handler in FileOverwriteDialogBox if the file exists. All the recorded data and the corresponding time will be saved in a ASCII file named by FileName.
{ char temp[100]; char *filename=SaveDialog->FileName.c_str(); ofstream outfile; outfile.open(filename, ios::trunc); // If the Multi controller was selected if (MultiController1->Checked) { sprintf(temp,"%7s %8s %8s %8s %8s","Seconds","Input1","Input2","Output1","Output2"); outfile << temp << endl << endl; for(int count=0;count<Multitimeindexstop;count++) { sprintf(temp,"%7.2f %8.0f %8.0f %8.0f %8.0f", Multitimerecorded[count], Multianaloginputrecorded[count], Multifreqinputrecorded[count], Multi8bitoutputrecorded[count],
200
MultiPWMoutputrecorded[count]); outfile << temp << endl; } } // If the Digital controller was selected if (DigitalController1->Checked) { sprintf(temp,"%7s %8s %8s","Seconds","Input","Output"); outfile << temp << endl << endl; for(int count=0;count<Digitaltimeindexstop;count++) { sprintf(temp,"%7.2f %8.0f %8.0f",Digitaltimerecorded[count], Digitalinputrecorded[count],Digitaloutputrecorded[count]); outfile << temp << endl; } } // If the PID controller was selected if (PIDController1->Checked) { sprintf(temp,"%7s %8s %8s","Seconds","Input","Output"); outfile << temp << endl << endl; for(int count=0;count<PIDtimeindexstop;count++) { sprintf(temp,"%7.2f %8.0f %8.0f",PIDtimerecorded[count], PIDinputrecorded[count],PIDoutputrecorded[count]); outfile << temp << endl; } } // If the TestDACADC program was selected if (TestDACADC1->Checked) { sprintf(temp,"%7s %8s %8s","Seconds","Input","Output"); outfile << temp << endl << endl; for(int count=0;count<TestDACADCtimeindexstop;count++) { sprintf(temp,"%7.2f %8.0f %8.0f",TestDACADCtimerecorded[count], TestDACADCinputrecorded[count],TestDACADCoutputrecorded[count]); outfile << temp << endl; } } outfile.close(); } //--------------------------------------------------------------------
//-------------------------------------------------------------------// The following section are the event handlers accociated with the // PID controller section. This includes all the edit boxes in which // the controller parameters are entered and the events and buttons // accociated with it. //--------------------------------------------------------------------
201
This event handler will store all incoming data received from the microcontroller in the 'data' array. The variable named 'output' is a global variable which will be used for display and recording purposes in other event handlers.
{ unsigned char *buffer,data[300]; buffer=data; // Clear the input buffer by writing zero's to it for (int n=0;n<=300;n++) { data[n]=0; } PIDMonitorComm->ReadComm(buffer,300); // The idea of PIDbuffercount is to ensure that there are at least // six characters of incoming data available before processing it. PIDbuffercount++; if (PIDbuffercount>6) { PIDbuffercount=1; } switch (PIDbuffercount) { case 1: strcpy((char *)PIDdata5,(char break; case 2: strcpy((char *)PIDdata4,(char break; case 3: strcpy((char *)PIDdata3,(char break; case 4: strcpy((char *)PIDdata2,(char break; case 5: strcpy((char *)PIDdata1,(char break; case 6: strcat((char *)PIDdata5,(char strcat((char *)PIDdata5,(char strcat((char *)PIDdata5,(char strcat((char *)PIDdata5,(char strcat((char *)PIDdata5,(char
*)data);
*)data);
*)data);
*)data);
*)data);
202
// // // // // // // // // //
The following lines will decode the data pattern into the different arrays. Data will be received from the microcontroller in the following format [251, Sample Frequency, 252, Input from plant, 253, Output to plant] The array may start and end at any point, but the data always have to be in the same order, for example: the following array is also a valid data array [Input from plant, 253, Output to plant, 251, Sample Frequency]
for (int scandata=0;scandata<6;scandata++) { if (PIDdata5[scandata]==251) { for (int count=1+scandata;count<len;count=count+6) { PIDsamplefrequency=PIDdata5[count]; float period=(float) 1/PIDdata5[count]; // The if statement ignores the first period value // since it is not significant. // The first meaningful period value will be available // after one sample cycle on the microcontroler is // finished. if (PIDtimeindex==0) { PIDtime[PIDtimeindex]=0; } else { PIDtime[PIDtimeindex]=PIDtime[PIDtimeindex-1]+period; } PIDtimedisplay=PIDtime[PIDtimeindex]; PIDtimeindex++; } } else if (PIDdata5[scandata]==252) { for (int count=1+scandata;count<len;count=count+6) { PIDinputdisplay=PIDdata5[count]; PIDinput[PIDinputindex]=PIDdata5[count]; PIDinputindex++; } } else if (PIDdata5[scandata]==253) { for (int count=1+scandata;count<len;count=count+6) { PIDoutputdisplay=PIDdata5[count]; PIDoutput[PIDoutputindex]=PIDdata5[count]; PIDoutputindex++; } }
203
}// end for scandata break; }// end switch } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDDisplayTimerTimer(TObject *Sender) { // // This event handler will display the data received from the // microcontroller with every time interval of the timer. // The time interval can be set with the 'interval' property // of the timer component. // PIDInputDigitBox->Target=PIDinputdisplay; PIDInputDigitBox->Value=PIDinputdisplay; PIDOutputDigitBox->Target=PIDoutputdisplay; PIDOutputDigitBox->Value=PIDoutputdisplay; PIDSamplefreqDigitBox->Target=PIDsamplefrequency; PIDSamplefreqDigitBox->Value=PIDsamplefrequency; // The stopbutton will only be enabled when recording // in in progress. if (PIDStopButton->Enabled==true) { PIDTimeDigitBox->Target=PIDtimedisplay; PIDTimeDigitBox->Value=PIDtimedisplay; } } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDStopButtonClick(TObject *Sender) { // This routine will stop data recording process and will enable // and disable all the appropriate buttons related to this stopping // action. PIDtimeindexstop=min(PIDtimeindex,PIDinputindex); PIDtimeindexstop=min(PIDoutputindex,PIDtimeindexstop); PIDDisconnectButton->Enabled=true; SaveButton->Enabled=true; PIDPlotButton->Enabled=true; SaveButton->Enabled=true; PIDStopButton->Enabled=false; PIDRecordButton->Enabled=true; for (int count=0;count<PIDtimeindexstop;count++) { PIDtimerecorded[count]=PIDtime[count]; PIDinputrecorded[count]=PIDinput[count]; PIDoutputrecorded[count]=PIDoutput[count]; } }
204
//-------------------------------------------------------------------void __fastcall TMonitorForm::PIDRecordButtonClick(TObject *Sender) { // This routine will start the data recording process and will enable // and disable all the appropriate buttons related to this recording // action. PIDDisconnectButton->Enabled=false; PIDPlotButton->Enabled=false; SaveButton->Enabled=false; PIDStopButton->Enabled=true; PIDRecordButton->Enabled=false; PlotForm1->XYPlot1->RemovePlot(1); PlotForm1->XYPlot1->RemovePlot(2); // Clear the current plot. PIDoutputindex=0; PIDinputindex=0; PIDtimeindex=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDPlotButtonClick(TObject *Sender) { // TXYPlot is a Borland C++ Builder VCL plotting component for // graphing x,y data. It is used to plot the recorded data // versus time. PlotForm1->Show(); PlotForm1->Caption="Input From Plant"; PlotForm1->XYPlot1->XAutoScale=true; PlotForm1->XYPlot1->YAutoScale=true; if (MonitorForm->PlotLines1->Checked) { PlotForm1->XYPlot1->XYPlot(PIDtimerecorded, PIDinputrecorded,PIDtimeindexstop,1, clBlue,LinesFilledPoints); } else { PlotForm1->XYPlot1->XYPlot(PIDtimerecorded, PIDinputrecorded,PIDtimeindexstop,1, clBlue,FilledPoints); } PlotForm2->Show(); PlotForm2->Caption="Output to Plant"; PlotForm2->XYPlot1->XAutoScale=true; PlotForm2->XYPlot1->YAutoScale=true; if (MonitorForm->PlotLines1->Checked) { PlotForm2->XYPlot1->XYPlot(PIDtimerecorded, PIDoutputrecorded,PIDtimeindexstop,1, clBlue,LinesFilledPoints); }
205
else { PlotForm2->XYPlot1->XYPlot(PIDtimerecorded, PIDoutputrecorded,PIDtimeindexstop,1, clBlue,FilledPoints); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDMonitorButtonClick(TObject *Sender) // // // // // // // This event handler will enable serial communication between the microcontroller and the PC and will enable and disable all the appropriate buttons on the main form. When data is available from the serial port, an OnDataAvailable event will be triggered and the MonitorCommDataAvailable event handler will be called which will handle the incomming data received from the microcontroller.
{ PIDMonitorComm->OpenConnection(); PIDRecordButton->Enabled=true; PIDMonitorButton->Enabled=false; PIDDisconnectButton->Enabled=true; PIDDownloadButton->Enabled=false; PIDRunButton->Enabled=false; } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDDisconnectButtonClick(TObject *Sender) // This event handler will disable serial communication between the // microcontroller and the PC and will enable and disable all the // appropriate buttons on the main form. { PIDMonitorComm->CloseConnection(); PIDRecordButton->Enabled=false; PIDMonitorButton->Enabled=true; PIDDisconnectButton->Enabled=false; PIDDownloadButton->Enabled=true; PIDRunButton->Enabled=true; } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDRunButtonClick(TObject *Sender) { // // // // // // // // This event handler will call the RunProgramForm which will prompt the user to reset the microcontroller. The reason for this is that it is not possible for this program to determine the microcontroller's current state. The Recorddatawhencontrolprogramstarts1->Checked is a flag which will determine whether the PIDMonitorButtonClick event handler will be called after the run instruction is
206
// // // //
completed. If Recorddatawhencontrolprogramstarts1->Checked=false, the program on the microcontroller will be started but the serial communication between the pc and the microcontroller have to be enabled manually.
RunProgramForm->ShowModal(); } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDSetpointEditEnter(TObject *Sender) { PIDSetpointEdit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDKpEditEnter(TObject *Sender) { PIDKpEdit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDKiEditEnter(TObject *Sender) { PIDKiEdit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDKdEditEnter(TObject *Sender) { PIDKdEdit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDSamplefreqEditEnter(TObject *Sender) { PIDSamplefreqEdit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDDownloadButtonClick(TObject *Sender) // // // // // // // // This event handler will check whether all the editboxes in the PID controller section is filled with data by the user. If all the editboxes are filled with data, the text will be converted to integer numbers. The integer numbers will then be converted to hexadecimal numbers which will be converted into a s-record line. The s-record line which contain the control parameters is added to the predefined control algorithm which will form the complete control algorim. The download procedure will then be called.
{ if (PIDSetpointEdit->EditText==" " || PIDKpEdit->EditText==" " || PIDKiEdit->EditText==" " || PIDKdEdit->EditText==" " || PIDSamplefreqEdit->EditText==" " || PIDPWMEdit->EditText==" " || PIDInputupperlimitEdit->EditText==" " || PIDInputlowerlimitEdit->EditText==" " || PIDOutputupperlimitEdit->EditText==" " ||
207
PIDOutputlowerlimitEdit->EditText==" ") { InsufficientDataForm->ShowModal(); // An error message will be // displayed if any of the // data entry fields is empty. } else { unsigned char chksum; unsigned char vars[50]; char *psetp; int setp; psetp=PIDSetpointEdit->Text.c_str(); setp=atoi(psetp); vars[0]=(unsigned char)setp; // Convert the variables //from text to float char *pstrkp; float kp; AnsiString ansikp=PIDKpEdit->Text; pstrkp=ansikp.c_str(); kp=atof(pstrkp); char *pstrki; float ki; AnsiString ansiki=PIDKiEdit->Text; pstrki=ansiki.c_str(); ki=atof(pstrki); char *pstrkd; float kd; AnsiString ansikd=PIDKdEdit->Text; pstrkd=ansikd.c_str(); kd=atof(pstrkd); char *psamplefreq; int samplefreq; psamplefreq=PIDSamplefreqEdit->Text.c_str(); samplefreq=atoi(psamplefreq); float T = (float) 1/samplefreq; // Calculate the coeficients A0,A1,B0,B1 and B2 for the // second-order transfer function from kp, ki and kd. float A0=1; float A1=-1; float A2=0; float A3=0; float A4=0; float B0=kp+(ki*T/2)+(kd/T); float B1=(-kp)+(ki*T/2)-(2*kd/T); float B2=kd/T; float B3=0; float B4=0;
208
float *pA0; unsigned int adrA0; unsigned char A0_byte0; unsigned char A0_byte1; unsigned char A0_byte2; unsigned char A0_byte3; pA0=&A0; adrA0=(int)pA0; (char *) adrA0; (char *) (adrA0+1); (char *) (adrA0+2); (char *) (adrA0+3); A0_byte3=*(char *) adrA0; A0_byte2=*(char *) (adrA0+1); A0_byte1=*(char *) (adrA0+2); A0_byte0=*(char *) (adrA0+3); vars[1]=A0_byte0; vars[2]=A0_byte1; vars[3]=A0_byte2; vars[4]=A0_byte3; float *pA1; unsigned int adrA1; unsigned char A1_byte0; unsigned char A1_byte1; unsigned char A1_byte2; unsigned char A1_byte3; pA1=&A1; adrA1=(int)pA1; (char *) adrA1; (char *) (adrA1+1); (char *) (adrA1+2); (char *) (adrA1+3); A1_byte3=*(char *) adrA1; A1_byte2=*(char *) (adrA1+1); A1_byte1=*(char *) (adrA1+2); A1_byte0=*(char *) (adrA1+3); vars[5]=A1_byte0; vars[6]=A1_byte1; vars[7]=A1_byte2; vars[8]=A1_byte3; float *pA2; unsigned int adrA2; unsigned char A2_byte0; unsigned char A2_byte1; unsigned char A2_byte2; unsigned char A2_byte3; pA2=&A2; adrA2=(int)pA2; (char *) adrA2; (char *) (adrA2+1); (char *) (adrA2+2);
209
(char *) (adrA2+3); A2_byte3=*(char *) adrA2; A2_byte2=*(char *) (adrA2+1); A2_byte1=*(char *) (adrA2+2); A2_byte0=*(char *) (adrA2+3); vars[9]=A2_byte0; vars[10]=A2_byte1; vars[11]=A2_byte2; vars[12]=A2_byte3; float *pA3; unsigned int adrA3; unsigned char A3_byte0; unsigned char A3_byte1; unsigned char A3_byte2; unsigned char A3_byte3; pA3=&A3; adrA3=(int)pA3; (char *) adrA3; (char *) (adrA3+1); (char *) (adrA3+2); (char *) (adrA3+3); A3_byte3=*(char *) adrA3; A3_byte2=*(char *) (adrA3+1); A3_byte1=*(char *) (adrA3+2); A3_byte0=*(char *) (adrA3+3); vars[13]=A3_byte0; vars[14]=A3_byte1; vars[15]=A3_byte2; vars[16]=A3_byte3; float *pA4; unsigned int adrA4; unsigned char A4_byte0; unsigned char A4_byte1; unsigned char A4_byte2; unsigned char A4_byte3; pA4=&A4; adrA4=(int)pA4; (char *) adrA4; (char *) (adrA4+1); (char *) (adrA4+2); (char *) (adrA4+3); A4_byte3=*(char *) adrA4; A4_byte2=*(char *) (adrA4+1); A4_byte1=*(char *) (adrA4+2); A4_byte0=*(char *) (adrA4+3); vars[17]=A4_byte0; vars[18]=A4_byte1; vars[19]=A4_byte2; vars[20]=A4_byte3; float *pB0; unsigned int adrB0;
210
unsigned char B0_byte0; unsigned char B0_byte1; unsigned char B0_byte2; unsigned char B0_byte3; pB0=&B0; adrB0=(int)pB0; (char *) adrB0; (char *) (adrB0+1); (char *) (adrB0+2); (char *) (adrB0+3); B0_byte3=*(char *) adrB0; B0_byte2=*(char *) (adrB0+1); B0_byte1=*(char *) (adrB0+2); B0_byte0=*(char *) (adrB0+3); vars[21]=B0_byte0; vars[22]=B0_byte1; vars[23]=B0_byte2; vars[24]=B0_byte3; float *pB1; unsigned int adrB1; unsigned char B1_byte0; unsigned char B1_byte1; unsigned char B1_byte2; unsigned char B1_byte3; pB1=&B1; adrB1=(int)pB1; (char *) adrB1; (char *) (adrB1+1); (char *) (adrB1+2); (char *) (adrB1+3); B1_byte3=*(char *) adrB1; B1_byte2=*(char *) (adrB1+1); B1_byte1=*(char *) (adrB1+2); B1_byte0=*(char *) (adrB1+3); vars[25]=B1_byte0; vars[26]=B1_byte1; vars[27]=B1_byte2; vars[28]=B1_byte3; float *pB2; unsigned int adrB2; unsigned char B2_byte0; unsigned char B2_byte1; unsigned char B2_byte2; unsigned char B2_byte3; pB2=&B2; adrB2=(int)pB2; (char *) adrB2; (char *) (adrB2+1); (char *) (adrB2+2); (char *) (adrB2+3); B2_byte3=*(char *) adrB2; B2_byte2=*(char *) (adrB2+1);
211
B2_byte1=*(char *) (adrB2+2); B2_byte0=*(char *) (adrB2+3); vars[29]=B2_byte0; vars[30]=B2_byte1; vars[31]=B2_byte2; vars[32]=B2_byte3; float *pB3; unsigned int adrB3; unsigned char B3_byte0; unsigned char B3_byte1; unsigned char B3_byte2; unsigned char B3_byte3; pB3=&B3; adrB3=(int)pB3; (char *) adrB3; (char *) (adrB3+1); (char *) (adrB3+2); (char *) (adrB3+3); B3_byte3=*(char *) adrB3; B3_byte2=*(char *) (adrB3+1); B3_byte1=*(char *) (adrB3+2); B3_byte0=*(char *) (adrB3+3); vars[33]=B3_byte0; vars[34]=B3_byte1; vars[35]=B3_byte2; vars[36]=B3_byte3; float *pB4; unsigned int adrB4; unsigned char B4_byte0; unsigned char B4_byte1; unsigned char B4_byte2; unsigned char B4_byte3; pB4=&B4; adrB4=(int)pB4; (char *) adrB4; (char *) (adrB4+1); (char *) (adrB4+2); (char *) (adrB4+3); B4_byte3=*(char *) adrB4; B4_byte2=*(char *) (adrB4+1); B4_byte1=*(char *) (adrB4+2); B4_byte0=*(char *) (adrB4+3); vars[37]=B4_byte0; vars[38]=B4_byte1; vars[39]=B4_byte2; vars[40]=B4_byte3; // samplefreq declared earlier to calculate A0,A1,B0,B1,B2 vars[41]=(unsigned char)samplefreq; char *ppwmhz; int pwmhz;
212
ppwmhz=PIDPWMEdit->Text.c_str(); pwmhz=atoi(ppwmhz); vars[42]=(unsigned char)pwmhz; // // // // if Check whether positive or feedback or negative feedback was selected. A 1 will be transmitted to the microcontroller for negative feedback and a 2 will be transmitted for positive feedback. (PIDNegativeFeedbackSelect->Checked) { vars[43]=1; //Negative feedback } else { vars[43]=2; //Positive feedback } char *poutputupperlimit; int outputupperlimit; poutputupperlimit=PIDOutputupperlimitEdit->Text.c_str(); outputupperlimit=atoi(poutputupperlimit); vars[44]=(unsigned char)outputupperlimit; char *poutputlowerlimit; int outputlowerlimit; poutputlowerlimit=PIDOutputlowerlimitEdit->Text.c_str(); outputlowerlimit=atoi(poutputlowerlimit); vars[45]=(unsigned char)outputlowerlimit; char *pinputupperlimit; int inputupperlimit; pinputupperlimit=PIDInputupperlimitEdit->Text.c_str(); inputupperlimit=atoi(pinputupperlimit); vars[46]=(unsigned char)inputupperlimit; char *pinputlowerlimit; int inputlowerlimit; pinputlowerlimit=PIDInputlowerlimitEdit->Text.c_str(); inputlowerlimit=atoi(pinputlowerlimit); vars[47]=(unsigned char)inputlowerlimit; vars[48]=pidinoutcombination; // The checksum byte will be added to the s-record line. // The checksum is the complement of the 8-bit sum of all the // bytes in the s-record line. int lenvars=49; unsigned char sum=0; for (int count1=0;count1<lenvars;count1++) { sum=sum+vars[count1]; } chksum=255-52-sum; // The second term indicates the number // of bytes which will be transmitted plus three (2 byte memory
213
// location and 1 byte checksum). char addline[100]="S1340000"; // Hex 33 indicates the number of int count; for (count=0;count<lenvars;count++) // Convert the decimal variables // into hexadecimal values { char temp[3]; if (vars[count]<16) sprintf(temp,"0%X",vars[count]); // Add a '0' if the // hexidecimal value is a // single digit value. else sprintf(temp,"%X",vars[count]); strcat(addline,temp); // Add the hexideciml value to the // s-record line. } char temp[3]; if (chksum<16) sprintf(temp,"0%X",chksum); // Add a '0' if the hexidecimal // value is a single digit value else sprintf(temp,"%X",chksum); strcat(addline,temp); // add the 8-bit checksum value // to the s-record line Download1->Memo1->Lines->Text=DigitalCode->Lines->Text; Download1->Memo1->Lines->Insert(1,addline); // Add the complete // existing control //algorithm. // Start download procedure
Download1->ShowModal(); } }
//-------------------------------------------------------------------// The following four event handlers will be activated whenever the // user exits the particular editbox and will check whether the // values in the editboxes exeed 255 which is the maximum value for // an unsigned 8-bit number. An errormessage named Max255Form will // be displayed if the value in the editbox is higher than 255. //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDSetpointEditExit(TObject *Sender) { char *psetp; psetp=MonitorForm->PIDSetpointEdit->EditText.c_str(); int setp=atoi(psetp); if (setp>255) { Max255Form->ShowModal(); MonitorForm->PIDSetpointEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDKpEditExit(TObject *Sender)
214
{ char *pkp; pkp=MonitorForm->PIDKpEdit->EditText.c_str(); int kp=atoi(pkp); if (kp>255) { Max255Form->ShowModal(); MonitorForm->PIDKpEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDKiEditExit(TObject *Sender) { char *pki; pki=MonitorForm->PIDKiEdit->EditText.c_str(); int ki=atoi(pki); if (ki>255) { Max255Form->ShowModal(); MonitorForm->PIDKiEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDKdEditExit(TObject *Sender) { char *pkd; pkd=MonitorForm->PIDKdEdit->EditText.c_str(); int kd=atoi(pkd); if (kd>255) { Max255Form->ShowModal(); MonitorForm->PIDKdEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDSamplefreqEditExit(TObject *Sender) { char *psamplefreq; psamplefreq=MonitorForm->PIDSamplefreqEdit->EditText.c_str(); int samplefreq=atoi(psamplefreq); if (samplefreq>255) { Max255Form->ShowModal(); MonitorForm->PIDSamplefreqEdit->SetFocus(); } }
215
// // // // // // // //
This event handler will check whether all the data entry boxes in the TestDACADC section is filled with data by the user. If all the editboxes are filled with data, the text will be converted to integer numbers. The integer numbers will then be converted to hexadecimal numbers which will be converted into a s-record line. The s-record line which contain the control parameters is added to the predefined microcontroller code. The download procedure will then be called.
{ if (TestDACADCStepsizeEdit->EditText==" " || TestDACADCDelayEdit->EditText==" " || TestDACADCSamplefreqEdit->EditText==" " || TestDACADCPWMEdit->EditText==" " || TestADCDACOutputupperlimitEdit->EditText==" " || TestADCDACOutputlowerlimitEdit->EditText==" ") { InsufficientDataForm->ShowModal(); // An error message will be // displayed if any of the // data entry fields is empty. } else { unsigned char chksum; unsigned char vars[20]; char *pstepsize; int stepsize; pstepsize=TestDACADCStepsizeEdit->Text.c_str(); stepsize=atoi(pstepsize); vars[0]=(unsigned char)stepsize; // Convert the variables //from text to integer char *pstepdelay; int stepdelay; pstepdelay=TestDACADCDelayEdit->Text.c_str(); stepdelay=atoi(pstepdelay); vars[1]=(unsigned char)stepdelay; char *psamplefreq; int samplefreq; psamplefreq=TestDACADCSamplefreqEdit->Text.c_str(); samplefreq=atoi(psamplefreq); vars[2]=(unsigned char)samplefreq; char *ppwmfreq; int pwmfreq; ppwmfreq=TestDACADCPWMEdit->Text.c_str(); pwmfreq=atoi(ppwmfreq); vars[3]=(unsigned char)pwmfreq; char *plowerlimit; int lowerlimit; plowerlimit=TestADCDACOutputlowerlimitEdit->Text.c_str();
216
lowerlimit=atoi(plowerlimit); vars[4]=(unsigned char)lowerlimit; char *pupperlimit; int upperlimit; pupperlimit=TestADCDACOutputupperlimitEdit->Text.c_str(); upperlimit=atoi(pupperlimit); vars[5]=(unsigned char)upperlimit; vars[6]=testdacinoutcombination; // // // // // The checksum byte will be added to the s-record line. The checksum is the least significant byte of the complement of the sum of all the bytes in the s-record line. The addline array forms the first part of the s-record line. The first two characters which is
chksum=255-10-(vars[0]+vars[1]+vars[2]+vars[3]+vars[4]+ vars[5]+vars[6]); char addline[9]="S10A0000"; int count; for (count=0;count<7;count++) // Convert the decimal variables // into hexadecimal values { char temp[3]; if (vars[count]<16) sprintf(temp,"0%X",vars[count]); // Add a '0' if the // hexidecimal value is a // single digit value. else sprintf(temp,"%X",vars[count]); strcat(addline,temp); // Add the hexideciml value to the // s-record line. } char temp[3]; if (chksum<16) sprintf(temp,"0%X",chksum); // Add a '0' if the hexidecimal // value is a single digit value else sprintf(temp,"%X",chksum); strcat(addline,temp); // add the 8-bit checksum value // to the s-record line
Download1->Memo1->Lines->Text=TestDACADCCode->Lines->Text; Download1->Memo1->Lines->Insert(1,addline); // Add the complete // the existing code // Start download procedure
Download1->ShowModal(); }
} //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCMonitorButtonClick( TObject *Sender) // This event handler will enable serial communication between the
217
// // // // // //
microcontroller and the PC and will enable and disable all the appropriate buttons on the main form. When data is available from the serial port, an OnDataAvailable event will be triggered and the MonitorCommDataAvailable event handler will be called which will handle the incomming data received from the microcontroller.
{ TestDACADCComm->OpenConnection(); TestDACADCRecordButton->Enabled=true; TestDACADCMonitorButton->Enabled=false; TestDACADCDisconnectButton->Enabled=true; TestDACADCDownloadButton->Enabled=false; TestDACADCRunButton->Enabled=false; } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCDisconnectButtonClick( TObject *Sender) // This event handler will disable serial communication between the // microcontroller and the PC and will enable and disable all the // appropriate buttons on the main form. { TestDACADCComm->CloseConnection(); TestDACADCRecordButton->Enabled=false; TestDACADCMonitorButton->Enabled=true; TestDACADCDisconnectButton->Enabled=false; TestDACADCDownloadButton->Enabled=true; TestDACADCRunButton->Enabled=true; } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCDisplayTimerTimer( TObject *Sender) { // // // // // //
This event handler will display the data received from the microcontroller with every time interval of the timer. The time interval can be set with the 'interval' property of the timer component.
TestDACADCInputDigitBox->Target=TestDACADCinputdisplay; TestDACADCInputDigitBox->Value=TestDACADCinputdisplay; TestDACADCOutputDigitBox->Target=TestDACADCoutputdisplay; TestDACADCOutputDigitBox->Value=TestDACADCoutputdisplay; TestDACADCSamplefreqDigitBox->Target=TestDACADCsamplefrequency; TestDACADCSamplefreqDigitBox->Value=TestDACADCsamplefrequency; // The stopbutton will only be enabled when recording // in in progress. if (TestDACADCStopButton->Enabled==true)
218
{ TestDACADCTimeDigitBox->Target=TestDACADCtimedisplay; TestDACADCTimeDigitBox->Value=TestDACADCtimedisplay; } } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCStepsizeEditEnter( TObject *Sender) { TestDACADCStepsizeEdit->SelStart=0; TestDACADCRunButton->Enabled=false; } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCStepsizeEditExit( TObject *Sender) { char *pTestDACADCStepsize; pTestDACADCStepsize=MonitorForm->TestDACADCStepsizeEdit-> EditText.c_str(); int TestDACADCStepsize=atoi(pTestDACADCStepsize); if (TestDACADCStepsize>255) { Max255Form->ShowModal(); MonitorForm->TestDACADCStepsizeEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCRecordButtonClick( TObject *Sender) { // This routine will start the data recording process and will enable // and disable all the appropriate buttons related to this recording // action. TestDACADCDisconnectButton->Enabled=false; TestDACADCPlotButton->Enabled=false; TestDACADCSaveButton->Enabled=false; TestDACADCStopButton->Enabled=true; TestDACADCRecordButton->Enabled=false; PlotForm1->XYPlot1->RemovePlot(1); PlotForm1->XYPlot1->RemovePlot(2); // Clear the current plot. TestDACADCoutputindex=0; TestDACADCinputindex=0; TestDACADCtimeindex=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCStopButtonClick( TObject *Sender) { // This routine will stop data recording process and will enable // and disable all the appropriate buttons related to this stopping // action.
219
TestDACADCtimeindexstop=min(TestDACADCtimeindex,TestDACADCinputindex); TestDACADCtimeindexstop=min(TestDACADCoutputindex, TestDACADCDisconnectButton->Enabled=true; TestDACADCSaveButton->Enabled=true; TestDACADCPlotButton->Enabled=true; TestDACADCSaveButton->Enabled=true; TestDACADCStopButton->Enabled=false; TestDACADCRecordButton->Enabled=true; for (int count=0;count<TestDACADCtimeindexstop;count++) { TestDACADCtimerecorded[count]=TestDACADCtime[count]; TestDACADCinputrecorded[count]=TestDACADCinput[count]; TestDACADCoutputrecorded[count]=TestDACADCoutput[count]; } } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCPlotButtonClick( TObject *Sender) { // TXYPlot is a Borland C++ Builder VCL plotting component for // graphing x,y data. It is used to plot the recorded data // versus time. PlotForm1->Show(); PlotForm1->XYPlot1->XAutoScale=true; PlotForm1->XYPlot1->YAutoScale=true; if (MonitorForm->PlotLines1->Checked) { PlotForm1->XYPlot1->XYPlot(TestDACADCtimerecorded, TestDACADCinputrecorded,TestDACADCtimeindexstop,1, clBlue,LinesFilledPoints); } else { PlotForm1->XYPlot1->XYPlot(TestDACADCtimerecorded, TestDACADCinputrecorded,TestDACADCtimeindexstop,1, clBlue,FilledPoints); } PlotForm2->Show(); PlotForm2->XYPlot1->XAutoScale=true; PlotForm2->XYPlot1->YAutoScale=true; if (MonitorForm->PlotLines1->Checked) { PlotForm2->XYPlot1->XYPlot(TestDACADCtimerecorded, TestDACADCoutputrecorded,TestDACADCtimeindexstop,1, clBlue,LinesFilledPoints); } else
220
{ PlotForm2->XYPlot1->XYPlot(TestDACADCtimerecorded, TestDACADCoutputrecorded,TestDACADCtimeindexstop,1, clBlue,FilledPoints); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCCommDataAvailable( TObject *Sender) { unsigned char *buffer,data[300]; buffer=data; // Clear the input buffer by writing zero's to it for (int n=0;n<=300;n++) { data[n]=0; } TestDACADCComm->ReadComm(buffer,300); // The idea of TestDACADCbuffercount is to ensure that there are at // least six characters of incoming data available before processing // it. TestDACADCbuffercount++; if (TestDACADCbuffercount>6) { TestDACADCbuffercount=1; } switch (TestDACADCbuffercount) { case 1: strcpy((char *)TestDACADCdata5,(char break; case 2: strcpy((char *)TestDACADCdata4,(char break; case 3: strcpy((char *)TestDACADCdata3,(char break; case 4: strcpy((char *)TestDACADCdata2,(char break; case 5: strcpy((char *)TestDACADCdata1,(char break; case 6: strcat((char *)TestDACADCdata5,(char strcat((char *)TestDACADCdata5,(char strcat((char *)TestDACADCdata5,(char strcat((char *)TestDACADCdata5,(char strcat((char *)TestDACADCdata5,(char
*)data);
*)data);
*)data);
*)data);
*)data);
221
// // // // // // // // // //
The following lines will decode the data pattern into the different arrays. Data will be received form the microcontroller in the following format [251, Sample Frequency, 252, Input from plant, 253, Output to plant] The array may start and end at any point, but the data always have to be in the same order, for example: the following array is also a valid data array [Input from plant, 253, Output to plant, 251, Sample Frequency]
for (int scandata=0;scandata<6;scandata++) { if (TestDACADCdata5[scandata]==251) { for (int count=1+scandata;count<len;count=count+6) { TestDACADCsamplefrequency=TestDACADCdata5[count]; float period=(float) 1/TestDACADCdata5[count]; // The if statement ignores the first period value // since it is not significant. // The first meaningful period value will be available // after one sample cycle on the microcontroler is // finished. if (TestDACADCtimeindex==0) { TestDACADCtime[TestDACADCtimeindex]=0; } else { TestDACADCtime[TestDACADCtimeindex]= TestDACADCtime[TestDACADCtimeindex-1]+period; } TestDACADCtimedisplay=TestDACADCtime[TestDACADCtimeindex]; TestDACADCtimeindex++; } } else if (TestDACADCdata5[scandata]==252) { for (int count=1+scandata;count<len;count=count+6) { TestDACADCinputdisplay=TestDACADCdata5[count]; TestDACADCinput[TestDACADCinputindex]= TestDACADCdata5[count]; TestDACADCinputindex++; } } else if (TestDACADCdata5[scandata]==253) { for (int count=1+scandata;count<len;count=count+6) { TestDACADCoutputdisplay=TestDACADCdata5[count]; TestDACADCoutput[TestDACADCoutputindex] =TestDACADCdata5[count];
222
TestDACADCoutputindex++; } } }// end for scandata break; }// end switch } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCDelayEditEnter( TObject *Sender) { TestDACADCDelayEdit->SelStart=0; TestDACADCRunButton->Enabled=false; } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCDelayEditExit(TObject *Sender) { char *pTestDACADCDelay; pTestDACADCDelay=MonitorForm->TestDACADCDelayEdit-> EditText.c_str(); int TestDACADCDelay=atoi(pTestDACADCDelay); if (TestDACADCDelay>255) { Max255Form->ShowModal(); MonitorForm->TestDACADCDelayEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCSamplefreqEditExit( TObject *Sender) { char *pTestDACADCSamplefreq; pTestDACADCSamplefreq=MonitorForm->TestDACADCSamplefreqEdit-> EditText.c_str(); int TestDACADCSamplefreq=atoi(pTestDACADCSamplefreq); if (TestDACADCSamplefreq>255) { Max255Form->ShowModal(); MonitorForm->TestDACADCSamplefreqEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCSamplefreqEditEnter( TObject *Sender) { TestDACADCSamplefreqEdit->SelStart=0; TestDACADCRunButton->Enabled=false; } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCPWMEditEnter(TObject *Sender) { TestDACADCPWMEdit->SelStart=0; TestDACADCRunButton->Enabled=false; }
223
//-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCPWMEditExit(TObject *Sender) { char *pTestDACADCPWM; pTestDACADCPWM=MonitorForm->TestDACADCPWMEdit-> EditText.c_str(); int TestDACADCPWM=atoi(pTestDACADCPWM); if (TestDACADCPWM>255) { Max255Form->ShowModal(); MonitorForm->TestDACADCPWMEdit->SetFocus(); } if (TestDACADCPWM<40) { Min40Form->ShowModal(); MonitorForm->TestDACADCPWMEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCPortBSelectClick( TObject *Sender) // TestDACADCCode1 : Input=Analog on PE5, Output=8-bit (PB0-PB7) // TestDACADCCode2 : Input=Frequency on IC3, Output=8-bit (PB0-PB7) // TestDACADCCode3 : Input=Frequency on IC3, Output=PWM on OC3 (PA5) // TestDACADCCode4 : Input=Analog on PE5, Output=PWM on OC3 (PA5) { PWMLabel->Enabled=false; TestDACADCPWMEdit->Enabled=false; TestDACADCRunButton->Enabled=false; TestDACADCOutputLabel->Caption="8-bit parallel output"; //AnsiString s19filename; if (TestDACADCFrequencySelect->Checked) { //s19filename="testdac2.s19"; testdacinoutcombination=2; } else { //s19filename="testdac1.s19"; testdacinoutcombination=1; } //MonitorForm->TestDACADCCode->Lines->LoadFromFile(s19filename); } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCPWMSelectClick( TObject *Sender) // TestDACADCCode1 : Input=Analog on PE5, Output=8-bit (PB0-PB7) // TestDACADCCode2 : Input=Frequency on IC3, Output=8-bit (PB0-PB7) // TestDACADCCode3 : Input=Frequency on IC3, Output=PWM on OC3 (PA5) // TestDACADCCode4 : Input=Analog on PE5, Output=PWM on OC3 (PA5) { PWMLabel->Enabled=true; TestDACADCPWMEdit->Enabled=true; TestDACADCRunButton->Enabled=false;
224
TestDACADCOutputLabel->Caption="PWM Output on OC3(PA5)"; //AnsiString s19filename; if (TestDACADCFrequencySelect->Checked) { //s19filename="testdac3.s19"; testdacinoutcombination=3; } else { //s19filename="testdac4.s19"; testdacinoutcombination=4; } //MonitorForm->TestDACADCCode->Lines->LoadFromFile(s19filename); } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCAnalogSelectClick( TObject *Sender) // TestDACADCCode1 : Input=Analog on PE5, Output=8-bit (PB0-PB7) // TestDACADCCode2 : Input=Frequency on IC3, Output=8-bit (PB0-PB7) // TestDACADCCode3 : Input=Frequency on IC3, Output=PWM on OC3 (PA5) // TestDACADCCode4 : Input=Analog on PE5, Output=PWM on OC3 (PA5) { TestDACADCRunButton->Enabled=false; MonitorForm->TestDACADCInputLabel->Caption="Analog Input on PE5"; //AnsiString s19filename; if (TestDACADCPortBSelect->Checked) { //s19filename="testdac1.s19"; testdacinoutcombination=1; } else { //s19filename="testdac4.s19"; testdacinoutcombination=4; } //MonitorForm->TestDACADCCode->Lines->LoadFromFile(s19filename); } //-------------------------------------------------------------------void __fastcall TMonitorForm::TestDACADCFrequencySelectClick( TObject *Sender) // TestDACADCCode1 : Input=Analog on PE5, Output=8-bit (PB0-PB7) // TestDACADCCode2 : Input=Frequency on IC3, Output=8-bit (PB0-PB7) // TestDACADCCode3 : Input=Frequency on IC3, Output=PWM on OC3 (PA5) // TestDACADCCode4 : Input=Analog on PE5, Output=PWM on OC3 (PA5) { TestDACADCRunButton->Enabled=false; MonitorForm->TestDACADCInputLabel-> Caption="Frequency input on IC3 (PA0)"; //AnsiString s19filename; if (TestDACADCPortBSelect->Checked) { //s19filename="testdac2.s19";
225
testdacinoutcombination=2; } else { //s19filename="testdac3.s19"; testdacinoutcombination=3; } //MonitorForm->TestDACADCCode->Lines->LoadFromFile(s19filename); } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDPWMEditEnter(TObject *Sender) { PIDPWMEdit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDPWMEditExit(TObject *Sender) { char *pPIDPWM; pPIDPWM=MonitorForm->PIDPWMEdit->EditText.c_str(); int PIDPWM=atoi(pPIDPWM); if (PIDPWM>255) { Max255Form->ShowModal(); MonitorForm->PIDPWMEdit->SetFocus(); } if (PIDPWM<40) { Min40Form->ShowModal(); MonitorForm->PIDPWMEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDPortBSelectClick(TObject *Sender) // PIDCode1 : Input=Analog on // PIDCode2 : Input=Frequency // PIDCode3 : Input=Frequency // PIDCode4 : Input=Analog on { PIDPWMLabel->Enabled=false; PIDPWMEdit->Enabled=false; PIDRunButton->Enabled=false; PE5, Output=8-bit (PB0-PB7) on IC3, Output=8-bit (PB0-PB7) on IC3, Output=PWM on OC3 (PA5) PE5, Output=PWM on OC3 (PA5)
PIDOutputLabel->Caption="8-bit parallel output"; //AnsiString s19filename; if (PIDFrequencySelect->Checked) { pidinoutcombination=2; //s19filename="digital2.s19"; } else { pidinoutcombination=1; //s19filename="digital1.s19"; }
226
//MonitorForm->DigitalCode->Lines->LoadFromFile(s19filename); } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDPWMSelectClick(TObject *Sender) // PIDCode1 : Input=Analog on // PIDCode2 : Input=Frequency // PIDCode3 : Input=Frequency // PIDCode4 : Input=Analog on { PIDPWMLabel->Enabled=true; PIDPWMEdit->Enabled=true; PIDRunButton->Enabled=false; PE5, Output=8-bit (PB0-PB7) on IC3, Output=8-bit (PB0-PB7) on IC3, Output=PWM on OC3 (PA5) PE5, Output=PWM on OC3 (PA5)
PIDOutputLabel->Caption="PWM Output on OC3(PA5)"; //AnsiString s19filename; if (PIDFrequencySelect->Checked) { pidinoutcombination=3; //s19filename="digital3.s19"; } else { pidinoutcombination=4; //s19filename="digital4.s19"; } //MonitorForm->DigitalCode->Lines->LoadFromFile(s19filename); } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDAnalogSelectClick(TObject *Sender) // PIDCode1 : Input=Analog on PE5, Output=8-bit (PB0-PB7) // PIDCode2 : Input=Frequency on IC3, Output=8-bit (PB0-PB7) // PIDCode3 : Input=Frequency on IC3, Output=PWM on OC3 (PA5) // PIDCode4 : Input=Analog on PE5, Output=PWM on OC3 (PA5) { PIDRunButton->Enabled=false; MonitorForm->PIDInputLabel->Caption="Analog Input on PE5"; //AnsiString s19filename; if (PIDPortBSelect->Checked) { pidinoutcombination=1; //s19filename="digital1.s19"; } else { pidinoutcombination=4; //s19filename="digital4.s19"; } //MonitorForm->DigitalCode->Lines->LoadFromFile(s19filename); } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDFrequencySelectClick(TObject *Sender) // PIDCode1 : Input=Analog on PE5, Output=8-bit (PB0-PB7)
227
// PIDCode2 : Input=Frequency on IC3, Output=8-bit (PB0-PB7) // PIDCode3 : Input=Frequency on IC3, Output=PWM on OC3 (PA5) // PIDCode4 : Input=Analog on PE5, Output=PWM on OC3 (PA5) { PIDRunButton->Enabled=false; MonitorForm->PIDInputLabel->Caption="Frequency input on IC3 (PA0)"; //AnsiString s19filename; if (PIDPortBSelect->Checked) { pidinoutcombination=2; //s19filename="digital2.s19"; } else { pidinoutcombination=3; //s19filename="digital3.s19"; } //MonitorForm->DigitalCode->Lines->LoadFromFile(s19filename); } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalDownloadButtonClick( TObject *Sender) { if (DigitalSetpointEdit->EditText==" " || DigitalA0Edit->EditText==" " || DigitalA1Edit->EditText==" " || DigitalA2Edit->EditText==" " || DigitalA3Edit->EditText==" " || DigitalA4Edit->EditText==" " || DigitalB0Edit->EditText==" " || DigitalB1Edit->EditText==" " || DigitalB2Edit->EditText==" " || DigitalB3Edit->EditText==" " || DigitalB4Edit->EditText==" " || DigitalSamplefreqEdit->EditText==" " || DigitalPWMEdit->EditText==" ") { InsufficientDataForm->ShowModal(); // An error message will be // displayed if any of the // data entry fields is empty. } else { unsigned char chksum; unsigned char vars[50]; // ! char *psetp; int setp; psetp=DigitalSetpointEdit->Text.c_str(); setp=atoi(psetp); vars[0]=(unsigned char)setp; // Convert the variables //from text to float
228
char *pstrA0; float A0; unsigned int adrA0; float *pA0; unsigned char A0_byte0; unsigned char A0_byte1; unsigned char A0_byte2; unsigned char A0_byte3; AnsiString ansiA0=DigitalA0Edit->Text; pstrA0=ansiA0.c_str(); A0=atof(pstrA0); pA0=&A0; adrA0=(int)pA0; (char *) adrA0; (char *) (adrA0+1); (char *) (adrA0+2); (char *) (adrA0+3); A0_byte3=*(char *) adrA0; A0_byte2=*(char *) (adrA0+1); A0_byte1=*(char *) (adrA0+2); A0_byte0=*(char *) (adrA0+3); vars[1]=A0_byte0; vars[2]=A0_byte1; vars[3]=A0_byte2; vars[4]=A0_byte3; char *pstrA1; float A1; unsigned int adrA1; float *pA1; unsigned char A1_byte0; unsigned char A1_byte1; unsigned char A1_byte2; unsigned char A1_byte3; AnsiString ansiA1=DigitalA1Edit->Text; pstrA1=ansiA1.c_str(); A1=atof(pstrA1); pA1=&A1; adrA1=(int)pA1; (char *) adrA1; (char *) (adrA1+1); (char *) (adrA1+2); (char *) (adrA1+3); A1_byte3=*(char *) adrA1; A1_byte2=*(char *) (adrA1+1); A1_byte1=*(char *) (adrA1+2); A1_byte0=*(char *) (adrA1+3); vars[5]=A1_byte0; vars[6]=A1_byte1; vars[7]=A1_byte2; vars[8]=A1_byte3; char *pstrA2; float A2;
229
unsigned int adrA2; float *pA2; unsigned char A2_byte0; unsigned char A2_byte1; unsigned char A2_byte2; unsigned char A2_byte3; AnsiString ansiA2=DigitalA2Edit->Text; pstrA2=ansiA2.c_str(); A2=atof(pstrA2); pA2=&A2; adrA2=(int)pA2; (char *) adrA2; (char *) (adrA2+1); (char *) (adrA2+2); (char *) (adrA2+3); A2_byte3=*(char *) adrA2; A2_byte2=*(char *) (adrA2+1); A2_byte1=*(char *) (adrA2+2); A2_byte0=*(char *) (adrA2+3); vars[9]=A2_byte0; vars[10]=A2_byte1; vars[11]=A2_byte2; vars[12]=A2_byte3; char *pstrA3; float A3; unsigned int adrA3; float *pA3; unsigned char A3_byte0; unsigned char A3_byte1; unsigned char A3_byte2; unsigned char A3_byte3; AnsiString ansiA3=DigitalA3Edit->Text; pstrA3=ansiA3.c_str(); A3=atof(pstrA3); pA3=&A3; adrA3=(int)pA3; (char *) adrA3; (char *) (adrA3+1); (char *) (adrA3+2); (char *) (adrA3+3); A3_byte3=*(char *) adrA3; A3_byte2=*(char *) (adrA3+1); A3_byte1=*(char *) (adrA3+2); A3_byte0=*(char *) (adrA3+3); vars[13]=A3_byte0; vars[14]=A3_byte1; vars[15]=A3_byte2; vars[16]=A3_byte3; char *pstrA4; float A4; unsigned int adrA4; float *pA4;
230
unsigned char A4_byte0; unsigned char A4_byte1; unsigned char A4_byte2; unsigned char A4_byte3; AnsiString ansiA4=DigitalA4Edit->Text; pstrA4=ansiA4.c_str(); A4=atof(pstrA4); pA4=&A4; adrA4=(int)pA4; (char *) adrA4; (char *) (adrA4+1); (char *) (adrA4+2); (char *) (adrA4+3); A4_byte3=*(char *) adrA4; A4_byte2=*(char *) (adrA4+1); A4_byte1=*(char *) (adrA4+2); A4_byte0=*(char *) (adrA4+3); vars[17]=A4_byte0; vars[18]=A4_byte1; vars[19]=A4_byte2; vars[20]=A4_byte3; char *pstrB0; float B0; unsigned int adrB0; float *pB0; unsigned char B0_byte0; unsigned char B0_byte1; unsigned char B0_byte2; unsigned char B0_byte3; AnsiString ansiB0=DigitalB0Edit->Text; pstrB0=ansiB0.c_str(); B0=atof(pstrB0); pB0=&B0; adrB0=(int)pB0; (char *) adrB0; (char *) (adrB0+1); (char *) (adrB0+2); (char *) (adrB0+3); B0_byte3=*(char *) adrB0; B0_byte2=*(char *) (adrB0+1); B0_byte1=*(char *) (adrB0+2); B0_byte0=*(char *) (adrB0+3); vars[21]=B0_byte0; vars[22]=B0_byte1; vars[23]=B0_byte2; vars[24]=B0_byte3; char *pstrB1; float B1; unsigned int adrB1; float *pB1; unsigned char B1_byte0; unsigned char B1_byte1;
231
unsigned char B1_byte2; unsigned char B1_byte3; AnsiString ansiB1=DigitalB1Edit->Text; pstrB1=ansiB1.c_str(); B1=atof(pstrB1); pB1=&B1; adrB1=(int)pB1; (char *) adrB1; (char *) (adrB1+1); (char *) (adrB1+2); (char *) (adrB1+3); B1_byte3=*(char *) adrB1; B1_byte2=*(char *) (adrB1+1); B1_byte1=*(char *) (adrB1+2); B1_byte0=*(char *) (adrB1+3); vars[25]=B1_byte0; vars[26]=B1_byte1; vars[27]=B1_byte2; vars[28]=B1_byte3; char *pstrB2; float B2; unsigned int adrB2; float *pB2; unsigned char B2_byte0; unsigned char B2_byte1; unsigned char B2_byte2; unsigned char B2_byte3; AnsiString ansiB2=DigitalB2Edit->Text; pstrB2=ansiB2.c_str(); B2=atof(pstrB2); pB2=&B2; adrB2=(int)pB2; (char *) adrB2; (char *) (adrB2+1); (char *) (adrB2+2); (char *) (adrB2+3); B2_byte3=*(char *) adrB2; B2_byte2=*(char *) (adrB2+1); B2_byte1=*(char *) (adrB2+2); B2_byte0=*(char *) (adrB2+3); vars[29]=B2_byte0; vars[30]=B2_byte1; vars[31]=B2_byte2; vars[32]=B2_byte3; char *pstrB3; float B3; unsigned int adrB3; float *pB3; unsigned char B3_byte0; unsigned char B3_byte1; unsigned char B3_byte2; unsigned char B3_byte3;
232
AnsiString ansiB3=DigitalB3Edit->Text; pstrB3=ansiB3.c_str(); B3=atof(pstrB3); pB3=&B3; adrB3=(int)pB3; (char *) adrB3; (char *) (adrB3+1); (char *) (adrB3+2); (char *) (adrB3+3); B3_byte3=*(char *) adrB3; B3_byte2=*(char *) (adrB3+1); B3_byte1=*(char *) (adrB3+2); B3_byte0=*(char *) (adrB3+3); vars[33]=B3_byte0; vars[34]=B3_byte1; vars[35]=B3_byte2; vars[36]=B3_byte3; char *pstrB4; float B4; unsigned int adrB4; float *pB4; unsigned char B4_byte0; unsigned char B4_byte1; unsigned char B4_byte2; unsigned char B4_byte3; AnsiString ansiB4=DigitalB4Edit->Text; pstrB4=ansiB4.c_str(); B4=atof(pstrB4); pB4=&B4; adrB4=(int)pB4; (char *) adrB4; (char *) (adrB4+1); (char *) (adrB4+2); (char *) (adrB4+3); B4_byte3=*(char *) adrB4; B4_byte2=*(char *) (adrB4+1); B4_byte1=*(char *) (adrB4+2); B4_byte0=*(char *) (adrB4+3); vars[37]=B4_byte0; vars[38]=B4_byte1; vars[39]=B4_byte2; vars[40]=B4_byte3; char *psamplefreq; int samplefreq; psamplefreq=DigitalSamplefreqEdit->Text.c_str(); samplefreq=atoi(psamplefreq); vars[41]=(unsigned char)samplefreq; char *ppwmhz; int pwmhz; ppwmhz=DigitalPWMEdit->Text.c_str(); pwmhz=atoi(ppwmhz);
233
vars[42]=(unsigned char)pwmhz; // // // // if Check whether positive or feedback or negative feedback was selected. A 1 will be transmitted to the microcontroller for negative feedback and a 2 will be transmitted for positive feedback. (DigitalNegativeFeedbackSelect->Checked) { vars[43]=1; //Negative feedback } else { vars[43]=2; //Positive feedback }
char *poutputupperlimit; int outputupperlimit; poutputupperlimit=DigitalOutputupperlimitEdit->Text.c_str(); outputupperlimit=atoi(poutputupperlimit); vars[44]=(unsigned char)outputupperlimit; char *poutputlowerlimit; int outputlowerlimit; poutputlowerlimit=DigitalOutputlowerlimitEdit->Text.c_str(); outputlowerlimit=atoi(poutputlowerlimit); vars[45]=(unsigned char)outputlowerlimit; char *pinputupperlimit; int inputupperlimit; pinputupperlimit=DigitalInputupperlimitEdit->Text.c_str(); inputupperlimit=atoi(pinputupperlimit); vars[46]=(unsigned char)inputupperlimit; char *pinputlowerlimit; int inputlowerlimit; pinputlowerlimit=DigitalInputlowerlimitEdit->Text.c_str(); inputlowerlimit=atoi(pinputlowerlimit); vars[47]=(unsigned char)inputlowerlimit; vars[48]=digitalinoutcombination; // The checksum byte will be added to the s-record line. // The checksum is the complement of the 8-bit sum of all the // bytes in the s-record line. int lenvars=49; unsigned char sum=0; for (int count1=0;count1<lenvars;count1++) { sum=sum+vars[count1]; } chksum=255-52-sum; // The second term indicates the number // of bytes which will be transmitted plus three (2 byte memory // location and 1 byte checksum). char addline[100]="S1340000"; // Hex 34 indicates the number of
234
int count; for (count=0;count<lenvars;count++) // Convert the decimal variables // into hexadecimal values { char temp[3]; if (vars[count]<16) sprintf(temp,"0%X",vars[count]); // Add a '0' if the // hexidecimal value is a // single digit value. else sprintf(temp,"%X",vars[count]); strcat(addline,temp); // Add the hexideciml value to the // s-record line. } char temp[3]; if (chksum<16) sprintf(temp,"0%X",chksum); // Add a '0' if the hexidecimal // value is a single digit value else sprintf(temp,"%X",chksum); strcat(addline,temp); // add the 8-bit checksum value // to the s-record line Download1->Memo1->Lines->Text=DigitalCode->Lines->Text; Download1->Memo1->Lines->Insert(1,addline); // Add the complete // existing control //algorithm. // Start download procedure
Download1->ShowModal(); }
} //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalMonitorButtonClick( TObject *Sender) // // // // // // // This event handler will enable serial communication between the microcontroller and the PC and will enable and disable all the appropriate buttons on the main form. When data is available from the serial port, an OnDataAvailable event will be triggered and the MonitorCommDataAvailable event handler will be called which will handle the incomming data received from the microcontroller.
235
TObject *Sender) // This event handler will disable serial communication between the // microcontroller and the PC and will enable and disable all the // appropriate buttons on the main form. { DigitalComm->CloseConnection(); DigitalRecordButton->Enabled=false; DigitalMonitorButton->Enabled=true; DigitalDisconnectButton->Enabled=false; DigitalDownloadButton->Enabled=true; DigitalRunButton->Enabled=true; } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalRecordButtonClick( TObject *Sender) { // This routine will start the data recording process and will enable // and disable all the appropriate buttons related to this recording // action. DigitalDisconnectButton->Enabled=false; DigitalPlotButton->Enabled=false; DigitalSaveButton->Enabled=false; DigitalStopButton->Enabled=true; DigitalRecordButton->Enabled=false; PlotForm1->XYPlot1->RemovePlot(1); PlotForm1->XYPlot1->RemovePlot(2); // Clear the current plot. Digitaloutputindex=0; Digitalinputindex=0; Digitaltimeindex=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalStopButtonClick(TObject *Sender) { // This routine will stop data recording process and will enable // and disable all the appropriate buttons related to this stopping // action. Digitaltimeindexstop=min(Digitaltimeindex,Digitalinputindex); Digitaltimeindexstop=min(Digitaloutputindex,Digitaltimeindexstop); DigitalDisconnectButton->Enabled=true; SaveButton->Enabled=true; DigitalPlotButton->Enabled=true; DigitalSaveButton->Enabled=true; DigitalStopButton->Enabled=false; DigitalRecordButton->Enabled=true; for (int count=0;count<Digitaltimeindexstop;count++) { Digitaltimerecorded[count]=Digitaltime[count]; Digitalinputrecorded[count]=Digitalinput[count]; Digitaloutputrecorded[count]=Digitaloutput[count];
236
} } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalPlotButtonClick(TObject *Sender) { // TXYPlot is a Borland C++ Builder VCL plotting component for // graphing x,y data. It is used to plot the recorded data // versus time. PlotForm1->Show(); PlotForm1->Caption="Input From Plant"; PlotForm1->XYPlot1->XAutoScale=true; PlotForm1->XYPlot1->YAutoScale=true; if (MonitorForm->PlotLines1->Checked) { PlotForm1->XYPlot1->XYPlot(Digitaltimerecorded, Digitalinputrecorded,Digitaltimeindexstop,1, clBlue,LinesFilledPoints); } else { PlotForm1->XYPlot1->XYPlot(Digitaltimerecorded, Digitalinputrecorded,Digitaltimeindexstop,1, clBlue,FilledPoints); } PlotForm2->Show(); PlotForm2->Caption="Output to Plant"; PlotForm2->XYPlot1->XAutoScale=true; PlotForm2->XYPlot1->YAutoScale=true; if (MonitorForm->PlotLines1->Checked) { PlotForm2->XYPlot1->XYPlot(Digitaltimerecorded, Digitaloutputrecorded,Digitaltimeindexstop,1, clBlue,LinesFilledPoints); } else { PlotForm2->XYPlot1->XYPlot(Digitaltimerecorded, Digitaloutputrecorded,Digitaltimeindexstop,1, clBlue,FilledPoints); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::UsersManual1Click(TObject *Sender) { Application->HelpContext(0); } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalController1Click(TObject *Sender) { // This event handler will enable and display the PID controller // panel on which all the controls and editboxes for the PID
237
// controller are displayed. MonitorForm->Caption= "HC11Control - Single-Input-Single-Output Digital Controller"; DigitalController1->Checked=true; MonitorForm->DigitalPanel->Visible=true; MonitorForm->DigitalPanel->Enabled=true; MonitorForm->N68HC11Programs1->Enabled=false; MonitorForm->New1->Enabled=true; // Load microcontroller code from s-record file AnsiString s19filename="digital.s19"; MonitorForm->DigitalCode->Lines->LoadFromFile(s19filename); } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalCommDataAvailable( TObject *Sender) { unsigned char *buffer,data[300]; buffer=data; // Clear the input buffer by writing zero's to it for (int n=0;n<=300;n++) { data[n]=0; } DigitalComm->ReadComm(buffer,300); // The idea of Digitalbuffercount is to ensure that there are at least // six characters of incoming data available before processing it. Digitalbuffercount++; if (Digitalbuffercount>6) { Digitalbuffercount=1; } switch (Digitalbuffercount) { case 1: strcpy((char *)Digitaldata5,(char break; case 2: strcpy((char *)Digitaldata4,(char break; case 3: strcpy((char *)Digitaldata3,(char break; case 4: strcpy((char *)Digitaldata2,(char break; case 5: strcpy((char *)Digitaldata1,(char break; case 6: strcat((char *)Digitaldata5,(char strcat((char *)Digitaldata5,(char strcat((char *)Digitaldata5,(char
*)data);
*)data);
*)data);
*)data);
*)data);
238
strcat((char *)Digitaldata5,(char *)Digitaldata1); strcat((char *)Digitaldata5,(char *)data); unsigned int len=strlen((char *)Digitaldata5); // // // // // // // // // // The following lines will decode the data pattern into the different arrays. Data will be received form the microcontroller in the following format [251, Sample Frequency, 252, Input from plant, 253, Output to plant] The array may start and end at any point, but the data always have to be in the same order, for example: the following array is also a valid data array [Input from plant, 253, Output to plant, 251, Sample Frequency]
for (int scandata=0;scandata<6;scandata++) { if (Digitaldata5[scandata]==251) { for (unsigned int count=1+scandata;count<len;count=count+6) { Digitalsamplefrequency=Digitaldata5[count]; float period=(float) 1/Digitaldata5[count]; // The if statement ignores the first period value // since it is not significant. // The first meaningful period value will be available // after one sample cycle on the microcontroler is // finished. if (Digitaltimeindex==0) { Digitaltime[Digitaltimeindex]=0; } else { Digitaltime[Digitaltimeindex]= Digitaltime[Digitaltimeindex-1]+period; } Digitaltimedisplay=Digitaltime[Digitaltimeindex]; Digitaltimeindex++; } } else if (Digitaldata5[scandata]==252) { for (unsigned int count=1+scandata;count<len;count=count+6) { Digitalinputdisplay=Digitaldata5[count]; Digitalinput[Digitalinputindex]=Digitaldata5[count]; Digitalinputindex++; } } else if (Digitaldata5[scandata]==253) { for (unsigned int count=1+scandata;count<len;count=count+6)
239
{ Digitaloutputdisplay=Digitaldata5[count]; Digitaloutput[Digitaloutputindex]=Digitaldata5[count]; Digitaloutputindex++; } } }// end for scandata break; }// end switch } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalDisplayTimerTimer( TObject *Sender) { // // // // // //
This event handler will display the data received from the microcontroller with every time interval of the timer. The time interval can be set with the 'interval' property of the timer component.
DigitalInputDigitBox->Target=Digitalinputdisplay; DigitalInputDigitBox->Value=Digitalinputdisplay; DigitalOutputDigitBox->Target=Digitaloutputdisplay; DigitalOutputDigitBox->Value=Digitaloutputdisplay; DigitalSamplefreqDigitBox->Target=Digitalsamplefrequency; DigitalSamplefreqDigitBox->Value=Digitalsamplefrequency; // The stopbutton will only be enabled when recording // in in progress. if (DigitalStopButton->Enabled==true) { DigitalTimeDigitBox->Target=Digitaltimedisplay; DigitalTimeDigitBox->Value=Digitaltimedisplay; } } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalPortBSelectClick(TObject *Sender) // DigitalCode1 : Input=Analog on PE5, Output=8-bit (PB0-PB7) // DigitalCode2 : Input=Frequency on IC3, Output=8-bit (PB0-PB7) // DigitalCode3 : Input=Frequency on IC3, Output=PWM on OC3 (PA5) // DigitalCode4 : Input=Analog on PE5, Output=PWM on OC3 (PA5) { DigitalPWMLabel->Enabled=false; DigitalPWMEdit->Enabled=false; DigitalRunButton->Enabled=false; DigitalOutputLabel->Caption="8-bit Parallel Output"; if (DigitalFrequencySelect->Checked) { digitalinoutcombination=2;
240
} else { digitalinoutcombination=1; } } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalPWMSelectClick(TObject *Sender) // Combination1 : Input=Analog on PE5, Output=8-bit (PB0-PB7) // Combination2 : Input=Frequency on IC3, Output=8-bit (PB0-PB7) // Combination3 : Input=Frequency on IC3, Output=PWM on OC3 (PA5) // Combination4 : Input=Analog on PE5, Output=PWM on OC3 (PA5) { DigitalPWMLabel->Enabled=true; DigitalPWMEdit->Enabled=true; DigitalRunButton->Enabled=false; DigitalOutputLabel->Caption="PWM Output on OC3(PA5)"; if (DigitalFrequencySelect->Checked) { digitalinoutcombination=3; } else { digitalinoutcombination=4; } } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalAnalogSelectClick( TObject *Sender) // Combination1 : Input=Analog on PE5, Output=8-bit (PB0-PB7) // Combination2 : Input=Frequency on IC3, Output=8-bit (PB0-PB7) // Combination3 : Input=Frequency on IC3, Output=PWM on OC3 (PA5) // Combination4 : Input=Analog on PE5, Output=PWM on OC3 (PA5) { DigitalRunButton->Enabled=false; MonitorForm->DigitalInputLabel->Caption="Analog Input on PE5"; if (DigitalPortBSelect->Checked) { digitalinoutcombination=1; } else { digitalinoutcombination=4; } } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalFrequencySelectClick( TObject *Sender) // Combination1 : Input=Analog on PE5, Output=8-bit (PB0-PB7) // Combination2 : Input=Frequency on IC3, Output=8-bit (PB0-PB7) // Combination3 : Input=Frequency on IC3, Output=PWM on OC3 (PA5)
241
// Combination4 : Input=Analog on PE5, Output=PWM on OC3 (PA5) { DigitalRunButton->Enabled=false; MonitorForm->DigitalInputLabel-> Caption="Frequency Input on IC3 (PA0)"; if (DigitalPortBSelect->Checked) { digitalinoutcombination=2; } else { digitalinoutcombination=3; } } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalSetpointEditClick( TObject *Sender) { DigitalSetpointEdit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalSetpointEditExit(TObject *Sender) { char *psetp; psetp=MonitorForm->DigitalSetpointEdit->EditText.c_str(); int setp=atoi(psetp); if (setp>255) { Max255Form->ShowModal(); MonitorForm->DigitalSetpointEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalB0EditClick(TObject *Sender) { DigitalB0Edit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalB1EditClick(TObject *Sender) { DigitalB1Edit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalB2EditClick(TObject *Sender) { DigitalB2Edit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalB3EditClick(TObject *Sender) { DigitalB3Edit->SelStart=0; } //--------------------------------------------------------------------
242
void __fastcall TMonitorForm::DigitalB4EditClick(TObject *Sender) { DigitalB4Edit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalA0EditClick(TObject *Sender) { DigitalA0Edit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalA1EditClick(TObject *Sender) { DigitalA1Edit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalA2EditClick(TObject *Sender) { DigitalA2Edit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalA3EditClick(TObject *Sender) { DigitalA3Edit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalA4EditClick(TObject *Sender) { DigitalA4Edit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalSamplefreqEditClick( TObject *Sender) { DigitalSamplefreqEdit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalSamplefreqEditExit( TObject *Sender) { char *psamplefreq; psamplefreq=MonitorForm->DigitalSamplefreqEdit->EditText.c_str(); int samplefreq=atoi(psamplefreq); if (samplefreq>255) { Max255Form->ShowModal(); MonitorForm->DigitalSamplefreqEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalOutputupperlimitEditClick( TObject *Sender) { DigitalOutputupperlimitEdit->SelStart=0; }
243
//-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalOutputupperlimitEditExit( TObject *Sender) { char *ptemp; ptemp=MonitorForm->DigitalOutputupperlimitEdit->EditText.c_str(); int temp=atoi(ptemp); if (temp>250 || temp<1) { Max250min1Form->ShowModal(); MonitorForm->DigitalOutputupperlimitEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalOutputlowerlimitEditClick( TObject *Sender) { DigitalOutputlowerlimitEdit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalOutputlowerlimitEditExit( TObject *Sender) { char *ptemp; ptemp=MonitorForm->DigitalOutputlowerlimitEdit->EditText.c_str(); int temp=atoi(ptemp); if (temp>250 || temp<1) { Max250min1Form->ShowModal(); MonitorForm->DigitalOutputlowerlimitEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalInputupperlimitEditExit( TObject *Sender) { char *ptemp; ptemp=MonitorForm->DigitalInputupperlimitEdit->EditText.c_str(); int temp=atoi(ptemp); if (temp>250 || temp<1) { Max250min1Form->ShowModal(); MonitorForm->DigitalInputupperlimitEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalInputupperlimitEditClick( TObject *Sender) { DigitalInputupperlimitEdit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalInputlowerlimitEditExit( TObject *Sender)
244
{ char *ptemp; ptemp=MonitorForm->DigitalInputlowerlimitEdit->EditText.c_str(); int temp=atoi(ptemp); if (temp>250 || temp<1) { Max250min1Form->ShowModal(); MonitorForm->DigitalInputlowerlimitEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::DigitalInputlowerlimitEditClick( TObject *Sender) { DigitalInputlowerlimitEdit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDInputupperlimitEditClick( TObject *Sender) { PIDInputupperlimitEdit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDInputupperlimitEditExit( TObject *Sender) { char *ptemp; ptemp=MonitorForm->PIDInputupperlimitEdit->EditText.c_str(); int temp=atoi(ptemp); if (temp>250 || temp<1) { Max250min1Form->ShowModal(); MonitorForm->PIDInputupperlimitEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDInputlowerlimitEditClick( TObject *Sender) { PIDInputlowerlimitEdit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDInputlowerlimitEditExit( TObject *Sender) { char *ptemp; ptemp=MonitorForm->PIDInputlowerlimitEdit->EditText.c_str(); int temp=atoi(ptemp); if (temp>250 || temp<1) { Max250min1Form->ShowModal(); MonitorForm->PIDInputlowerlimitEdit->SetFocus(); } }
245
//-------------------------------------------------------------------void __fastcall TMonitorForm::PIDOutputupperlimitEditClick( TObject *Sender) { PIDOutputupperlimitEdit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDOutputupperlimitEditExit( TObject *Sender) { char *ptemp; ptemp=MonitorForm->PIDOutputupperlimitEdit->EditText.c_str(); int temp=atoi(ptemp); if (temp>250 || temp<1) { Max250min1Form->ShowModal(); MonitorForm->PIDOutputupperlimitEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDOutputlowerlimitEditClick( TObject *Sender) { PIDOutputlowerlimitEdit->SelStart=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::PIDOutputlowerlimitEditExit( TObject *Sender) { char *ptemp; ptemp=MonitorForm->PIDOutputlowerlimitEdit->EditText.c_str(); int temp=atoi(ptemp); if (temp>250 || temp<1) { Max250min1Form->ShowModal(); MonitorForm->PIDOutputlowerlimitEdit->SetFocus(); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::MultiController1Click( TObject *Sender) { // This event handler will enable and display the Double-Input// Double-Output controller panel on which all the controls and // editboxes for this controller are displayed. MonitorForm->Caption= "HC11Control - Double-Input-Double-Output Digital Controller"; MultiController1->Checked=true; MonitorForm->MultiPanel->Visible=true; MonitorForm->MultiPanel->Enabled=true; MonitorForm->N68HC11Programs1->Enabled=false; MonitorForm->New1->Enabled=true;
246
// Load microcontroller code from s-record file AnsiString s19filename="Multi.s19"; MonitorForm->MultiCode->Lines->LoadFromFile(s19filename); } //-------------------------------------------------------------------void __fastcall TMonitorForm::MultiMonitorButtonClick(TObject *Sender) // // // // // // // This event handler will enable serial communication between the microcontroller and the PC and will enable and disable all the appropriate buttons on the main form. When data is available from the serial port, an OnDataAvailable event will be triggered and the MonitorCommDataAvailable event handler will be called which will handle the incomming data received from the microcontroller.
{ MultiComm->OpenConnection(); MultiRecordButton->Enabled=true; MultiMonitorButton->Enabled=false; MultiDisconnectButton->Enabled=true; MultiDownloadButton->Enabled=false; MultiRunButton->Enabled=false; } //-------------------------------------------------------------------void __fastcall TMonitorForm::MultiDisconnectButtonClick( TObject *Sender) // This event handler will disable serial communication between the // microcontroller and the PC and will enable and disable all the // appropriate buttons on the main form. { MultiComm->CloseConnection(); MultiRecordButton->Enabled=false; MultiMonitorButton->Enabled=true; MultiDisconnectButton->Enabled=false; MultiDownloadButton->Enabled=true; MultiRunButton->Enabled=true; } //-------------------------------------------------------------------void __fastcall TMonitorForm::MultiRecordButtonClick(TObject *Sender) { // This routine will start the data recording process and will enable // and disable all the appropriate buttons related to this recording // action. MultiDisconnectButton->Enabled=false; MultiPlotButton->Enabled=false; MultiSaveButton->Enabled=false; MultiStopButton->Enabled=true; MultiRecordButton->Enabled=false; PlotForm1->XYPlot1->RemovePlot(1); PlotForm1->XYPlot1->RemovePlot(2);
247
Multianaloginputindex=0; Multifreqinputindex=0; Multi8bitoutputindex=0; MultiPWMoutputindex=0; Multitimeindex=0; } //-------------------------------------------------------------------void __fastcall TMonitorForm::MultiStopButtonClick(TObject *Sender) { // This routine will stop data recording process and will enable // and disable all the appropriate buttons related to this stopping // action. Multitimeindexstop=Multitimeindex; MultiDisconnectButton->Enabled=true; SaveButton->Enabled=true; MultiPlotButton->Enabled=true; MultiSaveButton->Enabled=true; MultiStopButton->Enabled=false; MultiRecordButton->Enabled=true; for (int count=0;count<Multitimeindexstop;count++) { Multitimerecorded[count]=Multitime[count]; Multianaloginputrecorded[count]=Multianaloginput[count]; Multifreqinputrecorded[count]=Multifreqinput[count]; Multi8bitoutputrecorded[count]=Multi8bitoutput[count]; MultiPWMoutputrecorded[count]=MultiPWMoutput[count]; } } //-------------------------------------------------------------------void __fastcall TMonitorForm::MultiPlotButtonClick(TObject *Sender) { // TXYPlot is a Borland C++ Builder VCL plotting component for // graphing x,y data. It is used to plot the recorded data // versus time. PlotForm1->Show(); PlotForm1->Caption="Analog Input From Plant"; PlotForm1->XYPlot1->XAutoScale=true; PlotForm1->XYPlot1->YAutoScale=true; if (MonitorForm->PlotLines1->Checked) { PlotForm1->XYPlot1->XYPlot(Multitimerecorded, Multianaloginputrecorded,Multitimeindexstop,1, clBlue,LinesFilledPoints); } else { PlotForm1->XYPlot1->XYPlot(Multitimerecorded, Multianaloginputrecorded,Multitimeindexstop,1, clBlue,FilledPoints); }
248
PlotForm2->Show(); PlotForm2->Caption="Frequency Input From Plant"; PlotForm2->XYPlot1->XAutoScale=true; PlotForm2->XYPlot1->YAutoScale=true; if (MonitorForm->PlotLines1->Checked) { PlotForm2->XYPlot1->XYPlot(Multitimerecorded, Multifreqinputrecorded,Multitimeindexstop,1, clBlue,LinesFilledPoints); } else { PlotForm2->XYPlot1->XYPlot(Multitimerecorded, Multifreqinputrecorded,Multitimeindexstop,1, clBlue,FilledPoints); } PlotForm3->Show(); PlotForm3->Caption="8-Bit Output to Plant"; PlotForm3->XYPlot1->XAutoScale=true; PlotForm3->XYPlot1->YAutoScale=true; if (MonitorForm->PlotLines1->Checked) { PlotForm3->XYPlot1->XYPlot(Multitimerecorded, Multi8bitoutputrecorded,Multitimeindexstop,1, clBlue,LinesFilledPoints); } else { PlotForm3->XYPlot1->XYPlot(Multitimerecorded, Multi8bitoutputrecorded,Multitimeindexstop,1, clBlue,FilledPoints); } PlotForm4->Show(); PlotForm4->Caption="PWM Output to Plant"; PlotForm4->XYPlot1->XAutoScale=true; PlotForm4->XYPlot1->YAutoScale=true; if (MonitorForm->PlotLines1->Checked) { PlotForm4->XYPlot1->XYPlot(Multitimerecorded, MultiPWMoutputrecorded,Multitimeindexstop,1, clBlue,LinesFilledPoints); } else { PlotForm4->XYPlot1->XYPlot(Multitimerecorded, MultiPWMoutputrecorded,Multitimeindexstop,1, clBlue,FilledPoints); } } //-------------------------------------------------------------------void __fastcall TMonitorForm::MultiDownloadButtonClick( TObject *Sender)
249
{ if (MultiAnalogSetpointEdit->EditText==" " || MultiFrequencySetpointEdit->EditText==" " || MultiP10Edit->EditText==" " || MultiP11Edit->EditText==" " || MultiP12Edit->EditText==" " || MultiP13Edit->EditText==" " || MultiP14Edit->EditText==" " || MultiP20Edit->EditText==" " || MultiP21Edit->EditText==" " || MultiP22Edit->EditText==" " || MultiP23Edit->EditText==" " || MultiP24Edit->EditText==" " || MultiQ10Edit->EditText==" " || MultiQ11Edit->EditText==" " || MultiQ12Edit->EditText==" " || MultiQ13Edit->EditText==" " || MultiQ14Edit->EditText==" " || MultiQ20Edit->EditText==" " || MultiQ21Edit->EditText==" " || MultiQ22Edit->EditText==" " || MultiQ23Edit->EditText==" " || MultiQ24Edit->EditText==" " || MultiR10Edit->EditText==" " || MultiR11Edit->EditText==" " || MultiR12Edit->EditText==" " || MultiR13Edit->EditText==" " || MultiR14Edit->EditText==" " || MultiR20Edit->EditText==" " || MultiR21Edit->EditText==" " || MultiR22Edit->EditText==" " || MultiR23Edit->EditText==" " || MultiR24Edit->EditText==" " || MultiSamplefreqEdit->EditText==" " || MultiPWMEdit->EditText==" ") { InsufficientDataForm->ShowModal(); // An error message will be // displayed if any of the // data entry fields is empty. } else { unsigned char chksum; unsigned char vars[134]; // ! char *panalogsetp; int analogsetp; panalogsetp=MultiAnalogSetpointEdit->Text.c_str(); analogsetp=atoi(panalogsetp); vars[0]=(unsigned char)analogsetp; char *pfreqsetp; int freqsetp; pfreqsetp=MultiFrequencySetpointEdit->Text.c_str();
250
freqsetp=atoi(pfreqsetp); vars[1]=(unsigned char)freqsetp; // Convert the variables //from text to float char *pstrP10; float P10; unsigned int adrP10; float *pP10; unsigned char P10_byte0; unsigned char P10_byte1; unsigned char P10_byte2; unsigned char P10_byte3; AnsiString ansiP10=MultiP10Edit->Text; pstrP10=ansiP10.c_str(); P10=atof(pstrP10); pP10=&P10; adrP10=(int)pP10; (char *) adrP10; (char *) (adrP10+1); (char *) (adrP10+2); (char *) (adrP10+3); P10_byte3=*(char *) adrP10; P10_byte2=*(char *) (adrP10+1); P10_byte1=*(char *) (adrP10+2); P10_byte0=*(char *) (adrP10+3); vars[2]=P10_byte0; vars[3]=P10_byte1; vars[4]=P10_byte2; vars[5]=P10_byte3; char *pstrP11; float P11; unsigned int adrP11; float *pP11; unsigned char P11_byte0; unsigned char P11_byte1; unsigned char P11_byte2; unsigned char P11_byte3; AnsiString ansiP11=MultiP11Edit->Text; pstrP11=ansiP11.c_str(); P11=atof(pstrP11); pP11=&P11; adrP11=(int)pP11; (char *) adrP11; (char *) (adrP11+1); (char *) (adrP11+2); (char *) (adrP11+3); P11_byte3=*(char *) adrP11; P11_byte2=*(char *) (adrP11+1); P11_byte1=*(char *) (adrP11+2); P11_byte0=*(char *) (adrP11+3); vars[6]=P11_byte0; vars[7]=P11_byte1; vars[8]=P11_byte2;
251
vars[9]=P11_byte3; char *pstrP12; float P12; unsigned int adrP12; float *pP12; unsigned char P12_byte0; unsigned char P12_byte1; unsigned char P12_byte2; unsigned char P12_byte3; AnsiString ansiP12=MultiP12Edit->Text; pstrP12=ansiP12.c_str(); P12=atof(pstrP12); pP12=&P12; adrP12=(int)pP12; (char *) adrP12; (char *) (adrP12+1); (char *) (adrP12+2); (char *) (adrP12+3); P12_byte3=*(char *) adrP12; P12_byte2=*(char *) (adrP12+1); P12_byte1=*(char *) (adrP12+2); P12_byte0=*(char *) (adrP12+3); vars[10]=P12_byte0; vars[11]=P12_byte1; vars[12]=P12_byte2; vars[13]=P12_byte3; char *pstrP13; float P13; unsigned int adrP13; float *pP13; unsigned char P13_byte0; unsigned char P13_byte1; unsigned char P13_byte2; unsigned char P13_byte3; AnsiString ansiP13=MultiP13Edit->Text; pstrP13=ansiP13.c_str(); P13=atof(pstrP13); pP13=&P13; adrP13=(int)pP13; (char *) adrP13; (char *) (adrP13+1); (char *) (adrP13+2); (char *) (adrP13+3); P13_byte3=*(char *) adrP13; P13_byte2=*(char *) (adrP13+1); P13_byte1=*(char *) (adrP13+2); P13_byte0=*(char *) (adrP13+3); vars[14]=P13_byte0; vars[15]=P13_byte1; vars[16]=P13_byte2; vars[17]=P13_byte3;
252
char *pstrP14; float P14; unsigned int adrP14; float *pP14; unsigned char P14_byte0; unsigned char P14_byte1; unsigned char P14_byte2; unsigned char P14_byte3; AnsiString ansiP14=MultiP14Edit->Text; pstrP14=ansiP14.c_str(); P14=atof(pstrP14); pP14=&P14; adrP14=(int)pP14; (char *) adrP14; (char *) (adrP14+1); (char *) (adrP14+2); (char *) (adrP14+3); P14_byte3=*(char *) adrP14; P14_byte2=*(char *) (adrP14+1); P14_byte1=*(char *) (adrP14+2); P14_byte0=*(char *) (adrP14+3); vars[18]=P14_byte0; vars[19]=P14_byte1; vars[20]=P14_byte2; vars[21]=P14_byte3; char *pstrP20; float P20; unsigned int adrP20; float *pP20; unsigned char P20_byte0; unsigned char P20_byte1; unsigned char P20_byte2; unsigned char P20_byte3; AnsiString ansiP20=MultiP20Edit->Text; pstrP20=ansiP20.c_str(); P20=atof(pstrP20); pP20=&P20; adrP20=(int)pP20; (char *) adrP20; (char *) (adrP20+1); (char *) (adrP20+2); (char *) (adrP20+3); P20_byte3=*(char *) adrP20; P20_byte2=*(char *) (adrP20+1); P20_byte1=*(char *) (adrP20+2); P20_byte0=*(char *) (adrP20+3); vars[22]=P20_byte0; vars[23]=P20_byte1; vars[24]=P20_byte2; vars[25]=P20_byte3; char *pstrP21; float P21;
253
unsigned int adrP21; float *pP21; unsigned char P21_byte0; unsigned char P21_byte1; unsigned char P21_byte2; unsigned char P21_byte3; AnsiString ansiP21=MultiP21Edit->Text; pstrP21=ansiP21.c_str(); P21=atof(pstrP21); pP21=&P21; adrP21=(int)pP21; (char *) adrP21; (char *) (adrP21+1); (char *) (adrP21+2); (char *) (adrP21+3); P21_byte3=*(char *) adrP21; P21_byte2=*(char *) (adrP21+1); P21_byte1=*(char *) (adrP21+2); P21_byte0=*(char *) (adrP21+3); vars[26]=P21_byte0; vars[27]=P21_byte1; vars[28]=P21_byte2; vars[29]=P21_byte3; char *pstrP22; float P22; unsigned int adrP22; float *pP22; unsigned char P22_byte0; unsigned char P22_byte1; unsigned char P22_byte2; unsigned char P22_byte3; AnsiString ansiP22=MultiP22Edit->Text; pstrP22=ansiP22.c_str(); P22=atof(pstrP22); pP22=&P22; adrP22=(int)pP22; (char *) adrP22; (char *) (adrP22+1); (char *) (adrP22+2); (char *) (adrP22+3); P22_byte3=*(char *) adrP22; P22_byte2=*(char *) (adrP22+1); P22_byte1=*(char *) (adrP22+2); P22_byte0=*(char *) (adrP22+3); vars[30]=P22_byte0; vars[31]=P22_byte1; vars[32]=P22_byte2; vars[33]=P22_byte3; char *pstrP23; float P23; unsigned int adrP23; float *pP23;
254
unsigned char P23_byte0; unsigned char P23_byte1; unsigned char P23_byte2; unsigned char P23_byte3; AnsiString ansiP23=MultiP23Edit->Text; pstrP23=ansiP23.c_str(); P23=atof(pstrP23); pP23=&P23; adrP23=(int)pP23; (char *) adrP23; (char *) (adrP23+1); (char *) (adrP23+2); (char *) (adrP23+3); P23_byte3=*(char *) adrP23; P23_byte2=*(char *) (adrP23+1); P23_byte1=*(char *) (adrP23+2); P23_byte0=*(char *) (adrP23+3); vars[34]=P23_byte0; vars[35]=P23_byte1; vars[36]=P23_byte2; vars[37]=P23_byte3; char *pstrP24; float P24; unsigned int adrP24; float *pP24; unsigned char P24_byte0; unsigned char P24_byte1; unsigned char P24_byte2; unsigned char P24_byte3; AnsiString ansiP24=MultiP24Edit->Text; pstrP24=ansiP24.c_str(); P24=atof(pstrP24); pP24=&P24; adrP24=(int)pP24; (char *) adrP24; (char *) (adrP24+1); (char *) (adrP24+2); (char *) (adrP24+3); P24_byte3=*(char *) adrP24; P24_byte2=*(char *) (adrP24+1); P24_byte1=*(char *) (adrP24+2); P24_byte0=*(char *) (adrP24+3); vars[38]=P24_byte0; vars[39]=P24_byte1; vars[40]=P24_byte2; vars[41]=P24_byte3; char *pstrQ10; float Q10; unsigned int adrQ10; float *pQ10; unsigned char Q10_byte0; unsigned char Q10_byte1;
255
unsigned char Q10_byte2; unsigned char Q10_byte3; AnsiString ansiQ10=MultiQ10Edit->Text; pstrQ10=ansiQ10.c_str(); Q10=atof(pstrQ10); pQ10=&Q10; adrQ10=(int)pQ10; (char *) adrQ10; (char *) (adrQ10+1); (char *) (adrQ10+2); (char *) (adrQ10+3); Q10_byte3=*(char *) adrQ10; Q10_byte2=*(char *) (adrQ10+1); Q10_byte1=*(char *) (adrQ10+2); Q10_byte0=*(char *) (adrQ10+3); vars[42]=Q10_byte0; vars[43]=Q10_byte1; vars[44]=Q10_byte2; vars[45]=Q10_byte3; char *pstrQ11; float Q11; unsigned int adrQ11; float *pQ11; unsigned char Q11_byte0; unsigned char Q11_byte1; unsigned char Q11_byte2; unsigned char Q11_byte3; AnsiString ansiQ11=MultiQ11Edit->Text; pstrQ11=ansiQ11.c_str(); Q11=atof(pstrQ11); pQ11=&Q11; adrQ11=(int)pQ11; (char *) adrQ11; (char *) (adrQ11+1); (char *) (adrQ11+2); (char *) (adrQ11+3); Q11_byte3=*(char *) adrQ11; Q11_byte2=*(char *) (adrQ11+1); Q11_byte1=*(char *) (adrQ11+2); Q11_byte0=*(char *) (adrQ11+3); vars[46]=Q11_byte0; vars[47]=Q11_byte1; vars[48]=Q11_byte2; vars[49]=Q11_byte3; char *pstrQ12; float Q12; unsigned int adrQ12; float *pQ12; unsigned char Q12_byte0; unsigned char Q12_byte1; unsigned char Q12_byte2; unsigned char Q12_byte3;
256
AnsiString ansiQ12=MultiQ12Edit->Text; pstrQ12=ansiQ12.c_str(); Q12=atof(pstrQ12); pQ12=&Q12; adrQ12=(int)pQ12; (char *) adrQ12; (char *) (adrQ12+1); (char *) (adrQ12+2); (char *) (adrQ12+3); Q12_byte3=*(char *) adrQ12; Q12_byte2=*(char *) (adrQ12+1); Q12_byte1=*(char *) (adrQ12+2); Q12_byte0=*(char *) (adrQ12+3); vars[50]=Q12_byte0; vars[51]=Q12_byte1; vars[52]=Q12_byte2; vars[53]=Q12_byte3; char *pstrQ13; float Q13; unsigned int adrQ13; float *pQ13; unsigned char Q13_byte0; unsigned char Q13_byte1; unsigned char Q13_byte2; unsigned char Q13_byte3; AnsiString ansiQ13=MultiQ13Edit->Text; pstrQ13=ansiQ13.c_str(); Q13=atof(pstrQ13); pQ13=&Q13; adrQ13=(int)pQ13; (char *) adrQ13; (char *) (adrQ13+1); (char *) (adrQ13+2); (char *) (adrQ13+3); Q13_byte3=*(char *) adrQ13; Q13_byte2=*(char *) (adrQ13+1); Q13_byte1=*(char *) (adrQ13+2); Q13_byte0=*(char *) (adrQ13+3); vars[54]=Q13_byte0; vars[55]=Q13_byte1; vars[56]=Q13_byte2; vars[57]=Q13_byte3; char *pstrQ14; float Q14; unsigned int adrQ14; float *pQ14; unsigned char Q14_byte0; unsigned char Q14_byte1; unsigned char Q14_byte2; unsigned char Q14_byte3; AnsiString ansiQ14=MultiQ14Edit->Text; pstrQ14=ansiQ14.c_str();
257
Q14=atof(pstrQ14); pQ14=&Q14; adrQ14=(int)pQ14; (char *) adrQ14; (char *) (adrQ14+1); (char *) (adrQ14+2); (char *) (adrQ14+3); Q14_byte3=*(char *) adrQ14; Q14_byte2=*(char *) (adrQ14+1); Q14_byte1=*(char *) (adrQ14+2); Q14_byte0=*(char *) (adrQ14+3); vars[58]=Q14_byte0; vars[59]=Q14_byte1; vars[60]=Q14_byte2; vars[61]=Q14_byte3; char *pstrQ20; float Q20; unsigned int adrQ20; float *pQ20; unsigned char Q20_byte0; unsigned char Q20_byte1; unsigned char Q20_byte2; unsigned char Q20_byte3; AnsiString ansiQ20=MultiQ20Edit->Text; pstrQ20=ansiQ20.c_str(); Q20=atof(pstrQ20); pQ20=&Q20; adrQ20=(int)pQ20; (char *) adrQ20; (char *) (adrQ20+1); (char *) (adrQ20+2); (char *) (adrQ20+3); Q20_byte3=*(char *) adrQ20; Q20_byte2=*(char *) (adrQ20+1); Q20_byte1=*(char *) (adrQ20+2); Q20_byte0=*(char *) (adrQ20+3); vars[62]=Q20_byte0; vars[63]=Q20_byte1; vars[64]=Q20_byte2; vars[65]=Q20_byte3; char *pstrQ21; float Q21; unsigned int adrQ21; float *pQ21; unsigned char Q21_byte0; unsigned char Q21_byte1; unsigned char Q21_byte2; unsigned char Q21_byte3; AnsiString ansiQ21=MultiQ21Edit->Text; pstrQ21=ansiQ21.c_str(); Q21=atof(pstrQ21); pQ21=&Q21;
258
adrQ21=(int)pQ21; (char *) adrQ21; (char *) (adrQ21+1); (char *) (adrQ21+2); (char *) (adrQ21+3); Q21_byte3=*(char *) adrQ21; Q21_byte2=*(char *) (adrQ21+1); Q21_byte1=*(char *) (adrQ21+2); Q21_byte0=*(char *) (adrQ21+3); vars[66]=Q21_byte0; vars[67]=Q21_byte1; vars[68]=Q21_byte2; vars[69]=Q21_byte3; char *pstrQ22; float Q22; unsigned int adrQ22; float *pQ22; unsigned char Q22_byte0; unsigned char Q22_byte1; unsigned char Q22_byte2; unsigned char Q22_byte3; AnsiString ansiQ22=MultiQ22Edit->Text; pstrQ22=ansiQ22.c_str(); Q22=atof(pstrQ22); pQ22=&Q22; adrQ22=(int)pQ22; (char *) adrQ22; (char *) (adrQ22+1); (char *) (adrQ22+2); (char *) (adrQ22+3); Q22_byte3=*(char *) adrQ22; Q22_byte2=*(char *) (adrQ22+1); Q22_byte1=*(char *) (adrQ22+2); Q22_byte0=*(char *) (adrQ22+3); vars[70]=Q22_byte0; vars[71]=Q22_byte1; vars[72]=Q22_byte2; vars[73]=Q22_byte3; char *pstrQ23; float Q23; unsigned int adrQ23; float *pQ23; unsigned char Q23_byte0; unsigned char Q23_byte1; unsigned char Q23_byte2; unsigned char Q23_byte3; AnsiString ansiQ23=MultiQ23Edit->Text; pstrQ23=ansiQ23.c_str(); Q23=atof(pstrQ23); pQ23=&Q23; adrQ23=(int)pQ23; (char *) adrQ23;
259
(char *) (adrQ23+1); (char *) (adrQ23+2); (char *) (adrQ23+3); Q23_byte3=*(char *) adrQ23; Q23_byte2=*(char *) (adrQ23+1); Q23_byte1=*(char *) (adrQ23+2); Q23_byte0=*(char *) (adrQ23+3); vars[74]=Q23_byte0; vars[75]=Q23_byte1; vars[76]=Q23_byte2; vars[77]=Q23_byte3; char *pstrQ24; float Q24; unsigned int adrQ24; float *pQ24; unsigned char Q24_byte0; unsigned char Q24_byte1; unsigned char Q24_byte2; unsigned char Q24_byte3; AnsiString ansiQ24=MultiQ24Edit->Text; pstrQ24=ansiQ24.c_str(); Q24=atof(pstrQ24); pQ24=&Q24; adrQ24=(int)pQ24; (char *) adrQ24; (char *) (adrQ24+1); (char *) (adrQ24+2); (char *) (adrQ24+3); Q24_byte3=*(char *) adrQ24; Q24_byte2=*(char *) (adrQ24+1); Q24_byte1=*(char *) (adrQ24+2); Q24_byte0=*(char *) (adrQ24+3); vars[78]=Q24_byte0; vars[79]=Q24_byte1; vars[80]=Q24_byte2; vars[81]=Q24_byte3; char *pstrR10; float R10; unsigned int adrR10; float *pR10; unsigned char R10_byte0; unsigned char R10_byte1; unsigned char R10_byte2; unsigned char R10_byte3; AnsiString ansiR10=MultiR10Edit->Text; pstrR10=ansiR10.c_str(); R10=atof(pstrR10); pR10=&R10; adrR10=(int)pR10; (char *) adrR10; (char *) (adrR10+1); (char *) (adrR10+2);
260
(char *) (adrR10+3); R10_byte3=*(char *) adrR10; R10_byte2=*(char *) (adrR10+1); R10_byte1=*(char *) (adrR10+2); R10_byte0=*(char *) (adrR10+3); vars[82]=R10_byte0; vars[83]=R10_byte1; vars[84]=R10_byte2; vars[85]=R10_byte3; char *pstrR11; float R11; unsigned int adrR11; float *pR11; unsigned char R11_byte0; unsigned char R11_byte1; unsigned char R11_byte2; unsigned char R11_byte3; AnsiString ansiR11=MultiR11Edit->Text; pstrR11=ansiR11.c_str(); R11=atof(pstrR11); pR11=&R11; adrR11=(int)pR11; (char *) adrR11; (char *) (adrR11+1); (char *) (adrR11+2); (char *) (adrR11+3); R11_byte3=*(char *) adrR11; R11_byte2=*(char *) (adrR11+1); R11_byte1=*(char *) (adrR11+2); R11_byte0=*(char *) (adrR11+3); vars[86]=R11_byte0; vars[87]=R11_byte1; vars[88]=R11_byte2; vars[89]=R11_byte3; char *pstrR12; float R12; unsigned int adrR12; float *pR12; unsigned char R12_byte0; unsigned char R12_byte1; unsigned char R12_byte2; unsigned char R12_byte3; AnsiString ansiR12=MultiR12Edit->Text; pstrR12=ansiR12.c_str(); R12=atof(pstrR12); pR12=&R12; adrR12=(int)pR12; (char *) adrR12; (char *) (adrR12+1); (char *) (adrR12+2); (char *) (adrR12+3); R12_byte3=*(char *) adrR12;
261
R12_byte2=*(char *) (adrR12+1); R12_byte1=*(char *) (adrR12+2); R12_byte0=*(char *) (adrR12+3); vars[90]=R12_byte0; vars[91]=R12_byte1; vars[92]=R12_byte2; vars[93]=R12_byte3; char *pstrR13; float R13; unsigned int adrR13; float *pR13; unsigned char R13_byte0; unsigned char R13_byte1; unsigned char R13_byte2; unsigned char R13_byte3; AnsiString ansiR13=MultiR13Edit->Text; pstrR13=ansiR13.c_str(); R13=atof(pstrR13); pR13=&R13; adrR13=(int)pR13; (char *) adrR13; (char *) (adrR13+1); (char *) (adrR13+2); (char *) (adrR13+3); R13_byte3=*(char *) adrR13; R13_byte2=*(char *) (adrR13+1); R13_byte1=*(char *) (adrR13+2); R13_byte0=*(char *) (adrR13+3); vars[94]=R13_byte0; vars[95]=R13_byte1; vars[96]=R13_byte2; vars[97]=R13_byte3; char *pstrR14; float R14; unsigned int adrR14; float *pR14; unsigned char R14_byte0; unsigned char R14_byte1; unsigned char R14_byte2; unsigned char R14_byte3; AnsiString ansiR14=MultiR14Edit->Text; pstrR14=ansiR14.c_str(); R14=atof(pstrR14); pR14=&R14; adrR14=(int)pR14; (char *) adrR14; (char *) (adrR14+1); (char *) (adrR14+2); (char *) (adrR14+3); R14_byte3=*(char *) adrR14; R14_byte2=*(char *) (adrR14+1); R14_byte1=*(char *) (adrR14+2);
262
R14_byte0=*(char *) (adrR14+3); vars[98]=R14_byte0; vars[99]=R14_byte1; vars[100]=R14_byte2; vars[101]=R14_byte3; char *pstrR20; float R20; unsigned int adrR20; float *pR20; unsigned char R20_byte0; unsigned char R20_byte1; unsigned char R20_byte2; unsigned char R20_byte3; AnsiString ansiR20=MultiR20Edit->Text; pstrR20=ansiR20.c_str(); R20=atof(pstrR20); pR20=&R20; adrR20=(int)pR20; (char *) adrR20; (char *) (adrR20+1); (char *) (adrR20+2); (char *) (adrR20+3); R20_byte3=*(char *) adrR20; R20_byte2=*(char *) (adrR20+1); R20_byte1=*(char *) (adrR20+2); R20_byte0=*(char *) (adrR20+3); vars[102]=R20_byte0; vars[103]=R20_byte1; vars[104]=R20_byte2; vars[105]=R20_byte3; char *pstrR21; float R21; unsigned int adrR21; float *pR21; unsigned char R21_byte0; unsigned char R21_byte1; unsigned char R21_byte2; unsigned char R21_byte3; AnsiString ansiR21=MultiR21Edit->Text; pstrR21=ansiR21.c_str(); R21=atof(pstrR21); pR21=&R21; adrR21=(int)pR21; (char *) adrR21; (char *) (adrR21+1); (char *) (adrR21+2); (char *) (adrR21+3); R21_byte3=*(char *) adrR21; R21_byte2=*(char *) (adrR21+1); R21_byte1=*(char *) (adrR21+2); R21_byte0=*(char *) (adrR21+3); vars[106]=R21_byte0;
263
vars[107]=R21_byte1; vars[108]=R21_byte2; vars[109]=R21_byte3; char *pstrR22; float R22; unsigned int adrR22; float *pR22; unsigned char R22_byte0; unsigned char R22_byte1; unsigned char R22_byte2; unsigned char R22_byte3; AnsiString ansiR22=MultiR22Edit->Text; pstrR22=ansiR22.c_str(); R22=atof(pstrR22); pR22=&R22; adrR22=(int)pR22; (char *) adrR22; (char *) (adrR22+1); (char *) (adrR22+2); (char *) (adrR22+3); R22_byte3=*(char *) adrR22; R22_byte2=*(char *) (adrR22+1); R22_byte1=*(char *) (adrR22+2); R22_byte0=*(char *) (adrR22+3); vars[110]=R22_byte0; vars[111]=R22_byte1; vars[112]=R22_byte2; vars[113]=R22_byte3; char *pstrR23; float R23; unsigned int adrR23; float *pR23; unsigned char R23_byte0; unsigned char R23_byte1; unsigned char R23_byte2; unsigned char R23_byte3; AnsiString ansiR23=MultiR23Edit->Text; pstrR23=ansiR23.c_str(); R23=atof(pstrR23); pR23=&R23; adrR23=(int)pR23; (char *) adrR23; (char *) (adrR23+1); (char *) (adrR23+2); (char *) (adrR23+3); R23_byte3=*(char *) adrR23; R23_byte2=*(char *) (adrR23+1); R23_byte1=*(char *) (adrR23+2); R23_byte0=*(char *) (adrR23+3); vars[114]=R23_byte0; vars[115]=R23_byte1; vars[116]=R23_byte2;
264
vars[117]=R23_byte3; char *pstrR24; float R24; unsigned int adrR24; float *pR24; unsigned char R24_byte0; unsigned char R24_byte1; unsigned char R24_byte2; unsigned char R24_byte3; AnsiString ansiR24=MultiR24Edit->Text; pstrR24=ansiR24.c_str(); R24=atof(pstrR24); pR24=&R24; adrR24=(int)pR24; (char *) adrR24; (char *) (adrR24+1); (char *) (adrR24+2); (char *) (adrR24+3); R24_byte3=*(char *) adrR24; R24_byte2=*(char *) (adrR24+1); R24_byte1=*(char *) (adrR24+2); R24_byte0=*(char *) (adrR24+3); vars[118]=R24_byte0; vars[119]=R24_byte1; vars[120]=R24_byte2; vars[121]=R24_byte3; char *psamplefreq; int samplefreq; psamplefreq=MultiSamplefreqEdit->Text.c_str(); samplefreq=atoi(psamplefreq); vars[122]=(unsigned char)samplefreq; char *ppwmhz; int pwmhz; ppwmhz=MultiPWMEdit->Text.c_str(); pwmhz=atoi(ppwmhz); vars[123]=(unsigned char)pwmhz; // // // // if Check whether positive or feedback or negative feedback was selected. A 1 will be transmitted to the microcontroller for negative feedback and a 2 will be transmitted for positive feedback. (MultiAnalogNegFeedbackSelect->Checked) { vars[124]=1; //Negative feedback } else { vars[124]=2; //Positive feedback } if (MultiFreqNegFeedbackSelect->Checked)
265
//Negative feedback
//Positive feedback
char *panaloginputupperlimit; int analoginputupperlimit; panaloginputupperlimit= MultiAnalogInputupperlimitEdit->Text.c_str(); analoginputupperlimit=atoi(panaloginputupperlimit); vars[126]=(unsigned char)analoginputupperlimit; char *panaloginputlowerlimit; int analoginputlowerlimit; panaloginputlowerlimit= MultiAnalogInputlowerlimitEdit->Text.c_str(); analoginputlowerlimit=atoi(panaloginputlowerlimit); vars[127]=(unsigned char)analoginputlowerlimit;
char *pfreqinputupperlimit; int freqinputupperlimit; pfreqinputupperlimit= MultiFreqInputupperlimitEdit->Text.c_str(); freqinputupperlimit=atoi(pfreqinputupperlimit); vars[128]=(unsigned char)freqinputupperlimit; char *pfreqinputlowerlimit; int freqinputlowerlimit; pfreqinputlowerlimit= MultiFreqInputlowerlimitEdit->Text.c_str(); freqinputlowerlimit=atoi(pfreqinputlowerlimit); vars[129]=(unsigned char)freqinputlowerlimit;
char *p8bitoutputupperlimit; int _8bitoutputupperlimit; p8bitoutputupperlimit= Multi8BitOutputupperlimitEdit->Text.c_str(); _8bitoutputupperlimit=atoi(p8bitoutputupperlimit); vars[130]=(unsigned char)_8bitoutputupperlimit; char *p8bitoutputlowerlimit; int _8bitoutputlowerlimit; p8bitoutputlowerlimit= Multi8BitOutputlowerlimitEdit->Text.c_str(); _8bitoutputlowerlimit=atoi(p8bitoutputlowerlimit); vars[131]=(unsigned char)_8bitoutputlowerlimit;
266
char *pPWMoutputupperlimit; int PWMoutputupperlimit; pPWMoutputupperlimit= MultiPWMOutputupperlimitEdit->Text.c_str(); PWMoutputupperlimit=atoi(pPWMoutputupperlimit); vars[132]=(unsigned char)PWMoutputupperlimit; char *pPWMoutputlowerlimit; int PWMoutputlowerlimit; pPWMoutputlowerlimit= MultiPWMOutputlowerlimitEdit->Text.c_str(); PWMoutputlowerlimit=atoi(pPWMoutputlowerlimit); vars[133]=(unsigned char)PWMoutputlowerlimit; // The checksum byte will be added to the s-record line. // The checksum is the complement of the 8-bit sum of all the // bytes in the s-record line. int lenvars=134; unsigned char sum=0; for (int count1=0;count1<lenvars;count1++) { sum=sum+vars[count1]; } chksum=255-(137+1+sum); // The second term indicates the number // of bytes which will be transmitted plus three (2 byte memory // location and 1 byte checksum). // The third byte indicates the memory value in the second byte // of the S-record line. char addline[276]="S1890100"; // Hex 89 indicates the number of // // // // The last four characters specifies the memory location in which the parameters will be stored.
int count; for (count=0;count<lenvars;count++) // Convert the decimal variables // into hexadecimal values { char temp[3]; if (vars[count]<16) sprintf(temp,"0%X",vars[count]); // Add a '0' if the // hexidecimal value is a // single digit value. else sprintf(temp,"%X",vars[count]); strcat(addline,temp); // Add the hexideciml value to the // s-record line. } char temp[3]; if (chksum<16) sprintf(temp,"0%X",chksum); // Add a '0' if the hexidecimal // value is a single digit value else sprintf(temp,"%X",chksum);
267
strcat(addline,temp);
// add the 8-bit checksum value // to the s-record line Download1->Memo1->Lines->Text=MultiCode->Lines->Text; Download1->Memo1->Lines->Insert(1,addline); // Add the complete // existing control //algorithm. // Start download procedure
Download1->ShowModal(); }
} //-------------------------------------------------------------------void __fastcall TMonitorForm::MultiCommDataAvailable(TObject *Sender) { unsigned char *buffer,data[300]; buffer=data; // Clear the input buffer by writing zero's to it for (int n=0;n<=300;n++) { data[n]=0; } MultiComm->ReadComm(buffer,300); // The idea of Multibuffercount is to ensure that there are at least // ten characters of incoming data available before processing it. Multibuffercount++; if (Multibuffercount>10) { Multibuffercount=1; } switch (Multibuffercount) { case 1: strcpy((char *)Multidata9,(char break; case 2: strcpy((char *)Multidata8,(char break; case 3: strcpy((char *)Multidata7,(char break; case 4: strcpy((char *)Multidata6,(char break; case 5: strcpy((char *)Multidata5,(char break; case 6: strcpy((char *)Multidata4,(char break; case 7: strcpy((char *)Multidata3,(char break; case 8: strcpy((char *)Multidata2,(char
*)data);
*)data);
*)data);
*)data);
*)data);
*)data);
*)data);
*)data);
268
break; case 9: strcpy((char break; case 10: strcat((char strcat((char strcat((char strcat((char strcat((char strcat((char strcat((char strcat((char strcat((char
*)Multidata1,(char *)data);
unsigned int len=strlen((char *)Multidata9); // // // // // // // // The following lines will decode the data pattern into the different arrays. Data will be received form the microcontroller in the following format [251, Sample Frequency, 252, Analog Input from plant, 253, Frequency Input from plant, 254, 8-Bit Output to Plant, 255, PWM Output to Plant] The array may start and end at any point, but the data always have to be in the same order.
for (int scandata=0;scandata<10;scandata++) { if (Multidata9[scandata]==251) { for (unsigned int count=1+scandata;count<len;count=count+10) { Multisamplefrequency=Multidata9[count]; float period=(float) 1/Multidata9[count]; // The if statement ignores the first period value // since it is not significant. // The first meaningful period value will be available // after one sample cycle on the microcontroler is // finished. if (Multitimeindex==0) { Multitime[Multitimeindex]=0; } else { Multitime[Multitimeindex]= Multitime[Multitimeindex-1]+period; } Multitimedisplay=Multitime[Multitimeindex]; Multitimeindex++; } } else if (Multidata9[scandata]==252) {
269
for (unsigned int count=1+scandata;count<len;count=count+10) { Multianaloginputdisplay=Multidata9[count]; Multianaloginput[Multianaloginputindex]=Multidata9[count]; Multianaloginputindex++; } } else if (Multidata9[scandata]==253) { for (unsigned int count=1+scandata;count<len;count=count+10) { Multifreqinputdisplay=Multidata9[count]; Multifreqinput[Multifreqinputindex]=Multidata9[count]; Multifreqinputindex++; } } else if (Multidata9[scandata]==254) { for (unsigned int count=1+scandata;count<len;count=count+10) { Multi8bitoutputdisplay=Multidata9[count]; Multi8bitoutput[Multi8bitoutputindex]=Multidata9[count]; Multi8bitoutputindex++; } } else if (Multidata9[scandata]==255) { for (unsigned int count=1+scandata;count<len;count=count+10) { MultiPWMoutputdisplay=Multidata9[count]; MultiPWMoutput[MultiPWMoutputindex]=Multidata9[count]; MultiPWMoutputindex++; } } }// end for scandata break; }// end switch } //-------------------------------------------------------------------void __fastcall TMonitorForm::MultiDisplayTimerTimer(TObject *Sender) { // // // // // //
This event handler will display the data received from the microcontroller with every time interval of the timer. The time interval can be set with the 'interval' property of the timer component.
270
Multi8bitOutputDigitBox->Target=Multi8bitoutputdisplay; Multi8bitOutputDigitBox->Value=Multi8bitoutputdisplay; MultiPWMOutputDigitBox->Target=MultiPWMoutputdisplay; MultiPWMOutputDigitBox->Value=MultiPWMoutputdisplay; MultiSamplefreqDigitBox->Target=Multisamplefrequency; MultiSamplefreqDigitBox->Value=Multisamplefrequency; // The stopbutton will only be enabled when recording // in in progress. if (MultiStopButton->Enabled==true) { MultiTimeDigitBox->Target=Multitimedisplay; MultiTimeDigitBox->Value=Multitimedisplay; } } //-------------------------------------------------------------------void __fastcall TMonitorForm::PlotLines1Click(TObject *Sender) { if (PlotLines1->Checked) { PlotLines1->Checked=false; } else { PlotLines1->Checked=true; } } //-------------------------------------------------------------------void __fastcall TMonitorForm::menutimerTimer(TObject *Sender) { menutimer->Enabled=false; MenuForm->ShowModal(); } //------------------------------------------------------------------
271
272
TPanel *InputDataPanel; TPanel *Panel3; TPanel *PIDParameterPanel; TPanel *Panel1; TLabel *Label17; TMenuItem *Options1; TMenuItem *Recorddatawhencontrolprogramstarts1; TMemo *TestDACADCCode; TPanel *Panel4; TRadioButton *TestDACADCPortBSelect; TLabel *Label21; TPanel *Panel5; TLabel *Label22; TPanel *Panel6; TMaskEdit *TestDACADCStepsizeEdit; TMaskEdit *TestDACADCSamplefreqEdit; TMaskEdit *TestDACADCDelayEdit; TMaskEdit *TestDACADCPWMEdit; TLabel *PWMLabel; TLabel *Label18; TLabel *Label12; TLabel *Label8; TLabel *Label7; TRadioButton *TestDACADCPWMSelect; TRadioButton *TestDACADCAnalogSelect; TRadioButton *TestDACADCFrequencySelect; TPanel *Panel7; TLabel *Label1; TMaskEdit *PIDSetpointEdit; TMaskEdit *PIDKpEdit; TLabel *Label2; TLabel *Label3; TMaskEdit *PIDKiEdit; TMaskEdit *PIDKdEdit; TLabel *Label4; TLabel *Label15; TMaskEdit *PIDSamplefreqEdit; TMaskEdit *PIDPWMEdit; TLabel *PIDPWMLabel; TLabel *Label16; TPanel *Panel8; TRadioButton *PIDPortBSelect; TRadioButton *PIDPWMSelect; TLabel *Label23; TPanel *Panel9; TRadioButton *PIDAnalogSelect; TRadioButton *PIDFrequencySelect; TLabel *Label24; TPanel *Panel10; TLabel *PIDInputLabel; TLabel *PIDOutputLabel; TLabel *PIDSampleFrequencyLabel; TMenuItem *Help1; TPanel *PIDToolbar;
273
TSpeedButton *PIDDownloadButton; TSpeedButton *SaveButton; TSpeedButton *PIDRecordButton; TSpeedButton *PIDMonitorButton; TSpeedButton *PIDRunButton; TSpeedButton *PIDStopButton; TSpeedButton *PIDDisconnectButton; TSpeedButton *PIDPlotButton; TPanel *DigitalPanel; TPanel *DigitalToolbar; TSpeedButton *DigitalDownloadButton; TSpeedButton *DigitalSaveButton; TSpeedButton *DigitalRecordButton; TSpeedButton *DigitalMonitorButton; TSpeedButton *DigitalRunButton; TSpeedButton *DigitalStopButton; TSpeedButton *DigitalDisconnectButton; TSpeedButton *DigitalPlotButton; TPanel *DigitalParameterPanel; TLabel *Label5; TLabel *Label6; TLabel *Label14; TPanel *Panel12; TLabel *Label20; TLabel *DigitalA0Label; TLabel *Label28; TLabel *DigitalPWMLabel; TMaskEdit *DigitalA0Edit; TMaskEdit *DigitalSetpointEdit; TMaskEdit *DigitalSamplefreqEdit; TMaskEdit *DigitalPWMEdit; TPanel *Panel13; TRadioButton *DigitalPortBSelect; TRadioButton *DigitalPWMSelect; TPanel *Panel14; TRadioButton *DigitalAnalogSelect; TRadioButton *DigitalFrequencySelect; TMaskEdit *DigitalA1Edit; TMaskEdit *DigitalA2Edit; TMaskEdit *DigitalA3Edit; TMaskEdit *DigitalA4Edit; TMaskEdit *DigitalB0Edit; TMaskEdit *DigitalB1Edit; TMaskEdit *DigitalB2Edit; TMaskEdit *DigitalB3Edit; TMaskEdit *DigitalB4Edit; TMemo *DigitalCode; TPanel *Panel11; TLabel *Label25; TPanel *Panel15; TLabel *DigitalInputLabel; TLabel *DigitalOutputLabel; TLabel *DigitalSampleFrequencyLabel; TLabel *Label31;
274
TLabel *Label32; TLabel *Label33; TLabel *Label34; TLabel *Label35; TLabel *Label36; TLabel *Label37; TLabel *Label38; TLabel *Label39; TZComm *DigitalComm; TTimer *DigitalDisplayTimer; TMenuItem *PIDController1; TMenuItem *DigitalController1; TMenuItem *UsersManual1; TMenuItem *About1; TPanel *Panel16; TPanel *Panel17; TPanel *TestDACADCToolbar; TSpeedButton *TestDACADCDownloadButton; TSpeedButton *TestDACADCSaveButton; TSpeedButton *TestDACADCRecordButton; TSpeedButton *TestDACADCMonitorButton; TSpeedButton *TestDACADCRunButton; TSpeedButton *TestDACADCStopButton; TSpeedButton *TestDACADCDisconnectButton; TSpeedButton *TestDACADCPlotButton; TPanel *Panel18; TLabel *TestDACADCSampleFrequencyLabel; TLabel *TestDACADCOutputLabel; TLabel *TestDACADCInputLabel; TMaskEdit *TestADCDACOutputupperlimitEdit; TMaskEdit *TestADCDACOutputlowerlimitEdit; TLabel *Label27; TLabel *Label29; TPanel *Panel19; TLabel *Label26; TMaskEdit *DigitalInputlowerlimitEdit; TLabel *Label19; TMaskEdit *DigitalInputupperlimitEdit; TLabel *Label10; TMaskEdit *DigitalOutputlowerlimitEdit; TMaskEdit *DigitalOutputupperlimitEdit; TLabel *Label9; TLabel *Label43; TPanel *Panel20; TLabel *Label44; TLabel *Label45; TLabel *Label46; TLabel *Label47; TMaskEdit *PIDInputlowerlimitEdit; TMaskEdit *PIDInputupperlimitEdit; TMaskEdit *PIDOutputlowerlimitEdit; TMaskEdit *PIDOutputupperlimitEdit; TLabel *Label30; TPanel *Panel21;
275
TRadioButton *DigitalNegativeFeedbackSelect; TRadioButton *DigitalPositiveFeedbackSelect; TLabel *Label40; TPanel *Panel22; TRadioButton *PIDNegativeFeedbackSelect; TRadioButton *PIDPositiveFeedbackSelect; TLabel *Label41; TPanel *MultiPanel; TPanel *Panel23; TSpeedButton *MultiDownloadButton; TSpeedButton *MultiSaveButton; TSpeedButton *MultiRecordButton; TSpeedButton *MultiMonitorButton; TSpeedButton *MultiRunButton; TSpeedButton *MultiStopButton; TSpeedButton *MultiDisconnectButton; TSpeedButton *MultiPlotButton; TPanel *Panel24; TPanel *Panel25; TPanel *Panel27; TLabel *Label42; TLabel *Label50; TLabel *Label51; TPanel *Panel28; TLabel *Label52; TLabel *Label53; TLabel *Label54; TLabel *Label55; TLabel *Label56; TLabel *Label57; TLabel *Label58; TLabel *Label59; TLabel *Label60; TLabel *Label61; TLabel *Label62; TLabel *Label63; TLabel *Label64; TMaskEdit *MultiQ10Edit; TMaskEdit *MultiAnalogSetpointEdit; TMaskEdit *MultiPWMEdit; TMaskEdit *MultiQ11Edit; TMaskEdit *MultiQ12Edit; TMaskEdit *MultiQ13Edit; TMaskEdit *MultiQ14Edit; TMaskEdit *MultiP10Edit; TMaskEdit *MultiP11Edit; TMaskEdit *MultiP12Edit; TMaskEdit *MultiP13Edit; TMaskEdit *MultiP14Edit; TMemo *MultiCode; TPanel *Panel31; TLabel *Label65; TLabel *Label66; TLabel *Label67;
276
TLabel *Label68; TMaskEdit *MultiAnalogInputlowerlimitEdit; TMaskEdit *MultiAnalogInputupperlimitEdit; TMaskEdit *MultiFreqInputlowerlimitEdit; TMaskEdit *MultiFreqInputupperlimitEdit; TPanel *Panel32; TRadioButton *MultiAnalogNegFeedbackSelect; TRadioButton *MultiAnalogPosFeedbackSelect; TPanel *Panel29; TRadioButton *MultiFreqNegFeedbackSelect; TRadioButton *MultiFreqPosFeedbackSelect; TLabel *Label48; TMaskEdit *MultiPWMOutputlowerlimitEdit; TLabel *Label49; TLabel *Label69; TMaskEdit *MultiPWMOutputupperlimitEdit; TMaskEdit *Multi8BitOutputlowerlimitEdit; TLabel *Label70; TLabel *Label71; TMaskEdit *Multi8BitOutputupperlimitEdit; TMaskEdit *MultiR14Edit; TMaskEdit *MultiR13Edit; TMaskEdit *MultiR12Edit; TMaskEdit *MultiR11Edit; TMaskEdit *MultiR10Edit; TLabel *Label72; TMaskEdit *MultiFrequencySetpointEdit; TLabel *Label73; TLabel *Label74; TLabel *Label75; TLabel *Label76; TLabel *Label77; TLabel *Label78; TLabel *Label79; TLabel *Label80; TLabel *Label81; TLabel *Label82; TLabel *Label83; TLabel *Label84; TLabel *Label85; TLabel *Label86; TLabel *Label87; TLabel *Label88; TLabel *Label89; TLabel *Label90; TLabel *Label91; TLabel *Label92; TLabel *Label93; TLabel *Label94; TLabel *Label95; TLabel *Label96; TLabel *Label97; TLabel *Label98; TMaskEdit *MultiP24Edit;
277
TMaskEdit *MultiP23Edit; TMaskEdit *MultiP22Edit; TMaskEdit *MultiP21Edit; TMaskEdit *MultiP20Edit; TMaskEdit *MultiQ24Edit; TMaskEdit *MultiQ23Edit; TMaskEdit *MultiQ22Edit; TMaskEdit *MultiQ21Edit; TMaskEdit *MultiQ20Edit; TMaskEdit *MultiR24Edit; TMaskEdit *MultiR23Edit; TMaskEdit *MultiR22Edit; TMaskEdit *MultiR21Edit; TMaskEdit *MultiR20Edit; TPanel *Panel30; TLabel *Label99; TPanel *Panel33; TLabel *Label100; TLabel *Label101; TLabel *Label102; TLabel *Label103; TLabel *Label104; TMaskEdit *MultiSamplefreqEdit; TZComm *MultiComm; TTimer *MultiDisplayTimer; TMenuItem *MultiController1; TMenuItem *PlotLines1; TDigitBox *PIDSamplefreqDigitBox; TDigitBox *PIDOutputDigitBox; TDigitBox *PIDInputDigitBox; TDigitBox *PIDTimeDigitBox; TDigitBox *MultiAnalogInputDigitBox; TDigitBox *MultiFreqInputDigitBox; TDigitBox *Multi8bitOutputDigitBox; TDigitBox *MultiPWMOutputDigitBox; TDigitBox *MultiSamplefreqDigitBox; TDigitBox *MultiTimeDigitBox; TDigitBox *DigitalTimeDigitBox; TDigitBox *DigitalInputDigitBox; TDigitBox *DigitalOutputDigitBox; TDigitBox *DigitalSamplefreqDigitBox; TDigitBox *TestDACADCInputDigitBox; TDigitBox *TestDACADCTimeDigitBox; TDigitBox *TestDACADCOutputDigitBox; TDigitBox *TestDACADCSamplefreqDigitBox; TTimer *menutimer; void __fastcall PIDMonitorCommDataAvailable(TObject *Sender); void __fastcall PIDDisplayTimerTimer(TObject *Sender); void __fastcall PIDStopButtonClick(TObject *Sender); void __fastcall PIDRecordButtonClick(TObject *Sender);
278
void __fastcall PIDPlotButtonClick(TObject *Sender); void __fastcall SaveNowHiddenButtonClick(TObject *Sender); void __fastcall PIDMonitorButtonClick(TObject *Sender); void __fastcall PIDDisconnectButtonClick(TObject *Sender); void __fastcall PIDRunButtonClick(TObject *Sender); void __fastcall FormClose(TObject *Sender, TCloseAction &Action); void __fastcall PIDSetpointEditEnter(TObject *Sender); void __fastcall PIDKpEditEnter(TObject *Sender); void __fastcall PIDKiEditEnter(TObject *Sender); void __fastcall PIDKdEditEnter(TObject *Sender); void __fastcall PIDDownloadButtonClick(TObject *Sender); void __fastcall PIDController1Click(TObject *Sender); void __fastcall New1Click(TObject *Sender); void __fastcall Exit1Click(TObject *Sender); void __fastcall PIDSetpointEditExit(TObject *Sender); void __fastcall PIDKpEditExit(TObject *Sender); void __fastcall PIDKiEditExit(TObject *Sender); void __fastcall PIDKdEditExit(TObject *Sender); void __fastcall Communicationsettings1Click(TObject *Sender); void __fastcall TestDACADCDownloadButtonClick(TObject *Sender); void __fastcall TestDACADCMonitorButtonClick(TObject *Sender); void __fastcall TestDACADCDisconnectButtonClick(TObject *Sender); void __fastcall TestDACADC1Click(TObject *Sender); void __fastcall TestDACADCDisplayTimerTimer(TObject *Sender); void __fastcall TestDACADCStepsizeEditEnter(TObject *Sender); void __fastcall TestDACADCStepsizeEditExit(TObject *Sender); void __fastcall TestDACADCRecordButtonClick(TObject *Sender); void __fastcall TestDACADCStopButtonClick(TObject *Sender); void __fastcall TestDACADCPlotButtonClick(TObject *Sender); void __fastcall Recorddatawhencontrolprogramstarts1Click( TObject *Sender); void __fastcall PIDSamplefreqEditEnter(TObject *Sender); void __fastcall PIDSamplefreqEditExit(TObject *Sender); void __fastcall TestDACADCCommDataAvailable(TObject *Sender); void __fastcall TestDACADCDelayEditEnter(TObject *Sender); void __fastcall TestDACADCDelayEditExit(TObject *Sender); void __fastcall TestDACADCSamplefreqEditExit(TObject *Sender); void __fastcall TestDACADCSamplefreqEditEnter(TObject *Sender); void __fastcall TestDACADCPWMEditEnter(TObject *Sender); void __fastcall TestDACADCPWMEditExit(TObject *Sender); void __fastcall TestDACADCPortBSelectClick(TObject *Sender); void __fastcall TestDACADCPWMSelectClick(TObject *Sender); void __fastcall TestDACADCAnalogSelectClick(TObject *Sender); void __fastcall TestDACADCFrequencySelectClick(TObject *Sender); void __fastcall PIDPWMEditEnter(TObject *Sender); void __fastcall PIDPWMEditExit(TObject *Sender); void __fastcall PIDPortBSelectClick(TObject *Sender); void __fastcall PIDPWMSelectClick(TObject *Sender); void __fastcall PIDAnalogSelectClick(TObject *Sender); void __fastcall PIDFrequencySelectClick(TObject *Sender); void __fastcall DigitalDownloadButtonClick(TObject *Sender); void __fastcall DigitalMonitorButtonClick(TObject *Sender); void __fastcall DigitalDisconnectButtonClick(TObject *Sender); void __fastcall DigitalRecordButtonClick(TObject *Sender);
279
void __fastcall DigitalStopButtonClick(TObject *Sender); void __fastcall DigitalPlotButtonClick(TObject *Sender); void __fastcall UsersManual1Click(TObject *Sender); void __fastcall DigitalController1Click(TObject *Sender); void __fastcall DigitalCommDataAvailable(TObject *Sender); void __fastcall DigitalDisplayTimerTimer(TObject *Sender); void __fastcall DigitalPortBSelectClick(TObject *Sender); void __fastcall DigitalPWMSelectClick(TObject *Sender); void __fastcall DigitalAnalogSelectClick(TObject *Sender); void __fastcall DigitalFrequencySelectClick(TObject *Sender); void __fastcall DigitalSetpointEditClick(TObject *Sender); void __fastcall DigitalSetpointEditExit(TObject *Sender); void __fastcall DigitalB0EditClick(TObject *Sender); void __fastcall DigitalB1EditClick(TObject *Sender); void __fastcall DigitalB2EditClick(TObject *Sender); void __fastcall DigitalB3EditClick(TObject *Sender); void __fastcall DigitalB4EditClick(TObject *Sender); void __fastcall DigitalA0EditClick(TObject *Sender); void __fastcall DigitalA1EditClick(TObject *Sender); void __fastcall DigitalA2EditClick(TObject *Sender); void __fastcall DigitalA3EditClick(TObject *Sender); void __fastcall DigitalA4EditClick(TObject *Sender); void __fastcall DigitalSamplefreqEditClick(TObject *Sender); void __fastcall DigitalSamplefreqEditExit(TObject *Sender); void __fastcall DigitalOutputupperlimitEditClick(TObject *Sender); void __fastcall DigitalOutputupperlimitEditExit(TObject *Sender); void __fastcall DigitalOutputlowerlimitEditClick(TObject *Sender); void __fastcall DigitalOutputlowerlimitEditExit(TObject *Sender); void __fastcall DigitalInputupperlimitEditExit(TObject *Sender); void __fastcall DigitalInputupperlimitEditClick(TObject *Sender); void __fastcall DigitalInputlowerlimitEditExit(TObject *Sender); void __fastcall DigitalInputlowerlimitEditClick(TObject *Sender); void __fastcall PIDInputupperlimitEditClick(TObject *Sender); void __fastcall PIDInputupperlimitEditExit(TObject *Sender); void __fastcall PIDInputlowerlimitEditClick(TObject *Sender); void __fastcall PIDInputlowerlimitEditExit(TObject *Sender); void __fastcall PIDOutputupperlimitEditClick(TObject *Sender); void __fastcall PIDOutputupperlimitEditExit(TObject *Sender); void __fastcall PIDOutputlowerlimitEditClick(TObject *Sender); void __fastcall PIDOutputlowerlimitEditExit(TObject *Sender); void __fastcall MultiController1Click( TObject *Sender); void __fastcall MultiMonitorButtonClick(TObject *Sender); void __fastcall MultiDisconnectButtonClick(TObject *Sender); void __fastcall MultiRecordButtonClick(TObject *Sender); void __fastcall MultiStopButtonClick(TObject *Sender); void __fastcall MultiPlotButtonClick(TObject *Sender); void __fastcall MultiDownloadButtonClick(TObject *Sender); void __fastcall MultiCommDataAvailable(TObject *Sender); void __fastcall MultiDisplayTimerTimer(TObject *Sender); void __fastcall PlotLines1Click(TObject *Sender); void __fastcall menutimerTimer(TObject *Sender);
280
private: // User declarations public: // User declarations __fastcall TMonitorForm(TComponent* Owner); }; //-------------------------------------------------------------------extern TMonitorForm *MonitorForm; //-------------------------------------------------------------------#endif
281
282
MonitorForm->VertScrollBar->Range=540; MonitorForm->Height=586; MonitorForm->Width=596; MonitorForm->Position=poScreenCenter; MonitorForm->MultiController1Click(Sender); MenuForm->Close(); } //-------------------------------------------------------------------void __fastcall TMenuForm::OfficeButton5Click(TObject *Sender) { // PID Select; MonitorForm->Visible=true; MonitorForm->VertScrollBar->Range=432; MonitorForm->Height=478; MonitorForm->Width=596; MonitorForm->Position=poScreenCenter; MonitorForm->PIDController1Click(Sender); MenuForm->Close(); } //-------------------------------------------------------------------void __fastcall TMenuForm::FormShow(TObject *Sender) { MonitorForm->Visible=false; } //--------------------------------------------------------------------
283
284
285
286
287
288
289
290
291
double xmax=XYPlot1->XMax; double xmin=XYPlot1->XMin; XYPlot1->XAutoScale=false; range=xmax-xmin; XYPlot1->XMax=xmax+(range/8); XYPlot1->XMin=xmin-(range/8); } //-------------------------------------------------------------------void __fastcall TPlotForm1::XZoomInButtonClick(TObject *Sender) { double range; double xmax=XYPlot1->XMax; double xmin=XYPlot1->XMin; XYPlot1->XAutoScale=false; range=xmax-xmin; XYPlot1->XMax=xmax-(range/8); XYPlot1->XMin=xmin+(range/8); } //-------------------------------------------------------------------void __fastcall TPlotForm1::FormClose(TObject *Sender, TCloseAction &Action) { XYPlot1->RemovePlot(1); } //-------------------------------------------------------------------void __fastcall TPlotForm1::XYPlot1StartDrag(TObject *Sender, TDragObject *&DragObject) { XYPlot1->Cursor=crDrag; } //-------------------------------------------------------------------void __fastcall TPlotForm1::XYPlot1EndDrag(TObject *Sender, TObject *Target, int X, int Y) { XYPlot1->Cursor=crCross; } //-------------------------------------------------------------------void __fastcall TPlotForm1::XYPlot1MouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { char temp[20]; AnsiString ansixmouse = XYPlot1->MouseX; char* pxmouse=ansixmouse.c_str(); float xmouse=atof(pxmouse); sprintf(temp,"%.2f",xmouse); XMouseLabel->Caption=temp; AnsiString ansiymouse = XYPlot1->MouseY; char* pymouse=ansiymouse.c_str(); float ymouse=atof(pymouse); sprintf(temp,"%.0f",ymouse); YMouseLabel->Caption=temp; }
292
//-------------------------------------------------------------------void __fastcall TPlotForm1::ScrollRightButtonClick(TObject *Sender) { double range; double xmax=XYPlot1->XMax; double xmin=XYPlot1->XMin; XYPlot1->XAutoScale=false; range=xmax-xmin; XYPlot1->XMax=xmax+(range/16); XYPlot1->XMin=xmin+(range/16); } //-------------------------------------------------------------------void __fastcall TPlotForm1::ScrollLeftButtonClick(TObject *Sender) { double range; double xmax=XYPlot1->XMax; double xmin=XYPlot1->XMin; XYPlot1->XAutoScale=false; range=xmax-xmin; XYPlot1->XMax=xmax-(range/16); XYPlot1->XMin=xmin-(range/16); } //-------------------------------------------------------------------void __fastcall TPlotForm1::ScrollUpButtonClick(TObject *Sender) { double range; double ymax=XYPlot1->YMax; double ymin=XYPlot1->YMin; XYPlot1->YAutoScale=false; range=ymax-ymin; XYPlot1->YMax=ymax+(range/16); XYPlot1->YMin=ymin+(range/16); } //-------------------------------------------------------------------void __fastcall TPlotForm1::ScrollDownButtonClick(TObject *Sender) { double range; double ymax=XYPlot1->YMax; double ymin=XYPlot1->YMin; XYPlot1->YAutoScale=false; range=ymax-ymin; XYPlot1->YMax=ymax-(range/16); XYPlot1->YMin=ymin-(range/16); } //--------------------------------------------------------------------
293
294
void __fastcall ScrollLeftButtonClick(TObject *Sender); void __fastcall ScrollUpButtonClick(TObject *Sender); void __fastcall ScrollDownButtonClick(TObject *Sender); private: // User declarations public: // User declarations __fastcall TPlotForm1(TComponent* Owner); }; //-------------------------------------------------------------------extern TPlotForm1 *PlotForm1; //-------------------------------------------------------------------#endif
295
296
double range; double xmax=XYPlot1->XMax; double xmin=XYPlot1->XMin; XYPlot1->XAutoScale=false; range=xmax-xmin; XYPlot1->XMax=xmax+(range/16); XYPlot1->XMin=xmin+(range/16); } //-------------------------------------------------------------------void __fastcall TPlotForm2::YZoomInButtonClick(TObject *Sender) { double range; double ymax=XYPlot1->YMax; double ymin=XYPlot1->YMin; XYPlot1->YAutoScale=false; range=ymax-ymin; XYPlot1->YMax=ymax-(range/8); XYPlot1->YMin=ymin+(range/8); } //-------------------------------------------------------------------void __fastcall TPlotForm2::YZoomOutButtonClick(TObject *Sender) { double range; double ymax=XYPlot1->YMax; double ymin=XYPlot1->YMin; XYPlot1->YAutoScale=false; range=ymax-ymin; XYPlot1->YMax=ymax+(range/8); XYPlot1->YMin=ymin-(range/8); } //-------------------------------------------------------------------void __fastcall TPlotForm2::XZoomInButtonClick(TObject *Sender) { double range; double xmax=XYPlot1->XMax; double xmin=XYPlot1->XMin; XYPlot1->XAutoScale=false; range=xmax-xmin; XYPlot1->XMax=xmax-(range/8); XYPlot1->XMin=xmin+(range/8); } //-------------------------------------------------------------------void __fastcall TPlotForm2::XZoomOutButtonClick(TObject *Sender) { double range; double xmax=XYPlot1->XMax; double xmin=XYPlot1->XMin; XYPlot1->XAutoScale=false; range=xmax-xmin; XYPlot1->XMax=xmax+(range/8); XYPlot1->XMin=xmin-(range/8); } //-------------------------------------------------------------------void __fastcall TPlotForm2::XYPlot1MouseMove(TObject *Sender,
297
TShiftState Shift, int X, int Y) { char temp[20]; AnsiString ansixmouse = XYPlot1->MouseX; char* pxmouse=ansixmouse.c_str(); float xmouse=atof(pxmouse); sprintf(temp,"%.2f",xmouse); XMouseLabel->Caption=temp; AnsiString ansiymouse = XYPlot1->MouseY; char* pymouse=ansiymouse.c_str(); float ymouse=atof(pymouse); sprintf(temp,"%.0f",ymouse); YMouseLabel->Caption=temp; } //-------------------------------------------------------------------void __fastcall TPlotForm2::CloseButtonClick(TObject *Sender) { PlotForm2->Close(); } //--------------------------------------------------------------------
298
299
300
301
XYPlot1->YMin=ymin-(range/8); } //-------------------------------------------------------------------void __fastcall TPlotForm3::ScrollDownButtonClick(TObject *Sender) { double range; double ymax=XYPlot1->YMax; double ymin=XYPlot1->YMin; XYPlot1->YAutoScale=false; range=ymax-ymin; XYPlot1->YMax=ymax-(range/16); XYPlot1->YMin=ymin-(range/16); } //-------------------------------------------------------------------void __fastcall TPlotForm3::ScrollLeftButtonClick(TObject *Sender) { double range; double xmax=XYPlot1->XMax; double xmin=XYPlot1->XMin; XYPlot1->XAutoScale=false; range=xmax-xmin; XYPlot1->XMax=xmax-(range/16); XYPlot1->XMin=xmin-(range/16); } //-------------------------------------------------------------------void __fastcall TPlotForm3::XZoomInButtonClick(TObject *Sender) { double range; double xmax=XYPlot1->XMax; double xmin=XYPlot1->XMin; XYPlot1->XAutoScale=false; range=xmax-xmin; XYPlot1->XMax=xmax-(range/8); XYPlot1->XMin=xmin+(range/8); } //-------------------------------------------------------------------void __fastcall TPlotForm3::XZoomOutButtonClick(TObject *Sender) { double range; double xmax=XYPlot1->XMax; double xmin=XYPlot1->XMin; XYPlot1->XAutoScale=false; range=xmax-xmin; XYPlot1->XMax=xmax+(range/8); XYPlot1->XMin=xmin-(range/8); } //-------------------------------------------------------------------void __fastcall TPlotForm3::ScrollRightButtonClick(TObject *Sender) { double range; double xmax=XYPlot1->XMax; double xmin=XYPlot1->XMin; XYPlot1->XAutoScale=false; range=xmax-xmin;
302
XYPlot1->XMax=xmax+(range/16); XYPlot1->XMin=xmin+(range/16); } //-------------------------------------------------------------------void __fastcall TPlotForm3::XYPlot1MouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { char temp[20]; AnsiString ansixmouse = XYPlot1->MouseX; char* pxmouse=ansixmouse.c_str(); float xmouse=atof(pxmouse); sprintf(temp,"%.2f",xmouse); XMouseLabel->Caption=temp; AnsiString ansiymouse = XYPlot1->MouseY; char* pymouse=ansiymouse.c_str(); float ymouse=atof(pymouse); sprintf(temp,"%.0f",ymouse); YMouseLabel->Caption=temp; } //--------------------------------------------------------------------
303
304
305
306
XYPlot1->YMin=ymin-(range/8); } //-------------------------------------------------------------------void __fastcall TPlotForm4::ScrollDownButtonClick(TObject *Sender) { double range; double ymax=XYPlot1->YMax; double ymin=XYPlot1->YMin; XYPlot1->YAutoScale=false; range=ymax-ymin; XYPlot1->YMax=ymax-(range/16); XYPlot1->YMin=ymin-(range/16); } //-------------------------------------------------------------------void __fastcall TPlotForm4::ScrollLeftButtonClick(TObject *Sender) { double range; double xmax=XYPlot1->XMax; double xmin=XYPlot1->XMin; XYPlot1->XAutoScale=false; range=xmax-xmin; XYPlot1->XMax=xmax-(range/16); XYPlot1->XMin=xmin-(range/16); } //-------------------------------------------------------------------void __fastcall TPlotForm4::XZoomInButtonClick(TObject *Sender) { double range; double xmax=XYPlot1->XMax; double xmin=XYPlot1->XMin; XYPlot1->XAutoScale=false; range=xmax-xmin; XYPlot1->XMax=xmax-(range/8); XYPlot1->XMin=xmin+(range/8); } //-------------------------------------------------------------------void __fastcall TPlotForm4::XZoomOutButtonClick(TObject *Sender) { double range; double xmax=XYPlot1->XMax; double xmin=XYPlot1->XMin; XYPlot1->XAutoScale=false; range=xmax-xmin; XYPlot1->XMax=xmax+(range/8); XYPlot1->XMin=xmin-(range/8); } //-------------------------------------------------------------------void __fastcall TPlotForm4::ScrollRightButtonClick(TObject *Sender) { double range; double xmax=XYPlot1->XMax; double xmin=XYPlot1->XMin; XYPlot1->XAutoScale=false; range=xmax-xmin;
307
XYPlot1->XMax=xmax+(range/16); XYPlot1->XMin=xmin+(range/16); } //-------------------------------------------------------------------void __fastcall TPlotForm4::XYPlot1MouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { char temp[20]; AnsiString ansixmouse = XYPlot1->MouseX; char* pxmouse=ansixmouse.c_str(); float xmouse=atof(pxmouse); sprintf(temp,"%.2f",xmouse); XMouseLabel->Caption=temp; AnsiString ansiymouse = XYPlot1->MouseY; char* pymouse=ansiymouse.c_str(); float ymouse=atof(pymouse); sprintf(temp,"%.0f",ymouse); YMouseLabel->Caption=temp; } //--------------------------------------------------------------------
308
309
310
{ unsigned char *buffer,data[10]; buffer=data; // Clear the input buffer by writing zero's to it for (int n=0;n<=10;n++) {data[n]=0;} CommReceive->ReadComm(buffer,4); // The next line can also be: // if (data[0]==13 && data[1]==10 && data[2]==66 && data[3]==85) if (data[0]==13) // if reset is detected { BlinkTimer->Enabled=false;
311
CommReceive->CloseConnection(); RunProgramForm->Close(); RunComm->OpenConnection(); RunComm->WriteCommByte(13); delay_1(50); char gb600[]="g 2000"; for (int cnt=0;cnt<=5;cnt++) { RunComm->WriteCommByte(gb600[cnt]); delay_1(50); } RunComm->WriteCommByte(13); RunComm->CloseConnection(); // // // // // The following four lines will convert the AnsiString type text into an integer. If the selected microcontroller program is: PID -> mcuprogram = 1 TestADCDAC -> mcuprogram = 2
if (MonitorForm->Recorddatawhencontrolprogramstarts1->Checked) { // if the Digital Control program was selected if (MonitorForm->DigitalController1->Checked) { MonitorForm->DigitalMonitorButtonClick(Sender); MonitorForm->DigitalRecordButtonClick(Sender); } // if the Multi Control program was selected else if (MonitorForm->MultiController1->Checked) { MonitorForm->MultiMonitorButtonClick(Sender); MonitorForm->MultiRecordButtonClick(Sender); } // if the PID program was selected else if (MonitorForm->PIDController1->Checked) { MonitorForm->PIDMonitorButtonClick(Sender); MonitorForm->PIDRecordButtonClick(Sender); } // if the TestDACADC program was selected else if (MonitorForm->TestDACADC1->Checked) { MonitorForm->TestDACADCMonitorButtonClick(Sender); MonitorForm->TestDACADCRecordButtonClick(Sender); } } else { // if the Digital Control program was selected if (MonitorForm->DigitalController1->Checked) { MonitorForm->DigitalMonitorButtonClick(Sender); }
312
// if the Multi Control program was selected else if (MonitorForm->MultiController1->Checked) { MonitorForm->MultiMonitorButtonClick(Sender); } // if the PID program was selected else if (MonitorForm->PIDController1->Checked) { MonitorForm->PIDMonitorButtonClick(Sender); } // if the TestDACADC program was selected else if (MonitorForm->TestDACADC1->Checked) { MonitorForm->TestDACADCMonitorButtonClick(Sender); } } } // if reset is not detected else { // // // // // // The next six lines of program code will disable serial communication between the PC and the microcontroller and will activate the ErrorDownloadStartMessage form in which the user will be prompted that a running program on the microcontroller has to be stopped before the progrma can be started on the microcontroller.
BlinkTimer->Enabled=false; RunComm->CloseConnection(); RunProgramForm->Close(); ErrorDownloadStartMessage->Label3->Caption= "starting the new program."; ErrorDownloadStartMessage->ShowModal(); } } //-------------------------------------------------------------------void __fastcall TRunProgramForm::BlinkTimerTimer(TObject *Sender) { if (Messagebox->Caption=="") { Messagebox->Caption="Reset The Microcontroller"; } else Messagebox->Caption=""; } //-------------------------------------------------------------------void __fastcall TRunProgramForm::CancelButtonClick(TObject *Sender) { RunProgramForm->Close(); } //-------------------------------------------------------------------void __fastcall TRunProgramForm::FormActivate(TObject *Sender)
313
{ // This event handler enable the serial communication between // the PC and the microcontroller. CommReceive->OpenConnection(); BlinkTimer->Enabled=true; } //-------------------------------------------------------------------void __fastcall TRunProgramForm::FormClose(TObject *Sender, TCloseAction &Action) // This event handler will check whether serial communication between // the PC and the microcontroller is enabled when RunProgrmaForm // closes and will disconnect the serail port before the form closes. { if (CommReceive->Connected==true) CommReceive->CloseConnection(); } //--------------------------------------------------------------------
314
315
316
317
318
Appendix G
This section contains all the microcontroller software source code. The different modules will be listed as follow: Hc11e9.h Start11.c Non_bank.sgm Lustart.h Testio.c Testio.prm Digital.c Digital.prm Multi.c Multi.prm Header file for the 68HC11 register definitions Default startup code for the 68HC11 HC11 Small and medium memory model Header file which defines the startup description Source code for the Test I/O module Linker parameter file for the Test I/O module Source code for the SISO Digial Controller Module Linker parameter file for the SISO Digial Controller Module Source code for the MIMO Digial Controller Module Linker parameter file for the MIMO Digial Controller Module
319
: : : : : : : :
1; 1; 1; 1; 1; 1; 1; 1;
unsigned int Register_Set=0x1000; #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define LCD1 PORTA PIOC PORTC PORTB PORTCL DDRC PORTD DDRD PORTE CFORC OC1M OC1D TCNT TCNTUP TIC1 TIC2 TIC3 TIC3UP TOC1 TOC1UP TOC2 TOC2UP TOC3 TOC3UP TOC4 TOC4UP TOC5 TOC5UP TCTL1 TCTL2 TMSK1 (*(volatile Register*)(0x0000)) (*(volatile Register*)(Register_Set + 0x00)) (*(Register*)(Register_Set + 0x02)) (*(volatile Register*)(Register_Set + 0x03)) (*(volatile Register*)(Register_Set + 0x04)) (*(volatile Register*)(Register_Set + 0x05)) (*(Register*)(Register_Set + 0x07)) (*(volatile Register*)(Register_Set + 0x08)) (*(Register*)(Register_Set + 0x09)) (*(volatile Register*)(Register_Set + 0x0A)) (*(Register*)(Register_Set + 0x0B)) (*(volatile RegisterBits*)(Register_Set + 0x0C)) (*(volatile RegisterBits*)(Register_Set + 0x0D)) (*(volatile unsigned int *)(Register_Set + 0x0E)) (*(volatile unsigned char *)(Register_Set + 0x0E)) (*(volatile unsigned int *)(Register_Set + 0x10)) (*(volatile unsigned int *)(Register_Set + 0x12)) (*(volatile unsigned int *)(Register_Set + 0x14)) (*(volatile unsigned char *)(Register_Set + 0x14)) (*(volatile unsigned int *)(Register_Set + 0x16)) (*(volatile unsigned char *)(Register_Set + 0x16)) (*(volatile unsigned int *)(Register_Set + 0x18)) (*(volatile unsigned char *)(Register_Set + 0x18)) (*(volatile unsigned int *)(Register_Set + 0x1A)) (*(volatile unsigned char *)(Register_Set + 0x1A)) (*(volatile unsigned int *)(Register_Set + 0x1C)) (*(volatile unsigned char *)(Register_Set + 0x1C)) (*(volatile unsigned int *)(Register_Set + 0x1E)) (*(volatile unsigned char *)(Register_Set + 0x1E)) (*(RegisterBits*)(Register_Set + 0x20)) (*(Register*)(Register_Set + 0x21)) (*(volatile RegisterBits*)(Register_Set + 0x22))
320
#define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define
TFLG1 (*(volatile RegisterBits*)(Register_Set + 0x23)) TFLG1BYTE (*(volatile Register*)(Register_Set + 0x23)) TMSK2 (*(volatile RegisterBits*)(Register_Set + 0x24)) TFLG2 (*(volatile RegisterBits*)(Register_Set + 0x25)) TFLG2BYTE (*(volatile Register*)(Register_Set + 0x25)) PACTL (*(RegisterBits*)(Register_Set + 0x26)) PACNT (*(volatile Register*)(Register_Set + 0x27)) SPCR (*(Register*)(Register_Set + 0x28)) SPSR (*(volatile Register*)(Register_Set + 0x29)) SPDR (*(volatile Register*)(Register_Set + 0x2A)) BAUD (*(Register*)(Register_Set + 0x2B)) SCCR1 (*(volatile Register*)(Register_Set + 0x2C)) SCCR2 (*(Register*)(Register_Set + 0x2D)) SCSR (*(volatile Register*)(Register_Set + 0x2E)) SCDR (*(volatile unsigned char*)(Register_Set + 0x2F)) ADCTL (*(volatile Register*)(Register_Set + 0x30)) ADR1 (*(volatile unsigned char*)(Register_Set + 0x31)) ADR2 (*(volatile unsigned char*)(Register_Set + 0x32)) ADR3 (*(volatile unsigned char*)(Register_Set + 0x33)) ADR4 (*(volatile unsigned char*)(Register_Set + 0x34)) BPROT (*(Register*)(Register_Set + 0x35)) OPTION (*(Register*)(Register_Set + 0x39)) COPRSR (*(Register*)(Register_Set + 0x3A)) PPROG (*(Register*)(Register_Set + 0x3B)) HPRIO (*(RegisterBits*)(Register_Set + 0x3C)) INIT (*(Register*)(Register_Set + 0x3D)) TEST1 (*(Register*)(Register_Set + 0x3E)) CONFIG (*(Register*)(Register_Set + 0x3F))
/* CFORC bit definitions 0x0B */ #define #define #define #define #define FOC1 FOC2 FOC3 FOC4 FOC5 bit7 bit6 bit5 bit4 bit3
/* OC1M bit definitions 0x0C */ #define #define #define #define #define OC1M7 OC1M6 OC1M5 OC1M4 OC1M3 bit7 bit6 bit5 bit4 bit3
/* OC1D bit definitions 0x0D */ #define #define #define #define #define OC1D7 OC1D6 OC1D5 OC1D4 OC1D3 bit7 bit6 bit5 bit4 bit3
321
/* TCTL2 bit definitions 0x21 */ #define #define #define #define #define #define #define #define EDG4B EDG4A EDG1B EDG1A EDG2B EDG2A EDG3B EDG3A bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
/* TMSK1 bit definitions 0x22 */ #define #define #define #define #define #define #define #define OC1I OC2I OC3I OC4I I4O5I IC1I IC2I IC3I bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
/* TFLG1 bit definitions 0x23 */ #define #define #define #define #define #define #define #define OC1F OC2F OC3F OC4F I4O5F IC1F IC2F IC3F 0x80 0x40 0x20 0x10 0x08 0x04 0x02 0x01
/* TMSK2 bit definitions 0x24 */ #define #define #define #define #define #define TOI RTII PAOVI PAII PR1 PR0 bit7 bit6 bit5 bit4 bit1 bit0
322
/* PACTL bit definitions 0x26 */ #define #define #define #define #define #define #define #define DDRA7 PAEN PAMOD PEDGE DDRA3 I4O5 RTR1 RTR0 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
/* SPCR bit definitions 0x28 */ #define #define #define #define #define #define #define #define SPIE SPE DWOM MSTR CPOL CPHA SPR1 SPR0 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
/* SPSR bit definitions 0x29 */ #define SPIF #define WCOL #define MODF bit7 bit6 bit4
/* BAUD bit definitions 0x2B */ #define #define #define #define #define #define #define TCLR SCP1 SCP0 RCKB SCR2 SCR1 SCR0 bit7 bit5 bit4 bit3 bit2 bit1 bit0
/* SCCR1 bit definition 0x2C */ #define #define #define #define R8 T8 M WAKE bit7 bit6 bit4 bit3
/* SCCR2 bit definitions 0x2D */ #define TIE #define TCIE bit7 bit6
323
#define #define #define #define #define #define /* SCSR #define #define #define #define #define #define #define
bit definitions 0x2E */ TDRE TC RDRF IDLE OR NF FE bit7 bit6 bit5 bit4 bit3 bit2 bit1
/* SCDR bit definitions 0x2F */ #define #define #define #define #define #define #define #define R7T7 R6T6 R5T5 R4T4 R3T3 R2T2 R1T1 R0T0 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
/* ADCTL bit definitions 0x30 */ #define #define #define #define #define #define #define CCF SCAN MULT CD CC CB CA bit7 bit5 bit4 bit3 bit2 bit1 bit0
/* BPROT bit definitions 0x35 */ #define #define #define #define #define PTCON BPRT3 BPRT2 BPRT1 BPRT0 bit4 bit3 bit2 bit1 bit0
/* OPTION bit definitions 0x39 */ #define #define #define #define #define #define ADPU CSEL IRQE DLY CME CR1 bit7 bit6 bit5 bit4 bit3 bit1
324
#define CR0
bit0
/* PPROG bit definitions 0x3B */ #define #define #define #define #define #define #define #define ODD EVEN ELAT BYTE ROW ERASE EELAT EEPGM bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
/* HPRIO bit definitions 0x3C */ #define #define #define #define #define #define #define #define /* INIT #define #define #define #define #define #define #define #define RBOOT SMOD MDA IRV PSEL3 PSEL2 PSEL1 PSEL0 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
bit definitions 0x3D */ RAM3 RAM2 RAM1 RAM0 REG3 REG2 REG1 REG0 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
/* TEST1 bit definitions 0x3E */ #define #define #define #define #define #define #define TILOP OCCR CBYP DISR FCM FCOP TCON bit7 bit5 bit4 bit3 bit2 bit1 bit0
/* CONFIG bit definitions 0x3F */ #define #define #define #define NOSEC NOCOP ROMON EEON bit3 bit2 bit1 bit0
325
/* PORTC bit definitions 0x03 */ #define #define #define #define #define #define #define #define PC7 PC6 PC5 PC4 PC3 PC2 PC1 PC0 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
/* PORTB bit definitions 0x04 */ #define #define #define #define #define #define #define #define PB7 PB6 PB5 PB4 PB3 PB2 PB1 PB0 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
/* PORTCL bit definitions 0x05 */ #define #define #define #define #define #define #define #define PCL7 PCL6 PCL5 PCL4 PCL3 PCL2 PCL1 PCL0 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
/* DDRC bit definitions 0x07 */ #define #define #define #define #define #define #define #define DDC7 DDC6 DDC5 DDC4 DDC3 DDC2 DDC1 DDC0 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
326
/* DDRD bit definitions 0x09 */ #define #define #define #define #define #define DDD5 DDD4 DDD3 DDD2 DDD1 DDD0 bit5 bit4 bit3 bit2 bit1 bit0
/* PORTE bit definitions 0x0A */ #define #define #define #define #define #define #define #define PE7 PE6 PE5 PE4 PE3 PE2 PE1 PE0 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 to permit interrupt service routine
/* Macros and function programming from C */ #define #define #define #define #define #define #define #define #define typedef typedef #endif
vector(isr,vector_address) (*(void**)(vector_address)=(isr)) cli() _asm("cli\n") sei() _asm("sei\n") return_int() _asm("rti\n)" ldaa50 asm {ldaa #50} staa asm {staa 0x0000} TRUE 1 FALSE 0 FOREVER while(TRUE) unsigned int WORD; unsigned char BYTE;
327
#pragma NO_FRAME static void NEAR Init(void) /* purpose: 1) zero out RAM-areas where data is allocated 2) copy initialization data from ROM to RAM parameters: EK page where '_startupData' is allocated
called from: _Startup, LibInits */ { asm { ZeroOut: LDX LDY BEQ NextZeroOut: PSHX LDD LDX NextWord: CLR INX ADDD _startupData.pZeroOut ; *pZeroOut _startupData.nofZeroOuts ; nofZeroOuts CopyDown ; if nothing to zero out ; save *pZeroOut 2,X ; byte count 0,X ; start address 0,X ; clear memory byte ; inc address #-1 ; dec byte count
328
BNE PULX INX INX INX INX DEY BNE CopyDown: LDX copy down desc. NextBlock: LDD BEQ INX INX LDY Y INX INX Copy: PSHB LDAB and STAB area PULB INX INY ADDD BNE BRA FuncInits: } } static long tmp; #pragma NO_FRAME
NextWord
NextZeroOut
; dec nofZeroOuts ;
_startupData.toCopyDownBeg:2 ; load address of 0,X FuncInits ; size of init-data -> D ; end of copy down desc.
0,X
0,X 0,Y
void NEAR _Startup(void) /* purpose: 1) initialize the stack 2) initialize the RAM, copy down init dat etc (Init) 3) call main; parameters: NONE called from: _PRESTART-code generated by the Linker */ { for(;;) { /* forever: procedure */ asm{ #ifdef __BANKED__ JSR InitBank initialize the program; call the root-
329
#endif LDD BNE LDX TXS Initialize: JSR _startupData.flags Initialize _startupData.stackOffset:2 ; if flags then ; init stack pointer ; stack pointer
Init
CallMain: } #ifdef __BANKED__ { tmp = (long)_startupData.main; *(int *)&tmp |= 0x8000; (*(void (*) (void)) tmp) (); } #else (* _startupData.main) (); #endif } /* end loop forever */ }
330
331
/*******************************************************************/ /* ABOUT */ /*-----------------------------------------------------------------*/ #if 0 the following datastructures contain the data needed to initialize the processor and memory #endif typedef struct{ unsigned char } _Range;
/* [beg..beg+size] */
extern struct _tagStartup{ unsigned flags; _PFunc main; unsigned dataPage; */ long stackOffset; int nofZeroOuts; _Range *pZeroOut; with nofZeroOuts elements */ long toCopyDownBeg; begins */ _PFunc *mInits; pointers, terminated by 0 */ _PFunc *libInits; pointers, terminated by 0 */ } _startupData; extern void _Startup(void); */
/* pZeroOut is a vector of ranges /* rom-address where copydown-data /* mInits is a vector of function /* libInits is a vector of function
332
/*-------------------------------------------------------------------*/ #endif
333
334
{ getparameters(); initsci(); inittimerinterrupt(); switch(inoutcombination) { case 1: initadc(); initportb(); break; case 2: initic3(); initportb(); break; case 3: initic3(); initpwm(); break; case 4: initadc(); initpwm(); break; } output=outputlowerlimit; asm cli; /* Enable the system interrupts */
while(1) { /* Save starttime of routine (the upper 8-bit value of TCNT) */ samplestarttime=TCNTUP; /* Determine the number of TCNTUP counts which correspond to a samplefrequency of maxsamplefreq Hz*/ sampletime=(7813/maxsamplefreq) + samplestarttime; reqoverflows=sampletime/256; sampleendtime=sampletime%256; count++; if ((float) count/realsamplefreq>=0.1*dly) { output=output+step; if (output>outputupperlimit) { output=outputlowerlimit; } count=0; } switch(inoutcombination) { case 2: /* Pulse Width Measurement */ case 3: asm sei; /* disable interrupts */ input=(unsigned int)7813/period;
335
switch(inoutcombination) { case 1: /* 8-Bit PIA Output */ case 2: MEMPORTB=output; break; case 3: /* PWM Output */ case 4: asm sei; pwmtimeon=((float) output/0x100)*pwmperiod; asm cli; break; } if (realsamplefreq>250) { realsamplefreq=250; } if (realsamplefreq<1) { realsamplefreq=1; } if (input>250) { input=250; } if (input<1) { input=1; } if (output>outputupperlimit) { output=outputupperlimit; } if (output<outputlowerlimit) { output=outputlowerlimit; } putchar(251); putchar(realsamplefreq); putchar(252); putchar(input); putchar(253); putchar(output);
/*Wait for desired sample delay time to elapse */ while (sampletimeroverflows<reqoverflows); while (TCNTUP<sampleendtime); realsampletime=sampletimeroverflows*256 + TCNTUP - samplestarttime;
336
/***************************************************/ /* TCNT counter overflow interrupt service routine */ /***************************************************/ void timeroverflow(void) { timeroverflows++; /* Timeroverflow counter for inputcapture */ sampletimeroverflows++; /*TFLG2.bit7=1; /* Reset timer overflow flag */ /*TFLG2BYTE=0x80;*/ asm { PSHA LDAA #0x80 STAA 0x1025 PULA RTI } }
/***************************************************/ /* Frequency Measurement Interrupt Service Routine */ /***************************************************/ void inputcapture(void) { /* Note: It is not neccesary to use the lower 8-bit of the counter function. Because the 68HC11 is an 8-bit microcontroller,processing speed will significantly increase by leaving the lower 8-bits of the counter out of the calculations. The resolution of the upper 8-bits of the TCNT counter is 256*0.5 micro seconds = 0.000128 seconds which is still high enough to provide accurate timing. */ asm { PSHA LDAA #0x01 STAA 0x1023 PULA } /* Reset input flag IC3F by writing 1 to it */
if (TFLG2BYTE>=0x80 && TIC3UP<=0x80) { timeroverflows++; asm { /*Reset TOF Flag*/ PSHA LDAA #0x80
337
STAA 0x1025 PULA } } overflow=(unsigned int) timeroverflows*0x100; /* Copy upper 8-bits of the TIC3 register into tic3upint */ tic3upint=(unsigned int)TIC3UP; /* The overflow variable is the number of the upper 8-bit counts of the TNCT counter that correspond to the amount of TNCT overflows recorded in the timeroverflows variable */ period=(unsigned int) (overflow+tic3upint-time1); /* The period variable is the total number of TCNT upper 8-bit counts between this interrupt service routine and the previous one.*/ time1=(unsigned int)TIC3UP; /* Save the endtime of this ISR */ timeroverflows=0; /* Reset the timeroverflows variable */ asm RTI; } /**************************************/ /* SCI Character transmission routine */ /**************************************/ void putchar(char new_character) { char temp; temp=SCSR; temp=SCDR; while(SCSR==0); SCDR=new_character; } /************************************************/ /* Routine to initialize the microcontroller to */ /* generate a PWM signal on OC3 (PA5) */ /************************************************/ void initpwm(void) { /* Memory locations D9-DB is a 3 byte pseudo interrupt vector for TOC3(PA5) */ (char *) 0x00D9; /* Place jump instruction (op code 7E) in 00D9 */ *(char *) 0x00D9 = 0x7E; (int * ) 0x00DA; /* Place starting address of interrupt routine in DA&DB */ *(int *) 0x00DA = (int)oc3interrupt; OC1M.OC1M5=1; TMSK1.OC3I=1; /*couple OC1 to PA7*/ /*enable the OC3 interrupt*/
338
OC1D.OC1D5=1; /*turn on OC3 when OC1 occurs*/ TCTL1.OL3=1; /*toggle OC3 when OC3 occurs*/ TOC1=TCNT+50000; /* initialize OC1*/ TOC3=TOC1+25000; /* initialize OC3*/ } /*********************************/ /* OC3 Interrupt service routine */ /*********************************/ void oc3interrupt(void) { asm { PSHA LDAA #0x20 STAA 0x1023 PULA } toc1=TOC1+pwmperiod; TOC1=toc1; toc3=toc1+pwmtimeon; TOC3=toc3; asm rti; /*return from interrupt*/ } /************************************************************/ /* Routine to get the downloaded parameters from the PC */ /* The downloaded parameters are stored in 0x0000 - 0x0041 */ /************************************************************/ void getparameters(void) { unsigned char pwmhz; (unsigned char *) 0x0000; (unsigned char *) 0x0001; (unsigned char *) 0x0002; (unsigned char *) 0x0003; (unsigned char *) 0x0004; (unsigned char *) 0x0005; (unsigned char *) 0x0006; step=*(unsigned char *) 0x0000; dly=*(unsigned char *) 0x0001; maxsamplefreq=*(unsigned char *) pwmhz=*(unsigned char *) 0x0003; pwmperiod=2000000/pwmhz; outputlowerlimit=*(unsigned char outputupperlimit=*(unsigned char inoutcombination=*(unsigned char }
0x0002;
/************************************************/ /* Routine to initialize the microcontroller to */ /* send and receive serial data */ /************************************************/
339
void initsci(void) { BAUD=0x30; /* Set serial communication to 9600 baud rate */ SCCR2=0xC; /* TE and RE high=Serial Transmit and Receive Enabled*/ } /***************************************************/ /* Routine to initialize the input capture 3 (IC3) */ /* PA0 pin to receive incoming data */ /***************************************************/ void initic3(void) { /* Set input capture in TIC3(PA0) on falling (rising=0x01) edges only */ TCTL2=0x02; TMSK1.bit0=1; /* Enable the interrupt mask for IC3 (PA0) */ asm { PSHA LDAA #0x01 STAA 0x1023 /* Reset flag */ PULA } /* Memory locations E2-E4 is a 3 byte pseudo interrupt vector for IC3(PA0) */ (char *) 0x00E2; /* Place jump instruction (op code 7E) in 00E2 */ *(char *) 0x00E2 = 0x7E; (int * ) 0x00E3; /* Place starting address of interrupt routine in E3&E4 */ *(int *) 0x00E3 = (int)inputcapture; } /*******************************************************/ /* Routine to initialize the timer overflow interrupt */ /*******************************************************/ void inittimerinterrupt(void) { TMSK2.bit7=1; /* Enable the timer overflow interrupt */ asm { PSHA LDAA #0x80 STAA 0x1025 PULA }
/* Memory locations D0-D2 is a 3 byte pseudo interrupt vector for the timer overflow */ (char *) 0x00D0; /* Place jump instruction (op code 7E) in 00D0 */
340
*(char *) 0x00D0 = 0x7E; (int * ) 0x00D1; /* Place starting address of interrupt routine in D1&D2 */ *(int *) 0x00D1 = (int)timeroverflow; } /***************************************/ /* Function to initialize the */ /* analog-to-digital converter system */ /***************************************/ void initadc(void) { OPTION=0x80;/*Set bit 7 in OPTION high for A/D conversion*/ } /****************************************/ /* Analog to Digital conversion routine */ /****************************************/ unsigned char adc(void) { unsigned char temp; ADCTL=0x5; /* Set analog inputs on PE5 (pin 46) */ while (ADCTL<128); /* Wait for input */ temp=ADR1; return temp; } /******************************************************************/ /* Initialaise Port B on the MC6821 on the Memory extension board */ /* to serve as an 8-bit output port at memory address 0xA102 */ /******************************************************************/ void initportb(void) { (unsigned char *) 0xA102; (unsigned char *) 0xA103; *(unsigned char *) 0xA103=0x00; *(unsigned char *) 0xA102=0xFF; *(unsigned char *) 0xA103=0x04; }
341
Testio.prm
LINK testio.abs NAMES END
SECTIONS MY_RAM = READ_WRITE 0x4000 TO 0x4FFF; MY_ROM = READ_ONLY 0x2000 TO 0x3FFF; PLACEMENT DEFAULT_ROM DEFAULT_RAM END STACKSIZE 0x600
INTO INTO
MY_ROM; MY_RAM;
342
343
unsigned char feedbackmode; unsigned int setpoint; unsigned int input; unsigned int output; unsigned int outputupperlimit; unsigned int outputlowerlimit; unsigned int inputupperlimit; unsigned int inputlowerlimit; float a0; float a1; float a2; float a3; float a4; float b0; float b1; float b2; float b3; float b4; float u=0; float um1=0; float um2=0; float um3=0; float um4=0; int e=0; int em1=0; int em2=0; int em3=0; int em4=0; /* Main Program loop-control global variables*/ unsigned char sampletimeroverflows=0; unsigned int sampletime; unsigned char samplestarttime; unsigned char sampleendtime; unsigned char realsamplefreq=250; unsigned int realsampletime; unsigned char reqoverflows; unsigned int maxsamplefreq; /*Define the memory location for the 8-bit parallel output*/ #define MEMPORTB (*(volatile unsigned char*)(0xA102)) void main(void) { getparameters(); initsci(); inittimerinterrupt(); delay(); switch(inoutcombination) { case 1: initadc(); initportb(); break;
344
case 2: initic3(); initportb(); break; case 3: initic3(); initpwm(); break; case 4: initadc(); initpwm(); break; } asm cli; /* Enable the system interrupts */
/*************************************************************/ /* This is the starting point of the control algorithm in an */ /* infinite loop */ /*************************************************************/ while(1) { /* Save starttime of routine (the upper 8-bit value of TCNT) */ samplestarttime=TCNTUP; /* Determine the number of TCNTUP counts which correspond to a samplefrequency of maxsamplefreq*/ sampletime=(7813/maxsamplefreq) + samplestarttime; reqoverflows=sampletime/0x100; sampleendtime=sampletime%0x100; /* Start Digital controller calculations */ switch(inoutcombination) { case 2: /* Pulse Width Measurement */ case 3: asm sei; /* disable interrupts */ input=7813/period; asm cli; /* enable interrupts */ break; case 1: /* Analog input */ case 4: input=adc(); break; } /* Set Inputlimits */ if (input<inputlowerlimit) input=inputlowerlimit; if (input>inputupperlimit) input=inputupperlimit; if (feedbackmode==1) { e=setpoint-input; }
/* negative feedback */
345
else { e=setpoint+input; }
/* positive feedback */
u=(float) (b0*e + b1*em1 + b2*em2 + b3*em3 + b4*em4 - a1*um1 - a2*um2 - a3*um3 - a4*um4)/a0; if (u<(float)outputlowerlimit) u=(float)outputlowerlimit; if (u>(float)outputupperlimit) u=(float)outputupperlimit; /* Save current values for the next sample period */ um4=um3; um3=um2; um2=um1; um1=u; em4=em3; em3=em2; em2=em1; em1=e; output=u; switch(inoutcombination) { case 1: /* 8-Bit PIA Output */ case 2: MEMPORTB=output; break; case 3: /* PWM Output */ case 4: asm sei; pwmtimeon=((float) output/0x100)*pwmperiod; asm cli; break; } /* Transmit the sample frequecy, input and output values to /* the serial port. putchar(251); putchar(realsamplefreq); putchar(252); putchar(input); putchar(253); putchar(output); /* Wait for the desired sample period to elapse */ while (sampletimeroverflows<reqoverflows); while (TCNTUP<sampleendtime); /* /* /* /* Calculate the actual time for the sample period. (This realsampletime will be higher than the desired sample time entered in the maxsamplefreq variable if the desired sample period cannot be met due to */ */ */ */ */ */
346
*/
realsampletime=sampletimeroverflows*256+TCNTUP-samplestarttime; realsamplefreq=7813/realsampletime; /* Restrict the realsamplefreq between 1 and 250 */ if (realsamplefreq>250) realsamplefreq=250; if (realsamplefreq<1) realsamplefreq=1; sampletimeroverflows=0; } /*end while(1) loop*/ } /* Reset sampletimeroverflows */
/************************************************************/ /* Routine to get the downloaded parameters which was */ /* downloaded from the PC. The downloaded parameters are */ /* stored in memory locations 0x0000 - 0x0041 */ /* */ /* The Memory location of the parameters are as follow: */ /* */ /* setpoint 0x0000 (0 decimal) */ /* a0 0x0001-0x0004 (1-4 decmial) */ /* a1 0x0005-0x0008 (5-8 decmial) */ /* a2 0x0009-0x000C (9-12 decmial) */ /* a3 0x000D-0x0010 (13-16 decimal) */ /* a4 0x0011-0x0014 (17-20 decimal) */ /* b0 0x0015-0x0018 (21-24 decimal) */ /* b1 0x0019-0x001C (25-28 decimal) */ /* b2 0x001D-0x0020 (28-32 decimal) */ /* b3 0x0021-0x0024 (33-36 decimal) */ /* b4 0x0025-0x0028 (37-40 decimal) */ /* maxsamplefreq 0x0029 (41 decimal) */ /* pwmhz 0x002A (42 decimal) */ /* feedbackmode 0x002B (43 decimal) */ /* outputupperlimit 0x002C (44 decimal) */ /* outputlowerlimit 0x002D (45 decimal) */ /* inputupperlimit 0x002E (46 decimal) */ /* inputlowerlimit 0x002F (47 decimal) */ /* inoutcombination 0x0030 (48 decimal) */ /************************************************************/ void getparameters(void) { unsigned char pwmhz; (unsigned char *) 0x0000; (float *) 0x0001; (float *) 0x0005; (float *) 0x0009; (float *) 0x000D; (float *) 0x0011; (float *) 0x0015; (float *) 0x0019; (float *) 0x001D; (float *) 0x0021; (float *) 0x0025;
347
*) *) *) *) *) *) *) *)
setpoint=*(unsigned char *) 0x0000; a0=*(float *) 0x0001; a1=*(float *) 0x0005; a2=*(float *) 0x0009; a3=*(float *) 0x000D; a4=*(float *) 0x0011; b0=*(float *) 0x0015; b1=*(float *) 0x0019; b2=*(float *) 0x001D; b3=*(float *) 0x0021; b4=*(float *) 0x0025; maxsamplefreq=*(unsigned char *) 0x0029; pwmhz=*(unsigned char *) 0x002A; pwmperiod=2000000/pwmhz; feedbackmode=*(unsigned char *) 0x002B; outputupperlimit=*(unsigned char *) 0x002C; outputlowerlimit=*(unsigned char *) 0x002D; inputupperlimit=*(unsigned char *) 0x002E; inputlowerlimit=*(unsigned char *) 0x002F; inoutcombination=*(unsigned char *) 0x0030; }
/***************************************************/ /* TCNT counter overflow interrupt service routine */ /***************************************************/ void timeroverflow(void) { timeroverflows++; /* Timeroverflow counter for inputcapture */ sampletimeroverflows++; asm { PSHA /* Reset timer overflow flag */ LDAA #0x80 STAA 0x1025 PULA RTI /* Return from Interrupt Service Routine */ } }
348
void inputcapture(void) { /* Note: It is not neccesary to use the lower 8-bit of the counter function. Because the 68HC11 is an 8-bit microcontroller, processing speed will significantly increase by leaving the lower 8-bits of the counter out of the calculations. The 'period' variable, which represents all the TCNT counts, have to be a 32-bit long integer if this program uses all the 16 bits of the TCNT counter. It was found that the compiler used for this program does not compile calculations with 32-bit long integers correctly. The resolution of the upper 8-bits of the TCNT counter is 256*0.5 micro seconds = 0.000128 seconds which is still high enough to provide accurate timing. */ asm { PSHA LDAA #0x01 STAA 0x1023 PULA } /* Reset input flag IC3F by writing 1 to it */
if (TFLG2BYTE>=0x80 && TIC3UP<=0x80) { timeroverflows++; asm { PSHA LDAA #0x80 STAA 0x1025 PULA } } overflow=(unsigned int) timeroverflows*0x100; /* Copy upper 8-bits of the TIC3 register into tic3upint */ tic3upint=(unsigned int)TIC3UP; /* The overflow variable is the number of the upper 8-bit counts of the TNCT counter that correspond to the amount of TNCT overflows recorded in the timeroverflows variable */ period=(unsigned int) (overflow+tic3upint-time1); /* The period variable is the total number of TCNT upper 8-bit counts between this interrupt service routine and the previous one.*/ time1=(unsigned int)TIC3UP; /* Save the endtime of this ISR */ timeroverflows=0; /* Reset the timeroverflows variable */
349
asm RTI; } /**************************************/ /* SCI Character transmission routine */ /**************************************/ void putchar(char new_character) { char temp; temp=SCSR; temp=SCDR; while(SCSR==0); SCDR=new_character; } /************************************************/ /* Routine to initialize the microcontroller to */ /* generate a PWM signal on OC3 (PA5) */ /************************************************/ void initpwm(void) { /* Memory locations D9-DB is a 3 byte pseudo */ /* interrupt vector for TOC3(PA5) */ (char *) 0x00D9; /* Place jump instruction (op code 7E) in 00D9 */ *(char *) 0x00D9 = 0x7E; /* Place starting address of interrupt routine in DA&DB */ (int * ) 0x00DA; *(int *) 0x00DA = (int)oc3interrupt; OC1M.OC1M5=1; TMSK1.OC3I=1; OC1D.OC1D5=1; TCTL1.OL3=1; TOC1=TCNT+50000; TOC3=TOC1+1; } /* /* /* /* /* /* couple OC1 to OC3 */ enable the OC3 interrupt */ turn on OC3 when OC3 occurs */ toggle OC3 when OC3 occurs */ initialize OC1 */ initialize OC3 */
/*********************************/ /* OC3 Interrupt service routine. */ /* This Interrupt Service Routine will generate a PWM signal /* on OC3 (PA5) */ /*********************************/ void oc3interrupt(void) { asm { PSHA LDAA #0x20 STAA 0x1023 PULA }
350
toc1=TOC1+pwmperiod; TOC1=toc1; toc3=toc1+pwmtimeon; TOC3=toc3; asm RTI /* Return from Interrupt Service Routine */ } /************************************************/ /* Routine to initialize the microcontroller to */ /* send and receive serial data */ /************************************************/ void initsci(void) { BAUD=0x30; /* Set serial communication to 9600 baud rate */ SCCR2=0xC; /* TE and RE high=Serial Transmit and Receive Enabled */ } /***************************************************/ /* Routine to initialize the input capture 3 (IC3) */ /* PA0 pin to receive incoming data */ /***************************************************/ void initic3(void) { /* Set input capture in TIC3(PA0) on rising edges only */ /* (For falling edges, TCTL2=0x02) */ TCTL2=0x01; TMSK1.bit0=1; /* Enable the interrupt mask for IC3 (PA0) */ asm { PSHA LDAA #0x01 STAA 0x1023 PULA } /* Reset input flag IC3F by writing 1 to it */
/* Memory locations E2-E4 is a 3 byte */ /* pseudo interrupt vector for IC3(PA0) */ /* Place jump instruction (op code 7E) in 00E2 */ (char *) 0x00E2; *(char *) 0x00E2 = 0x7E; /* Place starting address of interrupt routine in E3&E4 */ (int * ) 0x00E3; *(int *) 0x00E3 = (int)inputcapture; } /*******************************************************/ /* Routine to initialize the timer overflow interrupt */ /*******************************************************/ void inittimerinterrupt(void) {
351
/* Memory locations D0-D2 is a 3 byte pseudo interrupt vector for the timer overflow */ (char *) 0x00D0; /* Place jump instruction (op code 7E) in 00D0 */ *(char *) 0x00D0=0x7E; (int * ) 0x00D1; /* Place starting address of interrupt routine in D1&D2 */ *(int *) 0x00D1 = (int)timeroverflow; } /*****************************************************************/ /* It was found that the first few characters received by the PC */ /* is corrupt.This function will transmit the first few */ /* non-significant characters to the PC before starting the */ /* control algorithm */ /*****************************************************************/ void delay(void) { unsigned int temp; for (temp=0;temp<100;temp++) { putchar(251); putchar(maxsamplefreq); putchar(252); putchar(1); putchar(253); putchar(1); } } /***************************************/ /* Function to initialize the */ /* analog-to-digital converter system */ /***************************************/ void initadc(void) { OPTION=0x80;/*Set bit 7 in OPTION high for A/D conversion*/ } /****************************************/ /* Analog to Digital conversion routine */ /****************************************/
352
unsigned char adc(void) { unsigned char temp; ADCTL=0x5; /* Set analog inputs on PE5 (pin 46) */ while (ADCTL<128); /* Wait for input */ temp=ADR1; return temp; } /******************************************************************/ /* Initialaise Port B on the MC6821 on the Memory extension board */ /* to serve as an 8-bit output port at memory address 0xA102 */ /******************************************************************/ void initportb(void) { (unsigned char *) 0xA102; (unsigned char *) 0xA103; *(unsigned char *) 0xA103=0x00; *(unsigned char *) 0xA102=0xFF; *(unsigned char *) 0xA103=0x04; }
353
Digital.prm - Linker parameter file for the SISO Digial Controller Module
LINK Digital.abs NAMES END Digital.o start11.o ansi.lib
SECTIONS MY_RAM = READ_WRITE 0x4000 TO 0x4FFF; MY_ROM = READ_ONLY 0x2000 TO 0x3FFF; PLACEMENT DEFAULT_ROM DEFAULT_RAM END STACKSIZE 0x600
INTO INTO
MY_ROM; MY_RAM;
354
355
unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned float float float float float float float float float float float float float float float float float float float float float float float float float float float float float float
char input1; int input2; int output1; int output2; int freqinputupperlimit; int freqinputlowerlimit; int analoginputupperlimit; int analoginputlowerlimit; int _8bitoutputupperlimit; int _8bitoutputlowerlimit; int PWMoutputupperlimit; int PWMoutputlowerlimit;
p10; p11; p12; p13; p14; p20; p21; p22; p23; p24; q10; q11; q12; q13; q14; q20; q21; q22; q23; q24; r10; r11; r12; r13; r14; r20; r21; r22; r23; r24;
float u1=0; float u1m1=0; float u1m2=0; float u1m3=0; float u1m4=0; int e1=0; int e1m1=0; int e1m2=0; int e1m3=0; int e1m4=0;
356
float u2=0; float u2m1=0; float u2m2=0; float u2m3=0; float u2m4=0; int e2=0; int e2m1=0; int e2m2=0; int e2m3=0; int e2m4=0; /* Main Program loop-control global variables*/ unsigned char sampletimeroverflows=0; unsigned int sampletime; unsigned char samplestarttime; unsigned char sampleendtime; unsigned char realsamplefreq=250; unsigned int realsampletime; unsigned char reqoverflows; unsigned int maxsamplefreq; /*Define the memory location for the 8-bit parallel output*/ #define MEMPORTB (*(volatile unsigned char*)(0xA102)) void main(void) { getparameters(); initsci(); inittimerinterrupt(); initic3(); delay(); initpwm(); initportb(); initadc(); asm cli; /* Enable the system interrupts */
/*************************************************************/ /* This is the starting point of the control algorithm in an */ /* infinite loop */ /*************************************************************/ while(1) { /* Save starttime of routine (the upper 8-bit value of TCNT) */ samplestarttime=TCNTUP; /* Determine the number of TCNTUP counts which correspond to a samplefrequency of maxsamplefreq*/ sampletime=(7813/maxsamplefreq) + samplestarttime; reqoverflows=sampletime/256; sampleendtime=sampletime%256; /* Start MIMO Controller calculations */
357
input1=adc(); asm SEI; /* disable interrupts */ input2=7813/period; asm CLI; /* enable interrupts */ if if if if (input1<analoginputlowerlimit) input1=analoginputlowerlimit; (input1>analoginputupperlimit) input1=analoginputupperlimit; (input2<freqinputlowerlimit) input2=freqinputlowerlimit; (input2>freqinputupperlimit) input2=freqinputupperlimit;
u1=(float) ( q10*e1 + q11*e1m1 + q12*e1m2 + q13*e1m3 + q14*e1m4 + r10*e2 + r11*e2m1 + r12*e2m2 + r13*e2m3 + r14*e2m4 - p11*u1m1 - p12*u1m2 - p13*u1m3 - p14*u1m4); u2=(float) ( q20*e1 + q21*e1m1 + q22*e1m2 + q23*e1m3 + q24*e1m4 + r20*e2 + r21*e2m1 + r22*e2m2 + r23*e2m3 + r24*e2m4 - p21*u2m1 - p22*u2m2 - p23*u2m3 - p24*u2m4); if (u1<(float)_8bitoutputlowerlimit) u1=(float)_8bitoutputlowerlimit; if (u1>(float)_8bitoutputupperlimit) u1=(float)_8bitoutputupperlimit; if (u2<(float)PWMoutputlowerlimit) u2=(float)PWMoutputlowerlimit; if (u2>(float)PWMoutputupperlimit) u2=(float)PWMoutputupperlimit; /* Save current controller input and output values for the next sample period */ u1m4=u1m3; u1m3=u1m2; u1m2=u1m1; u1m1=u1; e1m4=e1m3; e1m3=e1m2; e1m2=e1m1; e1m1=e1;
358
u2m4=u2m3; u2m3=u2m2; u2m2=u2m1; u2m1=u2; e2m4=e2m3; e2m3=e2m2; e2m2=e2m1; e2m1=e2; output1=u1; output2=u2; MEMPORTB=output1; asm SEI; /*disable interrupts*/ pwmtimeon=((float) output2/0x100)*pwmperiod; asm CLI; /*enable interrupts*/ /* Transmit the sample frequecy, input and output values to /* the serial port. putchar(251); putchar(realsamplefreq); putchar(252); putchar(input1); putchar(253); putchar(input2); putchar(254); putchar(output1); putchar(255); putchar(output2); /* Wait for the desired sample period to elapse */ while (sampletimeroverflows<reqoverflows); while (TCNTUP<sampleendtime); /* /* /* /* /* Calculate the actual time for the sample period. (This realsampletime will be higher than the desired sample time entered in the maxsamplefreq variable if the desired sample period cannot be met due to insufficient processing speed.) */ */ */ */ */ */ */
realsampletime=sampletimeroverflows*256+TCNTUP-samplestarttime; realsamplefreq=7813/realsampletime; /* Restrict the realsamplefreq between 1 and 250 */ if (realsamplefreq>250) realsamplefreq=250; if (realsamplefreq<1) realsamplefreq=1; sampletimeroverflows=0; } /*end while(1) loop*/ } /* Reset sampletimeroverflows */
359
/************************************************************/ /* Routine to get the downloaded parameters which was */ /* downloaded from the PC. The downloaded parameters are */ /* stored in memory locations 0x0100 - 0x01FF */ /* */ /* The Memory location of the parameters are as follow: */ /* */ /* analogsetpoint 0x0100 (0) */ /* freqsetpoint 0x0101 (1) */ /* p10 0x0102-0x0105 (2-5) */ /* p11 0x0106-0x0109 (6-9) */ /* p12 0x010A-0x010D (10-13) */ /* p13 0x010E-0x0111 (14-17) */ /* p14 0x0112-0x0115 (18-21) */ /* p20 0x0116-0x0119 (22-25) */ /* p21 0x011A-0x011D (26-29) */ /* p22 0x011E-0x0121 (30-33) */ /* p23 0x0122-0x0125 (34-37) */ /* p24 0x0126-0x0129 (38-41) */ /* q10 0x012A-0x012D (42-45) */ /* q11 0x012E-0x0131 (46-49) */ /* q12 0x0132-0x0135 (50-53) */ /* q13 0x0136-0x0139 (54-57) */ /* q14 0x013A-0x013D (58-61) */ /* q20 0x013E-0x0141 (62-65) */ /* q21 0x0142-0x0145 (66-69) */ /* q22 0x0146-0x0149 (70-73) */ /* q23 0x014A-0x014D (74-77) */ /* q24 0x014E-0x0151 (78-81) */ /* r10 0x0152-0x0155 (82-85) */ /* r11 0x0156-0x0159 (86-89) */ /* r12 0x015A-0x015D (90-93) */ /* r13 0x015E-0x0161 (94-97) */ /* r14 0x0162-0x0165 (98-101) */ /* r20 0x0166-0x0169 (102-105) */ /* r21 0x016A-0x016D (106-109) */ /* r22 0x016E-0x0171 (110-113) */ /* r23 0x0172-0x0175 (114-117) */ /* r24 0x0176-0x0179 (118-121) */ /* maxsamplefreq 0x017A (122) */ /* pwmhz 0x017B (123) */ /* analoginputfeedbackmode 0x017C (124) */ /* freqinputfeedbackmode 0x017D (125) */ /* analoginputupperlimit 0x017E (126) */ /* analoginputlowerlimit 0x017F (127) */ /* freqinputupperlimit 0x0180 (128) */ /* freqinputlowerlimit 0x0181 (129) */ /* _8bitoutputupperlimit 0x0182 (130) */ /* _8bitoutputlowerlimit 0x0183 (131) */ /* PWMoutputupperlimit 0x0184 (132) */ /* PWMoutputlowerlimit 0x0185 (133) */ /************************************************************/
360
void getparameters(void) { unsigned char pwmhz; (unsigned (unsigned (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (float *) (unsigned (unsigned (unsigned (unsigned (unsigned (unsigned (unsigned (unsigned (unsigned (unsigned (unsigned (unsigned char *) char *) 0x0102; 0x0106; 0x010A; 0x010E; 0x0112; 0x0116; 0x011A; 0x011E; 0x0122; 0x0126; 0x012A; 0x012E; 0x0132; 0x0136; 0x013A; 0x013E; 0x0142; 0x0146; 0x014A; 0x014E; 0x0152; 0x0156; 0x015A; 0x015E; 0x0162; 0x0166; 0x016A; 0x016E; 0x0172; 0x0176; char *) char *) char *) char *) char *) char *) char *) char *) char *) char *) char *) char *) 0x0100; 0x0101;
0x017A; 0x017B; 0x017C; 0x017D; 0x017E; 0x017F; 0x0180; 0x0181; 0x0182; 0x0183; 0x0184; 0x0185;
analogsetpoint= *(unsigned char *) 0x0100; freqsetpoint=*(unsigned char *) 0x0101; p10=*(float *) 0x0102; p11=*(float *) 0x0106; p12=*(float *) 0x010A;
361
p13=*(float *) 0x010E; p14=*(float *) 0x0112; p20=*(float *) 0x0116; p21=*(float *) 0x011A; p22=*(float *) 0x011E; p23=*(float *) 0x0122; p24=*(float *) 0x0126; q10=*(float *) 0x012A; q11=*(float *) 0x012E; q12=*(float *) 0x0132; q13=*(float *) 0x0136; q14=*(float *) 0x013A; q20=*(float *) 0x013E; q21=*(float *) 0x0142; q22=*(float *) 0x0146; q23=*(float *) 0x014A; q24=*(float *) 0x014E; r10=*(float *) 0x0152; r11=*(float *) 0x0156; r12=*(float *) 0x015A; r13=*(float *) 0x015E; r14=*(float *) 0x0162; r20=*(float *) 0x0166; r21=*(float *) 0x016A; r22=*(float *) 0x016E; r23=*(float *) 0x0172; r24=*(float *) 0x0176; maxsamplefreq=*(unsigned char *) 0x17A; pwmhz=*(unsigned char *) 0x017B; pwmperiod=2000000/pwmhz; analoginputfeedbackmode=*(unsigned char *) 0x017C; freqinputfeedbackmode=*(unsigned char *) 0x017D; analoginputupperlimit=*(unsigned char *) 0x017E; analoginputlowerlimit=*(unsigned char *) 0x017F; freqinputupperlimit=*(unsigned char *) 0x0180; freqinputlowerlimit=*(unsigned char *) 0x0181; _8bitoutputupperlimit=*(unsigned char *) 0x0182; _8bitoutputlowerlimit=*(unsigned char *) 0x0183; PWMoutputupperlimit=*(unsigned char *) 0x0184; PWMoutputlowerlimit=*(unsigned char *) 0x0185; }
/***************************************************/ /* TCNT counter overflow interrupt service routine */ /***************************************************/ void timeroverflow(void) { timeroverflows++; /* Timeroverflow counter for inputcapture sampletimeroverflows++; /* Counter for sample period regulation asm { PSHA /* Save the contents of accumulator A LDAA #0x80 /* Load accumulator A with the mask data
*/ */
*/ */
362
/* Reset the timer overflow flag (TOF) */ /* Retrieve the saved contents of accumulator A */ /* Return from Interrupt Service Routine */
/***************************************************/ /* Frequency Measurement Interrupt Service Routine */ /***************************************************/ void inputcapture(void) { /* Note: It is not neccesary to use the lower 8-bit of the counter function. Because the 68HC11 is an 8-bit microcontroller, processing speed will significantly increase by leaving the lower 8-bits of the counter out of the calculations. The 'period' variable, which represents all the TCNT counts, have to be a 32-bit long integer if this program uses all the 16 bits of the TCNT counter. It was found that the compiler used for this program does not compile calculations with 32-bit long integers correctly. The resolution of the upper 8-bits of the TCNT counter is 256*0.5 micro seconds = 0.000128 seconds which is still high enough to provide accurate timing. */ asm { PSHA LDAA #0x01 STAA 0x1023 PULA } /* Reset input flag IC3F by writing 1 to it */ /* /* /* /* Save the contents of accumulator A Load accumulator A with the mask data Reset TOF flag by writing 1 to it Retrieve the saved contents of accumulator A */ */ */ */
&& TIC3UP<=0x80)
Save the contents of accumulator A Load accumulator A with the mask data Reset IC3F flag by writing 1 to it Retrieve the saved contents of accumulator A
*/ */ */ */
overflow=(unsigned int) timeroverflows*0x100; /* Copy upper 8-bits of the TIC3 register into tic3upint */ tic3upint=(unsigned int)TIC3UP; /* The overflow variable is the number of the upper 8-bit counts of the TNCT counter that correspond to the amount of TNCT overflows recorded in the timeroverflows variable */
363
period=(unsigned int) (overflow+tic3upint-time1); /* The period variable is the total number of TCNT upper 8-bit counts between this interrupt service routine and the previous one.*/ time1=(unsigned int)TIC3UP; /* Save the endtime of this ISR */ timeroverflows=0; /* Reset the timeroverflows variable */ asm RTI; /* Return to main program */ } /**************************************/ /* SCI Character transmission routine */ /**************************************/ void putchar(char new_character) { char temp; temp=SCSR; temp=SCDR; while(SCSR==0); SCDR=new_character; } /************************************************/ /* Routine to initialize the microcontroller to */ /* generate a PWM signal on OC3 (PA5) */ /************************************************/ void initpwm(void) { /* Memory locations D9-DB is a 3 byte pseudo */ /* interrupt vector for TOC3(PA5) */ (char *) 0x00D9; /* Place jump instruction (op code 7E) in 00D9 */ *(char *) 0x00D9 = 0x7E; /* Place starting address of interrupt routine in DA&DB */ (int * ) 0x00DA; *(int *) 0x00DA = (int)oc3interrupt; OC1M.OC1M5=1; TMSK1.OC3I=1; OC1D.OC1D5=1; TCTL1.OL3=1; TOC1=TCNT+50000; TOC3=TOC1+1; } /* /* /* /* /* /* couple OC1 to OC3 */ enable the OC3 interrupt */ turn on OC3 when OC1 occurs */ toggle OC3 when OC3 occurs */ initialize OC1 */ initialize OC3 */
/*****************************************************/ /* OC3 Interrupt Service Routine. This routine will */ /* generate a PWM signal on OC3 (PA5) */ /*****************************************************/ void oc3interrupt(void) {
364
asm { PSHA /* Save the contents of accumulator A LDAA #0x20 /* Load accumulator A with the mask data STAA 0x1023 /* Reset OC3F flag by writing 1 to it PULA /* Retrieve the saved contents of accumulator A } toc1=TOC1+pwmperiod; TOC1=toc1; toc3=toc1+pwmtimeon; TOC3=toc3; asm RTI /* Return from Interrupt Service Routine */ }
*/ */ */ */
/************************************************/ /* Routine to initialize the microcontroller to */ /* send and receive serial data */ /************************************************/ void initsci(void) { BAUD=0x30; /* Set serial communication to 9600 baud rate */ SCCR2=0xC; /* TE and RE high=Serial Transmit and Receive Enabled */ } /***************************************************/ /* Routine to initialize the input capture 3 (IC3) */ /* interrupt service routine. */ /***************************************************/ void initic3(void) { /* Set input capture in TIC3(PA0) on rising edges only */ /* (For falling edges, TCTL2=0x02) */ TCTL2=0x01; TMSK1.bit0=1; /* Enable the interrupt mask for IC3 (PA0) */ asm { PSHA LDAA #0x01 STAA 0x1023 PULA }
/* /* /* /*
Save the contents of accumulator A Load accumulator A with the mask data Reset input flag IC3F by writing 1 to it Retrieve the saved contents of accumulator A
*/ */ */ */
/* Memory locations E2-E4 is a 3 byte */ /* pseudo interrupt vector for IC3(PA0) */ /* Place jump instruction (op code 7E) in 00E2 */ (char *) 0x00E2; *(char *) 0x00E2 = 0x7E; /* Place starting address of interrupt routine in E3&E4 */ (int * ) 0x00E3; *(int *) 0x00E3 = (int)inputcapture; }
365
/*******************************************************/ /* Routine to initialize the timer overflow interrupt */ /*******************************************************/ void inittimerinterrupt(void) { TMSK2.bit7=1; /* Enable the timer overflow interrupt */ asm { PSHA LDAA #0x80 STAA 0x1025 PULA }
/* /* /* /*
Save the contents of accumulator A Load accumulator A with the mask data Reset TOF Flag by writing 1 to it Retrieve the saved contents of accumulator A
*/ */ */ */
/* Memory locations D0-D2 is a 3 byte pseudo interrupt vector for the timer overflow */ (char *) 0x00D0; /* Place jump instruction (op code 7E) in 00D0 */ *(char *) 0x00D0=0x7E; (int * ) 0x00D1; /* Place starting address of interrupt routine in D1&D2 */ *(int *) 0x00D1 = (int)timeroverflow; } /*****************************************************************/ /* It was found that the first few characters received by the PC */ /* is corrupt.This function will transmit the first few */ /* non-significant characters to the PC before starting the */ /* control algorithm */ /*****************************************************************/ void delay(void) { unsigned int temp; for (temp=0;temp<100;temp++) { putchar(251); putchar(maxsamplefreq); putchar(252); putchar(1); putchar(253); putchar(1); } } /***************************************/ /* Function to initialize the */ /* analog-to-digital converter system */ /***************************************/ void initadc(void) {
366
OPTION=0x80;/*Set bit 7 in OPTION high for A/D conversion*/ } /****************************************/ /* Analog to Digital conversion routine */ /****************************************/ unsigned char adc(void) { unsigned char temp; ADCTL=0x5; /* Set analog inputs on PE5 (pin 46) */ while (ADCTL<128); /* Wait for input */ temp=ADR1; return temp; } /******************************************************************/ /* Initialaise Port B on the MC6821 on the Memory extension board */ /* to serve as an 8-bit output port at memory address 0xA102 */ /******************************************************************/ void initportb(void) { (unsigned char *) 0xA102; (unsigned char *) 0xA103; *(unsigned char *) 0xA103=0x00; *(unsigned char *) 0xA102=0xFF; *(unsigned char *) 0xA103=0x04; }
367
Multi.prm - Linker parameter file for the MIMO Digial Controller Module
LINK Multi.abs NAMES END Multi.o start11.o ansi.lib
SECTIONS MY_RAM = READ_WRITE 0x4000 TO 0x4FFF; MY_ROM = READ_ONLY 0x2000 TO 0x3FFF; PLACEMENT DEFAULT_ROM DEFAULT_RAM END STACKSIZE 0x9FF
INTO INTO
MY_ROM; MY_RAM;
368