ARDUINO巡线小车主控部分

概要

在这里插入图片描述

一、基本概念

1. 变量类型

(1) 无符号变量
类型 范围 等效类型
unsigned char 0-255 uint8_t
unsigned int 0-65,535 uint16_t
unsigned long 0-4,294,967,295 uint32_t
bool 0(False)/1(True) -
(2) 有符号变量
类型 范围
char -128~127
int -32,768~32,767
float ±3.4e±38 (~7位精度)
(3) 特殊修饰符
修饰符 作用
static 函数内:保持局部变量值
全局:限制作用域到当前文件
const 声明只读变量(初始化后不可修改)
volatile 禁止编译器优化(用于多线程/中断变量)

2. 阻塞 vs 非阻塞延时

类型 特点 适用场景
阻塞(delay()) 独占CPU
代码执行暂停
简单任务
单任务系统
非阻塞(定时器) 后台计时
多任务并行
复杂系统
实时响应需求

3. 宏定义

#define LED_PIN 12      // 无类型简单替换

二、定时器的配置与使用

1.定时器的基本概念(点击蓝色字体跳转)

(1)Arduino的定时器功能
(2)Arduino的定时器配置

2.定时器配置模板(1ms中断)

//定时器2初始化配置 1ms
void timer2_init() 
{
  // 配置Timer2
  noInterrupts();           // 禁用所有中断
  TCCR2A = 0;               // 清零控制寄存器A
  TCCR2B = 0;               // 清零控制寄存器B
  TCNT2  = 0;               // 初始化计数器为0
  
  // 设置比较匹配寄存器 - 每1ms中断一次
  OCR2A = 249;              // = (16MHz / (64 * 1000Hz)) - 1
  
  TCCR2A |= (1 << WGM21);   // 启用CTC模式(比较匹配时清零计数器)
  TCCR2B |= (1 << CS22);    // 设置64预分频 (CS22=1)
  TIMSK2 |= (1 << OCIE2A);  // 启用定时器比较匹配中断
  interrupts();             // 启用所有中断
}

void setup() {
  timer2_init(); //1ms
}

// Timer2比较匹配中断服务函数
ISR(TIMER2_COMPA_vect) {

}

void loop() {

}

3.定时器功能

(1)阻塞与非阻塞函数的区别

单任务场景(无明显差异)

#include <stdbool.h> //声明bool

unsigned char led_slow_down = 0;//led延时函数
unsigned int Motor_delay = 0;//延时计数
bool led_start = 0;//1-点亮 0熄灭

//LED处理函数
void led_proc(void)
{
  if(led_slow_down) return;
  led_slow_down = 1;

  /* 非阻塞延时 */
  // digitalWrite(12,led_start);

  /* 阻塞延时 */
  digitalWrite(12,0);
  delay(500);
  digitalWrite(12,1);
  delay(500);
}

//定时器2初始化配置 1ms
void timer2_init() 
{
  // 配置Timer2
  noInterrupts();           // 禁用所有中断
  TCCR2A = 0;               // 清零控制寄存器A
  TCCR2B = 0;               // 清零控制寄存器B
  TCNT2  = 0;               // 初始化计数器为0
  
  // 设置比较匹配寄存器 - 每1ms中断一次
  OCR2A = 249;              // = (16MHz / (64 * 1000Hz)) - 1
  
  TCCR2A |= (1 << WGM21);   // 启用CTC模式(比较匹配时清零计数器)
  TCCR2B |= (1 << CS22);    // 设置64预分频 (CS22=1)
  TIMSK2 |= (1 << OCIE2A);  // 启用定时器比较匹配中断
  interrupts();             // 启用所有中断
}

void setup() {
  pinMode(12,OUTPUT);
  timer2_init(); //1ms
}

// Timer2比较匹配中断服务函数
ISR(TIMER2_COMPA_vect) {
  if(++led_slow_down == 10) led_slow_down = 0;//led刷新时间

  /* 定时器延时计数(非阻塞) */
  if(++Motor_delay >= 500)
  {
    Motor_delay = 0;//清空计数值
    led_start ^= 1;//切换亮灭状态
  }
}

void loop() {
  led_proc();//刷新LED
}

多任务场景(关键差异)
演示代码:

/*  调用库声明 */
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <stdbool.h> //声明bool

/* Oled配置相关 */
Adafruit_SSD1306 OLED(128,64,&Wire,-1);

