初探STM32F4(3)--WIFI模块(1)

WIFI

  • WIFI测试的代码架构剖析
    • 步骤1:wifi模块的主函数流程
    • 步骤2:剖析atk_8266_send_cmd()函数
    • 步骤3:剖析atk_8266_wifista_test()函数
    • 步骤4:剖析如何配置成TCP客户端
    • 步骤5:作为客户端如何向服务器发送数据

通过上几节的分析,对STM32常用外设的配置有了基本概念,可以开始本文研究,本文并不是具体分析WIFI模块通讯机理,仅是对 正点原子的代码例程架构的详细剖析。

阅读完本文,要能回答以下问题:

  1. 简述单片机配置WIFI模块的主程序流程
  2. 解释WIFI模块的三种工作模式STA+AP、STA、AP都是什么?
  3. 单片机是通过发送AT指令来操作WIFI模块,简述发送AT指令的代码流程
  4. 如果将WIFI模块配置成STA模式,简述STA配置代码流程
  5. 配置好WIFI模块后,要与远端进行收发数据,收发数据的协议有UDP或者TCP(客户端/服务器),这些都是什么?
  6. 如果将WIFI模块配置成TCP客户端与远端通信,简述配置成TCP客户端的代码流程?简述发送数据的代码流程?
  7. STA/AP与TCP/UDP是两类不同层次的概念,简单区分一下。

WIFI测试的代码架构剖析

步骤1:wifi模块的主函数流程

1、阐述例程主函数代码结构?首先是单片机串口初始化,然后是其余用到的外设初始化,最后是WIFI模块初始化,之后就是测试WIFI模块收发数据。详细代码如下:

int main(void)
{
	u8 key,fontok=0; 	
//基本的外设初始化
  Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhz   
  HAL_Init();                     //HAL库初始化
  delay_init(180);                //初始化延时函数
  uart_init(115200);              //初始化USART1
  usart3_init(115200);  		//初始化USART3
  
  //下面就是些其他外设的初始化配置了,以后再来学习。
  usmart_dev.init(90);				//³õʼ»¯usmart
  LED_Init();                     //³õʼ»¯LED 
  KEY_Init();                     //³õʼ»¯°´¼ü
  SDRAM_Init();                   //SDRAM³õʼ»¯
  LCD_Init();                     //LCD³õʼ»¯
	W25QXX_Init();				    			//³õʼ»¯W25Q256
	tp_dev.init();									//³õʼ»¯´¥ÃþÆÁ
  my_mem_init(SRAMIN);            //³õʼ»¯ÄÚ²¿ÄÚ´æ³Ø
  my_mem_init(SRAMEX);            //³õʼ»¯ÍⲿSDRAMÄÚ´æ³Ø
  my_mem_init(SRAMCCM);           //³õʼ»¯ÄÚ²¿CCMÄÚ´æ³Ø
  exfuns_init();		            	//ΪfatfsÏà¹Ø±äÁ¿ÉêÇëÄÚ´æ  
  f_mount(fs[0],"0:",1);          //¹ÒÔØSD¿¨ 
 	f_mount(fs[1],"1:",1);          //¹ÒÔØSPI FLASH.  
	key=KEY_Scan(0);
	if((key==KEY0_PRES)&&(tp_dev.touchtype&0X80)==0)		//Ç¿ÖÆУ׼£¬µç×èÆÁ²ÅÐèҪУ׼
	{
		LCD_Clear(WHITE);	//ÇåÆÁ
		TP_Adjust();  		//ÆÁĻУ׼ 
		TP_Save_Adjdata();	  
		LCD_Clear(WHITE);	//ÇåÆÁ
	}
	fontok=font_init();		//¼ì²é×Ö¿âÊÇ·ñOK
	if(fontok||key==KEY1_PRES)//ÐèÒª¸üÐÂ×Ö¿â				 
	{
		LCD_Clear(WHITE);		   	//ÇåÆÁ
 		POINT_COLOR=RED;			//ÉèÖÃ×ÖÌåΪºìÉ«	   	   	  
		LCD_ShowString(60,50,200,16,16,"ALIENTEK STM32");
		while(SD_Init())			//¼ì²âSD¿¨
		{
			LCD_ShowString(60,70,200,16,16,"SD Card Failed!");
			delay_ms(200);
			LCD_Fill(60,70,200+60,70+16,WHITE);
			delay_ms(200);		    
		}								 						    
		LCD_ShowString(60,70,200,16,16,"SD Card OK");
		LCD_ShowString(60,90,200,16,16,"Font Updating...");
		key=update_font(20,110,16,"0:");//´ÓSD¿¨¸üÐÂ
		while(key)//¸üÐÂʧ°Ü		
		{			 		  
			LCD_ShowString(60,110,200,16,16,"Font Update Failed!");
			delay_ms(200);
			LCD_Fill(20,110,200+20,110+16,WHITE);
			delay_ms(200);		       
		} 		  
		LCD_ShowString(60,110,200,16,16,"Font Update Success!");
		delay_ms(1500);	
		LCD_Clear(WHITE);//ÇåÆÁ	       
	}

//到这里都是其他外设的配置,先不研究

 	atk_8266_test();		//ATK_ESP8266的测试
}

