【百问网】7天物联网智能家居实战 Day3_4

WIFI驱动

Day3-四、WIFI驱动

(一)、初始化串口

wifi模块需要用到接收中断用来保存命令的返回值或者数据,所以还要配置USART2的接收

使用WIFI模块的底层本质是利用串口进行收发。

主要包括对串口2相关参数的配置、初始化串口2的IO引脚以及设置其优先级配置、使能接收中断、串口2的中断回调函数等。

static void HAL_UART2_MspInit(UART_HandleTypeDef *huart);

static UART_HandleTypeDef huart2;

int Driver_Net_UART_Init(void)
{	
	huart2.Instance = USART2;
	huart2.Init.BaudRate = 115200;
	huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	huart2.Init.Mode = UART_MODE_TX_RX;
	huart2.Init.OverSampling = UART_OVERSAMPLING_16;
	huart2.Init.Parity = UART_PARITY_NONE;
	huart2.Init.StopBits = UART_STOPBITS_1;
	huart2.Init.WordLength = UART_WORDLENGTH_8B;	
	
	HAL_UART2_MspInit(&huart2);   //初始化串口2的IO引脚以及设置其优先级
	
	HAL_UART_Init(&huart2);
	if(HAL_UART_Init(&huart2) != HAL_OK)
		return -1;                //初始化串口2失败
	
	__HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);  //使能串口2的接收中断,串口2已接受到数据就进入接收中断,执行USART2_IRQHandler函数
	
	return 0;                     //初始化串口2成功
}
	
static void HAL_UART2_MspInit(UART_HandleTypeDef *huart)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
    
	if(huart->Instance == USART2)
	{
		__HAL_RCC_USART2_CLK_ENABLE();
		__HAL_RCC_GPIOA_CLK_ENABLE();
        
		/* USART2 TX/RX */
		/* PA2  - TX
		 * PA3  - RX
		 */
		GPIO_InitStruct.Pin = GPIO_PIN_2;
		GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;//复用推挽输出
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
                                               
		GPIO_InitStruct.Pin = GPIO_PIN_3;
		GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;//复用输入
		GPIO_InitStruct.Pull = GPIO_PULLUP;       //配置上拉
		HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
		
		//设置串口2的接收中断
		HAL_NVIC_SetPriority(USART2_IRQn,1,0);//设置优先级  
		HAL_NVIC_EnableIRQ(USART2_IRQn);      //使能中断
	}
}

void USART2_IRQHandler(void)
{
	uint8_t rx_data = 0;
	if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE) == SET)//进入接收中断后首先判断以下RXNE标志位是不是1
	{
		__HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_RXNE);   //如果是就先清除一下标志位
		rx_data = USART2->DR;                             //并读出接收到的数据
	}
}

(二)、Net模块接口
1、发送指令函数

Driver_Net_TransmitCmd(const char * cmd, const char * reply, uint16_t timeout)

static int Driver_Net_TransmitCmd(const char * cmd, const char * reply, uint16_t timeout)
{//cmd和reply都是字符指针,不是字符
	char buf[128] = {0};
	uint8_t i = 0;
	
	strcat(buf, cmd);				//先把cmd拷贝到buf字符串的后面,因为buf字符串初始值为0,所以就相当于把cmd复制到buf里。
	if(strstr(buf,"\r\n") == NULL)	//判断命令里是否有“\r\n”
	{				
		strcat(buf,"\r\n");			//如果没有则把"\r\n"拷贝到buf的最后
	}
	//以上,buf里存放的是含有结束符\r\n的的命令cmd
	
	Driver_Buffer_Clean(&WifiCMDReturnBuffer);	//再把环形缓冲区WifiCMDReturnBuffer清空
	HAL_UART_Transmit(&huart2, (uint8_t *)buf, strlen(buf), 500);	//再利用串口2把buf里的数据发送出去,也即把命令发送出去
	memset(buf,0,128);							//清空buf
	
	while(timeout != 0)
	{
		if(Driver_Buffer_Read(&WifiCMDReturnBuffer, (uint8_t *)&buf[i]) == 0)//读环形缓冲区WifiCMDReturnBuffer的数据到buf,如果读成功则返回0,条件成立
		{											//把&buf[i]强转成uint8_t *类型,读缓冲区数据的一个字节到buf[i]这个char字符,由于用的是&buf[i],buf[i]这个字符的地址,也是指针,符合参数类型
            i = (i+1)%128; //就相当于i的自增,同时保证i不会超过128而溢出。比如说i初始值为0,那么现在i=1。
            
            if(strstr(buf, reply) != 0)	//判断buf字符串中是否包含有reply字符串
            {
                return 0;               //如果有就返回0
			}
        }
        else//如果读取环形缓冲区WifiCMDReturnBuffer失败
        {
            timeout--;		//超时时间自减
            HAL_Delay(1);   //延时1ms
        }
	}
	return -1;
}

