258 CONTROLLER REALIZATION
/********************************************************************
DIGITAL CONTROLLER
==================
This program implements a second-order digital controller module on a PIC16F877 (or equivalent) microcontroller. The microcontroller operates with a 4MHz crystal. The analog input AN0 of the microcontroller is connected to the output sensor of the plant (y). The PORT B output of the microcontroller is connected to a AD7302 type D/A converter. The WR input of the controller is controlled from port pin RC0 of the microcontroller. The sampling interval is 0.01s (10ms) and the timer interrupt service routine is used to obtain the required sampling interval.
Program : Second Order Module.C
Date : July 2005
********************************************************************/
#include <pic.h> #define DA Write RC0
float DA LSB,AD LSB,a0,a1,a2, b1,b2,M1,M2,rk,rk 1,rk 2,ek,sk,yk,uk;
/* This function initializes the A/D converter so that analog data can be received from channel AN0 of the microcontroller */
void Initialize AD(void)
{
}
/* This function initilizes the timer TMR0 so that interrupts can be generated at 10ms intervals */
void Initialize Timer(void)
{
}
/* This function reads data from the A/D converter and stores in variable yk */
void Read AD Input(void)
{
}
/* Interrupt Service Routine. The program jumps here every 10 ms */
void interrupt ISR(void) |
|
{ |
|
Read AD Input(); |
/* Read A/D input */ |
ek = sk - yk; |
/* Calculate error term */ |
rk = ek + M1; |
|
yk = a0*rk + M2; |
/* Calculate output */ |
uk = yk*DA LSB; |
|
PORTB = uk; |
/* Send to PORT B */ |
DA Write = 0; |
/* Write to D/A converter */ |
DA Write = 1; |
|
Figure 10.19 First program attempt (Continued )
|
|
|
MICROCONTROLLER IMPLEMENTATIONS |
259 |
rk 2 |
= rk 1; |
/* Update variables */ |
|
rk 1 |
= rk; |
|
|
M1 |
= -b1*rk 1 - b2*rk 2; |
|
|
M2 |
= a1*rk 1 + a2*rk 2; |
|
|
T0IF = 0; |
/* Re-enable timer interrupts */ |
|
}
/* Main Program. The main program initializes the variables, A/D converter, D/A converter etc. and then waits in an endless loop for timer interrupts to Occur every 10 ms */
main(void)
{
a0 |
= 1; a1 = 0.8; |
a2 = 1.2; |
|
b1 |
= 1.85; |
|
b2 = 0.92; |
|
M1 |
= 0; |
M2 = 0; |
rk = 0; |
rk 1 = 0; rk 2 = 0; |
sk = 1.0; |
|
|
|
DA LSB = 5000.0/1024.0; |
|
|
TRISA = 1; |
|
/* RA0 (AN0) is input */ |
TRISB = 0; |
|
/* PORT B is output */ |
TRISC = 0; |
|
/* RC0 is output */ |
DA Write = 1; |
|
/* Disable D/A converter */ |
Initialize AD(); |
/* Initialize A/D converter */ |
Initialize Timer(); |
/* Initialize timer interrupts */ |
ei(); |
|
/* Enable interrupts */ |
for(;;); |
|
/* Wait for an interrupt */ |
}
Figure 10.19 (Continued )
Initialize AD. This function initializes the A/D converter so that analog data can be received from channel AN0 of the microcontroller. As described in Chapter 3, the A/D converter is initialized by first selecting the reference voltage and the output data format using the ADCON1 register, and then selecting the A/D converter clock using the ADCON0 register. Thus, channel AN0 is configured by loading 0x8E into the ADCON1 register, and 0x41 into the ADCON0 register. Thus, the Initialize AD function is programmed as follows:
/* This function initializes the A/D converter so that analog data can be received from channel AN0 of the microcontroller */
void Initialize AD(void)
{
ADCON1 |
= |
0 |
× |
8E; |
/* Configure |
AN0 for +5V reference */ |
ADCON0 |
= |
0 |
× |
41; |
/* Select A/D |
converter clock */ |
}
Read AD Input. This function starts the A/D converter and reads a sample at the end of the conversion. The conversion is started by setting the GO/DONE bit of ADCON0. The program
260 CONTROLLER REALIZATION
should then wait until the conversion is complete, which is indicated by the GO/DONE bit becoming zero. The high 2 bits of the converted data are in ADRESH, and the low 8 bits are in register ADRESL. The 10-bit converted data is extracted and stored in variable yk .
The Read AD Input function is programmed as follows:
/* This function reads data from the A/D converter and stores in variable yk */
void Read AD Input(void)
{
ADCON0 = 0 × 45; while(ADCON0 & 4) != 0); y high = ADRESH;
y low = ADRESL;
yk = 256.0*y high + y low; yk = yk*AD LSB;
}
/* Start A/D conversion */
/* Wait until conversion completes */ /* High 2 bytes of converted data */ /* Low byte of converted data */
/* Converted data in yk */ /* Sensor output in mV */
AD LSB converts the A/D value to into millivolts.
The complete program is shown in Figure 10.20.
10.5.2 Implementing First-Order Modules
In Section 10.2 we saw how a first-order module can be realized using adders, multipliers and delay elements. The first-order module is shown in Figure 10.21. The difference equations describing a first-order module were found to be
rk = ek − b1rk−1
and
uk = a0rk + a1rk−1.
If we let
M1 = −b1rk−1
and
M2 = a1rk−1,
then the difference equations for the first-order module becomes
rk = ek + M1
uk = a0rk + M2
The implementation of the first-order module is similar to the second-order module and an example is given below.
MICROCONTROLLER IMPLEMENTATIONS |
261 |
/****************************************************************************
DIGITAL CONTROLLER
==================
This program implements a second-order digital controller module on a PIC16F877 (or equivalent) microcontroller. The microcontroller operates with a 4MHz crystal. The analog input AN0 of the microcontroller is connected to the output sensor of the plant (y). The PORT B output of the microcontroller is connected to an AD7302 type D/A converter. The WR input of the controller is controlled from port pin RC0 of the microcontroller. The sampling interval is 0.01s (10ms) and the timer interrupt service routine is used to obtain the required sampling interval.
Program : Second Order Module.C
Date : July 2005
*****************************************************************************/
#include <pic.h> #define DA Write RC0
float DA LSB,AD LSB,a0,a1,a2, b1,b2,M1,M2,rk,rk 1,rk 2,ek,sk,yk,uk; float y high,y low;
/* This function initializes the A/D converter so that analog data can be received from channel AN0 of the microcontroller */
void Initialize AD(void)
{
ADCON1 |
= |
0x8E; |
/* |
Configure AN0 for +5V reference |
*/ |
ADCON0 |
= |
0x41; |
/* |
Select A/D clock and channel */ |
|
}
/* This function initilizes the timer TMR0 so that interrupts can be generated at 10ms intervals */
void Initialize Timer(void)
{
T0CS = 0; |
/* Select f/4 clock for |
TMR0 */ |
PSA = 0; |
/* Select pre-scaler */ |
|
PS0 = 1; |
/* Set pre-scaler to 64 |
*/ |
PS1 = 0; |
/* PS2,PS1,PS0 = 101 */ |
|
PS2 = 1; |
|
|
TMR0 = 100; |
/* Load TMR0=100 */ |
|
T0IE = 1; |
/* Enable TMR0 interrupts */ |
T0IF = 0; |
/* Clear TMR0 interrupt flag */ |
*/
}
/* This function reads data from the A/D converter and stores in variable yk */
void Read AD Input(void)
{
ADCON0 = 0x45; |
/* |
Start A/D conversion */ |
While((ADCON0 & 4) != 0); |
/* |
Wait until conversion completes */ |
Figure 10.20 Complete program of the second-order controller (Continued )
262 |
CONTROLLER REALIZATION |
|
|
y high = ADRESH; |
/* High 2 bits of converted data */ |
|
y low = ADRESL; |
/* Low byte of converted data */ |
|
yk = 256.0*y high + y low; |
/* Converted data in yk */ |
|
yk = yk*AD LSB; |
/* Sensor output in mV */ |
} |
|
|
/* Interrupt Service Routine. The program jumps here every 10 ms */ |
void interrupt ISR(void) |
|
{ |
|
|
|
TMR0 = 100; |
/* Reload TMR0 */ |
|
Read AD Input(); |
/* Read A/D input */ |
|
ek = sk - yk; |
/* Calculate error term */ |
|
rk = ek + M1; |
|
|
yk = a0*rk + M2; |
/* Calculate output */ |
|
uk = yk*DA LSB; |
|
|
PORTB = uk; |
/* Send to PORT B */ |
|
DA Write = 0; |
/* Write to D/A converter */ |
|
DA Write = 1; |
|
|
rk 2 = rk 1; |
/* Update variables */ |
|
rk 1 = rk; |
|
|
M1 = -b1*rk 1 - b2*rk 2; |
|
|
M2 = a1*rk 1 + a2*rk 2; |
|
|
T0IF = 0; |
/* Re-enable timer interrupts */ |
} |
|
|
/* Main Program. The main program initializes the variables, A/D converter, D/A converter etc. and then waits in an endless loop for timer interrupts to Occur every 10 ms */
main(void)
{
a0 |
= 1; |
a1 = 0.8; |
a2 = 1.2; |
|
b1 |
= 1.85; |
b2 = 0.92; |
|
|
M1 |
= 0; |
M2 = 0; |
rk = 0; |
rk 1 = 0; rk 2 = 0; |
sk = 1.0; |
|
|
|
AD LSB = 5000.0/1024.0; |
|
|
DA LSB = 256.0/5000.0; |
|
|
TRISA = 1; |
|
/* RA0 (AN0) is input */ |
TRISB = 0; |
|
/* PORT B is output */ |
TRISC = 0; |
|
/* RC0 is output */ |
DA Write = 1; |
|
/* Disable D/A converter */ |
Initialize AD(); |
/* Initialize A/D converter */ |
Initialize Timer(); |
/* Initialize timer interrupts */ |
ei(); |
|
/* Enable interrupts */ |
for(;;); |
|
/* Wait for an interrupt */ |
}
Figure 10.20 (Continued )