/* 变量声明区 */
unsigned char led_slow_down = 0;//led延时函数
unsigned char oled_slow_down = 0;//oled延时函数
unsigned int Motor_delay = 0;//延时计数
bool led_start = 0;//1-点亮 0熄灭
unsigned int i = 0;

//LED处理函数
void led_proc(void)
{
  if(led_slow_down) return;
  led_slow_down = 1;

  /* 非阻塞延时 */
  digitalWrite(12,led_start);

  /* 阻塞延时 */
  // digitalWrite(12,0);
  // delay(500);
  // digitalWrite(12,1);
  // delay(500);
}

/* Oled显示函数 */
void oled_display(void)
{
  if(oled_slow_down) return;
    oled_slow_down = 1;

  OLED.clearDisplay();//清屏
  OLED.setTextSize(1);//字号大小

  // 第一行:显示左侧距离
  OLED.setCursor(0,0);
  OLED.print("i: ");
  OLED.print(i);

  OLED.display();
}

//定时器2初始化配置 1ms
void timer2_init() 
{
  // 配置Timer2
  noInterrupts();           // 禁用所有中断
  TCCR2A = 0;               // 清零控制寄存器A
  TCCR2B = 0;               // 清零控制寄存器B
  TCNT2  = 0;               // 初始化计数器为0
  
  // 设置比较匹配寄存器 - 每1ms中断一次
  OCR2A = 249;              // = (16MHz / (64 * 1000Hz)) - 1
  
  TCCR2A |= (1 << WGM21);   // 启用CTC模式(比较匹配时清零计数器)
  TCCR2B |= (1 << CS22);    // 设置64预分频 (CS22=1)
  TIMSK2 |= (1 << OCIE2A);  // 启用定时器比较匹配中断
  interrupts();             // 启用所有中断
}

void setup() {
  /*  LED相关  */
  pinMode(12,OUTPUT);

  /*  Oled相关  */
  OLED.begin(SSD1306_SWITCHCAPVCC,0x3c);
  OLED.setTextColor(1);

  timer2_init(); //1ms
}

// Timer2比较匹配中断服务函数
ISR(TIMER2_COMPA_vect) {
  if(++led_slow_down == 10) led_slow_down = 0;//led刷新时间
  if(++oled_slow_down == 10) oled_slow_down = 0;//oled刷新时间

  i++;

  /* 定时器延时计数(非阻塞) */
  if(++Motor_delay >= 500)
  {
    Motor_delay = 0;//清空计数值
    led_start ^= 1;//切换亮灭状态
  }
}

void loop() {
  led_proc();//刷新LED
  oled_display();//刷新OLED
}

阻塞延时效果:
现象:LED闪烁期间OLED刷新完全停止
原因:delay()函数独占CPU资源

非阻塞延时效果:
现象:LED闪烁时OLED保持流畅刷新
优势:定时器中断后台计时,不占用主循环资源

小结:在多任务系统中,应减少阻塞延时函数的使用,防止延时任务对其他任务响应速度造成影响。

三、本次考核使用到的模块

1.OLED显示

(1)基本信息

视频教程:Arduino控制oled屏幕,速通-哔哩哔哩
详细信息:机器谱-OLED

(2)OLED显示代码(模块化+定时器2刷新)
/*  调用库声明 */
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

/* Oled配置相关 */
Adafruit_SSD1306 OLED(128,64,&Wire,-1);

/* 变量声明区 */
unsigned char oled_slow_down = 0;//oled延时函数
unsigned char i = 0;//oled显示变量
float j = 1.234;//oled显示变量

/* Oled显示函数 */
void oled_display(void)
{
  if(oled_slow_down) return;
    oled_slow_down = 1;

  OLED.clearDisplay();//清屏

  // 第一行
  OLED.setCursor(0,0);//x:0 y:0
  OLED.print("i: ");//显示字符
  OLED.print(i);//显示变量值

  // 第二行
  OLED.setCursor(0,10);//x:0 y:10
  OLED.print("j: ");
  OLED.print(j);

  OLED.display();//oled刷新函数
}

