基于STM32-Socket-Qt 遥控小车(一代)

18 篇文章 12 订阅


一、项目分析

1. 项目简介

本项目本质为客户端与服务器之间的通信,通过发送不同的指令,服务器和客户端进行不同的操作。

客户端:基于STM32制作简单行驶小车
服务器:安卓手机,基于Socket编程下 用QT进行安卓开发,将app传输到手机上。
TCP通信:ESP8266

手机端发送不同指令,小车执行不同操作

2. 知识储备

3. 硬件选择

1. esp8266WiFi模块
在这里插入图片描述
2. 直流电机
(笔者这里用编码器电机代替,普通直流电机即可)
在这里插入图片描述

3. STM32F103C8T8
在这里插入图片描述
4. 驱动电机模块
(笔者这里用的是TB6612,L298N啥的都行)
在这里插入图片描述
5. 烧写器 ST-Link
在这里插入图片描述
6. 安卓手机
在这里插入图片描述

7. 电池、杜邦线、螺母、轮胎等等

在这里插入图片描述

二、STM32部分

1. pwm

void TIM3_PWM_Init(u16 per,u16 psc)
{
	/*使能TIM4时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	
	/*使能GPIO*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	/*使能AFIO*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
	/*配置GPIO*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	/*设置重映射*/
	//GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);//部分重映射	
	
	/*初始化定时器参数*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频为1分频
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//选择计数模式为向上计数
	TIM_TimeBaseInitStructure.TIM_Period = per;//配置周期(ARR自动重装器的值)
	TIM_TimeBaseInitStructure.TIM_Prescaler = psc;//配置PSC预分频器的值
	//TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值,高级计数器才需配置
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
	//TIM_ClearFlag(TIM4,TIM_FLAG_Update);//先清除标志位,避免刚初始化就进入中断
	
	/*初始化PWM参数*/
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCInitStructure.TIM_Pulse = 0;
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;   //选择空闲状态下的非工作状态 低电平
	TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set;  //选择互补空闲状态下的非工作状态 低电平
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//选择PWM1模式
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性:高电平有效
	TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  //输出比较使能
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;  //互补输出比较使能
	TIM_OC1Init(TIM3,&TIM_OCInitStructure);
	TIM_OC2Init(TIM3,&TIM_OCInitStructure);
	TIM_OC3Init(TIM3,&TIM_OCInitStructure);
	TIM_OC4Init(TIM3,&TIM_OCInitStructure);
	
	/*使能TIMX在CCRX上的预装载寄存器*/
	TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable);
	TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);
	TIM_OC3PreloadConfig(TIM3,TIM_OCPreload_Enable);
	TIM_OC4PreloadConfig(TIM3,TIM_OCPreload_Enable);
	
	TIM_CtrlPWMOutputs(TIM3,ENABLE);
	
	/*使能TIMX在ARR上的预装载寄存器允许位*/
	//TIM_ARRPreloadConfig(TIM4,ENABLE);
	
	/*开启定时器*/
	TIM_Cmd(TIM3,ENABLE);
}	

2. Car

void forward()
{
	GPIO_SetBits(GPIOB,GPIO_Pin_7| GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_15);
	GPIO_ResetBits(GPIOB,GPIO_Pin_8| GPIO_Pin_10 | GPIO_Pin_12 | GPIO_Pin_14);
}

void back()
{
	GPIO_SetBits(GPIOB,GPIO_Pin_8| GPIO_Pin_10 | GPIO_Pin_12 | GPIO_Pin_14);
	GPIO_ResetBits(GPIOB,GPIO_Pin_7| GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_15);
}

//  PB7,PB8:  右后	 
//	PB14,PB15:右前   
//	PB12,PB13:左前	 
//	PB10,PB11:左后   

void turn_left()
{
	GPIO_SetBits(GPIOB,GPIO_Pin_7| GPIO_Pin_10 | GPIO_Pin_13 | GPIO_Pin_15);
	GPIO_ResetBits(GPIOB,GPIO_Pin_8| GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_14);
}

void turn_left1()
{
	GPIO_SetBits(GPIOB,GPIO_Pin_8| GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_14);
	GPIO_ResetBits(GPIOB,GPIO_Pin_7| GPIO_Pin_10 | GPIO_Pin_12 | GPIO_Pin_15);
}

void turn_right()
{
	GPIO_SetBits(GPIOB,GPIO_Pin_8| GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_15);
	GPIO_ResetBits(GPIOB,GPIO_Pin_7| GPIO_Pin_10 | GPIO_Pin_12 | GPIO_Pin_14);
}

