在 FreeRTOS 环境下使用WiFi 模块进行 Socket 编程.

MCU STM32F4系列(STM32F407ZGT6)
WIFI模块 ESP8266(通过串口与MCU通信,使用AT指令集)
RTOS   FreeRTOS(通过STM32CubeMX生成)
IDE Keil MDK v5
配置工具 STM32CubeMX
调试工具 Sscom5(串口调试助手,用于调试AT指令)

一.确定完整的框架

完整框架如下:

① socket 接口层:实现通用的 socket 接口,可以是函数,也可以是宏(我是函数)。

② socket 适配层:以 send 函数为例,它可能调用 at_send,也可能调用 lwip_send。本项目里使用“at_” 前缀的函数。

③ 模组管理层: 如果想支持多个 WIFI 模组,比如 ESP8266、W800,那么还应该引入“模组管理层”。底下的 esp8266.c、w800.c 分别调用 at_client_register 向本层注册一个结构体,以后上层代码就可以使用 at_client_get 获得这些结构体。为了让本项目的代码简单一点,我没有实现这层。而是如此操作(举例):at_send 直接调用 esp8266_send。

④ 模组驱动层:比如 esp8266.c,实现 esp8266_send 等模块相关的函数。

⑤ AT 命令层:在上一层的函数比如 esp8266_send 函数里会构造芯片相关的 AT 命令,这

些 AT 命令要使用本层的函数发送出去、等待结果。

⑥ UART 封装层:上一层的“at_exec_cmd”需要选择某个 UART 发送数据,本层把串口设

备封装为“UART_Device”以便屏蔽底层不同芯片、不同串口的操作函数。

⑦ UART HAL 层:这层实现 UART 的硬件操作。

二.Socket层

AT设备(ESP8266)和Socket的封装

typedef uint32_t socklen_t;

struct sockaddr
{
    uint8_t sa_len;
    uint8_t sa_family;
    char sa_data[14];
};

struct in_addr
{
    uint32_t s_addr;
};

struct sockaddr_in
{
    uint8_t sin_len;
    uint8_t sin_family;
    uint16_t sin_port;
    struct in_addr sin_addr;
#define SIN_ZERO_LEN 8
    char sin_zero[SIN_ZERO_LEN];
};

typedef struct AT_Socket
{
	uint32_t used; /* 0-未被占用, 1-被占用 */
	int type;      /* TCP(SOCK_STREAM) or UDP(SOCK_DGRAM) */
	struct sockaddr local;  /* 用来记录本地IP/PORT */
	struct sockaddr remote; /* 用来记录远端IP/PORT */
	void *user_data;        /* AT模块自己的数据,
												 * 对于W800就是硬件socket值,
												 * 对于ESP8266就是link id
												 */
	SemaphoreHandle_t at_packet_sem; /* 读取网络数据时,等待这个信号量 */
	SemaphoreHandle_t at_send_lock;      /* socket的加锁 */
	uint8_t           at_socket_open_flag;//socket打开的标志
	uint8_t           at_socket_server_flag;//服务器标志
	QueueHandle_t recv_queue;        /* 队列,用来存放接收到的网络数据 */
} AT_Socket, *PAT_Socket;

typedef struct AT_Device
{
	char *name; /* WIFI模块的名字 */
	SemaphoreHandle_t at_lock;      /* 发送AT命令前需要先获得这个锁 */
	SemaphoreHandle_t at_resp_sem;  /* 发送AT命令后等待这个信号量(等待AT命令的回应) */
	uint8_t resp[AT_RESP_MAX_LINE][AT_RESP_BUF_SIZE]; /* 存放AT命令的回应数据 */
	uint32_t resp_len[AT_RESP_MAX_LINE];              /* AT命令回应数据的长度 */
	uint32_t resp_line_counts;      /* AT命令回应的数据有多少行 */
	uint32_t resp_status;           /* AT命令的回应是OK还是ERR */
	Uart_Device *ptUARTDev;         /* 使用这个串口设备访问WIFI模块 */
	AT_Socket sockets[AT_MAX_SOCKETS_NUM]; /* socket结构体数组 */
} AT_Device, *PAT_Device;

接口层

int at_socket(int domain, int type, int protocol);
int at_closesocket(int socket);
int at_shutdown(int socket, int how);
int at_bind(int socket, const struct sockaddr *name, socklen_t namelen);
int at_connect(int socket, const struct sockaddr *name, socklen_t namelen);
int at_sendto(int socket, const void *data, size_t size, int flags, const struct sockaddr *to, socklen_t tolen);
int at_send(int socket, const void *data, size_t size, int flags);
int at_recvfrom(int socket, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);
int at_recv(int socket, void *mem, size_t len, int flags);
int at_flush(int socket);
int at_listen(int socket, int backlog);
int at_accept(int socket, struct sockaddr *name, socklen_t *namelen);


