文章目录
- 一、项目分析
- 1. 项目简介
- 2. 知识储备
- 3. 硬件选择
- 二、STM32部分
- 三、QT部分
- 四、遥控小车演示
- 程序源码
一、项目分析
1. 项目简介
本项目本质为客户端与服务器之间的通信,通过发送不同的指令,服务器和客户端进行不同的操作。
客户端:基于STM32制作简单行驶小车
服务器:安卓手机,基于Socket编程下 用QT进行安卓开发,将app传输到手机上。
TCP通信:ESP8266
手机端发送不同指令,小车执行不同操作
2. 知识储备
- STM32基础 可移步 “ STM32专栏 ” 进行知识充能
- ESP8266WiFi模块 可移步 “ ESP8266WiFi模块的基本通信 ” 学习使用
- Socket编程基础 可移步 “ Socket编程基础 ” 学习使用
- QT_Socket_TCP 可移步 “ QT_Socket_tcp通信 ” 学习使用
- QT_Android环境搭建 可移步 “ QT Android环境搭建 ” 学习使用
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邮箱 或 直接私信即可