Implementing a Voice-Controlled Smart Car with C51 Microcontroller
This project is built on the STC89C52RC microcontroller, utilizing PWM for speed control. It features voice-controlled mode switching for line tracking, following, obstacle avoidence, and remote control via Bluetooth/Wi-Fi/4G. The system also includes speed measurement with OLED display and data transmission to a host computer.
Motor Driver Module (L9110s, later upgraded to L298N with dual 18650 batteries)
Connecting VCC and GND powers the module, indicated by its LED. The following logic controls motor direction (Note: actual wiring may require adjustments based on practical testing):
- Setting IA1 high and IB1 low causes the motor connected to [OA1, OB1] to rotate forward.
- Setting IA1 low and IB1 high causes the motor connected to [OA1, OB1] to rotate backward.
- Similar logic applies to IA2/IB2 for the second motor.
Crucial Wiring Note: The microcontroller must have a seperate GND connection to the power supply.
Basic Motor Control
Adjust motor wire connections to ensure both wheels spin forward.
#include "reg52.h"
sbit MotorR_A = P3^2;
sbit MotorR_B = P3^3;
sbit MotorL_A = P3^4;
sbit MotorL_B = P3^5;
void main()
{
MotorL_A = 1;
MotorL_B = 0;
MotorR_A = 1;
MotorR_B = 0;
}
Implementing Car Movement
1. Direction Control Functions
void moveForward(){
MotorL_A = 1; MotorL_B = 0;
MotorR_A = 1; MotorR_B = 0;
}
void moveBackward(){
MotorL_A = 0; MotorL_B = 1;
MotorR_A = 0; MotorR_B = 1;
}
void turnLeft(){
MotorL_A = 1; MotorL_B = 1;
MotorR_A = 1; MotorR_B = 0;
}
void turnRight(){
MotorL_A = 1; MotorL_B = 0;
MotorR_A = 1; MotorR_B = 1;
}
void haltCar(){
MotorL_A = 1; MotorL_B = 1;
MotorR_A = 1; MotorR_B = 1;
}
2. RF Remote Control Using simple GPIO reading to interpret RF module outputs.
#include "reg52.h"
#include "motor.h"
sbit RF_D0 = P2^4;
sbit RF_D1 = P2^5;
sbit RF_D2 = P2^6;
sbit RF_D2 = P2^7;
void main()
{
unsigned char command = 0;
while(1){
if(RF_D0) command = 1;
else if(RF_D1) command = 2;
else if(RF_D2) command = 3;
else if(RF_D3) command = 4;
else command = 0;
switch(command){
case 1: moveForward(); break;
case 2: moveBackward(); break;
case 3: turnLeft(); break;
case 4: turnRight(); break;
case 0: haltCar(); break;
}
}
}
Bluetooth Control via UART
1. UART Initialization and Communication
// uart.c
#include "reg52.h"
sfr AUXR = 0x8E;
char bluetoothCmd = 's';
void initUART(void) // 9600bps @ 11.0592MHz
{
AUXR = 0x01;
SCON = 0x50;
TMOD &= 0x0F;
TMOD |= 0x20;
TH1 = TL1 = 0xFD;
TR1 = 1;
EA = ES = 1;
}
void sendUARTChar(unsigned char dat)
{
SBUF = dat;
while(!TI);
TI = 0;
}
char fetchCommand(){
return bluetoothCmd;
}
void handleUART() interrupt 4
{
unsigned char receivedByte;
if(RI){
RI = 0;
receivedByte = SBUF;
bluetoothCmd = receivedByte;
sendUARTChar('A'); // Acknowledge
}
}
2. Motor Control Based on UART Command
// motor_control.c
void executeCmd(char cmd)
{
switch(cmd){
case 'f': moveForward(); break;
case 'b': moveBackward(); break;
case 'l': turnLeft(); break;
case 'r': turnRight(); break;
case 's': haltCar(); break;
}
}
3. Momentary Control via Bluetooth Adjust the Bluetooth app to send commands every 0.2s while a button is pressed. The interrupt handler executes the movement for 200ms.
void handleUART() interrupt 4
{
unsigned char cmdByte;
if(RI){
RI = 0;
cmdByte = SBUF;
bluetoothCmd = cmdByte;
executeCmd(bluetoothCmd);
delayMs(200); // Move for 200ms
}
}
The main loop keeps the car stopped when no command is received.
void main()
{
initUART();
while(1){
haltCar();
}
}
PWM-Based Speed Control
The principle involves varying the duty cycle of the signal sent to the motor drivers within a fixed period (e.g., 20ms). A higher duty cycle (more time active) results in higher average power and faster speed.
Testing PWM Speed
#include "reg52.h"
sbit MotorR_A = P3^2;
sbit MotorR_B = P3^3;
sbit MotorL_A = P3^4;
sbit MotorL_B = P3^5;
int pwmDuty = 40; // Range 0-40
int timerTicks = 0;
void configTimer0() // 500us interval
{
TMOD = 0x01;
TH0 = 0xFE; TL0 = 0x33;
TR0 = 1;
ET0 = EA = 1;
}
void main()
{
pwmDuty = 40;
timerTicks = 0;
configTimer0();
while(1){
// Test different speeds
pwmDuty = 40; // Fast
delayMs(2000);
pwmDuty = 20; // Slow
delayMs(2000);
pwmDuty = 0; // Stop
delayMs(2000);
}
}
void timer0_ISR() interrupt 1
{
timerTicks++;
TH0 = 0xFE; TL0 = 0x33; // Reload
if(timerTicks < pwmDuty){
moveForward();
} else {
haltCar();
}
if(timerTicks == 40){ // 20ms period
timerTicks = 0;
}
}
Independent Wheel Speed Control Using two separate PWM variables and two timers (or one timer with two counters) for independent control.
int leftWheelSpeed = 30;
int rightWheelSpeed = 30;
int leftTickCnt = 0;
int rightTickCnt = 0;
void configTimer0_1() // For left and right wheels
{
// Timer0 for left wheel
TMOD = 0x01;
TH0 = 0xFE; TL0 = 0x33;
TR0 = ET0 = 1;
// Timer1 for right wheel
TMOD |= 0x10;
TH1 = 0xFE; TL1 = 0x33;
TR1 = ET1 = 1;
EA = 1;
}
void timer0_ISR() interrupt 1 // Left wheel control
{
leftTickCnt++;
TH0 = 0xFE; TL0 = 0x33;
if(leftTickCnt < leftWheelSpeed){
MotorL_A = 1; MotorL_B = 0;
} else {
MotorL_A = 1; MotorL_B = 1;
}
if(leftTickCnt == 40) leftTickCnt = 0;
}
void timer1_ISR() interrupt 3 // Right wheel control
{
rightTickCnt++;
TH1 = 0xFE; TL1 = 0x33;
if(rightTickCnt < rightWheelSpeed){
MotorR_A = 1; MotorR_B = 0;
} else {
MotorR_A = 1; MotorR_B = 1;
}
if(rightTickCnt == 40) rightTickCnt = 0;
}
Infrared Line Tracking and Object Following
The TCRT5000 sensor emits infrared light. When no object is detected, the light is not reflected back, and the digital output (D0) remains high. When a object (like a black line for tracking or a surface for following) reflects sufficient IR light back, the output goes low.
Integration Principle: For line tracking, sensors are placed under the car. A high output over a white surface and a low output over a black line guide the car's steering logic to stay on the line. For object following, sensors face forward, and the car adjusts its position to maintain a specific sensor reading pattern.
Combined Logic Example:
// Define sensor pins
sbit IRSensor_L = P1^0;
sbit IRSensor_R = P1^1;
void lineTrackingMode()
{
// Simple two-sensor line tracking
if(!IRSensor_L && IRSensor_R){ // Left sensor on line, turn right
turnRight();
} else if(IRSensor_L && !IRSensor_R){ // Right sensor on line, turn left
turnLeft();
} else if(!IRSensor_L && !IRSensor_R){ // Both on line, go forward
moveForward();
} else { // Both off line, stop or search
haltCar();
}
}
void followMode()
{
// Simple following: aim to have both sensors triggered equally
if(IRSensor_L && !IRSensor_R){
// Object is more to the right, turn slightly right
// Adjust right wheel speed lower
rightWheelSpeed = 15;
leftWheelSpeed = 30;
} else if(!IRSensor_L && IRSensor_R){
// Object is more to the left
leftWheelSpeed = 15;
rightWheelSpeed = 30;
} else {
// Centered, move forward
leftWheelSpeed = rightWheelSpeed = 30;
moveForward();
}
}