#define socket(domain, type, protocol)                      at_socket(domain, type, protocol)
#define closesocket(socket)                                 at_closesocket(socket)
#define shutdown(socket, how)                               at_shutdown(socket, how)
#define bind(socket, name, namelen)                         at_bind(socket, name, namelen)
#define connect(socket, name, namelen)                      at_connect(socket, name, namelen)
#define sendto(socket, data, size, flags, to, tolen)        at_sendto(socket, data, size, flags, to, tolen)
#define send(socket, data, size, flags)                     at_send(socket, data, size, flags)
#define recvfrom(socket, mem, len, flags, from, fromlen)    at_recvfrom(socket, mem, len, flags, from, fromlen)
#define recv(socket, mem, len, flags)                       at_recv(socket, mem, len, flags)
#define net_flush(socket)                                   at_flush(socket)
#define listen(socket, backlog)                             at_listen(socket, backlog)
#define accept(socket, name, namelen)                       at_accept(socket, name, namelen)

适配层

int at_connect_ap(char *ssid, char *passwd)
{
    return esp8266_connect_ap(ssid, passwd);
}

int at_socket(int domain, int type, int protocol)
{
    return esp8266_socket(domain, type, protocol);
}

int at_closesocket(int socket)
{
    return esp8266_closesocket(socket);
}

int at_shutdown(int socket, int how)
{
    return at_closesocket(socket);
}

int at_bind(int socket, const struct sockaddr *name, socklen_t namelen)
{
    return esp8266_bind(socket, name, namelen);
}

int at_connect(int socket, const struct sockaddr *name, socklen_t namelen)
{
    return esp8266_connect(socket, name, namelen);
}

int at_sendto(int socket, const void *data, size_t size, int flags, const struct sockaddr *to, socklen_t tolen)
{
    return esp8266_sendto(socket, data, size, flags, to, tolen);
}

int at_send(int socket, const void *data, size_t size, int flags)
{
    return esp8266_sendto(socket, data, size, flags, NULL, 0);
}

int at_recvfrom(int socket, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen)
{
    return esp8266_recvfrom(socket, mem, len, flags, from, fromlen);
}

int at_recv(int socket, void *mem, size_t len, int flags)
{
    return at_recvfrom(socket, mem, len, flags, NULL, NULL);
}

int at_listen(int socket, int backlog)
{
    return esp8266_listen(socket, backlog);
}

int at_accept(int socket, struct sockaddr *name, socklen_t *namelen)
{
    return esp8266_accept(socket, name, namelen);
}

三.UART层

1.UART封装

typedef struct Uart_Data{
	UART_HandleTypeDef *handle;						    /* uart的句柄 */
	SemaphoreHandle_t xTxSem;				   			/* uart发送需要信号量 */
	QueueHandle_t xRxQueue;			        			/* uart接收到的数据放在队列里 */
	uint8_t rxdatas[UART_RX_BUF_LEN];				    /* 存放uart接收的数据 */
}Uart_Data;

typedef struct Uart_Device{
	char *name;								    /* uart名字 */
	uint8_t channel;						    /* uart通道 */																	
	int (*Init)(struct Uart_Device *ptdev);		/* uart初始化 */													
	int	(*Write)(struct Uart_Device *ptdev, uint8_t *pdatas, uint16_t len, uint32_t timeout);			                            /* uart写数据 */
	int (*Read)(struct Uart_Device *ptdev, uint8_t *pdata, uint32_t timeout);
                                                /* uart读数据 */											                             
	Uart_Data *priv_data;						/* uart的数据 */																    
	SemaphoreHandle_t uart_lock;				/* uart锁 */																													
	struct Uart_Device *next;					/* uart管理 */																														
}Uart_Device;

2.UART驱动

UART1:打印功能             UART2:ESP8266打印功能

TX:使用中断

RX:使用DMA+IDLE

#define UART_RX_QUEUE_LEN   260

extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart2;

static Uart_Data g_Uart1_Data;
static Uart_Data g_Uart2_Data;

static int UartDrvInit(struct Uart_Device *ptdev);																												
static int UartDrvWrite(struct Uart_Device *ptdev, uint8_t *pdatas, uint16_t len, uint32_t timeout);			
static int UartDrvRead(struct Uart_Device *ptdev, uint8_t *pdata, uint32_t timeout);											

static Uart_Device g_UartDevices[] = {
	uart1, uart2
};

