SmartCar 项目

概述

本文章是整理基于TM4C123G开发板的智能小车的项目笔记,它会包括整个项目的开发流程及相关技术知识等,但它没有任何工程学上的目标,只是一种学习或者说一种动手设计的乐趣。

功能目标

首先我们定义我们初步要达到的功能目标:

  • 支持自动左右转向,当遇到障碍物时可以转向或者倒
  • 支持加速和减速,自动模式或者手动模式
  • 支持红外线遥控,可以手动遥控左右转向、倒退/前进以及加减速

高级功能:

  • 增加WIFI模块,可以通过远程控制
  • 增加摄像头模块,可以进行视频采集和监控

使用的硬件模组

TM4C123G 微控制器开发版

红外对管距离传感器 3个

分别位于车体的前部、左部和右部

红外线接收器和遥控器

L298N电机驱动模块

L298N电机驱动

摄像头和WIFI模组

L298N电机驱动

L298N电机驱动

带有前后直流电机的模型小车

前部电机控制方向,后部电机负责前进和倒退

初步功能实现后:
L298N电机驱动

软件平台

  使用 Keil uVersion IDE.

代码结构

main函数主结构

首先我们实现程序的主结构,它包含如下步骤:

激活grader和设置系统时钟

设置系统时钟的方法是配置PLLTM4C123G使用16M的主振荡器,具体配置PLL的方法在上一章中已经介绍。我们这里只需要直接调用接口:

1
TexaS_INIT(SW_PIN_PE210, LED_PIN_PB543210);

它会设置系统时钟为 80M Hz.

初始化IO端口和定时器及中断

首先初始化 TM4C123G 板上自带的RGB LED端口为数字输出端口,它分别对应pin口为PF1(R)PF2(B)PF3(G),具体的函数定义在后面加入,这里只需要明白它做了什么。

1
RGB_LED_Init();

初始化和直流电机相关的端口

设置PD0PD1PD2PD3为数字输出端口,并设置其初始值为0;设置PE5为数字输出端口,并设初始值为1。上述5个端口都使用8mA驱动,具体如何用于控制电机后面会进行描述。

1
Motor_Port_Init()

前置电机定时器Timer0和中断初始化

我们在这里初始化Timer0A定时器,优先级为2,它的 IRQ 编号为19,ISRTimer0A_Handler

1
Motro_Front_Init()

后置电机定时器 Systick 和中断初始化

我们设置 SysTick 定时器,优先级为2ISRSysTick_HandlerSysTick中断不需要中断确认(ack)

1
Motro_Back_Init();

我们在使用定时器的目的是使用 PWM 来驱动电机,使其可以加速或者减速, 定时器可以帮助我们生成不同占空比的PWM,对于后置电机占空比由 BACK_LBACK_H 变量控制,这两个变量交替作为SysTick的初始计数器值。PD0PD1端口与LN298模块相连,在SysTick_Handler中会向PDOPD1输出 0 或者 1,如下所示:

1
2
3
4
5
6
7
8
9
10
void SysTick_Handler(void){
unsigned long bitmap = 0x01 << reversal;
if(GPIO_PORTD_DATA_R & bitmap){ // toggle ENA
GPIO_PORTD_DATA_R &= ~bitmap;
NVIC_ST_RELOAD_R = back_L-1; // reload value for low phase
}else{
GPIO_PORTD_DATA_R |= bitmap;
NVIC_ST_RELOAD_R = back_H-1; // reload value for low phase
}
}

其中reversal用来控制电机反向旋转;NVIC_ST_RELOAD_R寄存器存有 SysTick 的计数器初始值。

初始化 IRremote 模块

该模块负责红外遥控,主要包括NEC协议的解析等。

1
IRremote_init();

初始化和红外线距离传感器相关的端口及中断