//定时器2初始化配置 1ms
void timer2_init() 
{
  // 配置Timer2
  noInterrupts();           // 禁用所有中断
  TCCR2A = 0;               // 清零控制寄存器A
  TCCR2B = 0;               // 清零控制寄存器B
  TCNT2  = 0;               // 初始化计数器为0
  
  // 设置比较匹配寄存器 - 每1ms中断一次
  OCR2A = 249;              // = (16MHz / (64 * 1000Hz)) - 1
  
  TCCR2A |= (1 << WGM21);   // 启用CTC模式(比较匹配时清零计数器)
  TCCR2B |= (1 << CS22);    // 设置64预分频 (CS22=1)
  TIMSK2 |= (1 << OCIE2A);  // 启用定时器比较匹配中断
  interrupts();             // 启用所有中断
}

void setup() {
  /*  Oled相关  */
  OLED.begin(SSD1306_SWITCHCAPVCC,0x3c);//设置驱动型号及I2C地址
  OLED.setTextColor(1);//设置文本颜色
  OLED.setTextSize(1);//字号大小

  /*  定时器2初始化 */
  timer2_init(); //1ms
}

// Timer2比较匹配中断服务函数
ISR(TIMER2_COMPA_vect) {
  if(++oled_slow_down == 10) oled_slow_down = 0;//oled刷新时间 10ms刷新一次
}

void loop() {
  oled_display();//刷新OLED
}

2.电机驱动

(1)基本信息

电机信息:机器谱-直流电机
驱动方式介绍:PWM控制电机

(2)代码介绍

引脚定义

#define Motor_Right_PIN_A 6 //A相为黑线
#define Motor_Right_PIN_B 5
#define Motor_Left_PIN_A 10 //A相为黑线
#define Motor_Left_PIN_B 9

单极性驱动

analogWrite(Motor_Left_PIN_A , 70);      // 左电机正转
analogWrite(Motor_Left_PIN_B , 0);    
analogWrite(Motor_Right_PIN_A , 150);   // 右电机正转
analogWrite(Motor_Right_PIN_B , 0);     

双极性驱动
只建议少量使用,该方式比较伤电机

analogWrite(Motor_Left_PIN_A , 255);      //同侧朝速度最大的方向转
analogWrite(Motor_Left_PIN_B , 230);    
analogWrite(Motor_Right_PIN_A , 255);   // 同侧速度设置相同停转
analogWrite(Motor_Right_PIN_B , 255);     

完整示例代码(双极性)

/*  电机相关  */
#define Motor_Right_PIN_A 6 //A相为黑线
#define Motor_Right_PIN_B 5
#define Motor_Left_PIN_A 10 //A相为黑线
#define Motor_Left_PIN_B 9

void setup() {
  /*  电机相关  */
  pinMode(10,OUTPUT); //将引脚10设置为输出模式
  pinMode(9,OUTPUT);  //将引脚9设置为输出模式
  pinMode(5,OUTPUT);  //将引脚5设置为输出模式
  pinMode(6,OUTPUT);  //将引脚6设置为输出模式
}

void loop() {
  analogWrite(Motor_Left_PIN_A , 255);    //同侧朝速度最大的方向转
  analogWrite(Motor_Left_PIN_B , 230);    
  analogWrite(Motor_Right_PIN_A , 255);   // 同侧速度设置相同停转
  analogWrite(Motor_Right_PIN_B , 255);     
}

3.蓝牙模块/串口接收

(1)基本信息

蓝牙模块介绍及使用:机器谱-蓝牙模块
Arduino串口接收:Arduino 串口接受字符串操作

(2)串口接收数据并显示到OLED

参考数据帧为:帧头(W)+路况值(-99 - 103);如W99
参考代码:

/*  调用库声明 */
#include <Wire.h> 
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

/* Oled配置相关 */
Adafruit_SSD1306 OLED(128,64,&Wire,-1);

/* 变量声明区 */
unsigned char oled_slow_down = 0;//oled延时函数
unsigned char i = 0;//oled显示变量

uint8_t uart_slow_down  = 0; //串口刷新专用变量
int received_value  = 0;//串口接收函数
int direction_car = 0;//小车行驶方向函数

