Project 1 - Controlling a DC motor using PWM and monitoring its Running status
Solution:
#include <avr/io.h> //Including AVR Input/Output header file
#define F_CPU 16000000ul //define the CPU frequency
#include "avr/interrupt.h" //Including AVR interrupt header file
#include <util/delay.h> //Including Delay header file
#define rs_high PORTD |= (1<<5) //Define a macro for setting the RS (Register Select) line high
#define rs_low PORTD &= ~(1<<5) //Define a macro for setting the RS (Register Select) line low
#define en_high PORTD |= (1<<6) //Define a macro for setting the EN (Enable) line high
#define en_low PORTD &= ~(1<<6) //Define a macro for setting the EN (Enable) line low
#define lcd_port PORTB //Define the port for the LCD
#define sw_dir (PIND & (1<<1)) //Read the state of a switch connected to Port D, Pin 1
#define sw_dspd (PIND & (1<<2)) //Read the state of a switch connected to Port D, Pin 2
#define pw (PINC & (1<<3)) //Read the state of a switch connected to Port C, Pin 3
void lcd_command(unsigned char cmd); //Declare a function for sending a command to the LCD
void lcd_data(unsigned char data); //Declare a function for sending data to the LCD
void lcd_init(); //Declare a function to initialize the LCD
void cursorpos(char x, char y); //Declare a function to set the cursor position on the LCD
int adc_read(char channel); //Declare a function to read an analog value from a specified channel
void lcd_out(char x, char y, char *str); //Declare a function to display a string on the LCD
unsigned char i; //Declare a variable 'i' of type unsigned char
//Function to send a command to the LCD
void lcd_command(unsigned char cmd)
{
//Send the upper 4 bits of the command to the LCD
lcd_port = (cmd & 0xf0); //Extract the upper 4 bits of the command and send them to the LCD data port
rs_low; // Set the RS pin low
en_low; // Set the EN (enable) pin low to prepare for data transfer
_delay_ms(1); // Wait for a short delay (1 millisecond)
en_high; // Set the EN (enable) pin high
//Send the lower 4 bits of the command to the LCD
lcd_port = ((cmd << 4) & (0xf0)); //Shift the lower 4 bits of the command and send them to the LCD data port
rs_low; //Set the RS pin low again
en_low; //Set the EN (enable) pin low for the second data transfer
_delay_ms(1); //Another short delay
en_high; //Set the EN (enable) pin high
}
// Function to send data to the LCD
void lcd_data(unsigned char data)
{
//Send the upper 4 bits of the data to the LCD
lcd_port = (data & 0xf0); //Mask and assign the upper 4 bits of 'data' to 'lcd_port'
rs_high; //Set the register select (RS) to high
en_low; //Set the enable (EN) signal to low to prepare for data transfer
_delay_ms(1); //Delay for 1 millisecond
en_high; //Set the enable (EN) signal to high to transfer the upper 4 bits of data to the LCD
// Send the lower 4 bits of the data to the LCD
lcd_port = ((data << 4) & (0xf0)); //Shift the lower 4 bits of 'data' to the upper 4 bits position, mask them, and assign to 'lcd_port'
rs_high; //Set the register select (RS) to high
en_low; //Set the enable (EN) signal to low to prepare for data transfer
_delay_ms(1); //Delay for 1 millisecond
en_high; //Set the enable (EN) signal to high to transfer the lower 4 bits of data to the LCD
}
//Function to initialize the LCD
void lcd_init()
{
lcd_command(0x38); //Initialize LCD in 2-line, 5x8 font mode
_delay_ms(5);
lcd_command(0x02); //Return home
_delay_ms(5);
lcd_command(0x01); //Clear display
_delay_ms(5);
lcd_command(0x0C); //Turn on display, cursor off, and cursor blink off
_delay_ms(5);
lcd_command(0x06); //Entry mode set: cursor moves to the right
_delay_ms(5);
lcd_command(0x80); //Set cursor to the beginning of the first line
_delay_ms(5);
}
//Function to set the cursor position on the LCD
void cursorpos(char x, char y)
{
//Check and adjust the input coordinates if out of bounds
if ((x < 1 || x > 2) || (y < 1) || (y > 16))
{
x = 1;
y = 1;
}
//Set the cursor position on the LCD based on the input coordinates
if (x == 1)
{
lcd_command(0x80 + y);
}
else
{
lcd_command(0xC0 + y);
}
}
//Function to read an analog value from a specified channel
int adc_read(char channel)
{
unsigned int a, result; //Declare variables to store intermediate values and the result
ADMUX = channel; //Set the ADMUX register to select the desired ADC channel.
ADCSRA = 0X80;//Set the ADC Control and Status Register A (ADCSRA) with the binary value 10000000
ADCH = 0x00; //Initialize the high ADC data registers to 0
ADCL = 0x00; //Initialize the low ADC data registers to 0
ADCSRA |= (1 << 6); //Set the 6th bit of ADCSRA to 1, which starts the ADC conversion
while ((ADCSRA & 0x40) != 0);
a = ADCL; //Store the low byte of the ADC result in 'a'.
result = ADCH; //Store the high byte of the ADC result in 'result'.
result = result << 8; //Shift the high byte left by 8 bits to prepare it for bitwise OR operation
result = result | a; //Combine the high and low bytes to form the 16-bit result
return result; //Return the 16-bit ADC result.
}
// Function to display a string on the LCD at the specified coordinates
void lcd_out(char x, char y, char *str)
{
cursorpos(x, y);
while (*str)
{
lcd_data(*str);
str++;
}
}
int main(void)
{
// Setting the data direction for various ports and initializing 'i' to 64
DDRB = 0xFF; //Set all pins of Port B as outputs (data direction register)
DDRA |= ((1 << 0) | (1 << 1)); //Set pins 0 and 1 of Port A as inputs
DDRD &= ~(1 << 1); //Set pin 1 of Port D as an input by clearing its bit in the data direction register
DDRD &= ~(1 << 2); //Set pin 2 of Port D as an input by clearing its bit in the data direction register
DDRD |= (1 << 5); //Set pin 5 of Port D as an output by setting its bit in the data direction register
DDRD |= (1 << 6); //Set pin 6 of Port D as an output by setting its bit in the data direction register
DDRC &= ~(1 << 3); //Set pin 3 of Port C as an input by clearing its bit in the data direction register
PORTD |= (1 << 2); //Set pin 2 of Port D as high (turn on) by setting its bit in the port register
PORTC |= (1 << 3); // Set pin 3 of Port C as high (turn on) by setting its bit in the port register
i = 64; //Assign the value 64 to the variable 'i'.
//Configuring Timer 0 and External Interrupt 0
TCCR0 = 0X61; //Configure Timer 0 with specific settings (hexadecimal value).
GICR = (1 << INT0); //Enable External Interrupt 0 by setting its bit in the GICR register.
MCUCR = 0X02; //Configure the mode of External Interrupt 0 (hexadecimal value).
sei(); //Enable global interrupts
lcd_init(); //Initialize the LCD.
while (1)
{
while (pw == 0)
{
if (sw_dir == 0)
{
PORTA = 0b00000001;
lcd_out(1, 1, "Motor ON-CLW");
}
else
{
PORTA = 0b00000010;
lcd_out(1, 1, "Motor ON-ACL");
}
OCR0 = i;
}
PORTA = 0X00;
lcd_out(1, 1, "Motor is_OFF");
}
}
// Interrupt Service Routine for External Interrupt 0
ISR(INT0_vect)
{
i = i + 20; // Increment 'i' by 20 when INT0 is triggered
}