You are on page 1of 17

Lab 4 Results Memo

By:
Jichuan Hu
Belvia Huo
Jeff Lehrer
Gabe Viscariello




TA: Dibakar Das
Lab Section: 2 B side
Lab Time: Monday 1pm -- 4pm
Due Date: 4/18/14

Contents
INTRODUCTION .................................................................................................. 1
OBJECTIVES ......................................................................................................... 1
CONTROL ALGORISM FOR STEERING ................................................................... 1
RESULTS AND ANALYSIS ...................................................................................... 2
CONCLUSION ...................................................................................................... 4
APPENDIX A: CIRCUITRY DIAGRAM ..................................................................... 5
APPENDIX B: PSEUDO CODE ................................................................................ 6
APPENDIX C: C CODE ........................................................................................... 8



1

Introduction
For lab 4, the team needed to integrate the software and hardware of lab 3 from
both teams with the addition of the LCD screen and keypad. The car must be able to
take inputs of desired direction and steering gain through the keypad. It should also
react accordingly to these inputs and perform a run in the non-uniform magnetic field.
Car battery voltage was read from analog-to-digital conversion and it was displayed with
current heading, current altitude on the LCD screen.
Objectives
The three essential objectives were:
1. Familiarization objectives for new lab equipment such as properly receiving
variables from the keypad and displaying the control information on the LCD
screen.
2. Software design objectives which were to combine the C codes from the motor
team and steering team so that none of the variables. This would also include
setting of gain constants and desired heading using the keypad and displaying
current car info on to the LCD screen. Lastly, it needed to record and display
data for compass heading error, servo and motor pulse width value on
SecureCRT.
3. Hardware designs objectives which were combining the hardware from the
both teams on to a single protoboard, which included the Ultrasonic Ranger and
an Electronic Compass. New additions to lab 3 must be integrated as well, such
as the keypad, LCD screen, and wireless RF serial transceiver.

Control algorism for steering
The first thing the team considered about when starting the lab is the control
algorism for steering with reading from the compass and desired heading input. A
variable error is calculated by subtracting current heading value from desired heading
value. It means that the desired heading is 0.1 times error degrees from the current
heading. Generally, positive error means desired heading is to the right and negative
heading is to the left. However, since the variable error is simply the difference, it
would not make sense for some of the values. When error is larger than 1800, though it
seems desired heading is more than 180 degrees to the right of current heading, the
common way to understand it is that the desired heading is to the left and, practically,
turning to the left would make a quicker change to fix the error. So is the case when
error is less than -1800; the actual error is some degree to the right of the current
heading. Since one round is 360 degrees, error value out of range is offset by +/- 3600
to adjust to a better error value. This simple algorism takes desired heading as zero
error value and all possible error values falls between the -1800 and 1800. After an

2

adjustment to the error variable, the pulse width will just be adding gain term times
error to the neutral pulse width.

Results and Analysis
After finishing the software and circuits of lab 4, the team tested cars behavior
under different gain terms used for adjusting steering. Three different gain terms were
used, which are 1, 4 and 8. For each gain term, the team collected data of error in
degrees to desired heading, steering pulse width and motor pulse width, all with respect
to time. When plotting, pulse width for steering was adjusted so that 0% is straight, 100%
is maximum to the right and -100% is maximum to the left. Since the maximum speed
was used for testing, the pulse width for motor was always 100%, thus always going
forward at maximum speed. In order to fit all the data for one gain term in to one plot,
the number on y-axis is read in such way that error is read in degrees and pulse width is
read in percent. For example, when 20 is read for error, it means 20 degrees to the right
is the desired heading; when 20 is read for pulse width, it means the pulse width is
neutral pulse width plus 20 percent of the difference between neutral and maximum
pulse width, thus the steering is turning right at 20% of the maximum turning angle.

Graph 1: Error in degrees, Steering and Motor pulse width in percent vs. time at Gain = 1
The first gain the team tested is 1. At this gain, a 0.1 degree error to desired
heading will result in a 1 unit change in pulse width. During testing, the car started to
move with very little change in moving direction. After a few seconds, the car hit the wall
and stopped moving, which showed as 2 horizontal lines on the graph lasting for about
10 seconds. After one of the team members retrieved the car, there was a few seconds
delay before the car was turned off, thus data after 15 seconds were considered as
-100
-80
-60
-40
-20
0
20
40
60
80
100
0 5 10 15 20 25
E
r
r
o
r
(

)

P
W
(
%
)

Time (s)
Error
Steering
Motor

3

