完成了基础的中文和英文字符显示
This commit is contained in:
@@ -1,53 +1,214 @@
|
||||
/*
|
||||
* remoteDisplay.c - TCP 服务器线程实现
|
||||
* 逻辑:等待客户端连接,循环接收消息并原样回发(echo),直到客户端断开或主线程请求退出
|
||||
* 实现 RemoDispBus 协议:解析 remo_disp_server.py 的请求,返回 lcd 显存数据等。
|
||||
* 帧格式: [TAG][cmd][len_hi][len_lo][data][crc],CRC = data 区逐字节异或低 8 位。
|
||||
* 客户端 TAG=0xAA,设备回复 TAG=0xBB。
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "TCP/tcp.h"
|
||||
|
||||
#include "remoteDisplay.h"
|
||||
#include "Drv/lcd.h"
|
||||
#include "TCP/tcp.h"
|
||||
|
||||
|
||||
/* RemoDispBus 协议常量(与 remo_disp_server.py 一致) */
|
||||
#define TAG_CLIENT 0xAAu
|
||||
#define TAG_DEVICE 0xBBu
|
||||
#define CMD_KEEPLIVE 0
|
||||
#define CMD_INIT 1
|
||||
#define CMD_KEY 2
|
||||
#define CMD_LCDMEM 3
|
||||
|
||||
/* 计算 data 区 CRC:逐字节异或取低 8 位 */
|
||||
static uint8_t calc_crc(const uint8_t* data, unsigned int len)
|
||||
{
|
||||
uint8_t crc = 0;
|
||||
while (len--)
|
||||
crc ^= *data++;
|
||||
return crc;
|
||||
}
|
||||
|
||||
/* 从 recv 缓冲区解析一帧: [TAG][cmd][len_hi][len_lo][data...][crc]
|
||||
* 成功返回 1 并设置 *p_cmd, *p_data, *p_data_len;失败返回 0。
|
||||
* *p_consume 返回本帧总字节数(含头尾),调用方从缓冲区移除。 */
|
||||
static int parse_frame(const uint8_t* buf, unsigned int buf_len,
|
||||
uint8_t* p_cmd, const uint8_t** p_data, unsigned int* p_data_len,
|
||||
unsigned int* p_consume)
|
||||
{
|
||||
if (buf_len < 5 || buf[0] != TAG_CLIENT)
|
||||
return 0;
|
||||
unsigned int data_len = ((unsigned int)buf[2] << 8) | buf[3];
|
||||
unsigned int frame_len = 4 + data_len + 1; /* 头4 + data + crc */
|
||||
if (buf_len < frame_len)
|
||||
return 0;
|
||||
uint8_t crc = calc_crc(buf + 4, data_len);
|
||||
if (crc != buf[4 + data_len])
|
||||
return 0;
|
||||
*p_cmd = buf[1];
|
||||
*p_data = buf + 4;
|
||||
*p_data_len = data_len;
|
||||
*p_consume = frame_len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 回复帧最大长度:头4 + 最大 payload(4+LCD_DISPLAYMEMORYSIZE) + crc1 */
|
||||
#define REMO_REPLY_BUF_SIZE (4 + 4 + LCD_DISPLAYMEMORYSIZE + 1)
|
||||
|
||||
/* 构造设备回复帧并发送: [TAG_DEVICE][cmd][len_hi][len_lo][data][crc],一次性发送 */
|
||||
static int send_reply(int client, uint8_t cmd, const uint8_t* data, unsigned int data_len)
|
||||
{
|
||||
uint8_t buf[REMO_REPLY_BUF_SIZE];
|
||||
unsigned int total = 4 + data_len + 1;
|
||||
|
||||
if (total > REMO_REPLY_BUF_SIZE)
|
||||
return -1;
|
||||
buf[0] = TAG_DEVICE;
|
||||
buf[1] = cmd & 0xFF;
|
||||
buf[2] = (data_len >> 8) & 0xFF;
|
||||
buf[3] = data_len & 0xFF;
|
||||
if (data_len)
|
||||
memcpy(buf + 4, data, data_len);
|
||||
buf[4 + data_len] = calc_crc(data, data_len);
|
||||
|
||||
return TcpClient_Send(client, (const char*)buf, total) == (int)total ? 0 : -1;
|
||||
}
|
||||
|
||||
/* 处理 CMD_LCDMEM:请求 data 为 4 字节大端起始地址;回复 [4B 地址][显存数据] */
|
||||
static void handle_cmd_lcdmem(int client, const uint8_t* req_data, unsigned int req_len)
|
||||
{
|
||||
unsigned int start_addr = 0;
|
||||
|
||||
printf("handle_cmd_lcdmem\n");
|
||||
if (req_len >= 4)
|
||||
start_addr = ((unsigned int)req_data[0] << 24) | ((unsigned int)req_data[1] << 16)
|
||||
| ((unsigned int)req_data[2] << 8) | req_data[3];
|
||||
|
||||
/* 回复 payload = [4B 地址大端][显存],Python 取 payload[4:] 为位图 */
|
||||
uint8_t payload[4 + LCD_DISPLAYMEMORYSIZE];
|
||||
payload[0] = (start_addr >> 24) & 0xFF;
|
||||
payload[1] = (start_addr >> 16) & 0xFF;
|
||||
payload[2] = (start_addr >> 8) & 0xFF;
|
||||
payload[3] = start_addr & 0xFF;
|
||||
|
||||
unsigned int copy_len = LCD_DISPLAYMEMORYSIZE;
|
||||
if (start_addr < LCD_DISPLAYMEMORYSIZE) {
|
||||
unsigned int offset = start_addr;
|
||||
copy_len = LCD_DISPLAYMEMORYSIZE - offset;
|
||||
memcpy(payload + 4, g_tCVsr.pwbyLCDMemory + offset, copy_len);
|
||||
} else {
|
||||
copy_len = 0;
|
||||
}
|
||||
send_reply(client, CMD_LCDMEM, payload, 4 + copy_len);
|
||||
}
|
||||
|
||||
/* 处理 CMD_INIT:回复宽、高、显存大小(与 Python 端约定一致时可扩展) */
|
||||
static void handle_cmd_init(int client)
|
||||
{
|
||||
uint8_t data[8];
|
||||
data[0] = (LCD_SIZE_X >> 8) & 0xFF;
|
||||
data[1] = LCD_SIZE_X & 0xFF;
|
||||
data[2] = (LCD_SIZE_Y >> 8) & 0xFF;
|
||||
data[3] = LCD_SIZE_Y & 0xFF;
|
||||
data[4] = (LCD_DISPLAYMEMORYSIZE >> 24) & 0xFF;
|
||||
data[5] = (LCD_DISPLAYMEMORYSIZE >> 16) & 0xFF;
|
||||
data[6] = (LCD_DISPLAYMEMORYSIZE >> 8) & 0xFF;
|
||||
data[7] = LCD_DISPLAYMEMORYSIZE & 0xFF;
|
||||
send_reply(client, CMD_INIT, data, 8);
|
||||
}
|
||||
|
||||
/* 处理 CMD_KEY:可选转交菜单,此处仅回 ACK */
|
||||
static void handle_cmd_key(int client, const uint8_t* data, unsigned int len)
|
||||
{
|
||||
(void)data;
|
||||
(void)len;
|
||||
send_reply(client, CMD_KEY, (const uint8_t*)NULL, 0);
|
||||
}
|
||||
|
||||
/* 处理 CMD_KEEPLIVE:保活,回空 */
|
||||
static void handle_cmd_keeplive(int client)
|
||||
{
|
||||
send_reply(client, CMD_KEEPLIVE, (const uint8_t*)NULL, 0);
|
||||
}
|
||||
|
||||
void tcp_server_thread_fn(void* arg)
|
||||
{
|
||||
server_thread_arg_t* ctx = (server_thread_arg_t*)arg; /* 强转为参数结构体 */
|
||||
int server_sock = TcpServer_Listen((uint16_t)ctx->port); /* 在指定端口创建监听 socket */
|
||||
if (server_sock == TCP_INVALID_SOCKET) { /* 监听失败(端口占用或权限等) */
|
||||
server_thread_arg_t* ctx = (server_thread_arg_t*)arg;
|
||||
int server_sock = TcpServer_Listen((uint16_t)ctx->port);
|
||||
if (server_sock == TCP_INVALID_SOCKET) {
|
||||
fprintf(stderr, "[TCP Server] TcpServer_Listen(%d) failed\n", ctx->port);
|
||||
return; /* 线程直接退出,主线程通过 server_sock 仍为 INVALID 可知失败 */
|
||||
return;
|
||||
}
|
||||
*ctx->p_server_sock = server_sock; /* 回写监听 socket,主线程可用来关闭以结束 Accept 阻塞 */
|
||||
printf("[TCP Server] 监听端口 %d,等待客户端连接...\n", ctx->port);
|
||||
*ctx->p_server_sock = server_sock;
|
||||
printf("[TCP Server] RemoDispBus 监听端口 %d,等待客户端连接...\n", ctx->port);
|
||||
|
||||
char recv_buf[1024]; /* 接收缓冲区,用于收客户端数据并回发 */
|
||||
#define REMO_BUF_SIZE 4096
|
||||
uint8_t recv_buf[REMO_BUF_SIZE];
|
||||
unsigned int recv_len = 0;
|
||||
|
||||
while (!*ctx->p_quit) { /* 主线程未请求退出时一直循环 */
|
||||
int client = TcpServer_Accept(server_sock); /* 阻塞等待一个客户端连接 */
|
||||
if (client == TCP_INVALID_SOCKET) { /* Accept 失败(含:主线程已关闭 server_sock) */
|
||||
if (*ctx->p_quit) /* 若是因退出请求导致,则跳出循环 */
|
||||
/* ========== 外层循环:主线程未请求退出时,持续等待并接受新客户端 ========== */
|
||||
while (!*ctx->p_quit) {
|
||||
/* 阻塞等待一个客户端连接;主线程关闭 server_sock 时 Accept 会失败并返回 INVALID */
|
||||
int client = TcpServer_Accept(server_sock);
|
||||
if (client == TCP_INVALID_SOCKET) {
|
||||
if (*ctx->p_quit)
|
||||
break;
|
||||
continue; /* 否则忽略本次错误,继续下一次 Accept */
|
||||
continue;
|
||||
}
|
||||
printf("[TCP Server] 客户端连接成功\n");
|
||||
/* 只要该客户端还连着,就循环等待消息并回发 */
|
||||
recv_len = 0; /* 新连接对应新的接收缓冲区,避免混用上一连接的残留数据 */
|
||||
|
||||
/* ========== 内层循环:处理当前连接上的 RemoDispBus 请求,直到断开或退出 ========== */
|
||||
while (!*ctx->p_quit) {
|
||||
printf("[TCP Server] 等待客户端消息\n");
|
||||
memset(recv_buf, 0, sizeof(recv_buf));
|
||||
int n = TcpClient_Recv(client, recv_buf, sizeof(recv_buf) - 1);
|
||||
/* ----- 1. 若缓冲区中已有一条完整且校验通过的帧,则解析并分发处理 ----- */
|
||||
uint8_t cmd;
|
||||
const uint8_t* pdata;
|
||||
unsigned int data_len, consume;
|
||||
if (parse_frame(recv_buf, recv_len, &cmd, &pdata, &data_len, &consume)) {
|
||||
switch (cmd) {
|
||||
case CMD_KEEPLIVE:
|
||||
handle_cmd_keeplive(client);
|
||||
break;
|
||||
case CMD_INIT:
|
||||
handle_cmd_init(client);
|
||||
break;
|
||||
case CMD_KEY:
|
||||
handle_cmd_key(client, pdata, data_len);
|
||||
break;
|
||||
case CMD_LCDMEM:
|
||||
handle_cmd_lcdmem(client, pdata, data_len);
|
||||
break;
|
||||
default:
|
||||
send_reply(client, cmd, (const uint8_t*)NULL, 0);
|
||||
break;
|
||||
}
|
||||
/* 从缓冲区头部移除已消费的 consume 字节,避免重复解析同一帧 */
|
||||
memmove(recv_buf, recv_buf + consume, recv_len - consume);
|
||||
recv_len -= consume;
|
||||
continue; /* 继续检查是否还有完整帧,避免不必要的 recv */
|
||||
}
|
||||
|
||||
/* ----- 2. 缓冲区中尚无完整帧:先防止溢出,再收一批数据 ----- */
|
||||
if (recv_len >= REMO_BUF_SIZE - 256) {
|
||||
recv_len = 0; /* 异常情况:数据过多且始终不成帧,清空缓冲区防止越界 */
|
||||
}
|
||||
int n = TcpClient_Recv(client, (char*)(recv_buf + recv_len), REMO_BUF_SIZE - recv_len - 1);
|
||||
if (n > 0) {
|
||||
printf("recv: %s\n", recv_buf);
|
||||
TcpClient_Send(client, recv_buf, (size_t)n);
|
||||
recv_len += (unsigned int)n;
|
||||
printf("recv_len = %d\n", recv_len);
|
||||
} else {
|
||||
/* n==0 表示对方关闭连接,n<0 表示出错,退出本连接循环 */
|
||||
/* n==0 表示对方正常关闭连接,n<0 表示读取出错;均退出本连接循环 */
|
||||
printf("[TCP Server] 客户端关闭连接\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* 当前客户端处理结束,关闭该连接;外层循环继续 Accept 下一个客户端 */
|
||||
TcpClient_Close(client);
|
||||
}
|
||||
|
||||
TcpServer_Close(server_sock); /* 关闭监听 socket */
|
||||
*ctx->p_server_sock = TCP_INVALID_SOCKET; /* 通知主线程 socket 已关闭,避免重复 Close */
|
||||
TcpServer_Close(server_sock);
|
||||
*ctx->p_server_sock = TCP_INVALID_SOCKET;
|
||||
printf("[TCP Server] 已退出\n");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user