// 串口接收处理函数
//接收数据 帧头(W) 数据(-100 - 103)
void uart_proc(void)
{
  if(uart_slow_down) return;
  uart_slow_down = 1;

  static char raw_buffer[64]; //接收数据存放数组
  static uint8_t idx = 0;     //接收数据长度
  
  // 非阻塞方式读取串口数据
  while (Serial.available() > 0) {
    char c = Serial.read();
    
    if(c == 'W')
    {
      idx = 0;
      raw_buffer[idx++] = c;
    }
    else if(c >= '0' && c <= '9')
    {
      if(idx < sizeof(raw_buffer) - 1)
        raw_buffer[idx++] = c;
    }
    else if(c == '-' && idx == 1)
    {
      raw_buffer[idx++] = c;
    }
    else if(c == '\n')
    {
      raw_buffer[idx] = '\0';
      received_value = atoi(raw_buffer + 1);

      if (received_value >= -100 && received_value <=103)  //接收完整信号后,进行处理
      {
        direction_car  = received_value; 
      }
      idx = 0;
      break;
    }
    else 
    {
      idx = 0;
    }
  }
}

/* Oled显示函数 */
void oled_display(void)
{
  if(oled_slow_down) return;
    oled_slow_down = 1;

  OLED.clearDisplay();//清屏
  
  i++;

  // 第一行
  OLED.setCursor(0,0);//x:0 y:0
  OLED.print("i:");//显示字符
  OLED.print(received_value);//显示变量值

  // 第一行
  OLED.setCursor(50,0);//x:0 y:0
  OLED.print("j:");//显示字符
  OLED.print(direction_car);//显示变量值

  OLED.setCursor(0,30);//x:0 y:0
  OLED.print("i: ");//显示字符
  OLED.print(i);//显示变量值

  OLED.display();//oled刷新函数
}

//定时器2初始化配置 1ms
void timer2_init() 
{
  // 配置Timer2
  noInterrupts();           // 禁用所有中断
  TCCR2A = 0;               // 清零控制寄存器A
  TCCR2B = 0;               // 清零控制寄存器B
  TCNT2  = 0;               // 初始化计数器为0
  
  // 设置比较匹配寄存器 - 每1ms中断一次
  OCR2A = 249;              // = (16MHz / (64 * 1000Hz)) - 1
  
  TCCR2A |= (1 << WGM21);   // 启用CTC模式(比较匹配时清零计数器)
  TCCR2B |= (1 << CS22);    // 设置64预分频 (CS22=1)
  TIMSK2 |= (1 << OCIE2A);  // 启用定时器比较匹配中断
  interrupts();             // 启用所有中断
}

void setup() {
  /*  Oled相关  */
  OLED.begin(SSD1306_SWITCHCAPVCC,0x3c);//设置驱动型号及I2C地址
  OLED.setTextColor(1);//设置文本颜色
  OLED.setTextSize(1);//字号大小

  /*  串口相关  */
  Serial.begin(9600);//波特率9600

  /*  定时器2初始化 */
  timer2_init(); //1ms
}

// Timer2比较匹配中断服务函数
ISR(TIMER2_COMPA_vect) {
  if(++oled_slow_down == 50) oled_slow_down = 0;//oled刷新时间 10ms刷新一次
  if(++uart_slow_down == 10) uart_slow_down = 0;//串口刷新时间 10ms刷新一次
}

void loop() {
  oled_display();//刷新OLED
  uart_proc();//串口刷新
}

电脑接收视觉模块串口发送值

Arduino接收视觉模块串口发送值,显示到OLED

蓝牙模块接收手机发送的数据

四、蓝牙遥控小车完整代码

/*  调用库声明 */
#include <Wire.h> 
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

/* oled配置相关 */
Adafruit_SSD1306 OLED(128,64,&Wire,-1);

/* 超声波配置引脚 */
#define LEFT_ECHOPIN A0
#define LEFT_TRIGPIN A1

/*  电机引脚配置  */
#define Motor_Right_PIN_A 6 //A相为黑线
#define Motor_Right_PIN_B 5
#define Motor_Left_PIN_A 10 //A相为黑线
#define Motor_Left_PIN_B 9

/* 变量声明区 */
uint8_t ultrasonic_slow_down  = 0; //超声波刷新专用变量
uint8_t oled_slow_down  = 0; //oled刷新专用变量
uint8_t motor_slow_down  = 0; //电机刷新专用变量
uint8_t uart_slow_down  = 0; //串口刷新专用变量
extern float distance_left  = 0;//超声波检测距离变量
String uart_buffer  = ""; //串口接收数组
uint8_t obstacle_count  = 0;//障碍物检测数量
bool obstacle_flag  = 0;//障碍物检测标志位
int received_value  = 0;//串口接收函数
int direction_car = 0;//小车行驶方向函数
int car_direction_state  = 0;//小车方向状态
bool car_diversion_flag = 0;//小车转向标志位
uint8_t crossroad_num = 0;//经过的十字路口数
bool return_flag = 0;//原路返回标志位
bool inversion_flag = 0;//倒转标志