我们设置PA4PA5PA6为数字输入端口,并设置为双边缘触发中断,优先级为1IRQ0,它们分别对应前方、左方和右方的传感器。连接到响应端口,当有信号到来时,会触发中断,进而执行 GPIOPortA_Handler ISR。在 ISR 中首先进行中断确认,然后会获取3个端口的数据值,根据数据值来判断当前小车前、左和右方是否有障碍物,会根据具体情况进行后退、转向或者加速等。在IR_sensor_Init中会时设置延迟定时器TIMER1A,但不能启动定时器,该定时器在GPIOPortA_Handler中启动,并根据具体参数设置延迟时间。在延迟期间,GPIOPortA_Handler不再处理来自传感器的数据而是直接返回。

1
IR_sensor_Init();

全局中断使能

调用如下接口:

1
EnableInterrupts();

其具体实现通过汇编代码表示为:

1
2
3
EnableInterrupts    CPSID    I ; set I = 1
BX LR

进入主循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
while (1) {
while (IRreceived()) {
unsigned long data_value;
unsigned long count = 1<<15;
data_value = getIRresult();

swicth(data_value) {
// ....
}
}
while (count) {count--;}
resumeIRremote();
}

主循环主要是监听红外遥控命令来作出反应。

其他子函数

后置电机停止 Motor_Back_Stop 函数

该函数通过禁止 SysTick 定时器和设置 PD0PD1 端口为 0

1
2
3
4
5
void Motor_Back_Stop(){
NVIC_ST_CTRL_R = 0; // disable SysTick
reversal = 0;
GPIO_PORTD_DATA_R &= ~0x03; // make PD0(IN1),PD1(IN2) low
}

后置电机运行

重新使能 SysTick 并设置 PD0PD1:

1
2
3
4
5
6
void Motor_Back_Run(){
reversal = 0;
NVIC_ST_CTRL_R = 0x07; // e`nable SysTick with core clock and interrupts
GPIO_PORTD_DATA_R |= 0x01; // make PD0(IN1) high
GPIO_PORTD_DATA_R &= ~0x02; // make PD1(IN2) low
}

后置电机减速

通过调节 Bask_LBack_H 全局变量来实现,我们只能这两个变量会与 SysTick 定时器合作完成 PWM 控制。

1
2
3
4
5
6
7
8
void Motor_Back_SlowDown(int n){
if(NVIC_ST_CTRL_R != 0x0){
while((n--)>0){
if(back_L>8000) back_L = back_L - 8000; //slow down
}
back_H = 80000 - back_L;
}
}

后置电机加速

1
2
3
4
5
6
7
8
void Motor_Back_SpeedUp(int n){
if(NVIC_ST_CTRL_R != 0x0){
while((n--)>0){
if(back_L<72000) back_L = back_L + 8000; // speed up
}
back_H = 80000 - back_L;
}
}

后置电机反向旋转

1
2
3
4
5
6
void Motor_Back_Reversal(){
reversal = 1;
NVIC_ST_CTRL_R = 0x07; // enable SysTick with core clock and interrupts
GPIO_PORTD_DATA_R &= ~0x01; // IN1 low
GPIO_PORTD_DATA_R |= 0x02; // IN2 high
}

左右转弯

左右转弯通过 PWM 控制前置电机完成,它要重新使能Timer0A定时器,因为在停止转向函数中该定时器会被禁止。

1
2
3
4
5
6
7
8
9
10
11
12
13
void Turn_Left(){
GPIO_PORTD_DATA_R &= ~0x04; // PD2(IN3)
GPIO_PORTD_DATA_R |= 0x08; // PD3 (IN4)
TIMER0_CTL_R |= 0x00000001;
turning =1;
}
void Turn_Right(){
//TIMER0_CTL_R |= 0x00000001;
GPIO_PORTD_DATA_R &= ~0x08; // PD3(IN4)
GPIO_PORTD_DATA_R |= 0x04; // PD2 (IN3)
TIMER0_CTL_R |= 0x00000001;
turning =2;
}

停止转向

1
2
3
4
5
void Turn_Stop(){
TIMER0_CTL_R &= ~0x00000001;
GPIO_PORTD_DATA_R &= ~0x0c; // make PD2(IN3),PD3(IN4) low
turning = 0;
}

高级功能

摄像头

使用WIFI模组联网