irrelevant. Since the car cannot complete the test at gain of 1, 1 is not considered as the
optimal gain for steering.

Graph 2: Error in degrees, Steering and Motor pulse width in percent vs. time at Gain = 4
The second gain the team tested was 4. At this gain, every 0.1 degree of error to
heading will result in a 4 units change in pulse width. The car started the test with a little
turning to the right, then to the left. At some moment, the car was really close to the wall,
but it managed to make enough turning right and move away from the wall; as shown in
the graph, the car made continuous turning to the right from 5 to 9 second. Since the
car could finish the test, 4 is considered as an option of gain term.


Graph 3: Percent Error, Steering, and Motor vs. time at Gain = 8
-100
-80
-60
-40
-20
0
20
40
60
80
100
0 2 4 6 8 10 12
E
r
r
o
r
(

)

P
W
(
%
)

Time(s)
Error
Steering
Motor
-100
-80
-60
-40
-20
0
20
40
60
80
100
0 5 10 15
E
r
r
o
r
(

)

P
W
(
%
)

Time(s)
Error
Steering
Motor

4

The last gain the team tested was 8. At this gain, every 0.1 degree error to
desired heading will result in 8 units change in pulse width. How this gain was affected
the steering was very similar to gain of 4. It moved quickly to fix the error in direction;
however, its changes were more drastic which led the car to a more sporadic movement
pattern. As shown on the graph, the steering was changing so rapidly in direction that
even a little error to the desired heading would result in maximum turning to the right or
left. The gain of 8 is too sensitive for steering, and as a result, the gain of 4 is used as
the optimal setting of gain term.
Conclusion
The objectives for this lab were fairly straight forward and very achievable within
the given time period. The circuitry was merely integrating one teams with another and
adding a slide switch, LCD screen, and keypad. Much like the hardware, the software
was easy enough to combine. The teams had the same variables for pulse width, but it
was simple to change the names. Some trouble the team had was getting the timing
correct when asking for inputs from the user. Our code initially had trouble waiting for
the user to make a second input if he was choosing from a preset list of desired
directions. By adding a simple while loop in the correct spot, we were able to get our
code to work and the keypad to work with the car.


5

Appendix A: Circuitry Diagram



6

Appendix B: Pseudo Code
include files
function prototypes
gloable variables

main function
Initializations
while true
wait until the slide switch is turned on
if new setting is flaaged
unflag new setting
set desired heading
1. from user input
2. from predefined value list
set steering gain from predefinsed list
if new heading is flagged, every
unflag new heading
read compass
set reading result to variable heading
if new range is flagged
unflag new range
read ranger
set reading result to variable range
print time, heading error and paulsewidth for analysis
calculate desired heading error
adjustment to error so it falls in -1800 to 1800
change steering pw according to error
adjustment to the steering pw so it falls between limits
apply change to compare model for steering
change to motor pw according to variable range
if less than 10, maximum pw
if between 10 and 40, decrease in pw proportional to range
if between 40 and 50, neutral pw
if between 50 and 90, increase in pw proportional to range
if more than 90, minimum pw
apply change to compare model for motor
if new display is flagged
unflag display
clear lcd
reading battery voltage from adc
convert adc result back to voltage
print on lcd the heading, altitude(range) and battery voltage
if slideswitch is turn off
flag new setting
end of main


7


PCA interrupt service routine
clear the interrupt flag
set starting count so that period is 20ms
increase counts1, counts2, counts3
increase time count for wait functions
if counts1 greater than 2
set counts1 to 0
flag new heading
if counts2 greater than 4
set counts2 to 0
flag new range
if counts3 greater than 17
set counts3 to 0
flag new display
end















8