串口通信的初始化配置已在前文介绍,其余用到的外设初始化本文暂时不涉及。下面重点关注WIFI测试函数void atk_8266_test(void),

2.atk_8266_test函数流程是什么样的?首先测试WIFI模块能否正常应答,当WIFI模块能够应答后,先关闭其事先设置好的透传模式(关于透传模式是什么,看这篇文章.),然后按键选择不同的透传模式。一共有三种模式可以选择:STA+AP、STA、AP。配置好模式后,测试WIFI模块收发数据。整个流程的具体代码如下所示:

void atk_8266_test(void)
{
//	u16 rlen=0;
	u8 key;
	u8 timex;
	POINT_COLOR=RED;
	Show_Str_Mid(0,30,"ATK-ESP8266 WIFIÄ£¿é²âÊÔ",16,240); 
	while(atk_8266_send_cmd("AT","OK",20))//检查WIFI模块是否能够应答
	{
		atk_8266_quit_trans();//退出透传
		atk_8266_send_cmd("AT+CIPMODE=0","OK",200);  //关闭透传模式
		Show_Str(40,55,200,16,"未检查到模块",16,0);
		delay_ms(800);
		LCD_Fill(40,55,200,55+16,WHITE);
		Show_Str(40,55,200,16,"尝试连接模块...",16,0); 
	} 
		while(atk_8266_send_cmd("ATE0","OK",20));//关闭回显,以为会死循环,后来才发现有break语句跳出while循环。如果没有break就一直配置。
		atk_8266_mtest_ui(32,30);
	while(1)
	{
		delay_ms(10); 
		atk_8266_at_response(1);//¼ì²éATK-ESP8266Ä£¿é·¢Ë͹ýÀ´µÄÊý¾Ý,¼°Ê±ÉÏ´«¸øµçÄÔ
		key=KEY_Scan(0); 
		if(key)
		{
			LCD_Clear(WHITE);
			POINT_COLOR=RED;
			switch(key)
			{
				case 1://KEY0
					Show_Str_Mid(0,30,"ATK-ESP WIFI-AP+STA ²âÊÔ",16,240);
					Show_Str_Mid(0,50,"ÕýÔÚÅäÖÃATK-ESP8266Ä£¿é£¬ÇëÉÔµÈ...",12,240);
					atk_8266_apsta_test();	//STA+AP
					break;
				case 2://KEY1
					Show_Str_Mid(0,30,"ATK-ESP WIFI-STA ²âÊÔ",16,240);
					Show_Str_Mid(0,50,"ÕýÔÚÅäÖÃATK-ESP8266Ä£¿é£¬ÇëÉÔµÈ...",12,240);
					atk_8266_wifista_test();//WIFI STA模式
					break;
				case 4://WK_UP
					atk_8266_wifiap_test();	//WIFI AP模式
					break;
			}
			atk_8266_mtest_ui(32,30);
			timex=0;
		} 	 
		if((timex%20)==0)LED0=!LED0;//200msÉÁ˸ 
		timex++;	 
	} 
}

