This shows you the differences between two versions of the page.
|
pm:prj2026:andrei.batasev:dorian.gilca [2026/05/23 18:05] dorian.gilca |
pm:prj2026:andrei.batasev:dorian.gilca [2026/05/25 09:55] (current) dorian.gilca |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| ====== Adaptive Cruise Control Vehicle ====== | ====== Adaptive Cruise Control Vehicle ====== | ||
| + | |||
| ===== Introduction ===== | ===== Introduction ===== | ||
| **What it does:** | **What it does:** | ||
| - | This project consists of a small autonomous vehicle capable of maintaining a constant safe distance from a target vehicle or object in front of it. | + | This project consists of a small autonomous vehicle capable of maintaining a constant safe distance from a target object or vehicle in front of it. |
| **Its purpose:** | **Its purpose:** | ||
| - | The main goal is to implement a real-time closed-loop control system (Adaptive Cruise Control) on an 8-bit microcontroller. It demonstrates bare-metal embedded programming by utilizing hardware interrupts, hardware timers, and a PID (Proportional-Integral-Derivative) control algorithm. | + | The main goal is to implement a real-time closed-loop control system (Adaptive Cruise Control) on an 8-bit microcontroller. It demonstrates bare-metal embedded programming by utilizing hardware interrupts, hardware timers, and custom serial protocols, completely avoiding high-level abstractions like Arduino libraries. |
| **The starting idea:** | **The starting idea:** | ||
| Line 16: | Line 17: | ||
| ===== General Description ===== | ===== General Description ===== | ||
| - | The system uses an array of ultrasonic sensors mounted on the front to continuously measure the distance to the vehicle ahead. | + | The system uses an ultrasonic sensor mounted on the front to continuously measure the distance to the obstacle ahead. |
| - | Based on the calculated distance error (the difference between the desired safe distance and the actual measured distance), a PID control algorithm calculates the necessary adjustments. These adjustments are sent as PWM signals to a TB6612FNG motor driver, enabling the car to smoothly accelerate or brake. An optical encoder on the rear wheel provides closed-loop speed feedback to ensure accurate motor control. Optionally, the front steering is controlled by a servo motor to follow the target's trajectory. | + | Based on the calculated distance, a proportional control algorithm calculates the necessary speed adjustments to maintain a safe gap. The system smoothly interpolates the motor speed using hardware PWM based on the target's distance. If the target is too close (under 25 cm), the vehicle applies the brakes. If the target is within the tracking range (25 cm - 85 cm), it adjusts the speed dynamically to follow it. |
| **Block Diagram:** | **Block Diagram:** | ||
| - | //To be uploaded// | + | |
| + | |||
| + | {{:pm:prj2026:andrei.batasev:schema_bloc.jpg?300|}} | ||
| ===== Hardware Design ===== | ===== Hardware Design ===== | ||
| **List of components:** | **List of components:** | ||
| - | * Arduino Nano (ATmega328P Microcontroller) | + | * Arduino UNO (ATmega328P Microcontroller) serving as the main processing unit. |
| - | * 3x HC-SR04+ Ultrasonic Sensors | + | * L293D Motor Drive Shield (featuring the 74HC595 shift register). |
| - | * TB6612FNG Motor Driver | + | * 1x HC-SR04 Ultrasonic Sensor. |
| - | * 1x DC Motor with Optical Encoder (Rear traction) | + | * 2x DC TT Motors (Rear-wheel or 4-wheel drive configuration). |
| - | * 1x Servo Motor (Front steering) | + | * 2x 18650 Li-Ion Batteries (7.4V total) with holder. |
| - | * LM2596 Step-Down Voltage Regulator | + | * Robot car chassis with wheels. |
| - | * 2x 18650 Li-Ion Batteries with holder | + | * Dupont connecting wires. |
| - | * Breadboard and Dupont connecting wires | + | |
| **Electrical Schematic:** | **Electrical Schematic:** | ||
| - | //To be uploaded after the circuit design is finalized.// | + | |
| + | |||
| + | |||
| + | {{:pm:prj2026:andrei.batasev:schema_electrica.jpg?300|}} | ||
| ===== Software Design ===== | ===== Software Design ===== | ||
| + | <code c> | ||
| + | #include <avr/io.h> | ||
| + | #include <avr/interrupt.h> | ||
| + | #include <util/delay.h> | ||
| + | #include <stdio.h> | ||
| + | |||
| + | #define PM_BAUD 9600 | ||
| + | |||
| + | #define TRIG_PIN PC0 | ||
| + | #define ECHO_PIN PC1 | ||
| + | |||
| + | #define LATCH_PIN PB4 | ||
| + | #define CLK_PIN PD4 | ||
| + | #define EN_PIN PD7 | ||
| + | #define DATA_PIN PB0 | ||
| + | |||
| + | #define M1_A 2 | ||
| + | #define M1_B 3 | ||
| + | #define M2_A 1 | ||
| + | #define M2_B 4 | ||
| + | |||
| + | volatile uint16_t timer_val = 0; | ||
| + | volatile uint8_t echo_done = 0; | ||
| + | |||
| + | static int _usart0_putchar(char c, FILE *stream) { | ||
| + | if (c == '\n') _usart0_putchar('\r', stream); | ||
| + | while (!(UCSR0A & (1<<UDRE0))); | ||
| + | UDR0 = c; | ||
| + | return 0; | ||
| + | } | ||
| + | |||
| + | static FILE USART0_stdout = FDEV_SETUP_STREAM(_usart0_putchar, NULL, _FDEV_SETUP_WRITE); | ||
| + | |||
| + | void usart_init() { | ||
| + | UBRR0H = (unsigned char)(103>>8); //br9600 | ||
| + | UBRR0L = (unsigned char)103; | ||
| + | UCSR0B = (1<<RXEN0) | (1<<TXEN0); //act transmisia | ||
| + | UCSR0C = (1<<USBS0) | (3<<UCSZ00); //8b | ||
| + | stdout = &USART0_stdout; | ||
| + | } | ||
| + | |||
| + | void shift_out_data(uint8_t data) { | ||
| + | PORTB &= ~(1<<LATCH_PIN); | ||
| + | | ||
| + | for(int i=0; i<8; i++) { //pt fiecare bit | ||
| + | PORTD &= ~(1<<CLK_PIN); | ||
| + | if(data & (1 << (7-i))) { | ||
| + | PORTB |= (1<<DATA_PIN); //punem val d | ||
| + | } else { | ||
| + | PORTB &= ~(1<<DATA_PIN); | ||
| + | } | ||
| + | PORTD |= (1<<CLK_PIN); //ridicam si coboram pin clock | ||
| + | } | ||
| + | | ||
| + | PORTB |= (1<<LATCH_PIN); | ||
| + | } | ||
| + | |||
| + | void motors_init() { | ||
| + | DDRB |= (1<<LATCH_PIN) | (1<<DATA_PIN) | (1<<PB3); | ||
| + | DDRD |= (1<<CLK_PIN) | (1<<EN_PIN) | (1<<PD3); | ||
| + | |||
| + | PORTD &= ~(1<<EN_PIN); | ||
| + | |||
| + | TCCR2A = (1<<COM2A1) | (1<<COM2B1) | (1<<WGM21) | (1<<WGM20); | ||
| + | TCCR2B = (1<<CS22); | ||
| + | | ||
| + | OCR2A = 0; // punem viteza in registrii | ||
| + | OCR2B = 0; | ||
| + | |||
| + | shift_out_data(0); | ||
| + | } | ||
| + | |||
| + | void set_motor_dir(int m1_fwd, int m1_rev, int m2_fwd, int m2_rev) { | ||
| + | uint8_t shift_data = 0; | ||
| + | if (m1_fwd) shift_data |= (1<<M1_A); | ||
| + | if (m1_rev) shift_data |= (1<<M1_B); | ||
| + | if (m2_fwd) shift_data |= (1<<M2_A); | ||
| + | if (m2_rev) shift_data |= (1<<M2_B); | ||
| + | shift_out_data(shift_data); | ||
| + | } | ||
| + | |||
| + | void sonar_init() { | ||
| + | DDRC |= (1<<TRIG_PIN); //pc0 output | ||
| + | DDRC &= ~(1<<ECHO_PIN); | ||
| + | |||
| + | TCCR1A = 0; | ||
| + | TCCR1B = (1<<CS11); | ||
| + | |||
| + | PCICR |= (1<<PCIE1); | ||
| + | PCMSK1 |= (1<<PCINT9); | ||
| + | } | ||
| + | |||
| + | ISR(PCINT1_vect) { | ||
| + | if (PINC & (1<<ECHO_PIN)) { | ||
| + | TCNT1 = 0; | ||
| + | } else { | ||
| + | timer_val = TCNT1; | ||
| + | echo_done = 1; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | int get_distance() { | ||
| + | echo_done = 0; | ||
| + | | ||
| + | PORTC &= ~(1<<TRIG_PIN); | ||
| + | _delay_us(2); | ||
| + | PORTC |= (1<<TRIG_PIN); | ||
| + | _delay_us(10); | ||
| + | PORTC &= ~(1<<TRIG_PIN); | ||
| + | |||
| + | uint32_t timeout = 60000; | ||
| + | while(!echo_done && timeout > 0) { | ||
| + | timeout--; | ||
| + | } | ||
| + | |||
| + | if(timeout == 0) return 400; | ||
| + | |||
| + | return timer_val / 116; | ||
| + | } | ||
| + | |||
| + | int map_speed(int x, int in_min, int in_max, int out_min, int out_max) { | ||
| + | return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; | ||
| + | } | ||
| + | |||
| + | int main() { | ||
| + | usart_init(); | ||
| + | motors_init(); | ||
| + | sonar_init(); | ||
| + | | ||
| + | sei(); | ||
| + | printf("ACC System Bare Metal Started\n"); | ||
| + | |||
| + | int safe_dist = 25; | ||
| + | int max_dist = 85; | ||
| + | |||
| + | while(1) { | ||
| + | int dist = get_distance(); | ||
| + | if(dist == 0 || dist > 400) dist = 400; | ||
| + | |||
| + | printf("Dist: %d cm\n", dist); | ||
| + | |||
| + | if (dist > max_dist) { | ||
| + | set_motor_dir(0, 0, 0, 0); | ||
| + | OCR2A = 0; | ||
| + | OCR2B = 0; | ||
| + | } else if (dist > safe_dist) { | ||
| + | int speed = map_speed(dist, safe_dist, max_dist, 115, 220); | ||
| + | set_motor_dir(1, 0, 1, 0); | ||
| + | OCR2A = speed; | ||
| + | OCR2B = speed; | ||
| + | } else { | ||
| + | set_motor_dir(0, 0, 0, 0); | ||
| + | OCR2A = 0; | ||
| + | OCR2B = 0; | ||
| + | } | ||
| + | |||
| + | _delay_ms(50); | ||
| + | } | ||
| + | return 0; | ||
| + | } | ||
| + | </code> | ||
| **Development Environment:** | **Development Environment:** | ||
| - | * PlatformIO / Arduino IDE (using pure C/C++ and register-level programming) | + | * VS Code with PlatformIO (using pure C and register-level programming). |
| **Libraries and 3rd-party sources:** | **Libraries and 3rd-party sources:** | ||
| - | * Standard AVR libc (<avr/io.h>, <avr/interrupt.h>) | + | * Standard AVR libc (<avr/io.h>, <avr/interrupt.h>, <util/delay.h>, <stdio.h>). |
| - | * No high-level Arduino libraries (like analogWrite or pulseIn) will be used for core functionalities, to adhere to the course requirements. | + | * **Zero** high-level Arduino libraries (like `analogWrite`, `digitalWrite`, or `pulseIn`) were used. |
| - | **Algorithms and Structures:** | + | **Algorithms and Hardware Features utilized:** |
| - | * **PID Controller:** A mathematical algorithm to smoothly calculate the motor speed based on the distance error. | + | * **Bit-Banging for Shift Register:** The L293D shield uses a 74HC595 shift register to control motor directions. A custom synchronous serial protocol was implemented (`shift_out_data`) to manually push bits to the latch, clock, and data pins using direct port manipulation (`PORTB`, `PORTD`). |
| - | * **Pin Change Interrupts (PCINT):** Used to read the echo signals from the ultrasonic sensors without blocking the CPU. | + | * **Hardware Timers (Fast PWM):** Timer2 was configured at the register level (`TCCR2A`, `TCCR2B`) to generate a pure hardware Fast PWM signal on pins OC2A and OC2B. This controls the motor speed without utilizing CPU cycles. |
| - | * **Hardware Timers:** Configured at the register level to generate the fast PWM signals for the motor driver and the precise signal for the servo motor. | + | * **Pin Change Interrupts (PCINT) & Timer1:** To avoid blocking the CPU while waiting for the ultrasonic echo, the Echo pin was mapped to a Pin Change Interrupt (`PCINT1_vect`). Timer1 counts the micro-seconds in the background, making the distance reading asynchronous and highly efficient. |
| + | * **Custom USART Stream:** The USART module was initialized manually (`UBRR0`, `UCSR0`), and the standard output stream (`stdout`) was redirected to the serial port, allowing the use of `printf()` for clean, professional real-time debugging. | ||
| ===== Obtained Results ===== | ===== Obtained Results ===== | ||
| - | //This section will be updated after the hardware assembly and software implementation// | + | The system reacts accurately and smoothly to dynamic environments. |
| + | * The custom non-blocking ultrasonic reading accurately updates the distance without hanging the main loop. | ||
| + | * The proportional mapping successfully overcomes the static friction of the DC motors by setting a calculated minimum PWM threshold (115), ensuring the car starts moving reliably even at low speeds. | ||
| + | * The car smoothly decelerates as it approaches the 25 cm safety threshold and completely halts to prevent collisions. | ||
| + | **Final Project Photo:** | ||
| + | |||
| + | |||
| + | |||
| + | {{:pm:prj2026:andrei.batasev:2111.jpeg?200|}} | ||
| + | {{:pm:prj2026:andrei.batasev:999.jpeg?200|}} | ||
| ===== Conclusions ===== | ===== Conclusions ===== | ||
| - | //This section will be updated at the end of the project// | + | Implementing this project in a bare-metal C environment provided profound insights into the internal workings of the ATmega328P microcontroller. Stripping away the Arduino framework forced a deeper understanding of memory mapping, the crucial difference between polling and hardware interrupts, and the complexity of generating custom serial signals (bit-banging). The project successfully replicates an industrial ADAS concept on an 8-bit architecture. |
| ===== Download ===== | ===== Download ===== | ||
| - | + | {{:pm:prj2026:andrei.batasev:dorian_gilca.zip|Project Archive - Source Code and PlatformIO Configuration}} | |
| - | //Source code and schematics will be uploaded here in a .zip archive and linked to a public GitHub repository at the end of the semester.// | + | |
| ===== Journal ===== | ===== Journal ===== | ||
| - | * **09.05.2026**: Chosen the project topic, validated the idea with the laboratory assistant, and completed the initial OCW documentation (Introduction, General Description, Hardware List). | + | * **09.05.2026**: Chosen the project topic and completed the initial OCW documentation. |
| + | * **17.05.2026**: Assembled the hardware components, tested the L293D motor shield, and resolved DC motor wiring issues. | ||
| + | * **19.05.2026**: Fully migrated to VS Code / PlatformIO. Successfully implemented bare-metal code integrating PCINT for the HC-SR04 sensor, Timer2 Fast PWM for motor speed, and custom bit-banging for directional control. Tuned the PWM thresholds to overcome static friction. | ||
| ===== Bibliography/Resources ===== | ===== Bibliography/Resources ===== | ||
| **Hardware Resources:** | **Hardware Resources:** | ||
| - | * ATmega328P Datasheet | + | * ATmega328P Datasheet (Microchip Technology) |
| - | * HC-SR04+ Ultrasonic Sensor specifications | + | * HC-SR04 Ultrasonic Sensor Specifications |
| - | * TB6612FNG Motor Driver datasheet | + | * L293D Motor Shield Schematic & 74HC595 Datasheet |
| **Software Resources:** | **Software Resources:** | ||
| * AVR Libc Reference Manual | * AVR Libc Reference Manual | ||
| + | * PlatformIO Documentation | ||