Appendix C: C code
// Jeff Lehrer, Gabe Viscariello, Jichuan Hu, Belvia Huo
// Litec section 2 side B
// Lab 4 Code
// April 14th 2014
#include <c8051_SDCC.h>
#include <stdio.h>
#include <stdlib.h>
#include <i2c.h>
//---------------------------------------------------------------------------
--
// Function Prototypes
//---------------------------------------------------------------------------
--
void Port_Init(void); // initialize pins for output and input
void PCA_Init (void); // initialize PCA
void ADC_Init(void); // initialize ADC
void XBR0_Init(void); // initialize cross bar
void PCA_ISR (void) __interrupt 9; // code after PCA overflows
void SMB_Init(void); // initialize SMB
void Read_Comp(void); // read compass and return current heading
void Servo(void); // adjust the steering according to current and desired
heading
void Read_Ranger(void); // read ranger and return altitude
void Motor(void); // adjust the speed according to altitude
void Motor_Init(void); // initialize motor
unsigned char read_AD_input(void); // read P1.7 and return batter voltage in
0-255
void Desired_heading(void); // set desired heading with keypad
void Steering_gain(void); // set steering gain with keypad
void Display(void); // update display every 350ms
void Heading_Gain(void); // set desired heading and steering gain every time
slideswith is toggled
void pause(void); // wait for 20ms
void wait(void); // wait for 1s
//---------------------------------------------------------------------------
--
// Global Variables
//---------------------------------------------------------------------------
--
__sbit __at 0xB7 SS; // slideswitch on port 3 pin 7
unsigned int T=0; // time variable for secure crt plotting data
unsigned char Data_C[2]={0}; // compass data
unsigned char Data_R[2]={0}; // ranger data
unsigned int heading; // current heading value
unsigned int range; // current range value
unsigned char new_heading=0; //flag for desired heading
unsigned char new_range=0; // flag for ranger
unsigned char new_display=0; // flag for lcd display
unsigned char new_setting=1; // flag for heading gain
unsigned char counts1; // timer for heading
unsigned char counts2; // timer for ranger
unsigned char counts3; // timer for gain
unsigned int time; // time variable for pause and wait
unsigned char input; // input for character, used for testing
unsigned int desired_heading=900; // default desired heading

9

signed int error; // error to desired heading
unsigned int temp_servo_pw; // current pw for steering
unsigned int temp_motor_pw; // current pw for motor

unsigned int pw_center_servo = 2834; //neutral steering pulsewidth
unsigned int pw_min_servo = 2294; // min steering pulsewidth
unsigned int pw_max_servo = 3414; // max steering pulsewidth

unsigned int pw_center_motor = 2774; // neutral motor pulsewidth
unsigned int pw_min_motor = 2027; // motor min pulsewidth
unsigned int pw_max_motor = 3502; // motor max pulsewidth

unsigned int cex0; // starting position for compare model one
unsigned int cex2; // starting position for compare model two

unsigned char battery; // battery voltage from adc
unsigned char voltage[2]; // float number for battery voltage in V
float gain=0.8333; // default gain for steering
//---------------------------------------------------------------------------
--
// Main Function
//---------------------------------------------------------------------------
--
void main(void)
{
// initialize board
Sys_Init();
putchar(' '); //the quotes in this line may not format correctly
Port_Init();
XBR0_Init();
PCA_Init();
ADC_Init();
SMB_Init();
Motor_Init();
wait();

while (1)
{
//printf("Start program. %d", SS);
while (SS); // wait until the slide switch is turned on
Heading_Gain(); // set gain and desired heading with keypad
Read_Comp(); // read the compass
Read_Ranger(); // read the ranger
Servo(); // call servo function that calculated steering
pulsewidth
Motor(); //call motor function that calculates speed pulsewidth
Display(); // display function prints to the LDC display
}
}

//---------------------------------------------------------------------------
--
// Port_Init
//---------------------------------------------------------------------------
--
//
// Set up ports for input and output

10

//
void Port_Init()
{
//P0
P0MDOUT |= 0x50; //set output pin for CEX0 and CEX2 in push-pull mode
//P1
P1MDIN &= ~0x80; //set for analog input on p1
P1MDOUT &= ~0x80; // set to open drain mode
P1 |= 0x80; // send logic 1 to input pins
//P3
P3MDOUT &= ~0x80; // set to push pull mode
P3 |= 0x80; // send logic 1 to pins
}

//---------------------------------------------------------------------------
--
// XBR0_Init
//---------------------------------------------------------------------------
--
//
// Set up the crossbar
//
void XBR0_Init()
{
XBR0 = 0x25; //configure crossbar as directed in the laboratory
XBR2 |= 0x40; //additional configuration that might not be necessary
}

//---------------------------------------------------------------------------
--
// PCA_Init
//---------------------------------------------------------------------------
--
//
// Set up Programmable Counter Array
//
void PCA_Init(void)
{
CR = 1; // enable PCA
PCA0MD = 0x81; // SYSCLK/12, enable PCA0 overflow interrupt
PCA0CPM0 = 0xC2; // PCA config for compass 16 bit compare mode
PCA0CPM2 = 0xC2; // PCA config for ranger 16 bit compare mode
EIE1 = 0x0A; //enable PCA interrupts
EA = 1; //enable all interrupts
PCA0L = 0x00; // starting value of pca counter
PCA0H = 0x70; // which gives 20ms period
}