void turn_right1()
{
	GPIO_SetBits(GPIOB,GPIO_Pin_8| GPIO_Pin_10 | GPIO_Pin_12 | GPIO_Pin_15);
	GPIO_ResetBits(GPIOB,GPIO_Pin_7| GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_14);
}

void stop(void)
{
	GPIO_ResetBits(GPIOB,GPIO_Pin_8 | GPIO_Pin_7 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);
}

3. esp8266

void esp8266_start_trans(void)
{
	//printf("等待初始化\r\n");
	while(esp8266_send_cmd((u8 *)"AT",(u8 *)"OK",20));
	
	//设置工作模式 1:station模式   2:AP模式  3:兼容 AP+station模式
	while(esp8266_send_cmd((u8*)"AT+CWMODE=1",(u8*)"OK",20));
	//printf("设置工作模式成功\r\n");
	
	delay_ms(1000);

	//让模块连接上自己的路由
	while(esp8266_send_cmd((u8*)"AT+CWJAP=\"226\",\"226226226\"",(u8*)"WIFI GOT IP",200));
	//printf("连接路由器成功\r\n");
	delay_ms(1000);
	
	//=0:单路连接模式     =1:多路连接模式
	while(esp8266_send_cmd((u8*)"AT+CIPMUX=0",(u8*)"OK",200)){printf("设置单路连接模式失败\r\n");}
	//printf("设置单路连接模式成功\r\n");
	delay_ms(1000);
	
while(esp8266_send_cmd((u8*)"AT+CIPSTART=\"TCP\",\"192.168.124.66\",8080",(u8*)"OK",500));
	//printf("TCP连接成功\r\n");
	delay_ms(1000);
	
	//是否开启透传模式  0:表示关闭 1:表示开启透传
	esp8266_send_cmd((u8*)"AT+CIPMODE=1",(u8*)"OK",200);
	//printf("开启透传模式\r\n");
	
	esp8266_send_cmd((u8*)"AT+CIPSEND",(u8*)"OK",50);
	//printf("开启透传成功\r\n");
}

4. usart

void USART2_init(u32 bound)
{  
	NVIC_InitTypeDef NVIC_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	// GPIOB时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); //串口3时钟使能

 	USART_DeInit(USART2);  //复位串口3
		 //USART2_TX   PB10
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PB10
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
  GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PB10
   
    //USART2_RX	  PB11
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);  //初始化PB11
	
	USART_InitStructure.USART_BaudRate = bound;//波特率一般设置为9600;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
  
	USART_Init(USART2, &USART_InitStructure); //初始化串口	3
  

	USART_Cmd(USART2, ENABLE);                    //使能串口 
	
	//使能接收中断
  USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启中断   
	
	//设置中断优先级
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
		
	TIM2_Int_Init(1000-1,7200-1);		//10ms中断
	USART2_RX_STA=0;		//清零
	TIM_Cmd(TIM2,DISABLE);			//关闭定时器7

}

5. time2

void TIM2_IRQHandler(void)
{ 	
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)//是更新中断
	{	 			   
		USART2_RX_STA|=1<<15;	//标记接收完成
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update  );  //清除TIM2更新中断标志    
		TIM_Cmd(TIM2, DISABLE);  //关闭TIM2 
	}	    
}
 
//通用定时器7中断初始化,这里时钟选择为APB1的2倍
//arr:自动重装值 psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz 
//通用定时器中断初始化 
void TIM2_Int_Init(u16 arr,u16 psc)
{	
	NVIC_InitTypeDef NVIC_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//TIM2时钟使能    
	
	//定时器TIM2初始化
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
 
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE ); //使能指定的TIM2中断,允许更新中断
	
	TIM_Cmd(TIM2,ENABLE);//开启定时器7
	
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;		//子优先级2
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
	
}

6. main