while的逻辑为:
判断超时时间timeout是否减为0
不为0则:
读取环形缓冲区WifiCMDReturnBuffer的一字节数据到buf[i]的char字符
	①如果读取成功:
		i自增并且防止溢出128;
		判断buf里是否包含reply字符串;
		即是否收到且收完了命令的返回值reply
		这里就是判断发出去一个命令后是否接收到命令的返回值reply,如果判断完整地接收到了命令的返回值则返回0,
		此时认为相当于成功发送出去了一个命令。
	②如果读取失败:
		timeout减1;
		延时1ms;

Driver_Net_TransmitCmd函数的逻辑:
①处理要发送的命令cmd,使其结尾总会有结束符\r\n;
②清空WifiCMDReturnBuffer,将cmd发送出去,清空buf; 。。。在串口2的接收中断会收到命令的返回值并写入环形缓冲区。。。
③while判断超时时间是否为0:
不为0则一个字节一个字节地读取环形缓冲区,读到buf[i]的字符去,并且i自增,每次读都判断buf字符串是否已经包含了命令的返回值reply,
如果发现已经包含了reply的完整字符串,说明已经完整地收到了cmd命令的返回值reply,就返回0,也即表示命令发送成功。
如果读取环形缓冲区失败,则timeout减1,并延时1ms,如果一直都读取失败,就一直减1并延时等待,直到timeout减为0 退出while循环。

2、发送数据函数

Driver_Net_TransmitSocket(const char * socket, int len, int timeout)

int Driver_Net_TransmitSocket(const char * socket, int len, int timeout)
{
    char buf[64] = {0};		//64个字符的数组64Bytes
    char cmd[16] = {0};		//16个字符的数组16Bytes
    uint8_t i = 0;
	
	sprintf(cmd, "AT+CIPSEND=%d\r\n", len);						   //将命令“AT+CIPSEND=len\r\n”赋给cmd
	HAL_UART_Transmit(&huart2, (uint8_t *)cmd, strlen(cmd), 500);	 //发送命令
	HAL_Delay(1);												 //延时一会
	Driver_Buffer_Clean(&WifiCMDReturnBuffer);						//清空环形缓冲区
	HAL_UART_Transmit(&huart2, (uint8_t *)socket, len, 500);     	  //发送数据
	while(timeout != 0)                       						//等待并读取命令的返回值
    {
		if(Driver_Buffer_Read(&WifiCMDReturnBuffer, (uint8_t *)&buf[i]) == 0)
		{
            i = (i+1)%64;
            if(strstr(buf, "SEND OK") != 0)
            {
                return 0;
			}
        }
        else
        {
            timeout--;
            HAL_Delay(1);
        }
	}
	return -1;
}

发送数据函数逻辑与发送指令函数基本一致。

3、接收数据函数

*Driver_Net_RecvSocket(char buf, int len, int timeout)

