Report1 - Final
Report1 - Final
🙞···☼···🙜
MICRO-CONTROLLER COURSE
CLASS EXERCISE 2
I. Output
This is the output of the project, every result will be printed on the LCD display
II. Input
1. Control buttons
Three buttons are used for switching between the exercises 1, 2 and 3. They are
connected to pin RD0, RD1 and RD2 respectively.
And the resistors R1, R2 and R3 act as pull-down resistors so that the inputs will
initially be on a LOW state.
2. Square wave
The input required for exercise 1, it is set-up with the settings below:
3. Count button
This in the input for exercise 2, used to manually send pulses to the micro controller.
4. Potentiometer
Three potentiometers are connected to pin RA0, RA1 and RA2 on the microcontroller
for exercise 3 with the reference voltage (5V) connected to pin RA3.
5. Crystal oscillator
An external oscillator is connected to the microcontroller to generate the frequency in
which the microcontroller operates.
The oscillator configurations:
III. Processing
1. PIC16F877A
In this exercise, we’ll be using the PIC16F877A microcontroller as requested.
We only need to connect the VPP pin to the power source.
Programming
I. Initial setup
1. Global Variables
These are just variables declared beforehand, their purposes will be explained later.
#define EN PORTB.B3
#define RS PORTB.B2
b) lcd_send
Similar to lcd_cmd but instead, setting the RS pin to HIGH, indicates that this is a
character to print.
void lcd_send(unsigned char send)
{
PORTB = (send&0xF0); //Write the upper 4-bits of the data
EN = 1;
RS = 1;
EN = 0;
PORTB = ((send<<4)&0xF0); //Write the lower 4-bits of the data
EN = 1;
RS = 1;
EN = 0;
delay_ms(3); //Small delay for the LCD to process the data
}
c) lcd_string
Use a loop to send multiple character to the LCD
void lcd_string(const unsigned char *str, unsigned char num)
{
unsigned int i;
for( i = 0; i < num; i++)
{
lcd_send(str[i]); //Send each character to the LCD seperately
}
}
d) lcd_init
Send a series of commands to the LCD in order to turn on the LCD as well as
configuring it’s operating mode.
void lcd_init()
{
delay_ms(50); //LCD Power ON delay
lcd_cmd(0x30); /////////////////////////
delay_ms(40); // //
lcd_cmd(0x30); // //
delay_ms(10); // Reset the LCD //
lcd_cmd(0x30); // //
delay_ms(10); /////////////////////////
lcd_cmd(0x02); //Function set
e) lcd_num
Print out an integer to the LCD digits by digits.
void lcd_num(float n)
{
long tmpf = n;
int p;
int k=0;
while(tmpf>0)
{
num[k]=tmpf%10;
tmpf=tmpf/10;
k++;
}
k--;
for (p=k;p>=0;p--)
{
lcd_send(num[p]+48);
}
}
f) lcd_float
Print out a decimal number with a precision up of 0.1
void lcd_float(float n)
{
long tmpf = n*10;
int p;
int k=0;
while(tmpf>0)
{
num[k]=tmpf%10;
tmpf=tmpf/10;
k++;
}
k--;
for (p=k;p>=1;p--)
{
lcd_send(num[p]+48);
}
if(n<1) lcd_send('0');
lcd_send('.');
lcd_send(num[0]+48);
lcd_send(' ');
}
II. Main program
1. Algorithm
a) Initial setup
A series of commands to set the working mode for each pin.
After that, initialize the LCD and setup the Timer 1.
The timer 1 will only be used for timing purposes in this program
void main(void)
{
PORTA = 0x00; //Clear all A pins
PORTB = 0x00; //Clear all B pins
PORTD = 0x00; //Clear all D pins
b) Main loop
Here we have the main operation for the PIC
As stated before, we have three buttons to switch between the exercises,
Each button will set the variable [ lcd_state] to 1 for exercise 1, 2 for exercise 2
and 3 for exercise 3.
The [lcd_state] variable will also determine if we needed to clear the display
before each exercise. Without it, the PIC will tell the LCD to clear each loop,
resulting in a flickering screen and bigger delay between each loop due to
unnecessary commands.
while(1)
{
//-----------------------------Setup-for-question-1----------------------------
lcd_cmd(0x80);
lcd_string("Freq:",5);
lcd_cmd(0xC0);
lcd_string("High: ", 6);
}
//-----------------------------Setup-for-question-2----------------------------
lcd_cmd(0x80);
lcd_string("Pulses read:",12);
lcd_cmd(0xC0);
lcd_send('0');
//----------------------------Setup-for-question-3-----------------------------
lcd_cmd(0x80);
lcd_string("AN0 - AN1 - AN2",15);
ADCON1 = 0x83;
}
//-----------------------------------------------------------------------------
switch(lcd_state)
{
case 1:
{
get_frequency(); //question 1
} break;
case 2:
{
if(PORTA.B4 == 1) pulse_count(); //question 2
} break;
case 3:
{
if(ad_time < 7) ad_value(); //question 3
else ad_time = 1;
} break;
}
}
return;
}
2. Exercise 1
a) Theory
In order to get the frequency from the input pulse, we can count the number of
ticks from the timer and then use the following formula:
CPU frequency
f=
4 × Prescaler × N umber of ticks
We have:
- CPU frequency=4 000000 ( Hz)
- Prescaler=8
- Number of ticks : will be measured
Next up is the pulse width. We can use similar method of counting ticks to get our
results:
HIGH period
Pulse width= × 100 %
Pulse period
b) Coding
First we declare some variables for the function:
- [float h]: HIGH period recorded in timer ticks
- [float l]: The pulse period recorded in timer ticks
- [float f]: Frequency of the pulse
Then we set-up the timer 0 as follows:
void get_frequency()
{
float h, l, f;
OPTION_REG.T0CS = 0; //Select normal mode
OPTION_REG.PSA = 0; //Select custom prescaler
OPTION_REG.PS2 = 0; //
OPTION_REG.PS1 = 1; //Timer0 Prescaler 1:8
OPTION_REG.PS0 = 0; //
After that we can begin by waiting out the current period then set the timer value
[TMR0] to 0 and start counting.
We stop counting when the pulse switch to HIGH the second time and assign
[TMR0] value to the required variables.
while(PORTC.B0 == 1) {}
while(PORTC.B0 == 0) {}
TMR0 = 0;
while(PORTC.B0 == 1) {}
while(PORTC.B0 == 0) {}
l = TMR0;
f = 1000000/l/8;
lcd_cmd(0x86); lcd_float(f); lcd_string("Hz",2);
while(PORTC.B0 == 1) {}
while(PORTC.B0 == 0) {}
TMR0 = 0;
while(PORTC.B0 == 1) {}
h = TMR0;
while(PORTC.B0 == 0) {}
l = TMR0;
lcd_cmd(0xC6); lcd_float(h/l*100); lcd_send('%');
}
After getting all the needed values and calculations, we printout the result.
c) Simulation
https://drive.google.com/file/d/1RkQ1YLou09Fm8WeV2n74USksYtzPGLLR/
view?usp=drive_link
3. Exercise 2
a) Theory
Use Timer 1 as a delay timer but instead of waiting for the [TMR1IF_bit] and do
nothing, we include a print function in the loop.
b) Coding
Since we already set-up Timer 1 in normal mode in the “Initial setup” section and
Timer 0 in counter mode, we don’t need to do that here.
void pulse_count()
{
long count = 0;
long time = 0;
int i, state;
TMR0 = 0;
4. Exercise 3
a) Theory
This time we will use the [ad_time] variable we mentioned above.
Each 1 second loop, we will increase [ad_time] by 1
- Every loop, we will read and convert signal from AN0
- Every two loop, when [ad_time] is divisible by 2,we will read and convert
signal from AN1
- Every three loop,when [ad_time] is divisible by 3, we will read and convert
signal from AN1
The algorithm is shown in the block diagram below:
b) Coding:
void ad_value()
{
unsigned int i, adc_value = 0, tmp[4];
ADCON0 = 0x80;
if(ad_time > 0)
{
CHS2_bit = 0; CHS1_bit = 0; CHS0_bit = 0; //Select channel AN0
ADON_bit = 1; //Turn on the ADC
adc_value = ADRESH<<8; //
adc_value += ADRESL; //assgin the result to adc_value
ADON_bit = 0;
lcd_cmd(0xC0);
lcd_string(" ",4);
lcd_cmd(0xC0);
lcd_num(adc_value); //print out the result
}
if(ad_time %2==0)
{
CHS2_bit = 0; CHS1_bit = 0; CHS0_bit = 1; //Select channel AN1
ADON_bit = 1; //Turn on the ADC
adc_value = ADRESH<<8; //
adc_value += ADRESL; //assgin the result to adc_value
ADON_bit = 0;
lcd_cmd(0xC6);
lcd_string(" ",4);
lcd_cmd(0xC6);
lcd_num(adc_value); //print out the result
}
delay_ms(10);
if(ad_time %3==0)
{
CHS2_bit = 0; CHS1_bit = 1; CHS0_bit = 0; //Select channel AN2
ADON_bit = 1; //Turn on the ADC
adc_value = ADRESH<<8; //
adc_value += ADRESL; //assgin the result to adc_value
ADON_bit = 0;
lcd_cmd(0xCC);
lcd_string(" ",4);
lcd_cmd(0xCC);
lcd_num(adc_value); //print out the result
}
ad_time++;
while(!TMR1IF_bit){}
TMR1IF_bit = 0;
a) Simulation
https://drive.google.com/file/d/1j5EiPRAGLt8wXEuZXIXJ_LOAANrS5Drr/
view?usp=sharing