3、STA+AP、STA、AP三种模式是什么意思?

  • 首先回答WIFI模块是干什么的?WIFI模块是串口转WIFI通信的一种传输转换模块,模块内置无线网络协议栈(IEEE802.11、TCP/IP)
  • 多个WIFI模块之间就可以组成无线网络拓扑,那么每个WIFI模块在无线网络中的地位是怎么样的呢?
    • AP,无线接入点,是一个无线网络的创建者,是网络的中心节点。一般家庭中的无线路由器就是一个AP
    • STA站点,每一个连接到无线网络中的终端都可以称为一个站点。
  • 因此就有了两种无线网络结构:
    • 基于AP组建的无线网络(Infra),是由AP创建,众多STA加入。网络中所有站点的通信都通过AP来转发完成
    • 基于自组网的无线网络(Adhoc),是仅由两个及以上STA组成,网络中不存在AP,所有STA都可以直接通信
  • 现在可以回答这三种模式的意思了。
    • STA模式。WIFI模块本身并不接受无线的接入,它可以连接到ap(路由器),然后连接到互联网。
    • AP模式。WIFI模块本身作为热点,提供无线接入服务,允许其它无线设备接入,提供数据访问。AP之间允许相互连接
    • STA+AP模式。同时支持两个功能,例如手机,既连到无线路由器上。同时由可以开热点,让其他的终端连进来

步骤2:剖析atk_8266_send_cmd()函数

通过上一节我们知道了,在WIFI测试函数里面,单片机与wifi模块的通讯,是通过发送AT指令并等待应答实现的。发送AT指令对应的函数是atk_8266_send_cmd()函数,代码如下:

u8 atk_8266_send_cmd(u8 *cmd,u8 *ack,u16 waittime)
{
	u8 res=0; 
	USART3_RX_STA=0;
	u3_printf("%s\r\n",cmd);	//串口3发送数据
	if(ack&&waittime)		//如果需要等待应答
	{
		while(--waittime)	//等待时间开始倒计时
		{
			delay_ms(10);
			if(USART3_RX_STA&0X8000)//接收到读操作的标记位
			{
				if(atk_8266_check_cmd(ack))
				{
					printf("ack:%s\r\n",(u8*)ack);
					break;//做出应答回复,结束程序
				}
					USART3_RX_STA=0;
			} 
		}
		if(waittime==0)res=1; 
	}
	return res;
} 

简单分析一轮atk_8266_send_cmd()函数:

  1. 该函数入口参数含义?第一个入口参数为u8 *cmd,u8是uint8_t的别名,uint8_t是unsigned char的别名,故cmd是一个指向无符号字符数据类型的指针,表示待发送的命令字符串。第二个入口参数为u8 *ack,ack也是一个指向无符号字符数据类型的指针,表示期待的应答结果。第三个入口参数是u16 waittime,u16是uint16_t的别名,uint16_t是unsigned short int的别名,是两字节的整形变量,表示等待时间。
  2. 该函数返回参数含义?返回参数数据类型也是一个无符号字符数据类型。为什么可以给该数据类型赋值1或者0?因为字符变量在内存中存放的是其对应的ASCII值,所以你给一个char变量赋值1的话,根据ASCLL表,也有相应的字符。所以能赋值。约定好0代表发送成功、1代表发送失败。
  3. 发送命令字符串通过u3_printf()函数实现,分析其函数内部细节。函数名称表明是通过串口uart3发送数据,代码细节如下,大体流程是函数接收可变参数,将接收到的参数写入UART3的数据寄存器,C语言中可变参数的用法参考这篇文章.
