Experiment 6
Experiment 6
The aim of this experiment is to get you familiar with Pulse Width Modulation (PWM) using
ATmega 2560 micro-controller present on Firebird V robot.
Your task is to accelerate and decelerate the robot from its minimum to maximum speed and
vice versa using the Boot switch and indicate the robot’s current speed on the LCD.
Divide the entire velocity range (maximum - minimum) into approximately 4 equal parts. The
robot’s velocity is proportional to the values passed in the Output Compare Registers
(OCR5AL and OCR5BL).
When the robot is turned ON, motors should be in stop condition and the LCD should display
“Speed: 000” at 1st row and 3rd column. When the boot switch is pressed once, the LCD should
display “Speed: 063” and motors should start rotating slowly.
On subsequent press of the boot switch, the speed should be incremented by 63 and the motor
should also rotate faster than before.
Once the speed 252 is reached, the subsequent press of boot switch should start decrementing
the speed by 63 and the motor should rotate slower than before. This continues till the speed 0
is reached, and this cycle should continue as shown in Table 1.
Table 1: Boot switch press and Speed relation
Boot switch press Speed
Initially 0
1st press 63
2nd press 126
3rd press 189
4th press 252
5th press 189
6th press 126
7th press 63
8th press 0
9th press 63
Procedure:
Step-1: Create project named “Experiment-6.atsln” in Atmel Studio.
Step-2: You will notice some pre-written function stubs in included for your assistance
(Experiment-6.c). Write the code to complete the function with the help of function description
and inline comments. Also, some pre-written functions related to LCD are called in main().
These functions are declared and defined in “lcd.h” and “lcd.c” respectively. Some other
functions related to LCD are listed below that can assist you in completing the task:
void lcd_numeric_value(char row, char column, int val, int digits);
// This function prints any integer value or value in a variable as integer on the specified
// location and up to the specified number of digits on LCD. This function calls
// This function prints the given string on the LCD at the specified (row,column) position
Use these pre-defined functions and write your code to do the following:
1. Display text “Speed: ” starting from the 1st row and 3rd column on LCD.
2. Display the motor’s current speed in 3 digits at 1st row and 10th column on LCD.
Step-3: Check and debug your code by loading the hex file on the robot. Save the project.
Ensure that code performs as expected.
Experiment-6.c
#define F_CPU 14745600
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
// LCD Header file included that contains function definitions essential to deal with LCD;
more details can be found in "Read Me" file
#include "lcd.h"
// Declare any global variables if you require here
/*
* Function Name: motion_pin_config
* Input: none
* Output: none
* Logic: Code to initialize desired I/O port using I/O port registers viz. DDRx and PORTx.
* Example Call: motion_pin_config ()
*/
void motion_pin_config (void)
{
DDRA = // Port A Direction pins (PA3-PA0) have to be set as
outputs
PORTA = // Write suitable value in the PORT A register to make
initial values to be "0"
DDRL = // Setting PL3 and PL4 pins as output for PWM generation
PORTL = // Write suitable value in the PORT L register to set
initial value of the Port L pins (PL3, PL4) to logic 1
}
/*
* Function Name: boot_switch_config
* Input: none
* Output: none
* Logic: Code to initialize desired I/O port using IO port registers viz. DDRx and PORTx.
Example Call: boot_switch_config()
*/
void boot_switch_config(void)
{
DDRE = // PE.7 is connected to Boot switch
PORTE = // Write a suitable value to activate pull up resistor for PE.7
only
}
/*
* Function name: port_init
* Input: none
* Output: none
* Logic: Code to initialize desired I/O port using I/O port registers viz. DDRx and PORTx
* Example Call: port_init();
*/
void port_init(void)
{
motion_pin_config();
boot_switch_config();
}
/*
* Function Name: motion_timer5_init
* Input: none
* Output: none
* Logic: Code to initialize the register to generate the PWM using timer 5
viz. TCCR5A, TCCR5B, TCNT5H, TCNT5L, OCR5AL and OCR5BL
* Example Call: motion_timer5_init ()
*/
void motion_timer5_init()
{
TCCR5B = // Stop the timer initially
TCNT5H = // Counter higher 8-bit value to which OCR5xL value is
compared with
TCNT5L = // Counter lower 8-bit value to which OCR5xL value is
compared with
OCR5AL = // Output compare register low value for Left Motor
OCR5BL = // Output compare register low value for Right Motor
TCCR5A = // Write suitable value in this register to override normal
port functionality and to select Fast PWM 8-bit Mode.
TCCR5B = // Write suitable value in this register to set the desired
waveform generation mode and to select a prescalar of 64.
}
/*
* Function Name: forward
* Input: none
* Output: none
* Logic: Code used for setting motor's direction viz. PORTx.
Example Call: forward()
*/
void forward (void)
{
PORTA = // clear the direction pins
PORTA = // set only those pins to rotate both wheels forward
}
/*
* Function Name: stop
* Input: none
* Output: none
* Logic: Code used for setting motor's direction viz.PORTx
Example Call: stop()
*/
void stop (void)
{
PORTA = // clear the direction pins
PORTA = // set only those pins to stop the motors
}
/*
* Function Name: motion_control
* Input: none
* Output: none
* Logic: Code to take input from boot switch press and increment/decrement speed of robot
while displaying it on LCD.
Example Call: motion_control()
*/
void motion_control()
{
/********************************************************************
***
*********************************************************************
***/
}
//Main Function
int main()
{
motion_control();
}
ldc.c
#include <avr/io.h>
#include <util/delay.h>
#include "lcd.h"
#define RS 0
#define RW 1
#define EN 2
#define lcd_port PORTC
#define sbit(reg,bit) reg |= (1<<bit) // Macro defined for Setting a bit of any
register
#define cbit(reg,bit) reg &= ~(1<<bit) // Macro defined for Clearing a bit of any
register
/*
* Function Name: lcd_port_config
* Input: None
* Output: None
* Logic: This function configures the LCD port pins as output and sets them to 0 initially
* Example Call: lcd_port_config();
*/
void lcd_port_config(void)
{
DDRC = DDRC | 0xF7; // all LCD pins direction set as output
PORTC = PORTC & 0x08; // all LCD pins set to logic 0 except PC.3 (Buzzer pin)
}
/*
* Function Name: lcd_set_4bit
* Input: None
* Output: None
* Logic: This function configures the LCD to work in 4 bit mode instead of 8 bit mode
* to reduce the number of data lines required to be used
* Example Call: lcd_set_4bit();
*/
void lcd_set_4bit (void)
{
_delay_ms(1);
cbit(lcd_port,RS); // RS=0 --- Command Input
cbit(lcd_port,RW); // RW=0 --- Writing to LCD
lcd_port = 0x30; // Sending 3
sbit(lcd_port,EN); // Set Enable Pin
_delay_ms(5); // Delay
cbit(lcd_port,EN); // Clear Enable Pin
_delay_ms(1);
cbit(lcd_port,RS); // RS=0 --- Command Input
cbit(lcd_port,RW); // RW=0 --- Writing to LCD
lcd_port = 0x30; // Sending 3
sbit(lcd_port,EN); // Set Enable Pin
_delay_ms(5); // Delay
cbit(lcd_port,EN); // Clear Enable Pin
_delay_ms(1);
cbit(lcd_port,RS); // RS=0 --- Command Input
cbit(lcd_port,RW); // RW=0 --- Writing to LCD
lcd_port = 0x30; // Sending 3
sbit(lcd_port,EN); // Set Enable Pin
_delay_ms(5); // Delay
cbit(lcd_port,EN); // Clear Enable Pin
_delay_ms(1);
cbit(lcd_port,RS); // RS=0 --- Command Input
cbit(lcd_port,RW); // RW=0 --- Writing to LCD
lcd_port = 0x20; // Sending 2 to initialize LCD in 4-bit
mode
sbit(lcd_port,EN); // Set Enable Pin
_delay_ms(5); // Delay
cbit(lcd_port,EN); // Clear Enable Pin
}
/*
* Function Name: lcd_wr_command
* Input: cmd => hex value of the command to be given to LCD
* Output: None
* Logic: This function gives specific command values to the LCD to perform necessary
functions
* Example Call: lcd_wr_command(0x80); => to bring cursor at home position
*/
void lcd_wr_command (unsigned char cmd)
{
unsigned char temp;
temp = cmd;
temp = temp & 0xF0;
lcd_port &= 0x0F;
lcd_port |= temp;
cbit(lcd_port,RS);
cbit(lcd_port,RW);
sbit(lcd_port,EN);
_delay_ms(5);
cbit(lcd_port,EN);
/*
* Function Name: lcd_init
* Input: None
* Output: None
* Logic: This function initializes the LCD
* Example Call: lcd_init();
*/
void lcd_init (void)
{
lcd_set_4bit();
_delay_ms(1);
lcd_wr_command(0x28); // LCD 4-bit mode and 2 lines
lcd_wr_command(0x01); // Clear display screen
lcd_wr_command(0x06); // Entry mode
lcd_wr_command(0x0E); // Display On and Cursor On
lcd_wr_command(0x80); // LCD cursor set to Home position
}
/*
* Function Name: lcd_home
* Input: None
* Output: This function sets the cursor's to home i.e. 1st row, 1st column
* Logic: Passes 0x80 command to LCD using lcd_wr_command
* Example Call: lcd_home();
*/
void lcd_home (void)
{
lcd_wr_command(0x80); // LCD cursor set to Home position
}
/*
* Function Name: lcd_cursor
* Input: row, column => where you want the cursor to be positioned on LCD
* Output: This function sets the cursor to the specified position
* Logic: Position the LCD cursor at position (row, column) by passing
* the required commands to the lcd_wr_command function
* col > 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
* 1......... . . . . . . .
* 2......... . . . . . . .
* ^
* row
*
* Example Call: lcd_cursor(2, 3);
*/
void lcd_cursor (char row, char column)
{
switch (row)
{
case 1: lcd_wr_command (0x80 + column - 1); break;
case 2: lcd_wr_command (0xC0 + column - 1); break;
case 3: lcd_wr_command (0x94 + column - 1); break;
case 4: lcd_wr_command (0xD4 + column - 1); break;
default: break;
}
}
/*
* Function Name: lcd_clear
* Input: None
* Output: LCD is cleared
* Logic: This function clears LCD by giving specific command as input to
lcd_wr_command() function
* Example Call: lcd_clear();
*/
void lcd_clear (void)
{
lcd_wr_command(0x01);
}
/*
* Function Name: lcd_wr_char
* Input: row, column => where you want the cursor to be positioned on LCD
* alpha_num_char => alpha-numeric type data to be printed on the LCD at the specified
(row, column) position
* Output: None
* Logic: This function prints an alpha-numeric character at specified (row, column) position
on LCD
* Example Call: lcd_wr_char(1, 4, 'A') => to write character 'A' to the LCD
*/
void lcd_wr_char(char row, char column, char alpha_num_char)
{
lcd_cursor (row, column);
char temp;
temp = alpha_num_char;
temp = (temp & 0xF0);
lcd_port &= 0x0F;
lcd_port |= temp;
sbit(lcd_port,RS);
cbit(lcd_port,RW);
sbit(lcd_port,EN);
_delay_ms(5);
cbit(lcd_port,EN);
/*
* Function Name: lcd_string
* Input: row, column => where you want the cursor to be positioned on LCD
* *str => pointer of the char data type which points to the address of first character of
the string
* Output: This function prints the given string on the LCD at the specified (row, column)
position
* Logic: Positions the cursor and prints each character on LCD in a while loop until EOF is
reached
* Example Call: lcd_string(1, 1, "Hello !");
*/
void lcd_string(char row, char column, char *str)
{
while(*str != '\0')
{
lcd_wr_char(row, column, *str);
str++;
column+=1;
}
}
if (val < 0)
{
val = 0 - val;
lcd_string(row, column, "-");
column+=1;
}
if(row == 0 || column == 0)
{
lcd_home();
}
else
{
lcd_cursor(row, column);
}
if(digits == 5 || flag == 1)
{
million = val/10000+48;
lcd_wr_char(row ,column, million);
column+=1;
flag = 1;
}
if(digits == 4 || flag == 1)
{
temp = val/1000;
thousand = temp%10 + 48;
lcd_wr_char(row, column, thousand);
column+=1;
flag = 1;
}
if(digits == 3 || flag == 1)
{
temp = val/100;
hundred = temp%10 + 48;
lcd_wr_char(row, column, hundred);
column+=1;
flag = 1;
}
if(digits == 2 || flag == 1)
{
temp = val/10;
tens = temp%10 + 48;
lcd_wr_char(row, column, tens);
column+=1;
flag = 1;
}
if(digits == 1 || flag == 1)
{
unit = val%10 + 48;
lcd_wr_char(row, column, unit);
column+=1;
}
if(digits > 5)
{
lcd_wr_char(row, column, 'E');
column+=1;
}
}