void UartDeviceCreate(void)
{
	uint8_t num = sizeof(g_UartDevices) / sizeof(Uart_Device);
	for(int i=0; i<num; i++)
	{
		UartDeviceInster(&g_UartDevices[i]);
	}
}

static int UartDrvInit(struct Uart_Device *ptdev)
{
	if(NULL == ptdev)
		return -1;
	
	switch(ptdev->channel)
	{
		case 1:
		{	
			ptdev->priv_data = &g_Uart1_Data;
			ptdev->uart_lock = xSemaphoreCreateMutex();
			if(NULL == ptdev->uart_lock)
				return -1;
			
			g_Uart1_Data.handle = &huart1;
			g_Uart1_Data.xTxSem = xSemaphoreCreateBinary();
			if(NULL == g_Uart1_Data.xTxSem)
				return -1;
			g_Uart1_Data.xRxQueue = xQueueCreate(UART_RX_QUEUE_LEN, 1);
			
			HAL_UARTEx_ReceiveToIdle_DMA(g_Uart1_Data.handle, g_Uart1_Data.rxdatas, UART_RX_BUF_LEN);
			
			break;
		}
		case 2:
		{
			ptdev->priv_data = &g_Uart2_Data;
			ptdev->uart_lock = xSemaphoreCreateMutex();
			if(NULL == ptdev->uart_lock)
				return -1;
			
			g_Uart2_Data.handle = &huart2;
			g_Uart2_Data.xTxSem = xSemaphoreCreateBinary();
			if(NULL == g_Uart2_Data.xTxSem)
				return -1;
			g_Uart2_Data.xRxQueue = xQueueCreate(UART_RX_QUEUE_LEN, 1);
				
			HAL_UARTEx_ReceiveToIdle_DMA(g_Uart2_Data.handle, g_Uart2_Data.rxdatas, UART_RX_BUF_LEN);
			
			break;
		}
		default:
		{
			break;
		}
	}
	return 0;
}

static int UartDrvWrite(struct Uart_Device *ptdev, uint8_t *pdatas, uint16_t len, uint32_t timeout)
{
	if(NULL == ptdev)
		return -1;
	if(NULL == pdatas)
		return -1;
	if(0 == len)
		return -1;
	if(0 == timeout)
		return -1;
	
	switch(ptdev->channel)
	{
		case 1:
		{	
			HAL_StatusTypeDef status = HAL_UART_Transmit_IT(g_Uart1_Data.handle, pdatas, len);
			if(HAL_OK != status)
				return -1;
			if(pdPASS != xSemaphoreTake(g_Uart1_Data.xTxSem, timeout))
				return -1;
			
			break;
		}
		case 2:
		{
			HAL_StatusTypeDef status = HAL_UART_Transmit_IT(g_Uart2_Data.handle, pdatas, len);
			if(HAL_OK != status)
				return -1;
			if(pdPASS != xSemaphoreTake(g_Uart2_Data.xTxSem, timeout))
				return -1;
			
			break;
		}
		default:
		{
			break;
		}
	}
	return 0;	
}

static int UartDrvRead(struct Uart_Device *ptdev, uint8_t *pdata, uint32_t timeout)
{
	if(NULL == ptdev)
		return -1;
	if(NULL == pdata)
		return -1;
	if(0 == timeout)
		return -1;
	
	switch(ptdev->channel)
	{
		case 1:
		{	
			//从队列中读取数据(一个数据)
			if(pdPASS != xQueueReceive(g_Uart1_Data.xRxQueue, pdata, timeout))
				return -1;
		
			break;
		}
		case 2:
		{
			//从队列中读取数据(一个数据)
			if(pdPASS != xQueueReceive(g_Uart2_Data.xRxQueue, pdata, timeout))
				return -1;
			
			break;
		}
		default:
		{
			break;
		}
	}	
	return 0;
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart == &huart1)
	{
		xSemaphoreGiveFromISR(g_Uart1_Data.xTxSem, NULL);
	}
	else if(huart == &huart2)
	{
		xSemaphoreGiveFromISR(g_Uart2_Data.xTxSem, NULL);	
	}
}

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	int len = Size;

	if(HAL_UART_RXEVENT_HT == huart->RxEventType)
		return; 
	
	if(huart == &huart1)
	{
		//需要将私有数据中数组接收到的数据写进队列中去
		for(int i=0; i<len; i++)
		{
			xQueueSendFromISR(g_Uart1_Data.xRxQueue, &g_Uart1_Data.rxdatas[i], NULL); 
		}
		
		HAL_UARTEx_ReceiveToIdle_DMA(g_Uart1_Data.handle, g_Uart1_Data.rxdatas, UART_RX_BUF_LEN);
	}
	else if(huart == &huart2)
	{
		for(int i=0; i<len; i++)
		{
			xQueueSendFromISR(g_Uart1_Data.xRxQueue, &g_Uart1_Data.rxdatas[i], NULL); 
		}
		
		HAL_UARTEx_ReceiveToIdle_DMA(g_Uart2_Data.handle, g_Uart2_Data.rxdatas, UART_RX_BUF_LEN);
	}
}