void ADC_Init(void)
{
REF0CN = 0x03; // set internal reference voltage to 2.4 v
ADC1CN = 0x80; // enable a/d converter ADC1
ADC1CF |= 0x01; // set a/d converter to a gain of 1
}

void SMB_Init(void)
{

11

SMB0CR=0x93; //set clock frequency 100khz
ENSMB=1; // enable SMBus
}

void Motor_Init(void)
{
PCA0CPL2 = 0xFFFF - pw_center_motor; // low byte of start count
PCA0CPH2 = (0xFFFF - pw_center_motor) >> 8; // high byte start count
time = 0;
while (time<28);// wait
}

//---------------------------------------------------------------------------
--
// PCA_ISR
//---------------------------------------------------------------------------
--
//
// Interrupt Service Routine for Programmable Counter Array Overflow
Interrupt
//
void PCA_ISR ( void ) __interrupt 9
{
// reference to the sample code in Example 4.5 -Pulse Width Modulation
// implemented using the PCA (Programmable Counter Array), p. 50 in Lab
Manual.
if (CF)
{
CF = 0; //clear overflow flag
PCA0L = 0x00;
PCA0H = 0x70;
counts1++; //increment counter for heading
counts2++; //increment counter for range
counts3++; //increment counter for lcd display
time++;
if (counts1>=2) //timer for heading
{
counts1 = 0;
new_heading = 1;
}
if (counts2 > 4) // wait 80ms timer for ranger
{
counts2 = 0;
new_range = 1;
}
if (counts3 > 17) //wait 350ms timer for display timer
{
counts3 = 0;
new_display = 1;
}
}
PCA0CN &= 0xC0; // handle all other PCA interrupt sources
}