int Driver_Net_RecvSocket(char *buf, int len, int timeout)
{
    static int tmp = 0;
    tmp += Driver_Buffer_ReadBytes(&NetDataBuffer, (uint8_t*)&buf[tmp], len);
    if(tmp == len)
    {
        tmp = 0;
        return len;
    }
    return tmp;
}

4、连接WIFI热点
/*连接WIFI热点*/
int Driver_Net_ConnectWiFi(const char * ssid, const char * pwd, int timeout)
{
	char buf[50] = "AT+CWJAP_CUR=\"";//转义字符把"也表示出来
    strcat(buf, ssid);
    strcat(buf, "\",\"");	//相当于“,”
    strcat(buf, pwd);
    strcat(buf, "\"");      //相当于"
	//则buf里就是:" AT+CWJAP_CUR="ssid","pwd" "
    return Driver_Net_TransmitCmd(buf, "GOT IP\r\n", timeout);//发送该命令
}

5、断开WIFI热点

Driver_Net_DisconnectWiFi(void)

/*断开WIFI热点连接*/
int Driver_Net_DisconnectWiFi(void)
{
    return Driver_Net_TransmitCmd("AT+CWQAP", "OK\r\n", 500);
}
6、建立TCP连接

*Driver_Net_ConnectTCP(const char ip, int port, int timeout)

int Driver_Net_ConnectTCP(const char *ip, int port, int timeout)
{
    char buf[128] = "AT+CIPSTART=\"TCP\",\"";	//AT+CIPSTART="TCP","  (19)
    sprintf(&buf[19], "%s\",%d", ip, port);		//ip",port
    return Driver_Net_TransmitCmd(buf, "OK\r\n", timeout);//buf里的数据就是AT+CIPSTART="TCP","ip",port.发送该命令
}
7、断开TCP/UDP连接

Driver_Net_Disconnect_TCP_UDP(void)

int Driver_Net_Disconnect_TCP_UDP(void)
{
    return Driver_Net_TransmitCmd("AT+CIPCLOSE", "OK\r\n", 500);
}
8、解析网络数据

NetDataProcess_Callback(uint8_t data)

该函数要放到串口2的接收中断中

typedef enum AT_STATUS{
	INIT_STATUS,
	 LEN_STATUS,
	DATA_STATUS	
}AT_STATUS;
static uint8_t g_DataBuff[256] = {0};
void NetDataProcess_Callback(uint8_t data)
{
	uint8_t *buf = g_DataBuff;
    static AT_STATUS g_status = INIT_STATUS;
    static int g_DataBuffIndex = 0;		//索引
    static int g_DataLen = 0;
    int i = g_DataBuffIndex;
    int m = 0;
	
	buf[i] = data;
    g_DataBuffIndex++;
	
	switch(g_status)
	{
		case INIT_STATUS:
		{
			if (buf[0] != '+')
			{
				g_DataBuffIndex = 0;
			}			
			else if (i == 4)
			{
				if (strncmp((char*)buf, "+IPD,", 5) == 0)
				{
					g_status = LEN_STATUS;
				}
				g_DataBuffIndex = 0;
			}
			break;
		}

		case LEN_STATUS:
		{
			if (buf[i] == ':')
			{
				/* 计算长度 */
				for (m = 0; m < i; m++)
				{
					g_DataLen = g_DataLen * 10 + buf[m] - '0';
				}
				g_status = DATA_STATUS;
				g_DataBuffIndex = 0;
			}
			else if (i >= 9)  /* ESP8266数据buffer大小是2920,  4位数: +IPD,YYYY:xxxxxx */
			{
				/* 都没有接收到':' */
				g_status = INIT_STATUS;
				g_DataBuffIndex = 0;
			}
			break;
		}

		case DATA_STATUS:
		{
			if (g_DataBuffIndex == g_DataLen)
			{
                /* 接收完数据 */
				Driver_Buffer_WriteBytes(&NetDataBuffer, buf, g_DataLen);
                /* 恢复初始状态 */
				g_status = INIT_STATUS;
				g_DataBuffIndex = 0;
				g_DataLen = 0;
			}
			break;
		}
        default:break;
	}
}