uint8_t turn_state = 0;//转向状态机

// 串口接收处理函数
void uart_proc(void)
{
  if(uart_slow_down) return;
  uart_slow_down = 1;

  static char raw_buffer[64]; //接收数据存放数组
  static uint8_t idx = 0;     //接收数据长度
  
  // 非阻塞方式读取串口数据
  while (Serial.available() > 0) {
    char c = Serial.read();
    
    if(c == 'W')
    {
      idx = 0;
      raw_buffer[idx++] = c;
    }
    else if(c >= '0' && c <= '9')
    {
      if(idx < sizeof(raw_buffer) - 1)
        raw_buffer[idx++] = c;
    }
    else if(c == '-' && idx == 1)
    {
      raw_buffer[idx++] = c;
    }
    else if(c == '\n')
    {
      raw_buffer[idx] = '\0';
      received_value = atoi(raw_buffer + 1);

      if (received_value >= -100 && received_value <=103)  //接收完整信号后,进行处理
      {
        direction_car  = received_value; 

        if(received_value != 101 && turn_state == 3)//转弯任务完成期间
        {
          turn_state = 0;//重置转向状态机
          car_diversion_flag = 0;//
          inversion_flag = 1;
        }
      }
      idx = 0;
      break;
    }
    else 
    {
      idx = 0;
    }
  }
}