void u3_printf(char* fmt,...)  
{  
	u16 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);			//不断将要发送的数据写入UART3的数据寄存器中
		USART3->DR=USART3_TX_BUF[j];  
	} 
}
  1. STM32通过u3_printf指令,已经向wifi模块发送了AT指令,后续代码是要干什么? wifi模块接收到AT指令后也会返回一个特征值给STM32去读,告诉STM32自己是否正常工作。因此,后续代码就是在倒计时之内一直等待串口进行读操作,产生中断,由于中断中设定了某些特征值的标志位,如果接收到了这些标志位,就可以通过printf发送应答指令了。printf函数也被重定义了,实现向串口1发送数据,我们就可以通过串口调试助手看到应答指令了。
int fputc(int ch, FILE *f)
{ 	
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
	USART1->DR = (u8) ch;      
	return ch;
}
  1. 串口1、3的中断服务程序中干了些啥事? 以后再研究。
  2. 发送AT指令函数中还调用了atk_8266_check_cmd函数,这函数是要实现什么功能?函数内部主要是strstr()函数的功能,具体看这篇文章.通过strstr()函数实现了这样的功能:STM32发送AT指令后,收到了wifi了应答,只有应答语句包含ok字符串,这才表明AT指令生效,将ok字符串打印到串口1中去,否则不打印,继续倒计时等待。
u8* atk_8266_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;
}

步骤3:剖析atk_8266_wifista_test()函数

WIFI模块有三种模式可以选择:STA+AP、STA、AP。本文只剖析sta模式(ap模式后续再说)的测试代码,对应于函数atk_8266_wifista_test()

1、简述配置STA模式的代码架构?代码流程如下:

  1. 打开STA模式
  2. 设置无线参数,连接指定路由器
  3. 选择网络模式,在STA模式下有三种子模式选择(TCP服务器、TCP客户端和UDP)
  4. 配置好后,进入主循环开始数据收发测试,此时如果连接成功建立,我们可以通过按键中断选择不同测试方式:
    1. 如果按KEY_UP按键,即退出当前测试,回到主界面
    2. 如果按KEY_0按键,即开始发送数据测试,按照不同子模式(TCP服务器、TCP客户端和UDP),有不同的发送指令格式。
  5. 如果接收到外部设备发送过来的数据,将串口接收回来的数据显示到设置好的位置。
  6. 如果一直没有收到数据,程序每隔10 秒会检查一次连接是否存在
  7. 同时LED灯每400ms 闪烁一次,提示程序正在运行。具体代码架构如下。
u8 atk_8266_wifista_test(void)
{
	//......
	//设置为STA模式
	atk_8266_send_cmd("AT+CWMODE=1","OK",50);		//设置为STA模式
	//......
	//配置无线参数,连接目标路由器
	sprintf((char*)p,"AT+CWJAP=\"%s\",\"%s\"",wifista_ssid,wifista_password);
	while(atk_8266_send_cmd(p,"WIFI GOT IP",300));
	//......
	//配置网络模式
	netpro|=atk_8266_netpro_sel(50,30,(u8*)ATK_ESP8266_CWMODE_TBL[0]);
	if(netpro&0X02)   //UDP
	{
	}		
	else//TCP
	{
		if(netpro&0X01)//TCP Client
		{}
		else//TCP Server
		{}
	}

	while(1)
	{	
		key=KEY_Scan(0);
		if(key==WKUP_PRES)//WK_UP 退出测试
		{
		}
		else if(key==KEY0_PRES)//KEY0 发送数据
		{
			if((netpro==3)||(netpro==2))   //UDP
			{}
			else if((netpro==1))   //TCP Client
			{}
			else    //TCP Server
		}
		else;
		//......
		
		if(USART3_RX_STA&0X8000)		//如果接收到数据
		{
			//将接收到的数据打印到串口
		}

		t++;
		if(t==1000)
		{
			//检查wifi模块连接状态
		}
		if((t%20)==0)LED0=!LED0;//程序正常运行的指示灯
	}
	myfree(SRAMIN,p);		//释放内存
	return res;		
}

