文章目录
1 背景 2 系统设计方案 2.1 实现功能 2.1.1 硬件部分: 2.1.2 软件部分: 2.1.3 WIFI通信功能: 2.2 系统架构 2.2.1 WiFi 通信 2.2.2 电机驱动 2.2.3 摄像头 2.2.4 舵机 2.2.5 PWM舵机控制 2.2.6 红外循迹模块 3 软件设计 4 测试效果 5 关键代码的一部分 6 最后
1 背景
近年来,人们的生活逐渐向智能化转变, 随着嵌入式技术和一些新技术的快速发展, 使人们的生活和工作越来越智能化 。智能汽车可以在环境中通过传感器自动化 判断人操作的情况下,判断和分析 主完成任务。
通过智能小车设计WIFI具有避障和温度采集功能的远程无线控制, 实现了汽车的智能化, 它可以作为智能汽车或其它移动机器人的基本模型,具有较大的研究空间。 2 系统设计方案 2.1 实现功能
学长设计的小车能够实现无线控制, 避障, 跟踪等功能, 由硬件, 软件由无线传输三部分组成,具体描述了上述三部分的功能 。 2.1.1 硬件部分:
输出 PWM 控制电机; 检测障碍物, 检测距离为 10cm;串口通信, 智能小车需要通过串口调试, 串口波特率设置为 115200;对接收到的命令进行处理和判断;通过温度传感器检测环境温度; 连接WIFI 模块及控制 WIFI模块;通过uCOS-II同时执行多个任务。 2.1.2 软件部分:
操作界面功能;通过SOCKET编程可以连接到网络 WIFI模块;接收STM32开发板传输的数据和发送指令数据;用相机拍照, 在小车行驶过程中接收小车传输的图片信息并显示;实现小车的模式切换, 模式 1 自动驾驶模式为无线控制驾驶模式。 2.1.3 WIFI通信功能:
与 PC 端联网连接;实现PC 单片机之间的数据交换功能。 2.2 系统架构
小车采用 STM32F103 开发板,与 51 与单片机相比,单片机, STM设计方案如下图所示,度更快,设计方案如下图所示。
STM32开发板作为本设计的控制中心, 使用 PWM 输出波驱动电机旋转, 通过内部定时器控制方向, 处理器判断和计算接收到的数据和命令IO口输出。
温度传感器通过串口收集温度信息 WIFI模块,在PC 端显示; 利用红外传感器检测障碍物和跟踪功能。 WIFI模块是 PC 数据交互的媒介端与开发板, PC 端操作指令由WIFI发送模块、开发板和 PC 端之间设置了相应的数据协议,以确定接收到的命令类型,汽车根据命令执行相应的操作。
插入图片描述 2.2.1 WiFi 通信
WIFI作为通信模块 STM32和 PC 端通信中介, 两端都通过 WIFI数据交互模块, 该模块选用 ESP8266芯片 [5] , 其特点是如果断开连接, 再次连接, 该模块将连接到最新连接的热点。
ESP8266支持三种模式, 分别为 STA 模式, AP模式, STA AP 模式。
学长在这里使用 AP 模式, 使其他网 络能 够连接到ESP8266。 本设计使用 ESP8266的 AP该模式可以连接到其他网络 ESP8266,与STM引脚连接如表所示。
插入图片描述
插入图片描述 2.2.2 电机驱动
插入图片描述
为了实现PC 端部可以控制汽车的速度,满足直流电机的驱动 电 压, 本设计选用 L298N 电 机驱动 模块给直流 电 机供电 。
电机状态与STM32输入如表2。 电机驱动有两个电源输入接口,一个是5V,另一个是12V,在使用时12V接口输入电压大于7V,5V接口可以向单片机供电。
插入图片描述 2.2.3 摄像头
ov7670是OmniVision30W像素。
插入图片描述 为什么要带?FIFO模块呢?stm32速度慢,带FIFO可以暂时存在相机拍摄的数据FIFO里,然后我们的stm32然后慢慢读取拍摄数据,通过串口发送到串口上位机显示。
FIFO,即first in first out缩写。FIFO的速度很快,可以将摄像头的数据暂时存起了。以便我们慢速的CPU慢慢取出和处理获得的数据,达到类似水库的效果。 2.2.4 舵机
插入图片描述 舵机有三条输入线, 红色中间, 是电源线, 一边黑色的是地线, 该根线向舵机提供最基本的能源保证, 主要是电机的旋转消耗。 电源有两种规格, 一是 4.8V, 一是 6.0V, 对应不同的扭矩标准, 即输出扭矩不同, 6.0V 对应的要大一点, 具体看应用条件; 另一条线是控制信号线, Futaba 一般为白色, JR 橙色通常是橙色。
另外要注意一点, SANWA 一些型号的舵机引线电源线在边缘而不是中间, 需要识别。但记住红色是电源, 黑色是地线, 一般没有错。 2.2.5 PWM舵机控制
舵机的控制信号是周期 20ms 的脉宽调制( PWM) 信号, 其中脉冲宽度从0.5ms-2.5ms, 对应舵盘的位置为 0-180 度, 呈线性变化。 也就是说, 提供一定的脉宽, 其输出轴将保持在相应的角度, 无论外部转矩如何变化, 直到提供另一个宽度的脉冲信号, 将输出角度改变到新的相应位置。 2.2.6 红外循迹模块
采用TCRT5000红外反射传感器是一种集发射和接收于一体的光电传感器,由一个红外发光二极管和一个NPN由红外光电三极管组成。检测反射距离1mm-25mm适用,传感器特设M3固定安装孔,方便调整方向和固定,使用宽压LM393比较器,信号干净,波形好,驱动能力强,超过15mA。可用于机器人避障、机器人跟踪白线或黑线、白线或黑线,是寻线机器人必备的传感器。流水线计件、电表脉冲数据采样、传真机碎纸机纸张检测等场合。
插入图片描述 3 软件设计
汽车控制是通过的 stm32单片机控制驱动电路和给舵机发送控制信号, 然而,计算机和其他终端设备通过无线路由器串口将这些控制信号的命令传输给单片机, 因此,我们需要在程序中设计使用串口, 定时器, I/O 口的使用。
系统主程序模块主要完成系统中各模块电路的初始化, 主要包括对定时器, 串口中断、 外部中断的初始化, 同时执行计算机等终端设备发送的命令, 根据所需的功能等待外部中断和相应操作。 如下图所示
插入图片描述 4 测试效果
插入图片描述
插入图片描述
插入图片描述 5 关键代码的一部分
主程序
#include "stm32f10x.h" #include "bsp_SysTick.h" #include "bsp_led.h" #include <string.h> #include <stdlib.h> #include "bsp_pwm_output.h" #include "infrared.h" #include "delay.h" ///全局变量 unsigned int Task_Delay[NumOfTask];
/** * @brief 主函数 * @param 无 * @retval 无 */ int main(void) { //初始化systick SysTick_Init(); &nsp; //用于延时等操作 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; TIMx_PWM_Init(); //PWM波初始化 infrared_Initial(); //红外初始化 while(1) { if((infrared_Scan(infrared_5_GPIO_PORT,infrared_5_GPIO_PIN)==INFRARED_ON)&&\ (infrared_Scan(infrared_6_GPIO_PORT,infrared_6_GPIO_PIN)==INFRARED_ON))//前方有障碍,必须两边同时检测到才能触发,否则均认为是误触 { DelayS(3); //延迟3秒 if((infrared_Scan(infrared_5_GPIO_PORT,infrared_5_GPIO_PIN)==INFRARED_ON)&&\ (infrared_Scan(infrared_6_GPIO_PORT,infrared_6_GPIO_PIN)==INFRARED_ON))//仍旧有障碍 { TIMx_Mode_Config(500,0,0,500);//右转500ms DelayMs(500); } else { TIMx_Mode_Config(500,0,0,500);//直行500ms DelayMs(500); } } else { if((infrared_Scan(infrared_3_GPIO_PORT,infrared_3_GPIO_PIN)==INFRARED_ON))//循迹,右侧内部传感器见到黑线,说明车身方向偏右 TIMx_Mode_Config(0,0,600,0);//轻微左转 else if((infrared_Scan(infrared_2_GPIO_PORT,infrared_2_GPIO_PIN)==INFRARED_ON))//循迹,左侧内部传感器见到黑线,说明车身方向偏左 TIMx_Mode_Config(600,0,0,0);//轻微右转 else if((infrared_Scan(infrared_4_GPIO_PORT,infrared_4_GPIO_PIN)==INFRARED_ON))//循迹,右侧外部传感器见到黑线,说明车身方向偏左 TIMx_Mode_Config(0,0,900,0);//剧烈左转 else if((infrared_Scan(infrared_1_GPIO_PORT,infrared_1_GPIO_PIN)==INFRARED_ON))//循迹,左侧外部传感器见到黑线,说明车身方向偏左 TIMx_Mode_Config(900,0,0,0);//剧烈右转 else TIMx_Mode_Config(500,0,500,0);//直行 } }
}
PWM程序
#include "bsp_pwm_output.h"
/** * @brief 配置TIM3复用输出PWM时用到的I/O * @param 无 * @retval 无 */ static void TIMx_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure;
/* 设置TIM3CLK 为 72MHZ */ // RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); macTIM_APBxClock_FUN (macTIM_CLK, ENABLE);
/* GPIOA and GPIOB clock enable */ macTIM_GPIO_APBxClock_FUN (macTIM_GPIO_CLK, ENABLE);
/*GPIOA Configuration: TIM3 channel 1 as alternate function push-pull */ GPIO_InitStructure.GPIO_Pin = macTIM_CH1_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(macTIM_CH1_PORT, &GPIO_InitStructure);
/*GPIOB Configuration: TIM3 channel 2 as alternate function push-pull */ GPIO_InitStructure.GPIO_Pin = macTIM_CH2_PIN; GPIO_Init(macTIM_CH2_PORT, &GPIO_InitStructure);
/*GPIOB Configuration: TIM3 channel 3 as alternate function push-pull */ GPIO_InitStructure.GPIO_Pin = macTIM_CH3_PIN; GPIO_Init(macTIM_CH3_PORT, &GPIO_InitStructure); /*GPIOB Configuration: TIM3 channel 4 as alternate function push-pull */ GPIO_InitStructure.GPIO_Pin = macTIM_CH4_PIN; GPIO_Init(macTIM_CH4_PORT, &GPIO_InitStructure); }
/** * @brief 配置TIM3输出的PWM信号的模式,如周期、极性、占空比 * @param 无 * @retval 无 */ /* * TIMxCLK/CK_PSC --> TIMxCNT --> TIMx_ARR --> TIMxCNT 重新计数 * TIMx_CCR(电平发生变化) * 信号周期=(TIMx_ARR +1 ) * 时钟周期 * 占空比=TIMx_CCR/(TIMx_ARR +1) */ void TIMx_Mode_Config(u16 CCR1_Val,u16 CCR2_Val,u16 CCR3_Val, u16 CCR4_Val) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure;
/* ----------------------------------------------------------------------- macTIMx Channel1 duty cycle = (macTIMx_CCR1/ macTIMx_ARR+1)* 100% = 50% macTIMx Channel2 duty cycle = (macTIMx_CCR2/ macTIMx_ARR+1)* 100% = 37.5% macTIMx Channel3 duty cycle = (macTIMx_CCR3/ macTIMx_ARR+1)* 100% = 25% macTIMx Channel4 duty cycle = (macTIMx_CCR4/ macTIMx_ARR+1)* 100% = 12.5% ----------------------------------------------------------------------- */
/* Time base configuration */ TIM_TimeBaseStructure.TIM_Period = 999; //当定时器从0计数到999,即为1000次,为一个定时周期 TIM_TimeBaseStructure.TIM_Prescaler = 0; //设置预分频:不预分频,即为72MHz TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ; //设置时钟分频系数:不分频(这里用不到) TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseInit(macTIMx, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel1 */ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //配置为PWM模式1 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = CCR1_Val; //设置跳变值,当计数器计数到这个值时,电平发生跳变 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //当定时器计数值小于CCR1_Val时为高电平 TIM_OC1Init(macTIMx, &TIM_OCInitStructure); //使能通道1 TIM_OC1PreloadConfig(macTIMx, TIM_OCPreload_Enable);
/* PWM1 Mode configuration: Channel2 */ TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = CCR2_Val; //设置通道2的电平跳变值,输出另外一个占空比的PWM TIM_OC2Init(macTIMx, &TIM_OCInitStructure); //使能通道2 TIM_OC2PreloadConfig(macTIMx, TIM_OCPreload_Enable);
/* PWM1 Mode configuration: Channel3 */ TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = CCR3_Val; //设置通道3的电平跳变值,输出另外一个占空比的PWM TIM_OC3Init(macTIMx, &TIM_OCInitStructure); //使能通道3 TIM_OC3PreloadConfig(macTIMx, TIM_OCPreload_Enable);
/* PWM1 Mode configuration: Channel4 */ TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = CCR4_Val; //设置通道4的电平跳变值,输出另外一个占空比的PWM TIM_OC4Init(macTIMx, &TIM_OCInitStructure); //使能通道4 TIM_OC4PreloadConfig(macTIMx, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(macTIMx, ENABLE); // 使能TIM3重载寄存器ARR
/* TIM3 enable counter */ TIM_Cmd(macTIMx, ENABLE); //使能定时器3 }
/** * @brief TIM3 输出PWM信号初始化,只要调用这个函数 * TIM3的四个通道就会有PWM信号输出 * @param 无 * @retval 无 */ void TIMx_PWM_Init(void) { TIMx_GPIO_Config(); TIMx_Mode_Config(0,0,0,0); }
延时程序
#include "stm32f10x.h" #include "delay.h"
static u8 fac_us=0;//us延时倍乘数 static u16 fac_ms=0;//ms延时倍乘数
//初始化延迟函数 //当使用ucos的时候,此函数会初始化ucos的时钟节拍 //SYSTICK的时钟固定为HCLK时钟的1/8 //SYSCLK:系统时钟 void DelayInit() { SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟 HCLK/8 fac_us=SystemCoreClock/8000000; //为系统时钟的1/8 fac_ms=(u16)fac_us*1000;//非ucos下,代表每个ms需要的systick时钟数 }
//延时nus //nus为要延时的us数. void DelayUs(unsigned long nus) { u32 temp; SysTick->LOAD=nus*fac_us; //时间加载 SysTick->VAL=0x00; //清空计数器 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do { temp=SysTick->CTRL; } while(temp&0x01&&!(temp&(1<<16)));//等待时间到达 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器 SysTick->VAL =0X00; //清空计数器 } //延时nms //注意nms的范围 //SysTick->LOAD为24位寄存器,所以,最大延时为: //nms<=0xffffff*8*1000/SYSCLK //SYSCLK单位为Hz,nms单位为ms //对72M条件下,nms<=1864 void DelayMs(unsigned int nms) { u32 temp; SysTick->LOAD=(u32)nms*fac_ms;//时间加载(SysTick->LOAD为24bit) SysTick->VAL =0x00; //清空计数器 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do { temp=SysTick->CTRL; } while(temp&0x01&&!(temp&(1<<16)));//等待时间到达 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器 SysTick->VAL =0X00; //清空计数器 }
void DelayS(unsigned int ns)//延时秒 { unsigned char i; for(i=0;i<ns;i++) { DelayMs(1000); } }
———————————————— 版权声明:本文为CSDN博主「DC-STDIO」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/huawei123444/article/details/121772419