bool flag = 0;//十字路口计数标志位
void motor_control(void)
{
  if(motor_slow_down) return;
    motor_slow_down = 1;

  if(return_flag == 0)//正常行驶
  {
    if(!car_diversion_flag)
    {
      if(direction_car  > 0 && direction_car  <= 100)//右转
        car_direction_state  = 6;
      else if(direction_car  < 0 && direction_car  >= -100)//左转
        car_direction_state  = 5;
      else if(direction_car  == 0)//直行
        car_direction_state  = 1;
      else if(direction_car  == 101)//无线条情况 停止
      {
        car_direction_state  = 0;

        if(crossroad_num == 1 && inversion_flag == 1) //如果经过的十字路口为1,并且未识别到线条,则执行原路返回
        {
          return_flag = 1;//启动返回
        }
      }
      else if(direction_car  == 102) //十字路口判断,识别到后进行停止,确保只进入一次
      {
        car_direction_state  = 4;
        crossroad_num++;//添加经过的十字路口数
        car_diversion_flag = 1;//打开转向标志
        // delay(150);
      }
    }
  }
  else if(return_flag == 1)//启动原路返回 ---仅有一小段路需要
  {
    if(!car_diversion_flag)
    {
      if(direction_car  > 0 && direction_car  <= 100)//右转
      {
        if(crossroad_num != 1)
          car_direction_state  = 6;
        else
          car_direction_state = 7;
      }
      else if(direction_car  < 0 && direction_car  >= -100)//左转
      {
        if(crossroad_num != 1)
          car_direction_state  = 5;
        else
          car_direction_state = 8;
      }
      else if(direction_car  == 0)//直行
      {
        if(crossroad_num != 1)
          car_direction_state  = 1;
        else
          car_direction_state  = 2;
      }
      else if(direction_car  == 101)//无线条情况 停止
      {
        if(crossroad_num != 1)
          car_direction_state  = 0;
        else
        {
          car_direction_state  = 2;
        }
      }
      else if(direction_car  == 102 && flag == 0) //十字路口判断,识别到后进行停止,确保只进入一次
      {
        // if(crossroad_num == 0)
        // {
        //   crossroad_num = 2;
        // }

        if(crossroad_num == 1)
        {
        //  crossroad_num--;//添加经过的十字路口数
         crossroad_num = 2;
          car_diversion_flag = 1;//打开转向标志
          inversion_flag = 0;//解除倒转计数限制
        car_direction_state  = 4;
        //  flag = 1;
        }
      }
    }
    if(direction_car != 102)
    {
      flag = 0;
    }
  }

  //转向判断状态机
  if(car_diversion_flag == 1)//转向标志
  {
    if(turn_state == 0 && direction_car != 101)//转向前红线
    {
      car_direction_state = 4;//速度设定为十字路口右转
      turn_state = 1;//状态1
    }
    else if(turn_state == 1 && direction_car == 101)//识别不到红线
    {
      car_direction_state = 4;//保持右转
      turn_state = 2;//状态2
    }
    else if(turn_state == 2 && direction_car != 101)//重新识别到红线
    {
      turn_state = 3;//状态3
    }
  }

  switch(car_direction_state)  // 根据小车方向指令执行相应动作
  {
    case 0:  // 停止
      // 左右电机同时断电
      analogWrite(Motor_Left_PIN_A , 0);      
      analogWrite(Motor_Left_PIN_B , 0);      
      analogWrite(Motor_Right_PIN_A , 0);
      analogWrite(Motor_Right_PIN_B , 0);
      break;
    
    case 1:  // 前进
      // 左右电机同时正转(向前)
      analogWrite(Motor_Left_PIN_A , 80);  // 左电机正转
      analogWrite(Motor_Left_PIN_B , 0);    
      analogWrite(Motor_Right_PIN_A , 80);  // 右电机正转
      analogWrite(Motor_Right_PIN_B , 0);   
      break;
    
    case 2:  // 后退
      // 左右电机同时反转(向后)
      analogWrite(Motor_Left_PIN_A , 0);     
      analogWrite(Motor_Left_PIN_B , 90);  // 左电机反转
      analogWrite(Motor_Right_PIN_A , 0);     
      analogWrite(Motor_Right_PIN_B , 90); // 右电机反转
      break;

    case 5:  // 左转微调
      // 左右电机同时反转(向后)
        analogWrite(Motor_Left_PIN_A , 70);      // 左电机停转
        analogWrite(Motor_Left_PIN_B , 0);    // 左电机反转
        analogWrite(Motor_Right_PIN_A , 130);   // 右电机正转
        analogWrite(Motor_Right_PIN_B , 0);     
      break;

    case 6:  // 右转微调
      // 左右电机同时反转(向后)
        analogWrite(Motor_Left_PIN_A , 130);    // 左电机正转
        analogWrite(Motor_Left_PIN_B , 0);     
        analogWrite(Motor_Right_PIN_A , 70);      // 右电机停转
        analogWrite(Motor_Right_PIN_B , 0);   // 右电机反转
      break;

    case 3:  // 左转
      // 左轮反转 + 右轮正转 = 原地左转
      analogWrite(Motor_Left_PIN_A , 0);      // 左电机停转
      analogWrite(Motor_Left_PIN_B , 200);    // 左电机反转
      analogWrite(Motor_Right_PIN_A , 130);   // 右电机正转
      analogWrite(Motor_Right_PIN_B , 0);     
      break;
      
    case 4:  // 右转
      // 左轮正转 + 右轮反转 = 原地右转
      analogWrite(Motor_Left_PIN_A , 255);    // 左电机正转 170
      analogWrite(Motor_Left_PIN_B , 170);     
      analogWrite(Motor_Right_PIN_A , 205);      // 右电机停转
      analogWrite(Motor_Right_PIN_B , 255);   // 右电机反转185
      break;

    case 7:  // 返回左偏微调
      // 左右电机同时反转(向后)
        analogWrite(Motor_Left_PIN_A , 0);      // 左电机停转
        analogWrite(Motor_Left_PIN_B , 0);    // 左电机反转110
        analogWrite(Motor_Right_PIN_A , 160);   // 右电机正转
        analogWrite(Motor_Right_PIN_B , 255);     //70
      break;

    case 8:  // 返回右偏微调
      // 左右电机同时反转(向后)
        analogWrite(Motor_Left_PIN_A , 160);    // 左电机正转
        analogWrite(Motor_Left_PIN_B , 255);     //70
        analogWrite(Motor_Right_PIN_A , 0);      // 右电机停转
        analogWrite(Motor_Right_PIN_B , 0);   // 右电机反转110
      break;
  }
}

