FreeRTOS下WIFI模块下的socket(一)
在 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方面的驱动
更多推荐



所有评论(0)