2、STA模式下有三种子模式(TCP服务器、TCP客户端和UDP)是什么意思?

  • TCP和UDP都是WIFI通信时需要遵守的规则,WIFI上的客户要在同一规则下交流
  • TCP,传输控制协议,通过制定一些规则,实现了数据准确无误的传递。由于TCP传送数据前需要客户与服务器建立连接,传输速度慢
  • UDP,用户数据报协议,它只是把数据发送出去,不能确保数据到达目的地,但无需客户与服务器建立连接,传输速度快
  • 在选择TCP进行通信的时候,通信的双方,必须一方为TCP客户端(TCP Client),一方为TCP服务器(TCP Server)。以打电话来类比,打电话的人是TCP客户端,接电话的一方为TCP服务器。TCP/IP协议中的IP地址类似电话号码,而TCP/IP中的端口号类似电话分机号。客户端(拨电话者),首先需要知道对方的IP地址和端口(电话号码和分机号),先连接上TCP服务器端(接听者的电话摘机),才能进行数据传输(通话)。和一般电话不同,在这里TCP服务器可以同时接入多个TCP客户端,就如有多路线路的电话,不会因为一路电话在接通中而不能接入其它电话。

  • 有了上述这些知识,再去看WIFI模块不同子模式下的测试步骤,就很好理解了。

步骤4:剖析如何配置成TCP客户端

1、如何将wifi模块配置成TCP客户端?此时WIFI模块作为打电话的一方,最重要的步骤就是输入对方的电话号码,对应函数atk_8266_ip_set(),该函数通过显示屏按键输入远端IP地址。

		if(netpro&0X01)     //TCP Client    ͸´«Ä£Ê½²âÊÔ
		{
			LCD_Clear(WHITE);
			POINT_COLOR=RED;
			Show_Str_Mid(0,30,"ATK-ESP WIFI-STA ²âÊÔ",16,240); 
			Show_Str(30,50,200,16,"ÕýÔÚÅäÖÃATK-ESPÄ£¿é,ÇëÉÔµÈ...",12,0);
			if(atk_8266_ip_set("WIFI-STA Ô¶¶ËIPÉèÖÃ",(u8*)ATK_ESP8266_WORKMODE_TBL[netpro],(u8*)portnum,ipbuf))goto PRESTA;	//IPÊäÈë
			atk_8266_send_cmd("AT+CIPMUX=0","OK",20);   //0£ºµ¥Á¬½Ó£¬1£º¶àÁ¬½Ó
			sprintf((char*)p,"AT+CIPSTART=\"TCP\",\"%s\",%s",ipbuf,(u8*)portnum);    //ÅäÖÃÄ¿±êTCP·þÎñÆ÷
			while(atk_8266_send_cmd(p,"OK",200))
			{
					LCD_Clear(WHITE);
					POINT_COLOR=RED;
					Show_Str_Mid(0,40,"WK_UP:·µ»ØÖØÑ¡",16,240);
					Show_Str(30,80,200,12,"ATK-ESP Á¬½ÓTCPʧ°Ü",12,0); //Á¬½Óʧ°Ü	 
					key=KEY_Scan(0);
					if(key==WKUP_PRES)goto PRESTA;
			}	
			atk_8266_send_cmd("AT+CIPMODE=1","OK",200);      //´«ÊäģʽΪ£ºÍ¸´«			
		}

步骤5:作为客户端如何向服务器发送数据

1、如何发送数据?当WIFI模块已经和远端IP建立好连接后,只要单片机开启透传模式,然后向串口3(连接WIFI模块的串口)写数据就可以发送数据了。

					else if((netpro==1))   //TCP Client
					{
						atk_8266_quit_trans();
						atk_8266_send_cmd("AT+CIPSEND","OK",20);         //¿ªÊ¼Í¸´«           
						sprintf((char*)p,"ATK-8266%s测试%d\r\n",ATK_ESP8266_WORKMODE_TBL[netpro],t/10);//待发送的数据
						Show_Str(30+54,100,200,12,p,12,0);
						u3_printf("%s",p);
						timex=100;
					}

你可能感兴趣的:(STM32开发)