网络模块初始化函数

Driver_Net_Init(void)

int Driver_Net_Init(void)
{
	if(Driver_Net_UART_Init() != 0)
		return -1;
	
	Driver_Buffer_Init(&WifiCMDReturnBuffer,128);//初始化一个存储wifi命令返回值的环形缓冲区,长度为128字节
	Driver_Buffer_Init(&NetDataBuffer,1024);
	
	Driver_Net_TransmitCmd("AT+RST","OK\r\n",10000);
	HAL_Delay(500);
	Driver_Net_TransmitCmd("AT+CWMODE_CUR=1","OK\r\n",500);//设置AT的模式为Station模式

	return 0;
}

以上就完成了对网络模块驱动的编写。

下面需要对接口进行测试:

测试代码:

	... ... ... ...

	Driver_LED_Init();
	Driver_DBG_Init();
	Driver_Net_Init();
	
	//连接WIFI热点测试:
	if(Driver_Net_ConnectWiFi("ok","123456",5000) == 0)
	{
		printf("WIFI Connect Successsed!\r\n");
	}
	else
	{
		printf("WIFI Connect Failed!\r\n");
	}
	//建立TCP连接测试:
	if(Driver_Net_ConnectTCP("223.104.39.172",8000,1000) == 0)
	{
		printf("TCP Connect Successsed!\r\n");
	}
	else
	{
		printf("TCP Connect Failed!\r\n");
	}
	
	while(1)
	{
		//发送数据测试
		if(Driver_Net_TransmitSocket("Hello World",11,500) == 0)
		{
			printf("Socket Transmit Success!\r\n");
		}
		else
		{
			printf("Socket Transmit Failed!\r\n");
		}
		//接收数据测试
		if(Driver_Net_RecvSocket((char *)buf, 5, 100) == 0)
		{
			printf("Receive Socket : %s\r\n",buf);
		}
		memset(buf, 0, 5);
		
		HAL_Delay(1000);
	}


... ... ... ...

经过测试发现,连接WIFI热点时,对于需要验证登录的网络如校园网是连接失败的,对于手机热点是可以连接的。

(三)、C知识回顾

strcat()函数用法:

描述

C 库函数 char *strcat(char *dest, const char *src)src 所指向的字符串追加到 dest 所指向的字符串的结尾。

声明

下面是 strcat() 函数的声明。

char *strcat(char *dest, const char *src)

参数

  • dest – 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串。
  • src – 指向要追加的字符串,该字符串不会覆盖目标字符串。

返回值

该函数返回一个指向最终的目标字符串 dest 的指针。

实例

下面的实例演示了 strcat() 函数的用法。

#include <stdio.h>
#include <string.h>
 
int main ()
{
   char src[50], dest[50];
 
   strcpy(src,  "This is source");
   strcpy(dest, "This is destination");
 
   strcat(dest, src);
 
   printf("最终的目标字符串: |%s|", dest);
   
   return(0);
}

让我们编译并运行上面的程序,这将产生以下结果:

最终的目标字符串: |This is destinationThis is source|

strstr()函数的用法:

描述

C 库函数 char *strstr(const char *haystack, const char *needle) 在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 ‘\0’。

声明

下面是 strstr() 函数的声明。

char *strstr(const char *haystack, const char *needle)

参数

  • haystack – 要被检索的 C 字符串。
  • needle – 在 haystack 字符串内要搜索的小字符串。

返回值

该函数返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 null。

实例

#include <stdio.h>
#include <string.h>
 
 
int main()
{
   const char haystack[20] = "RUNOOB";
   const char needle[10] = "NOOB";
   char *ret;
 
   ret = strstr(haystack, needle);
 
   printf("子字符串是: %s\n", ret);
   
   return(0);
}

让我们编译并运行上面的程序,这将产生以下结果:

子字符串是: NOOB

训练营导航:www.100ask.net

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值