本项目本质为客户端与服务器之间的通信,通过发送不同的指令,服务器和客户端进行不同的操作。
客户端:基于STM32制作简单行驶小车
服务器:安卓手机,基于Socket编程下 用QT进行安卓开发,将app传输到手机上。
TCP通信:ESP8266
手机端发送不同指令,小车执行不同操作
1. esp8266WiFi模块
2. 直流电机
(笔者这里用编码器电机代替,普通直流电机即可)

3. STM32F103C8T8

4. 驱动电机模块
(笔者这里用的是TB6612,L298N啥的都行)

5. 烧写器 ST-Link

6. 安卓手机

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

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 PB10GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PB10GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PB10//USART2_RX PB11GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PB11USART_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); //初始化串口 3USART_Cmd(USART2, ENABLE); //使能串口 //使能接收中断USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启中断 //设置中断优先级NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3NVIC_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_timTIM_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);//开启定时器7NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级2NVIC_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); //串口初始化为115200USART2_init(115200); //串口初始化为115200esp8266_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;} } }
}
Server
server::server(QWidget *parent) :QWidget(parent),ui(new Ui::server)
{ui->setupUi(this);tcpserver=nullptr;tcpsocket=nullptr;//创建监听套接字tcpserver=new QTcpServer(this);//指定父对象 回收空间//bind+listentcpserver->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邮箱 或 直接私信即可