where total_pulse_count is in x-msec.
As suggested by prof. Ning, mechanical devices like DC motor is not fast enough to adjust the speed in
very small time frame like 100 msec. Thus, we decided to record the pulses after 500 ms or second. A
small counter was used to count the 100 ms delay set by TIMER 3 overflow interrupt and after such 5
interrupt, total_pulses_counts were recorded using the above equation.
However, after trying multiple combinations of TIMER-3 overflow delay, we were not getting the result
expected. Then we decided to measure the frequency of the pulses recorded and we noticed the counter in
the TIMER-3 overflow interrupt subroutine was not going through. After going through the manual, we
got to know that in TMR3CN (Timer 3 control) register, bit TF3H and bit TF3L are not automatically
cleared by the hardware (fig. 15). Therefore, when first TIMER-3 overflow happened, the timer was not
going back to reset and was not running again. In order to do so, we had to clear TF3H and TF3L to start
the timer again. Hence, the TIMER-3 was enabled after each overflow interrupt.
Fig. 15: TMR3CN: Timer 3 Control
After enabling TIMER-3 again in TIMER-3 overflow interrupt subroutine, we were getting the results
expected. However, though we were getting better result, the values were still fluctuating so much. Thus,
we decided to record the pulses after 1000ms or 1 second. Therefore, after 10 TIMER-2 overflow
interrupts (each with 100ms), total_pulses_counts were recorded and used to find current speed of motor
in rpm as shown in following equation. Fig. 16 gives details about TIMER-3 overflow interrupt
subroutine.
----------------------------------------------------------
Timer2_Overflow_ISR
The Routine will count the number of pulses. The number
of pulses comes from TL0 and TH0 because Timer 0 was set to
record pulses
----------------------------------------------------------
*/
void Timer3_Overflow_ISR(void) interrupt 13 //Interrupts every 100msec (auto reload)
{
TMR3CN = 0x04; //Timer-3 is enabled again
ISRT3_count++;
if (ISRT3_count == 10) //refreshes actual_rpm every 100*10 = 1000 msec
{
pulse_count = TH0*256 + TL0; // the number of pulses
TH0 = 0x00; //Timer 0 high and low bytes reset
TL0 = 0x00;
//Routine convert pulses into RPM here
//rpm = pulse_count(1/1000msec)(1000msec/1sec)(60sec/1min)(1turn/80counts)
actual_rpm = pulse_count * 0.75;
ISRT3_count = 0;
target_rpm = ADC0;
FEEDBACK_Init();
}}
Fig. 16: Timer -3 Overflow Interrupt Service Routine
After configuring the LCD display, PWM generation using Programmable Counter Array (PCA), desired
speed target using A/D converter and calculating the actual rpm, everything was placed together to check
the whole embedded system. We were able to change the desired target speed and thus, the actual speed
of the motor. The change in the target speed and actual speed was successfully displaced on the LCD
display. However, there was no proper correlation between the desired target speed and actual speed. In
order to adjust the PWM signal to achieve a desired target speed a feedback controller was necessary.
Thus, feedback control was designed using the difference between the desired target speed and the actual
speed of the motor. Thus, when the desired target speed was different than the actual speed, duty cycle of
the PWM signal was adjusted accordingly by the controller to maintain the desired speed. When target
speed was higher than actual speed of the motor, duty cycle of PWM signal was increased accordingly by
decreasing the value of PCA0CPH0 in programmable counter array (PCA) and vice versa.
/*--------------------------------------------------------
Feedback
--------------------------------------------------------*/
void FEEDBACK_Init(void)
{ rpm_change = target_rpm - actual_rpm
if(rpm_change > 0 && PCA0CPH0 > 0x05)
{PCA0CPH0 = PCA0CPH0 - 0x01;}
else if(rpm_change < 0 && PCA0CPH0<0xFF)
{PCA0CPH0 = PCA0CPH0 + 0x01;}
Else {PCA0CPH0 = PCA0CPH0;}
}
Fig. 17: Initial approach to control the
Initially we wrote a simple program (fig. 17), which will increase duty cycle if the difference between
desired target speed and actual speed is higher than 0 and will decrease the duty cycle if difference
between desired target speed and actual speed is less than zero given that the duty cycle is between 0%
and 100%. However, the feedback was not responsive enough and it took so long for the actual speed to
adjust accordingly to the target speed. Furthermore, when we tried to change the incrementing value of
PCA capture module 0 high byte (PCA0CPH0), the actual speed started fluctuating. Thus, we decided to
keep checking the difference between target rpm and actual rpm constantly and then change the value of
PCA0CPH0 accordingly. The difference between target rpm and actual rpm was being checked from 0 to
1200 and appropriate change in PCA0CPH0 was placed. After employing the change in the feedback
control loop, our motor started adjusting the actual speed very quickly according the desired target speed.
In addition, by removing the load or increasing the load, the duty cycle was quickly being automatically
adjusted accordingly by the controller to maintain the desired speed. Furthermore, the actual speed was
not fluctuating and will stay within 15-20 rpm of target speed. This was also verified from the stable
actual speed in rpm and PWM signal from the oscilloscope. The final working code for the feedback
control is given in fig. 18.
/*--------------------------------------------------------
Feedback Control
--------------------------------------------------------*/
void FEEDBACK_Init(void)
{rpm_change = target_rpm - actual_rpm;
//If desired target speed is higher than actual speed of motor if duty cycle is less than 100%
if((rpm_change>0) && (PCA0CPH0> 0x00))
{
if((rpm_change>=1200))
{ PCA0CPH0 = PCA0CPH0 - 0x05;}
else if((rpm_change>=1000) && (rpm_change<=1200))
{ PCA0CPH0 = PCA0CPH0 - 0x05;}
else if((rpm_change>=800) && (rpm_change<=1000))
{ PCA0CPH0 = PCA0CPH0 - 0x04;}
else if((rpm_change>=500) && (rpm_change<=800))
{ PCA0CPH0 = PCA0CPH0 - 0x03;}
else if((rpm_change>=300) && (rpm_change<=500))
{ PCA0CPH0 = PCA0CPH0 - 0x02;}
else if((rpm_change>=100) && (rpm_change<=300))
{ PCA0CPH0 = PCA0CPH0 - 0x01;}
else if((rpm_change>=50) && (rpm_change<=100))
{ PCA0CPH0 = PCA0CPH0 - 0x01;}
else if((rpm_change>10) && (rpm_change<=50))
{ PCA0CPH0 = PCA0CPH0 - 0x01;}
else if((rpm_change>0) && (rpm_change<=10))
{ PCA0CPH0 = PCA0CPH0;}
else {PCA0CPH0 = PCA0CPH0;}
}
//If desired target speed is lower than actual speed of motor if duty cycle is higher than 0%
else if((rpm_change<0) && (PCA0CPH0< 0xFF))
{
if((rpm_change<=-1200))
{ PCA0CPH0 = PCA0CPH0 + 0x05;}
else if((rpm_change<=-1000) && (rpm_change>=-1200))
{ PCA0CPH0 = PCA0CPH0 + 0x05;}
else if((rpm_change<=-800) && (rpm_change>=-1000))
{ PCA0CPH0 = PCA0CPH0 + 0x04;}
else if((rpm_change<=-500) && (rpm_change>=-800))
{ PCA0CPH0 = PCA0CPH0 + 0x03;}
else if((rpm_change<=-300) && (rpm_change>=-500))
{ PCA0CPH0 = PCA0CPH0 + 0x02;}
else if((rpm_change<=-100) && (rpm_change>=-300))
{ PCA0CPH0 = PCA0CPH0 + 0x01;}
else if((rpm_change<=-50) && (rpm_change>=-100))
{ PCA0CPH0 = PCA0CPH0 + 0x01;}
else if((rpm_change<=-10) && (rpm_change>=-50))
{ PCA0CPH0 = PCA0CPH0 + 0x01;}
else if((rpm_change<=0) && (rpm_change>=-10))
{ PCA0CPH0 = PCA0CPH0;}
else {PCA0CPH0 = PCA0CPH0;}
}
else {PCA0CPH0 = PCA0CPH0;}
}
Fig. 18: Feedback control loop to adjust actual speed according to desired target speed
Implementation
As mentioned already, there were many components that were to be employed for the hardware as well as
software configuration. Thus, while configuring the whole system, each part was configured in small step
and next part was approached only after making sure that previous part was working perfectly. Initially,
given LCD display was properly wired using the wiring diagram given in fig.7. It was a good advantage
for us that an almost working code for the LCD was provided. Modifying the code a bit, we were able to
make our LCD display desired output. While I was working on the programming, Bicky finished building
the circuitry consisting of H-bridge, DC motor with optical encoder, comparator, inverter and op-amp.
With the LCD screen working with one set of code, we referenced the PWM example code given in the
lab handout. This helped us to set up PCA and to generate varying duty cycles of the PWM. Before we
hooked up the H-Bridge, we connected the PCA directly to the Port 0.2 (CEX0), which was configured
using the configuration wizard. In order to obtain a stable PWN signal, we needed to measure the actual
speed of the DC motor. Thus, timer 0 was configured as 16-bit counter while timer-3 overflow interrupt
was used for count the pulses measured by counter 0. However, the pulses we obtained from the PWM
were not being counted by Timer 0. To increase the current of the PWM pulses, we passed the output of
the comparator through a buffer. This solved the problem and enabled the timer to count the pulses.
Working with the PWM code, we then passed the PWM to the input of the H-Bridge to power the motor.
When we connected the pulses from the motor to the oscilloscope, we were able to get expected signal.
The desired rpm speed was then set using the Analog to Digital Converter with help of example code
given in the SILABs documentation. We used GND as reference voltage for VREF (P0.0). Using the
value of total-pulses obtained using counter 0 and TIMER-3 overflow interrupt, actual rotating spped was
found. Using actual and target speed, feedback control loop was finally employed to adjust actual speed
of the motor accordingly to the desired target speed. Multiple tests were performed to design the best
possible feedback control system.
When we thought we were almost done one week ago, we decided to check the progress with professor.
Then we realized that the H-bridge works best with the oscillator frequency that leads to the PWM signal
frequency of 1KHz. Hence, we had to change our clock from 0.75 MHz to 3 Mhz. This change brought us
back to the same place where we started. Nothing including LCD display, actual-rpm courting or
feedback-control was working. Three-four days were spent without any significant progress. However,
things started to be on track after we figured out the timing conflict with the delays in the timer overflow
interrupt due to the change in oscillator. After multiple tests with different values of almost every single
variable, the DC motor speed control embedded system was finalized.
Results and Discussion
As per the design goal of the lab, we were able to construct DC-motor based embedded system, where
PID feedback controller was designed that would allow the user to enter the desired target speed and
timely adjustment to the PWM would be made to accommodate the changing shaft-load to maintain the
desired rotating speed. As mentioned above in the fig.17, while demonstrating the lab to professor, the
feedback control was designed to slowly adjust the actual rotating speed of the motor according to desired
target speed. However, after the professors concern, we implemented the feedback using the code given
in fig.18, which was able to make quick adjustment in the actual rotating speed according to the target
speed when load was applied.
This lab opened the doors for what we can do with the 8051-based microcontrollers. While learning about
the Automatic Control System (ACS), I never thought that I could even use it literally. However, this lab
demonstrated practical ways of implementing closed-loop feedback systems. Furthermore, this lab also
helped a lot to improve our command on C/C++ language.
//____________________________________________________________________
//LAB 4: DC motor control using PWM and PID Controller
//Vishal Bharam
//Bicky Shakya
//Engr 323: MicroProcessor Systems
//Professor: Dr. Taikang Ning
//____________________________________________________________________
/*
This program is written to control the speed of the DC motor based on the feedback
from the C8051F500 microcontroller. First, the ADC converts the signal from the
potentiometer. Due to various reasons, the desired rpm speed and the current rpm speed
may not be equal. Therefore the program counts the number of pulses within a certain
time period.Then the current rpm speed of the DC motor is calculated. The program takes the
difference between the two values and adjusts the duty cycle of the PWM signal accordingly.
The current rpm speed as wells as the desired rpm speed will be displayed on the LCD.
*/
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include "compiler_defs.h"
#include "C8051F500_defs.h"
#include <stdio.h>
//-----------------------------------------------------------------------------
// Function Prototypes
//-----------------------------------------------------------------------------
void Oscillator_Init(void);
void Port_Init(void);
void Timer_Init(void);
void LCD_Init_ISR(void);
void Display_String(void);
void ADC_Init(void);
void PCA_Init(void);
void TIMER2_Init(void);
void DISPLAY_ISR(void);
int LCD_busy_flag (void);
void FEEDBACK_Init(void);
void AD0_ISR(void);
void Timer3_Overflow_ISR(void);
//-----------------------------------------------------------------------------
// Global Constants
//-----------------------------------------------------------------------------
#define SYSCLK 3000000
sbit LED = P1^6;
sbit RS = P3^0;
sbit RW = P3^1;
sbit LCD_en = P3^2;
sbit DB7 = P2^7;
//-----------------------------------------------------------------------------
// Global Variables
//-----------------------------------------------------------------------------
char xdata LCD_display[32]; //hold the data to be shown on an LCD
unsigned char LCD_pointer = 0;
char indicator;
char LCD_init_flag = 0;
short msec_count = 0; //each delay is about 1 msec
short RTH0, RTL0, RTH1, RTL1; //reload values for Timer_0 and Timer_1
short i, k;
short target_rpm = 500;
short actual_rpm = 0;
short pulse_count = 0;
short count = 0;
short ISRT3_count = 0;
short rpm_change;
//-----------------------------------------------------------------------------
// main() Routine
//-----------------------------------------------------------------------------
void main(void) //calling functions here
{
PCA0MD &= ~0x40; // Disable the watchdog timer
LCD_Init_ISR();
Oscillator_Init();
Port_Init();
Timer_Init();
PCA_Init();
ADC_Init();
TIMER2_Init();
DISPLAY_ISR();
FEEDBACK_Init();
Display_String();
IP= 0x2A; //timer 1,2,3 prioritized
while(1);
}
//-----------------------------------------------------------------------------
// Initialization Subroutines
//-----------------------------------------------------------------------------
/*--------------------------------
Oscillator_Init
This routine initializes the system clock to use
the precision internal oscillator as its clock
source
---------------------------------------------------
*/
void Oscillator_Init(void)
{
SFRPAGE = CONFIG_PAGE;
OSCICN = 0xC4; //Select the internal oscillator as system clock 12.25 MHz
CLKSEL = 0x00; //Select the internal oscillator as SYSCLK source
SFRPAGE = ACTIVE_PAGE;
}
/*-------------------------------------------------------
PORT_Init()
-------------------------------------------------------*/
void Port_Init(void)
{
char SFRPAGE_SAVE = SFRPAGE;
SFRPAGE = CONFIG_PAGE;
P0SKIP |= 0x03; // Skip P0.0 (VREF)
P0MDIN = 0xFC;
P0MDOUT = 0x0C; // Set External Timer 0 (P0.0) to push-pull
XBR1= 0x44; //CEX0 at port0.2, timer0 pin at p0.1
P1SKIP |= 0x04; // Skip P1.2 (ADC input)
P1MDIN &= ~0x04; // Set P1.2 as an analog input
P1MDOUT |= 0x08; //P1.6(LED)
P3MDOUT = 0x07; //LCD-en(P6.2) RS(P6.0) and RW(P6.1) are set
P2MDOUT = 0xFF; //DB0-DB7 are set push-pull
XBR2 = 0x40; //Enable Crossbar
SFRPAGE = SFRPAGE_SAVE;
}
//-----------------------------------------------------------------------------
// TIMER2_Init
//-----------------------------------------------------------------------------
//
// Return Value: None
// Parameters: None
//
// Configure Timer2 to 16-bit auto-reload and generate an interrupt at 100uS
// intervals. Timer 2 overflow automatically triggers ADC0 conversion.
//
//-----------------------------------------------------------------------------
void TIMER2_Init (void)
{
U8 SFRPAGE_save = SFRPAGE;
SFRPAGE = ACTIVE_PAGE;
TMR2CN = 0x00; // Stop Timer2; Clear TF2;
// use SYSCLK as timebase, 16-bit auto-reload
TMR2RL = 65535 - (SYSCLK / 10000); // Init reload value for 10uS
TMR2 = 0xFFFF; // Set to reload immediately
TR2 = 1; // Start Timer2
SFRPAGE = SFRPAGE_save;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// ADC_Init
//-----------------------------------------------------------------------------
//
// Return Value: None
// Parameters: None
//
// Configures ADC0 to make single-ended analog measurements on pin P1.2
// Also enabled the gain and the voltage reference
//
//-----------------------------------------------------------------------------
void ADC_Init()
{
U8 SFRPAGE_save = SFRPAGE;
SFRPAGE = ACTIVE_PAGE;
// Initialize the Gain to account for a 5V input and 2.25 VREF
// Solve the equation provided in Section 9.3.1 of the Datasheet
// The 5V input is scaled by a factor of 0.44 so that the maximum input
// voltage seen by the pin is 2.2V
// 0.44 = (GAIN/4096) + GAINADD * (1/64)
// Set GAIN to 0x6CA and GAINADD to 1
// GAIN = is the 12-bit word formed by ADC0GNH[7:0] ADC0GNL[7:4]
// GAINADD is bit ADC0GNA.0
ADC0CF |= 0x01; // Set GAINEN = 1
ADC0H = 0x04; // Load the ADC0GNH address
ADC0L = 0x6C; // Load the upper byte of 0x6CA to
// ADC0GNH
ADC0H = 0x07; // Load the ADC0GNL address
ADC0L = 0xA0; // Load the lower nibble of 0x6CA to
// ADC0GNL
ADC0H = 0x08; // Load the ADC0GNA address
ADC0L = 0x01; // Set the GAINADD bit
ADC0CF &= ~0x01; // Set GAINEN = 0
ADC0CN = 0x03; // ADC0 disabled, normal tracking,
// conversion triggered on TMR2 overflow
// Output is right-justified
REF0CN = 0x33; // Enable on-chip VREF and buffer
// Set voltage reference to GND
ADC0MX = 0x0A; // Set ADC input to P1.2
ADC0CF = ((SYSCLK / 3000000) - 1) << 3; // Set SAR clock to 3MHz
EIE1 |= 0x04; // Enable ADC0 conversion complete int.
AD0EN = 1; // Enable ADC0
SFRPAGE = SFRPAGE_save;
}
/*----------------------------------------------------
Timer_Init();
set Timer_0 and Timer_1 as 16 bit
-----------------------------------------------------
*/
void Timer_Init(void)
{
char SFRPAGE_SAVE = SFRPAGE;
SFRPAGE = ACTIVE_PAGE;
TMOD = 0x11; //Timer_0 and Timer_1 in 16-bit mode
RTL0 = 0x17; //1000 cycles for about 1 msec
RTH0 = 0xFC;
TR0 = 1;
ET0 = 1;
RTL1 = 0x77; //refresh rate of 100Hz (10 msec)
RTH1 = 0xF8;
TR1 = 1;
ET1 = 1;
EA = 1; //EA and TF0,TF1, and EA are enabled
SFRPAGE = SFRPAGE_SAVE;
}
/*------------------------------------------------------
PWM COUNTER
------------------------------------------------------*/
void PCA_Init(void)
{
PCA0CN = 0x40;
PCA0CPM0 = 0x42;
PCA0CPH0 = 0x80;
}
/* --------------------------------------------------
LCD_Init_ISR
setup Timer_0 overflow interrupt service routine
to perform LCD initialization Function
-------------------------------------------------*/
void LCD_Init_ISR(void) interrupt 1
{
if(LCD_init_flag == 0)
{
EA = 0;
TH0 = RTH0;
TL0 = RTL0;
EA = 1;
msec_count++;
switch(msec_count)
{
case 5:case 10:case 11:case 12:
//function set four times
LCD_en = 1;
RS = 0;
RW = 0;
P2 = 0x3F;
// pulse enable
LCD_en = 0;
break;
case 13:
//send display off
LCD_en = 1;
RS = 0;
RW = 0;
P2 = 0x08;
//pulse enable
LCD_en = 0;
break;
case 14:
//send display clear
LCD_en = 1;
RS = 0;
P2 = 0x01;
//pulse enable
LCD_en = 0;
break;
case 16:
//send entry mode set
LCD_en = 1;
RS = 0;
RW = 0;
P2 = 0x06;
//pulse enable
LCD_en = 0;
break;
case 18:
//send display ON
LCD_en = 1;
RS = 0;
RW = 0;
P2 = 0x0F;
//pulse enable
LCD_en = 0;
//set the LCD_init_flag
LCD_init_flag = 1;
break;
default:
break;
} //end of switch-case
} //end of if
else if(LCD_init_flag == 1)
{
EA = 0;
TMR3CN = 0x04; //Start running Timer 2
TMOD = 0x15; //Timer_0 set as event counter and Timer_1 still in 16-bit mode
TH0 = 0x00; //Timer 0 high and low bytes set
TL0 = 0x00;
ET0 = 0; //Disable Timer 0 interrupt, Timer 0 will now be used to count pulses and Timer 2
records
EIE1 |= 0x40; //Timer 2 interrupt enabled
TMR3L = 0x57; //Timer 2 high and low bytes set to record pulse count every 5 msec
TMR3H = 0x9E;
TMR3RLL = 0x57; //Timer 2 auto reload values set
TMR3RLH = 0x9E;
EA = 1;
}
// SFRPAGE = SFRPAGE_SAVE;
} //LCD_Init()
/* ---------------------------------------------------
LCD Display Refresh Function
---------------------------------------------------
*/
void DISPLAY_ISR(void) interrupt 3
{
//EA = 0;
TL1 = RTL1; //Refresh rate of 50Hz
TH1 = RTH1; //20-msec interval
Display_String();
//The program will not refresh the LCD until the intialization is complete
if((LCD_init_flag == 1) && (LCD_busy_flag() == 0))
{
switch(LCD_pointer)
{
case 16:
LCD_en = 1;
RS = 0;
RW = 0;
P2 = 0xC0; //set DD RAM address to 40H (A7 = 1 100 0000)
LCD_en = 0;
for(k=0;k<100;k++); //delay to let LCD settle
break;
case 32:
LCD_en = 1;
RS = 0;
RW = 0;
P2 = 0x02; //move cursor to home position
LCD_en = 0;
LCD_pointer = 0;
for(k=0;k<100;k++); //delay to let LCD settle
break;
default:
break;
} //end of switch
//Display the LCD data
LCD_en = 1;
RS = 1;
RW = 0;
P2 = LCD_display[LCD_pointer];
LCD_en = 0;
LED = ~LED;
LCD_pointer++;
}
}
/*
----------------------------------------------------------
Timer2_Overflow_ISR
The Routine will count the number of pulses. The number
of pulses comes from TL0 and TH0 because Timer 0 was set to
record pulses
----------------------------------------------------------
*/
void Timer3_Overflow_ISR(void) interrupt 13 //Interrupts every 5 msec and auto reload
{
TMR3CN = 0x04;
ISRT3_count++;
if (ISRT3_count == 10) //refreshes actual_rpm every 1000 msec
{
pulse_count = TH0*256 + TL0; // the number of pulses
TH0 = 0x00; //Timer 0 high and low bytes reset
TL0 = 0x00;
//Routine convert pulses into RPM here
//rpm = pulse_count(1/1000msec)(1000msec/1sec)(60sec/1min)(1turn/80counts)
actual_rpm = pulse_count * 0.75;
ISRT3_count = 0;
target_rpm = ADC0;
FEEDBACK_Init();
}}
/*--------------------------------------------------------
Feedback Control
--------------------------------------------------------*/
void FEEDBACK_Init(void)
{rpm_change = target_rpm - actual_rpm;
//If desired target speed is higher than actual speed of motor if duty cycle is less than 100%
if((rpm_change>0) && (PCA0CPH0> 0x00))
{
if((rpm_change>=1200))
{ PCA0CPH0 = PCA0CPH0 - 0x05;}
else if((rpm_change>=1000) && (rpm_change<=1200))
{ PCA0CPH0 = PCA0CPH0 - 0x05;}
else if((rpm_change>=800) && (rpm_change<=1000))
{ PCA0CPH0 = PCA0CPH0 - 0x04;}
else if((rpm_change>=500) && (rpm_change<=800))
{ PCA0CPH0 = PCA0CPH0 - 0x03;}
else if((rpm_change>=300) && (rpm_change<=500))
{ PCA0CPH0 = PCA0CPH0 - 0x02;}
else if((rpm_change>=100) && (rpm_change<=300))
{ PCA0CPH0 = PCA0CPH0 - 0x01;}
else if((rpm_change>=50) && (rpm_change<=100))
{ PCA0CPH0 = PCA0CPH0 - 0x01;}
else if((rpm_change>10) && (rpm_change<=50))
{ PCA0CPH0 = PCA0CPH0 - 0x01;}
else if((rpm_change>0) && (rpm_change<=10))
{ PCA0CPH0 = PCA0CPH0;}
else {PCA0CPH0 = PCA0CPH0;}
}
//If desired target speed is lower than actual speed of motor if duty cycle is higher than 0%
else if((rpm_change<0) && (PCA0CPH0< 0xFF))
{
if((rpm_change<=-1200))
{ PCA0CPH0 = PCA0CPH0 + 0x05;}
else if((rpm_change<=-1000) && (rpm_change>=-1200))
{ PCA0CPH0 = PCA0CPH0 + 0x05;}
else if((rpm_change<=-800) && (rpm_change>=-1000))
{ PCA0CPH0 = PCA0CPH0 + 0x04;}
else if((rpm_change<=-500) && (rpm_change>=-800))
{ PCA0CPH0 = PCA0CPH0 + 0x03;}
else if((rpm_change<=-300) && (rpm_change>=-500))
{ PCA0CPH0 = PCA0CPH0 + 0x02;}
else if((rpm_change<=-100) && (rpm_change>=-300))
{ PCA0CPH0 = PCA0CPH0 + 0x01;}
else if((rpm_change<=-50) && (rpm_change>=-100))
{ PCA0CPH0 = PCA0CPH0 + 0x01;}
else if((rpm_change<=-10) && (rpm_change>=-50))
{ PCA0CPH0 = PCA0CPH0 + 0x01;}
else if((rpm_change<=0) && (rpm_change>=-10))
{ PCA0CPH0 = PCA0CPH0;}
else {PCA0CPH0 = PCA0CPH0;}
}
else {PCA0CPH0 = PCA0CPH0;}
}
/*--------------------------------------------------------
ADConversion complete interrupt
--------------------------------------------------------*/
void AD0_ISR(void) interrupt 9 // End Of Conversion
{
AD0INT = 0;
}
/*--------------------------------------------------------
LCD busy_flag
----------------------------------------------------------*/
int LCD_busy_flag(void)
{
unsigned char busy;
char SFRPAGE_save = SFRPAGE;
SFRPAGE = CONFIG_PAGE;
P2MDOUT = 0x00; //set PORT for read
P2 = 0xFF;
// the while loop continues until DB7 == 0
while(DB7 != 0)
{
LCD_en = 1;
RS = 0;
RW = 1;
LCD_en = 0;
for(i=0;i<100;i++); //delay to let LCD settle
DB7 = 0; //break out of while loop
}
busy = 0x00;
P2MDOUT = 0xFF;
SFRPAGE = SFRPAGE_save;
return(busy); //Return from interrupt
}
/*----------------------------------------------------------
Display_String()
Initializes the LCD array [32]
----------------------------------------------------------*/
void Display_String(void)
{
//first line display data
LCD_display[0] = 'T';
LCD_display[1] = 'A';
LCD_display[2] = 'R';
LCD_display[3] = 'G';
LCD_display[4] = 'E';
LCD_display[5] = 'T';
LCD_display[6] = ' ';
LCD_display[7] = 'R';
LCD_display[8] = 'P';
LCD_display[9] = 'M';
LCD_display[10] = '-';
LCD_display[11] = ' ';
//generate the rpm number
LCD_display[12] = '0'+target_rpm/1000;
LCD_display[13] = '0'+(target_rpm%1000)/100;
LCD_display[14] = '0'+((target_rpm%1000)%100)/10;
LCD_display[15] = '0'+((target_rpm%1000)%100)%10;
//second line display data
LCD_display[16] = 'A';
LCD_display[17] = 'C';
LCD_display[18] = 'T';
LCD_display[19] = 'U';
LCD_display[20] = 'A';
LCD_display[21] = 'L';
LCD_display[22] = ' ';
LCD_display[23] = 'R';
LCD_display[24] = 'P';
LCD_display[25] = 'M';
LCD_display[26] = '-';
LCD_display[27] = ' ';
//generate the rpm number
LCD_display[28] = '0'+actual_rpm/1000;
LCD_display[29] = '0'+(actual_rpm%1000)/100;
LCD_display[30] = '0'+((actual_rpm%1000)%100)/10;
LCD_display[31] = '0'+((actual_rpm%1000)%100)%10;
}