//防止万一发生串口错误中断把DMA关闭
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{    
	if(huart == &huart1)
	{
		HAL_UARTEx_ReceiveToIdle_DMA(g_Uart1_Data.handle, g_Uart1_Data.rxdatas, UART_RX_BUF_LEN);	
	}
	else if(huart == &huart2)
	{
		HAL_UARTEx_ReceiveToIdle_DMA(g_Uart2_Data.handle, g_Uart2_Data.rxdatas, UART_RX_BUF_LEN);	
	}
}

UART1重定向:

struct __FILE{
    int handle;
};

FILE __stdout;

/* 写了上面2个,USE MicroLIB就不用✔ */

int fputc(int ch, FILE *f)
{
	(void)f;
	Uart_Device *ptdev = UartDeviceFind("uart1");
	if(NULL == ptdev)
		return -1;
	/*获取锁,防止别的打印打断当前打印*/
	xSemaphoreTake(ptdev->uart_lock, portMAX_DELAY);
	
	ptdev->Write(ptdev, (uint8_t *)&ch, 1, 1);
	
	/*释放锁*/
	xSemaphoreGive(ptdev->uart_lock);
	return ch;	
}

三.AT指令层

void at_reset_resp(PAT_Device ptDev)
{
	//接收数据清0
	memset(ptDev->resp, 0, sizeof(ptDev->resp));
	//每行个数都清0
	memset(ptDev->resp_len, 0, AT_RESP_MAX_LINE);
  //行数清0
  ptDev->resp_line_counts = 0;
	//状态清0
	ptDev->resp_status = 0;
}

int at_exec_cmd(PAT_Device ptDev, int8_t *cmd, uint8_t *resp, uint32_t max_len, uint32_t *resp_len, uint32_t timeout)
{
	if(NULL == ptDev)
		return -1;
	if(NULL == cmd)
		return -1;
	if(0 == timeout)
		return -1;
	
	uint32_t current_len = 0;
	uint32_t total_len = 0;
	int ret = -1;
	
	Uart_Device *ptUARTDev = ptDev->ptUARTDev;
	if(NULL == ptUARTDev)
		return -1;
	
	//获取锁
	xSemaphoreTake(ptDev->at_lock, portMAX_DELAY);
	
	//清空保存的原数据
	at_reset_resp(ptDev);
	
	//通过串口发送命令
	ptUARTDev->Write(ptUARTDev, (uint8_t *)cmd, strlen((char *)cmd), timeout);
	
	//等待后台串口接收任务释放的信号量
	if(pdPASS == xSemaphoreTake(ptDev->at_resp_sem, timeout))
	{
		//将接收到所有数据都保存在resp中(包括每一行的数据)
		//将接收的长度保存在resp_len(包括每一行的长度)
		if(resp)
		{
			for(int i=0; i<ptDev->resp_line_counts; i++)//循环每一行
			{
				current_len = (ptDev->resp_len[i] > max_len) ? max_len : ptDev->resp_len[i]; //当前行的长度
				if((current_len + total_len) <= max_len)
				{
					memcpy(resp+total_len, ptDev->resp[i], current_len);	//拷贝每一行的数据	
					total_len = total_len + current_len; 
				}
			}
			*resp_len = total_len;
		}
		ret = ptDev->resp_status;
	}
	//释放锁
	xSemaphoreGive(ptDev->at_lock);
	return ret;
}

//at发送数据,这个是在接收到">"后,再次发送数据
int at_send_datas(PAT_Device ptDev, uint8_t *datas, uint32_t data_len, uint32_t timeout)
{
	if(NULL == ptDev)
		return -1;
	if(NULL == datas)
		return -1;
	if(0 == data_len)
		return -1;
	if(0 == timeout)
		return -1;
	
	Uart_Device *ptUARTDev = ptDev->ptUARTDev;
	if(NULL == ptUARTDev)
		return -1;
	
	//获取锁
	xSemaphoreTake(ptDev->at_lock, portMAX_DELAY);

	//发送数据
	ptUARTDev->Write(ptUARTDev, datas, data_len, timeout);	
	
	//释放锁
	xSemaphoreGive(ptDev->at_lock);
	return 0;
}

接下来将理解ESP8266方面的驱动

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