int main(void)
{
	unsigned char*	m=NULL;
	int k1,k2,k3,k4,flag,f1,f2,f3;
	delay_init();	    	 			//延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 			//设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 				//串口初始化为115200
	USART2_init(115200);	 				//串口初始化为115200
	 
	esp8266_quit_trans();
	esp8266_start_trans();							//esp8266进行初始化
	 
	GPIOB_Init();
	
	TIM3_PWM_Init(100-1,72-1);
	int i,j=15;
	flag=1;
	while(1)
	{
		m=WIFI_Rece_Data();	
		if(flag == 1) 
		{
			m[0]='S';
			flag = 0;
		}

		else if(m[0]=='F')  i=0;
		
		else if(m[0]=='L')  i=1;
		
		else if(m[0]=='R')  i=3;
		
		else if(m[0]=='S')  i=5;
		
		else if(m[0]=='B')  i=7;
		
		else if(m[0]=='u'&& m[1]=='p')  i=2;
		
		else if(m[0]=='u'&& m[1]=='d')  i=4;
		
		switch(i)
		{
			case 0:
				f1=1;
				setpwm(j);
				forward();
			  break;
			
			case 1:
				f2=1;
				k1=j-15;
				k2=j+15;
				if(k1<=0) 	k1=0;
				if(k2>=100) k2=100;
				setpwm1(k1,k2);
			
				if(f1==1)  turn_left();	
				if(f1==-1) turn_left1();	
			  break;

			case 3:
				f3=1;
				k3=j+15;
				k4=j-15;
				if(k3>=100) k3=100;
				if(k4<=0) 	k4=0;
				setpwm1(k3,k4);
			
				if(f1==1)  turn_right();
				if(f1==-1) turn_right1();
			  break;

			case 5:
				stop();	
				break;
			
			case 7:
				f1=-1;
				setpwm(j);
				back();
				break;
			
			case 2:
					if(f2==1) 
					{ 
							k1+=2;k2+=10; 
							if(k1>=30) k1=30; 
							if(k2>=100) k2=100; 
							setpwm1(k1,k2);
							delay_ms(20);
							f2=0;
							break;
					} 
					else if(f3==1) 
						{ k3+=10;k4+=2; 
							if(k3>=100) k3=100; 
							if(k4>=30) k4=30; 
							setpwm1(k3,k4);
							delay_ms(20);
							f3=0;
							break;
					} 
					else {
						if(j>=100) j=100;	
						setpwm(j);
						j++;
						delay_ms(50);
						break;
					}
			
			case 4:
					if(f2==1) { k1-=2;k2-=5; if(k1<=0) k1=0; if(k2<=30) k2=30; setpwm1(k1,k2); delay_ms(20);f2=0; break;} 
					else if(f3==1) { k3-=5;k4-=2; if(k3<=30) k3=30; if(k4<=0) k4=0; setpwm1(k3,k4); delay_ms(20); f3=0;break;} 
					else{
							if(j<=0) j=0;
							setpwm(j);
							j--;
							delay_ms(50);
							break;
					}				
		}		
	}
}

三、QT部分

Server

server::server(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::server)
{
    ui->setupUi(this);
    tcpserver=nullptr;
    tcpsocket=nullptr;
    //创建监听套接字
    tcpserver=new QTcpServer(this);//指定父对象 回收空间

    //bind+listen
    tcpserver->listen(QHostAddress::Any,8080);//绑定当前网卡所有的ip 绑定端口 也就是设置服务器地址和端口号

    //服务器建立连接
    connect(tcpserver,&QTcpServer::newConnection,[=](){
        //取出连接好的套接字
        tcpsocket=tcpserver->nextPendingConnection();

        //获得通信套接字的控制信息
        QString ip=tcpsocket->peerAddress().toString();//获取连接的 ip地址
        quint16 port=tcpsocket->peerPort();//获取连接的 端口号
        QString temp=QString("[%1:%2] 客服端连接成功").arg(ip).arg(port);
       //显示连接成功
        ui->textEditRead->setText(temp);
    });
}

void server::on_forward_clicked()
{
    if(tcpsocket==nullptr)  return ;
    QString str="F";
    tcpsocket->write(str.toUtf8().data());
}


void server::on_back_clicked()
{
    if(tcpsocket==nullptr)  return ;
    QString str="B";
    tcpsocket->write(str.toUtf8().data());
}


void server::on_turn_left_clicked()
{
    if(tcpsocket==nullptr)  return ;
    QString str="L";
    tcpsocket->write(str.toUtf8().data());
}


void server::on_turn_right_clicked()
{
    if(tcpsocket==nullptr)  return ;
    QString str="R";
    tcpsocket->write(str.toUtf8().data());
}


void server::on_stop_clicked()
{
    if(tcpsocket==nullptr)  return ;
    QString str="S";
    tcpsocket->write(str.toUtf8().data());
}


void server::on_speed_up_clicked()
{
    if(tcpsocket==nullptr)  return ;
    QString str="up";
    tcpsocket->write(str.toUtf8().data());
}


void server::on_speed_down_clicked()
{
    if(tcpsocket==nullptr)  return ;
    QString str="ud";
    tcpsocket->write(str.toUtf8().data());
}

四、遥控小车演示

基于STM32-Socket-Qt 遥控小车(一代)

程序源码

若需程序源码 可评论区留言QQ邮箱 或 直接私信即可

  • 13
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 38
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 38
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

比特冬哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值