#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
// LCD Pin Definitions (4-bit mode)
#define LCD_RS_PORT PORTD
#define LCD_RS_DDR DDRD
#define LCD_RS_PIN PD0
#define LCD_EN_PORT PORTD
#define LCD_EN_DDR DDRD
#define LCD_EN_PIN PD1
#define LCD_D4_PORT PORTD
#define LCD_D4_DDR DDRD
#define LCD_D4_PIN PD4
#define LCD_D5_PORT PORTD
#define LCD_D5_DDR DDRD
#define LCD_D5_PIN PD5
#define LCD_D6_PORT PORTD
#define LCD_D6_DDR DDRD
#define LCD_D6_PIN PD6
#define LCD_D7_PORT PORTD
#define LCD_D7_DDR DDRD
#define LCD_D7_PIN PD7
// Traffic Light Pins
#define PR_GREEN PB0 // Park Road Green
#define PR_YELLOW PB1 // Park Road Yellow
#define PR_RED PB2 // Park Road Red
#define RS_GREEN PB3 // Railway Street Green
#define RS_YELLOW PB4 // Railway Street Yellow
#define RS_RED PB5 // Railway Street Red
#define DS_GREEN PC1 // Dam Street Green
#define DS_YELLOW PC2 // Dam Street Yellow
#define DS_RED PC3 // Dam Street Red
// Sensor Pins (Active-Low Buttons)
#define S0 PD2
#define S1 PD3
#define S2 PC4
#define S3 PC5
#define S4 PB6
#define S6 PB7 // Hazard Input
// States
typedef enum {
HAZARD,
PRT, // Park Road Through
RST, // Railway Street Through
DST // Dam Street Through
} Phase;
// Global Variables
volatile Phase currentPhase = HAZARD;
volatile uint8_t sensors = 0;
volatile uint16_t timePeriod = 1000; // Default: 1s
volatile uint32_t phaseTimer = 0;
volatile uint32_t systemTime = 0;
volatile uint8_t hazardFlag = 0;
// LCD Functions
void lcd_pulse_enable() {
LCD_EN_PORT |= (1 << LCD_EN_PIN);
_delay_us(1);
LCD_EN_PORT &= ~(1 << LCD_EN_PIN);
_delay_us(100);
}
void lcd_send_nibble(uint8_t nibble) {
if (nibble & (1 << 0)) LCD_D4_PORT |= (1 << LCD_D4_PIN);
else LCD_D4_PORT &= ~(1 << LCD_D4_PIN);
if (nibble & (1 << 1)) LCD_D5_PORT |= (1 << LCD_D5_PIN);
else LCD_D5_PORT &= ~(1 << LCD_D5_PIN);
if (nibble & (1 << 2)) LCD_D6_PORT |= (1 << LCD_D6_PIN);
else LCD_D6_PORT &= ~(1 << LCD_D6_PIN);
if (nibble & (1 << 3)) LCD_D7_PORT |= (1 << LCD_D7_PIN);
else LCD_D7_PORT &= ~(1 << LCD_D7_PIN);
lcd_pulse_enable();
}
void lcd_send_byte(uint8_t data, uint8_t rs) {
if (rs) LCD_RS_PORT |= (1 << LCD_RS_PIN);
else LCD_RS_PORT &= ~(1 << LCD_RS_PIN);
lcd_send_nibble(data >> 4);
lcd_send_nibble(data & 0x0F);
_delay_us(100);
}
void lcd_command(uint8_t cmd) {
lcd_send_byte(cmd, 0);
}
void lcd_data(uint8_t data) {
lcd_send_byte(data, 1);
}
void lcd_init() {
// Configure LCD pins as outputs
LCD_RS_DDR |= (1 << LCD_RS_PIN);
LCD_EN_DDR |= (1 << LCD_EN_PIN);
LCD_D4_DDR |= (1 << LCD_D4_PIN);
LCD_D5_DDR |= (1 << LCD_D5_PIN);
LCD_D6_DDR |= (1 << LCD_D6_PIN);
LCD_D7_DDR |= (1 << LCD_D7_PIN);
// Initialization sequence
_delay_ms(50);
lcd_send_nibble(0x03);
_delay_ms(5);
lcd_send_nibble(0x03);
_delay_us(100);
lcd_send_nibble(0x03);
lcd_send_nibble(0x02); // Switch to 4-bit mode
// Function set: 4-bit, 2-line, 5x8 dots
lcd_command(0x28);
// Display on, cursor off, blink off
lcd_command(0x0C);
// Clear display
lcd_command(0x01);
_delay_ms(2);
// Entry mode set
lcd_command(0x06);
}
void lcd_print(const char *str) {
while (*str) {
lcd_data(*str++);
}
}
void lcd_print_number(uint32_t num) {
char buffer[10];
uint8_t i = 0;
if (num == 0) {
lcd_data('0');
return;
}
while (num > 0) {
buffer[i++] = '0' + (num % 10);
num /= 10;
}
while (i > 0) {
lcd_data(buffer[--i]);
}
}
// Initialize Hardware
void init_hardware() {
// Set sensor pins as inputs with pull-ups
DDRD &= ~((1 << S0) | (1 << S1));
DDRC &= ~((1 << S2) | (1 << S3));
DDRB &= ~((1 << S4) | (1 << S6));
PORTD |= (1 << S0) | (1 << S1);
PORTC |= (1 << S2) | (1 << S3);
PORTB |= (1 << S4) | (1 << S6);
// Set traffic light pins as outputs
DDRB |= (1 << PR_GREEN) | (1 << PR_YELLOW) | (1 << PR_RED) |
(1 << RS_GREEN) | (1 << RS_YELLOW) | (1 << RS_RED);
DDRC |= (1 << DS_GREEN) | (1 << DS_YELLOW) | (1 << DS_RED);
// ADC for potentiometer (PC0)
ADMUX = (1 << REFS0); // AVCC reference, ADC0
ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); // Enable ADC, prescaler=64
// Timer1 for phase timing (1ms interrupt)
TCCR1B = (1 << WGM12) | (1 << CS11); // CTC mode, prescaler=8
OCR1A = 125; // 1ms interrupt @ 1MHz
TIMSK1 = (1 << OCIE1A);
}
// Timer1 Interrupt (Updates time period and system clock)
ISR(TIMER1_COMPA_vect) {
static uint16_t adcCounter = 0;
if (++adcCounter >= 1000) { // Read ADC every 1s
ADCSRA |= (1 << ADSC);
while (ADCSRA & (1 << ADSC));
uint16_t adcValue = ADC;
timePeriod = 50 + (adcValue * 950) / 1023; // Map to 50ms-1000ms
adcCounter = 0;
}
phaseTimer++;
systemTime++;
}
// Read Sensors (Active-Low)
uint8_t read_sensors() {
uint8_t sensorState = 0;
if (!(PIND & (1 << S0))) sensorState |= (1 << 0);
if (!(PIND & (1 << S1))) sensorState |= (1 << 1);
if (!(PINC & (1 << S2))) sensorState |= (1 << 2);
if (!(PINC & (1 << S3))) sensorState |= (1 << 3);
if (!(PINB & (1 << S4))) sensorState |= (1 << 4);
if (!(PINB & (1 << S6))) sensorState |= (1 << 6);
return sensorState;
}
// Control Traffic Lights
void set_lights(Phase phase) {
// Turn off all lights first
PORTB &= ~((1 << PR_GREEN) | (1 << PR_YELLOW) | (1 << PR_RED) |
(1 << RS_GREEN) | (1 << RS_YELLOW) | (1 << RS_RED));
PORTC &= ~((1 << DS_GREEN) | (1 << DS_YELLOW) | (1 << DS_RED));
switch (phase) {
case HAZARD:
Serial.println("Flash yellow lights");
// Flash yellow lights
if (phaseTimer % 2000 < 1000) {
PORTB |= (1 << PR_YELLOW) | (1 << RS_YELLOW);
PORTC |= (1 << DS_YELLOW);
}
break;
case PRT:
Serial.println("Park Road green, others red");
// Park Road green, others red
PORTB |= (1 << PR_GREEN);
PORTC |= (1 << DS_RED);
PORTB |= (1 << RS_RED);
break;
case RST:
Serial.println("Railway Street green, others red");
// Railway Street green, others red
PORTB |= (1 << RS_GREEN);
PORTC |= (1 << DS_RED);
PORTB |= (1 << PR_RED);
break;
case DST:
Serial.println("Dam Street green, others red");
// Dam Street green, others red
PORTC |= (1 << DS_GREEN);
PORTB |= (1 << PR_RED) | (1 << RS_RED);
break;
}
}
// State Machine Logic
void run_state_machine() {
static uint8_t lastPhase = HAZARD;
// Hazard mode override
if (sensors & (1 << 6)) { // S6 grounded
hazardFlag = 1;
currentPhase = HAZARD;
phaseTimer = 0;
set_lights(HAZARD);
return;
}
// Exit hazard after 10s
if (hazardFlag && phaseTimer >= 10000) {
hazardFlag = 0;
currentPhase = PRT;
phaseTimer = 0;
}
// Normal operation
if (!hazardFlag) {
switch (currentPhase) {
case PRT:
if (phaseTimer >= (4 * timePeriod)) {
if (sensors & ((1 << 2) | (1 << 3) | (1 << 4))) { // RST or DST sensors
if (lastPhase != RST) currentPhase = RST;
else currentPhase = DST; // Avoid repeats
phaseTimer = 0;
}
}
break;
case RST:
if (phaseTimer >= (2 * timePeriod)) {
if (sensors & (1 << 4)) { // DST sensor
currentPhase = DST;
phaseTimer = 0;
} else if (phaseTimer >= (3 * timePeriod)) {
currentPhase = PRT;
phaseTimer = 0;
}
}
break;
case DST:
if (phaseTimer >= (2 * timePeriod)) {
if (sensors & ((1 << 0) | (1 << 1))) { // PRT sensors
currentPhase = PRT;
phaseTimer = 0;
} else if (phaseTimer >= (4 * timePeriod)) {
currentPhase = PRT;
phaseTimer = 0;
}
}
break;
}
lastPhase = currentPhase;
set_lights(currentPhase);
}
}
// Update LCD Display
void update_lcd() {
lcd_command(0x01); // Clear LCD
_delay_ms(2);
// Top Row: Sensors (S0-S6)
lcd_command(0x80); // Row 1, Column 1
for (uint8_t i = 0; i < 7; i++) {
lcd_data((sensors & (1 << i)) ? 'X' : '_');
}
// Top Row Right: Time Counter (5 digits)
lcd_command(0x8B); // Row 1, Column 12
lcd_print_number(systemTime % 100000);
// Bottom Row: Phase and Light State
lcd_command(0xC0); // Row 2, Column 1
switch (currentPhase) {
case HAZARD:
lcd_print("HZD ");
break;
case PRT:
lcd_print("PRT G ");
break;
case RST:
lcd_print("RST G ");
break;
case DST:
lcd_print("DST G ");
break;
}
}
int main(void) {
Serial.begin(115200);
// Initialize hardware
init_hardware();
// Initialize LCD
lcd_init();
// Enable global interrupts
sei();
// Initial display
lcd_print("Traffic Control");
_delay_ms(1000);
while (1) {
// Read sensors
sensors = read_sensors();
// Run state machine
run_state_machine();
// Update LCD
update_lcd();
// Small delay for debounce
_delay_ms(100);
}
return 0;
}void setup() {
// put your setup code here, to run once:
}
void loop() {
// put your main code here, to run repeatedly:
}