提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
主要想玩一下遥控汽车通过stm32c8t6连接wifi模块控制小车前进、后退、左转、右转。
一、材料准备
硬件:
1.STM32F103C8T6最小核心板
2.ESP8266(原子云固件)
3.L298N驱动
4.ST-Link下载器
软件:
1.固件烧写工具
2.串口助手
3.网络调试助手
左边马达:
PA8----->ENA //定时器1引脚
PC14---->IN1
PC15---->IN2
右边马达:
PA0----->ENB //定时器2引脚
PB12---->IN1
PB13---->IN2
串口3:
PB10---->RX //RX为esp8266RX引脚
PB11---->TX
二、原子云固件下载
http://www.openedv.com/docs/modules/iot/atk-esp.html
2.这是我打包好的程序源码以及上述所用的软件
https://pan.baidu.com/s/1NcFIn4GRzuG9y_DV1aSB6w
提取码:1234
三、实验步骤
1.基本AT指令
我用的是STA+TCP透传模式:
AT //检测wifi模块是否存在
AT+CWMODE=1 //设置ESP8266为STA模式
AT+RST //重启模块
AT+CWJAP=”wifi名称“,”wifi密码“ //连接wifi
AT+CIPSTART=”TCP“,”ip地址“,端口 //连接TCP服务器
AT+CIPMODE=1 //开启透传
例子:
while(esp8266_send_cmd("AT","OK",100));
printf("wifi存在!\r\n\r\n");
//设置ESP8266为STA模式
while(esp8266_send_cmd("AT+CWMODE=1","OK",50));
printf("STA模式\r\n\r\n");
while(esp8266_send_cmd("AT+RST","ready\r\n",200));
printf("复位成功\r\n\r\n");
//连接WIFI
while(esp8266_send_cmd("AT+CWJAP=\"www\",\"20020318\"","WIFI GOT IP",300))
{
printf("wifi连接失败\r\n\r\n");
}
printf("wifi连接成功\r\n");
//www 为我的wifi名称
//20020318 为我的wifi密码
//WIFI GOT IP 为原子云固件连接上wifi后返回的答复
//300 为等待时间
while(esp8266_send_cmd("AT+CIPSTART=\"TCP\",\"10.121.35.2\",8080","CONNECT",200)){//这命令要等到手机端与esp8266建立连接后才会返回0,结束循环
printf("TCP连接失败\r\n");
}
printf("TCP连接成功\r\n\r\n");
while(esp8266_send_cmd("AT+CIPMODE=1","OK",200)){//这命令要等到手机端与esp8266建立连接后才会返回0,结束循环
}
printf("透传模式\r\n\r\n");
ps:一定要注意答复的地方要与原子云固件了的答复一样,否则会连接失败,可以先用usb-ttl与esp8266通过电脑测试一下回复。注意改为自己需要连接的wifi和tcp服务器。
2.串口接收中断
我使用的是串口3与wifi通信,由于使用cubemx生成代码,所以比较简单,只需要注意开启串口接收中断即可。
///////串口1重定向用于打印信息
int fputc(int ch,FILE *f)
{
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xffff);
//HAL_UART_Transmit(&huart3,(uint8_t *)&ch,1,0xffff);
return ch;
}
////////此函数用于向esp8266发送指令
void u3_printf(char* fmt,...)
{
uint16_t i,j;
va_list ap;
va_start(ap,fmt);
vsprintf((char*)USART3_TX_BUF,fmt,ap);
va_end(ap);
i=strlen((const char*)USART3_TX_BUF); //此次发送数据的长度
for(j=0;j<i;j++) //循环发送数据
{
while((USART3->SR&0X40)==0); //循环发送,直到发送完毕
USART3->DR=USART3_TX_BUF[j];
}
}
/////此函数接收esp8266返回的答复
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART3)
{
if((USART3_RX_STA&0x8000)==0)
{
if(USART3_RX_STA<USART_REC_LEN)
{
TIM3->CNT = 0;
if(USART3_RX_STA == 0)
{
HAL_TIM_Base_Start_IT(&htim3);
}
USART3_RX_BUF[USART3_RX_STA++] = aRxBuffer[0];
}
else
{
USART3_RX_STA |= 0x8000;
}
}
}
HAL_UART_Receive_IT(&huart3,(uint8_t *)&aRxBuffer,1);
}
中间用到定时器的目的是每隔100ms不管接没接收完成,都强制让串口3接收完成。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM3)
{
USART3_RX_STA |= 0x8000;
HAL_TIM_Base_Stop_IT(&htim3);
}
}
3.esp8266代码
//向ESP8266发送命令
//cmd:发送的命令字符串;ack:期待的应答结果,如果为空,则表示不需要等待应答;waittime:等待时间(单位:10ms)
//返回值:0,发送成功(得到了期待的应答结果);1,发送失败
u8 esp8266_send_cmd(u8 *cmd,u8 *ack,u16 waittime)
{
u8 res=0;
USART3_RX_STA=0;
u3_printf("%s\r\n",cmd); //发送命令
if(ack&&waittime) //需要等待应答
{
while(--waittime) //等待倒计时
{
HAL_Delay(10);
if(USART3_RX_STA&0X8000)//接收到期待的应答结果
{
if(esp8266_check_cmd(ack))
{
//printf("ack:%s\r\n",(u8*)ack);
break;//得到有效数据
}
USART3_RX_STA=0;
}
}
if(waittime==0)res=1;
}
return res;
}
//ESP8266发送命令后,检测接收到的应答
//str:期待的应答结果
//返回值:0,没有得到期待的应答结果;其他,期待应答结果的位置(str的位置)
u8* esp8266_check_cmd(u8 *str)
{
char *strx=0;
if(USART3_RX_STA&0X8000) //接收到一次数据了
{
USART3_RX_BUF[USART3_RX_STA&0X7FFF]=0;//添加结束符
strx=strstr((const char*)USART3_RX_BUF,(const char*)str);
}
return (u8*)strx;
}
strstr()函数:
简单来说就是你传入的参数如果与esp8266发送给串口3的USART3_RX_BUF里字符匹配,则会成功返回。
4.主函数
while(esp8266_send_cmd("AT","OK",100));
printf("wifi存在!\r\n\r\n");
//设置ESP8266为STA模式
while(esp8266_send_cmd("AT+CWMODE=1","OK",50));
printf("STA模式\r\n\r\n");
while(esp8266_send_cmd("AT+RST","ready\r\n",200));
printf("复位成功\r\n\r\n");
//连接WIFI
while(esp8266_send_cmd("AT+CWJAP=\"www\",\"20010318\"","WIFI GOT IP",300)){
printf("wifi连接失败\r\n\r\n");
}
printf("wifi连接成功\r\n");
while(esp8266_send_cmd("AT+CIPSTART=\"TCP\",\"10.121.35.19\",8080","CONNECT",200)){//这命令要等到手机端与esp8266建立连接后才会返回0,结束循环
printf("TCP连接失败\r\n");
}
printf("TCP连接成功\r\n\r\n");
while(esp8266_send_cmd("AT+CIPMODE=1","OK",200)){//这命令要等到手机端与esp8266建立连接后才会返回0,结束循环
}
printf("透传模式\r\n\r\n");
while (1)
{
if(USART3_RX_STA & 0x8000)
{
switch(USART3_RX_BUF[0])
{
case 'G':run();
break;
case 'S':stop();
break;
case 'B':back();
break;
case 'L':left();
break;
case 'R':right();
break;
}
USART3_RX_STA = 0 ;
}
}
5.电机驱动模块
#include "motor.h"
//pc14/pc15左走前轮 pb12/pb13
void run()
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_13,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_RESET);
}
void stop()
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_13,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_SET);
}
void back()
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_13,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_SET);
}
void left()
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_13,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_RESET);
}
void right()
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_13,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_14,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_15,GPIO_PIN_RESET);
}
总结
其实esp8266还是比较容易应用,多看几遍用户手册和AT指令集,咱就可以简单的入个门了;以下是我遇到的问题:1.串口接收回调函数里面一定不要乱判结束符!!!因为你的数据可能还没有接收完成就被你判结束了。具体可以打断点调试工程。 2.注意固件里每个指令的响应,不然弄错了会连不上你的wifi或者tcp。