/* 超声波函数 */
void ultrasonic_proc(void)
{
  if(ultrasonic_slow_down) return;
    ultrasonic_slow_down = 1;
  // Measure left sensor
  digitalWrite(LEFT_TRIGPIN, LOW);
  delayMicroseconds(2);
  digitalWrite(LEFT_TRIGPIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(LEFT_TRIGPIN, LOW);
  
  float duration_left = pulseIn(LEFT_ECHOPIN, HIGH, 300); // Timeout after 30ms
  distance_left = duration_left / 58.0; // Convert to cm

  /*  记录经过障碍的数量  */
  if(distance_left > 15 && distance_left < 20 && obstacle_flag == 0)
  {
    obstacle_flag = 1;
    obstacle_count++;
  }
  else if(distance_left <10 || distance_left > 30)
  {
    obstacle_flag = 0;
  }

}

/* Oled显示函数 */
void oled_display(void)
{
  if(oled_slow_down) return;
    oled_slow_down = 1;

  OLED.clearDisplay();//清屏
  OLED.setTextSize(1);//设置字号大小为1
  
  // 第一行:显示左侧超声波模块读数
  OLED.setCursor(0,0);
  OLED.print("DT:");
  OLED.print(distance_left);
  OLED.print("cm");

  // 第一行:障碍物计数
  OLED.setCursor(80,0);
  OLED.print("Obs:");
  OLED.print(obstacle_count);
  
  // 第二行:显示小车方向
  OLED.setCursor(0,10);
  OLED.print("State: ");
  switch (car_direction_state) 
  {
    case 0: OLED.print("Stop"); break;
    case 1: OLED.print("Forward"); break;
    case 5: OLED.print("Left Adj"); break;
    case 6: OLED.print("Right Adj"); break;
    case 3: OLED.print("Hard Left"); break;
    case 4: OLED.print("Hard Right"); break;
    default: OLED.print("Unknown");
  }
  

  //第四行:转弯标志
  OLED.setCursor(40,20);
  OLED.print("CDF:");
  OLED.print(car_diversion_flag);
  

  //第四行:显示视觉数据
  OLED.setCursor(0, 40);
  OLED.print("V:");
  OLED.print(received_value);

  //第四行:显示经过的十字路口数量
  OLED.setCursor(50, 40);
  OLED.print("CNUM:");
  OLED.print(crossroad_num);

  //第四行:原路返回标志位
  OLED.setCursor(0, 50);
  OLED.print("R_FLAG:");
  OLED.print(return_flag);
  
  OLED.setCursor(50, 50);
  OLED.print("FLAG:");
  OLED.print(inversion_flag);

  //刷新显示屏
  OLED.display();
}

/*  上电初始化 */
void setup() {
  /*  串口相关  */
  Serial.begin(9600);
  
  /*  超声波相关  */
  pinMode(LEFT_TRIGPIN, OUTPUT);
  pinMode(LEFT_ECHOPIN, INPUT);

  /*  Oled相关  */
  OLED.begin(SSD1306_SWITCHCAPVCC,0x3c);
  OLED.setTextColor(1);

  /*  电机相关  */
  pinMode(10,OUTPUT); //将引脚10设置为输出模式
  pinMode(9,OUTPUT);  //将引脚9设置为输出模式
  pinMode(5,OUTPUT); //将引脚5设置为输出模式
  pinMode(6,OUTPUT);  //将引脚6设置为输出模式

  /*  定时器2相关  */
  // 配置Timer2
  noInterrupts();           // 禁用所有中断
  TCCR2A = 0;               // 清零控制寄存器A
  TCCR2B = 0;               // 清零控制寄存器B
  TCNT2  = 0;               // 初始化计数器为0
  
  // 设置比较匹配寄存器 - 每1ms中断一次
  OCR2A = 249;              // = (16MHz / (64 * 1000Hz)) - 1
  
  TCCR2A |= (1 << WGM21);   // 启用CTC模式(比较匹配时清零计数器)
  TCCR2B |= (1 << CS22);    // 设置64预分频 (CS22=1)
  TIMSK2 |= (1 << OCIE2A);  // 启用定时器比较匹配中断
  interrupts();             // 启用所有中断

  //根据视觉模块的图像稳定时间,适当调整
  delay(1000);

}


// Timer2比较匹配中断服务函数
ISR(TIMER2_COMPA_vect) {
  if(++ultrasonic_slow_down == 10) ultrasonic_slow_down = 0;  //超声波刷新
  if(++oled_slow_down == 50) oled_slow_down = 0; //Oled刷新
  if(++motor_slow_down == 10) motor_slow_down = 0; //Motor刷新
  if(++uart_slow_down == 10) uart_slow_down = 0; //Motor刷新
}

void loop() {
  ultrasonic_proc();
  oled_display();
  motor_control();
  uart_proc();
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