void Read_Comp(void)
{
if (new_heading) //if new heading flag is flagged

12

{
i2c_read_data(0xC0, 2, Data_C, 2); // read 2 bytes to register 2
heading = (((unsigned int)Data_C[0]<<8) | Data_C[1]); //set
heading
new_heading = 0; // reset heading
//printf("heading = %u", heading);
//printf("Compass reading finished.\r\n");
}
}
void Read_Ranger(void)
{
if (new_range) // if new range flag is flagged
{
unsigned char Data[1]; // local variable for setting return unit
i2c_read_data(0xE0,2,Data_R,2); // read 2 byte at register 2
range =(((unsigned int)Data_R[0]<<8) | Data_R[1]); //set range
new_range = 0; //reset range
Data[0]=0x51; // set address of ranger
i2c_write_data(0xE0,0,Data,1); //write the address
T++; // time used for data acquisition
printf("%d,%d,%d,%d\r\n",T, error, temp_servo_pw, temp_motor_pw);
// print error,pulsewitdh, and time
//printf(" the range =%u\r\n",range);
//printf("PW = %u\r\n",temp_motor_pw);
}
}
void Servo(void)
{
//float k=0.8333;
error = desired_heading - heading; //set error
if (error<=-1800) // if error is less than -1800 then the new error is
calculated
{
error+=3600; //new error adds 3600
}
else if (error>=1800) // if greater than -1800 then calculate new error
{
error-=3600; //subtract 3600
}
temp_servo_pw = gain*(error) + pw_center_servo; //calculate temporary
servo pulsewitdh
//printf("%d, %d, %d\r\n", heading, error, temp_servo_pw);
if (temp_servo_pw>pw_max_servo) // if temp pulsewidth is greater than
max
{
temp_servo_pw = pw_max_servo; //set temp pulsewidth equal to max
pulsewidth
}
else if (temp_servo_pw<pw_min_servo)// if temp pulsewidth less than
minimum pulsewidth
{
temp_servo_pw = pw_min_servo; // set temp pulsewidth equal to
minimum pulsewidth
}
//printf("%d\r\n", temp_servo_pw);
cex0=0xFFFF - temp_servo_pw;
PCA0CPL0 = cex0;

13

PCA0CPH0 = cex0 >> 8;
//printf("Servo adjusted.\r\n");
}
void Motor(void)
{
if(range<=10) //if object is 10 cm or less above then adjust pulsewidth
{
temp_motor_pw= pw_max_motor; // sets max pulsewidth
}
else if ((range>10) && (range<40)) // if range is between 40 and 50
{
temp_motor_pw= ((-25*range)+3748); // linear decay of speed
}
else if((range>=40) && (range<=50)) // if range is between 40 and 50
{
temp_motor_pw = pw_center_motor; //car is in neutral
}
else if ((range>50) && (range<90)) // if range greater than 50 and less
than 90
{
temp_motor_pw = ((-18*range)+3686); // temp motor pulsewidth
takes linear decay
}
else if (range >=90) // if greater than 90
{
temp_motor_pw = pw_min_motor; // set car in full reverse
pulsewidth
}
cex2 = 0xFFFF - temp_motor_pw;
PCA0CPL2 = cex2;
PCA0CPH2 = cex2 >> 8;
}
unsigned char read_AD_input(void)
{
AMX1SL = 7; // set analog input on pin 7
ADC1CN &= ~0x20; //clear the conversion flag
ADC1CN |= 0x10; //initiate the ad conversion
while ((ADC1CN & 0x20) == 0x00); //wait for conversion to be complete
return ADC1; // return digital value
}
void Desired_heading(void)
{
char keypad;
lcd_clear(); // clear ldc
lcd_print("Desired heading\n"); //print
lcd_print("1:Specific Value\n2:Predefined Value"); // print options to
lcd
keypad = read_keypad(); //read the keypad for user input
pause(); // wait 20ms
while (keypad == -1) //when the keypad is released
{
keypad = read_keypad(); //read the keypad
pause(); // wait for response
}
if (keypad == 49) //if 1 is pressed
{

14

while (keypad != -1){keypad = read_keypad();pause();}//wait until
the keypad is unpressed
lcd_clear(); // clear lcd
lcd_print("Please type in the desired heading.\nDesired
heading:");
desired_heading = kpd_input(1); //desired heading is user input
}
else if (keypad == 50) // if 2 is pressed
{
while (keypad != -1){keypad = read_keypad();pause();}
lcd_clear();//clear lcd display
lcd_print("Please select\n1:0 2:90\n3:180 4:270\n"); //prints
a list to select from
while (keypad == -1){keypad = read_keypad();pause();}
if (keypad == 49) //when 1 is pressed
{
desired_heading = 0; //heading is 0
}
else if (keypad == 50) //when 2 is pressed
{
desired_heading = 900; //heading is 90 degrees
}
else if (keypad == 51) // when 3 is pressed
{
desired_heading = 1800; // heading is 180 degrees
}
else if (keypad == 52) // if 3 pressed
{
desired_heading = 2700; //heading is 270 degrees
}
}
while (keypad != -1) // wait until the keypad is unpressed
{
keypad = read_keypad();
}
}
void Steering_gain(void)
{
char keypad; //loc variable
lcd_clear(); // clear the lcd
lcd_print("Steering gain\n1:1 2:4\n3:8 4:14\n"); // print gain
options
keypad = read_keypad(); //read the keypad for input
pause(); //wait
while (keypad == -1) //wait until the keypad is pressed
{
keypad = read_keypad(); //read the keypad for input
pause();// wait
}
if (keypad == 49) // if 1 is pressed
{
gain = 1; //gain equals 1
}
else if (keypad == 50) // if 2 is pressed
{
gain = 4; // gain equals 4
}

15

else if (keypad == 51) //if 3 is pressed
{
gain = 8; // gain equals 8
}
else if (keypad == 52) //if 4 is pressed
{
gain = 14; //gain equals 14
}
while (keypad != -1) //wait until the keypad is unpressed
{
keypad = read_keypad();//read for input
}
}
void pause(void) // pause timing
{
time = 0;
while (time < 1);// 1 count -> (65536-PCA_START) x 12/22118400 = 20ms
} // 6 counts avoids most of the repeated hits
void wait(void) //wait timing
{
time = 0;
while (time < 50); // 50 counts -> 50 x 20ms = 1000ms
}
void Battery_voltage(void) //function to calculate battery voltage
{
battery = read_AD_input(); //battery is equal to the ad conversion on 7
voltage[0] = 142*battery/233; //10*battery voltage
voltage[1]=voltage[0]%10;// voltage before the decimal
voltage[0]=voltage[0]/10; //voltage after the decimal to the tenths
place
}
void Display(void) //diplay function for the lcd
{
if (new_display == 1) //if new display flagged
{
new_display = 0; //reset flag
lcd_clear(); //clear lcd
Battery_voltage(); //convert adc to voltage
lcd_print("heading: %d\n", heading); //print heading to lcd
lcd_print("altitude: %d\n", range); // print range to lcd
lcd_print("battery: %d.%d\n", voltage[0], voltage[1]); // print
voltage to lcd
}
if (SS == 1) // if slideswitch is off
{
new_setting = 1; //
}
}
void Heading_Gain(void)
{
if (new_setting == 1) // if new setting if flagged
{
new_setting = 0;
Desired_heading(); //call heading function
Steering_gain(); //call steering gain function
}
}

You might also like