1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <termios.h> #include <getopt.h> #include <time.h>
// 核心配置参数 #define UART_DEV_PATH "/dev/ttyS4" // 串口输入设备(读取触摸数据) #define REPORT_DEV_PATH "/dev/uart_tp_4"// 上报设备(输出x,y,1/0格式) #define KTV_FRAME_LEN 10 // KTV协议一帧长度(10字节) #define SERIAL_BAUDRATE B9600 // 串口波特率(9600bps)
// 坐标分辨率配置(原始→目标) #define RAW_MAX_X 4095 // KTV协议X轴最大值(12位:0~4095) #define RAW_MAX_Y 4095 // KTV协议Y轴最大值(12位:0~4095) #define SCREEN_MAX_X 522 // 屏幕X轴最大值(根据实际屏幕调整) #define SCREEN_MAX_Y 1280 // 屏幕Y轴最大值(根据实际屏幕调整)
// KTV协议定义 #define KTV_HEADER1 0x55 #define KTV_HEADER2 0x54 #define STATUS_FIRST_TOUCH 0x81 // 首次触摸(按下) #define STATUS_CONTINUE_TOUCH 0x82 // 持续触摸(按下) #define STATUS_RELEASE_TOUCH 0x84 // 释放触摸(抬起)
// 旋转角度定义(支持命令行配置) typedef enum { ROTATE_0 = 0, // 0度(默认) ROTATE_90 = 1, // 90度顺时针 ROTATE_180 = 2, // 180度 ROTATE_270 = 3 // 270度顺时针(=90度逆时针) } RotateType;
// KTV协议帧结构体 typedef struct { unsigned char header1; // 帧头1:0x55 unsigned char header2; // 帧头2:0x54 unsigned char status; // 触摸状态 unsigned char xCoorL; // X坐标低8位 unsigned char xCoorH; // X坐标高4位 unsigned char yCoorL; // Y坐标低8位 unsigned char yCoorH; // Y坐标高4位 unsigned char tail1; // 尾码1:0xFF unsigned char tail2; // 尾码2:0x00 unsigned char checksum; // 校验字节 } KTV_Touch_Frame;
// 触摸状态缓存(解决无释放问题) typedef struct { int last_x; int last_y; int last_pressed; struct timespec last_touch_time; // 最后触摸时间 } TouchCache;
static TouchCache touch_cache = {0, 0, 0, {0, 0}};
/** * @brief 计算KTV协议校验和(遵循协议公式) */ static unsigned char calc_ktv_checksum(KTV_Touch_Frame *frame) { return (0x52 + frame->status + frame->xCoorL + frame->xCoorH + frame->yCoorL + frame->yCoorH) & 0xFF; }
/** * @brief 串口配置(优化超时和读取模式) */ static int uart_config(int fd) { struct termios opt; memset(&opt, 0, sizeof(opt));
if (tcgetattr(fd, &opt) < 0) { perror("tcgetattr failed"); return -1; }
// 波特率配置 cfsetispeed(&opt, SERIAL_BAUDRATE); cfsetospeed(&opt, SERIAL_BAUDRATE);
// 8位数据位、无校验、1位停止位 opt.c_cflag &= ~PARENB; opt.c_cflag &= ~CSTOPB; opt.c_cflag &= ~CSIZE; opt.c_cflag |= CS8; opt.c_cflag |= CLOCAL | CREAD; // 本地模式 + 启用接收
// 原始模式(禁用输入输出处理) opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); opt.c_oflag &= ~OPOST;
// 优化读取超时(100ms超时,至少读取1字节) opt.c_cc[VTIME] = 1; // 100ms(单位:0.1秒) opt.c_cc[VMIN] = 1; // 至少读取1字节才返回
// 应用配置 tcflush(fd, TCIOFLUSH); if (tcsetattr(fd, TCSANOW, &opt) < 0) { perror("tcsetattr failed"); return -1; }
return 0; }
/** * @brief 坐标旋转(支持0/90/180/270度) */ static void rotate_coordinate(int raw_x, int raw_y, RotateType rotate, int *rotated_x, int *rotated_y) { switch (rotate) { case ROTATE_90: *rotated_x = raw_y; *rotated_y = RAW_MAX_X - raw_x; break; case ROTATE_180: *rotated_x = RAW_MAX_X - raw_x; *rotated_y = RAW_MAX_Y - raw_y; break; case ROTATE_270: *rotated_x = RAW_MAX_Y - raw_y; *rotated_y = raw_x; break; case ROTATE_0: default: *rotated_x = raw_x; *rotated_y = raw_y; break; } }
/** * @brief 坐标缩放(线性映射:原始坐标→屏幕坐标) */ static void scale_coordinate(int raw_x, int raw_y, int *screen_x, int *screen_y) { *screen_x = (raw_x * SCREEN_MAX_X) / RAW_MAX_X; *screen_y = (raw_y * SCREEN_MAX_Y) / RAW_MAX_Y;
// 边界保护 *screen_x = (*screen_x < 0) ? 0 : (*screen_x > SCREEN_MAX_X ? SCREEN_MAX_X : *screen_x); *screen_y = (*screen_y < 0) ? 0 : (*screen_y > SCREEN_MAX_Y ? SCREEN_MAX_Y : *screen_y); }
/** * @brief 坐标防抖(仅当坐标变化超过阈值时更新) */ static int coordinate_debounce(int new_x, int new_y, int last_x, int last_y) { const int DEBOUNCE_THRESHOLD = 5; // 防抖阈值(根据实际调整) int dx = abs(new_x - last_x); int dy = abs(new_y - last_y); return (dx > DEBOUNCE_THRESHOLD || dy > DEBOUNCE_THRESHOLD); }
/** * @brief 上报数据到/dev/uart_tp4(格式:x,y,1/0) */ static int report_to_uarttp4(int report_fd, int x, int y, int is_pressed) { char report_buf[32]; int len;
// 防抖:仅当状态/坐标变化时上报 if (is_pressed == touch_cache.last_pressed && !coordinate_debounce(x, y, touch_cache.last_x, touch_cache.last_y)) { return 0; // 无变化,无需上报 }
// 格式化上报数据(修复is_pressed=10的笔误) len = snprintf(report_buf, sizeof(report_buf), "%d,%d,%d\n", x, y, is_pressed); if (len < 0 || len >= sizeof(report_buf)) { fprintf(stderr, "report buffer overflow\n"); return -1; }
// 写入上报设备 if (write(report_fd, report_buf, len) != len) { perror("write to /dev/uart_tp_4 failed"); return -1; }
// 更新缓存 touch_cache.last_x = x; touch_cache.last_y = y; touch_cache.last_pressed = is_pressed; clock_gettime(CLOCK_MONOTONIC, &touch_cache.last_touch_time);
// 打印日志(调试用) printf("Report: %s", report_buf); return 0; }
/** * @brief 检查触摸超时,自动释放(解决无释放问题) */ static int check_touch_timeout(int report_fd) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now);
// 计算超时时间(300ms无触摸则自动释放) long long elapsed_ms = (now.tv_sec - touch_cache.last_touch_time.tv_sec) * 1000 + (now.tv_nsec - touch_cache.last_touch_time.tv_nsec) / 1000000;
if (touch_cache.last_pressed == 1 && elapsed_ms > 300) { // 超时自动上报释放状态 return report_to_uarttp4(report_fd, touch_cache.last_x, touch_cache.last_y, 0); } return 0; }
/** * @brief 帧同步:找到有效的KTV帧头(解决粘包问题) */ static int sync_frame(int uart_fd) { unsigned char byte; int ret;
// 循环读取直到找到帧头0x55 while (1) { ret = read(uart_fd, &byte, 1); if (ret != 1) { return -1; } if (byte == KTV_HEADER1) { // 找到0x55后,检查下一字节是否为0x54 ret = read(uart_fd, &byte, 1); if (ret == 1 && byte == KTV_HEADER2) { // 帧头同步成功,回退两个字节(让后续read读取完整帧) lseek(uart_fd, -2, SEEK_CUR); return 0; } } } }
/** * @brief 帮助信息 */ static void print_help(const char *prog_name) { printf("Usage: %s [options]\n", prog_name); printf("Options:\n"); printf(" -r, --rotate <angle> Set coordinate rotation (0/90/180/270, default:0)\n"); printf(" -h, --help Show this help message\n"); printf("\nExample:\n"); printf(" %s -r 90 # Rotate 90 degrees clockwise\n", prog_name); }
int main(int argc, char **argv) { int uart_fd, report_fd; KTV_Touch_Frame frame; int ret; RotateType rotate = ROTATE_0; int raw_x, raw_y; int rotated_x, rotated_y; int screen_x, screen_y; int is_pressed;
// 1. 打开串口输入设备 uart_fd = open(UART_DEV_PATH, O_RDWR | O_NOCTTY | O_NONBLOCK); if (uart_fd < 0) { perror("open /dev/ttyS4 failed"); return -1; }
// 2. 配置串口 if (uart_config(uart_fd) < 0) { close(uart_fd); return -1; }
// 3. 打开上报设备 report_fd = open(REPORT_DEV_PATH, O_WRONLY | O_NONBLOCK); if (report_fd < 0) { perror("open /dev/uart_tp_4 failed"); close(uart_fd); return -1; }
printf("UART touch report start:\n"); printf(" - UART input: %s\n", UART_DEV_PATH); printf(" - Report output: %s\n", REPORT_DEV_PATH); printf(" - Coordinate scale: %dx%d -> %dx%d\n", RAW_MAX_X+1, RAW_MAX_Y+1, SCREEN_MAX_X+1, SCREEN_MAX_Y+1); printf(" - Rotate angle: %d degrees\n", rotate * 90);
// 初始化触摸缓存时间 clock_gettime(CLOCK_MONOTONIC, &touch_cache.last_touch_time);
// 替换原main函数中“循环读取→解析”的部分 unsigned char uart_buf[64] = {0}; // 串口缓存 int buf_len = 0; // 缓存中未处理的字节数
while (1) { // 1. 读取串口数据到缓存(每次读最多64字节) ret = read(uart_fd, uart_buf + buf_len, sizeof(uart_buf) - buf_len); if (ret < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK) { perror("read uart data failed"); break; } usleep(20000); // 无数据休眠20ms,降低CPU占用 continue; } else if (ret == 0) { usleep(10000); continue; } buf_len += ret;
// 2. 解析缓存中的完整KTV帧(帧头0x55 0x54,长度10字节) while (buf_len >= KTV_FRAME_LEN) { // 查找帧头(0x55 0x54) int frame_start = -1; for (int i = 0; i <= buf_len - KTV_FRAME_LEN; i++) { if (uart_buf[i] == KTV_HEADER1 && uart_buf[i+1] == KTV_HEADER2) { frame_start = i; break; } }
if (frame_start == -1) { // 未找到帧头,清空缓存(避免脏数据累积) buf_len = 0; break; }
// 拷贝完整帧到frame结构体 memcpy(&frame, uart_buf + frame_start, KTV_FRAME_LEN); // 移除已解析的帧(将剩余数据移到缓存头部) memmove(uart_buf, uart_buf + frame_start + KTV_FRAME_LEN, buf_len - frame_start - KTV_FRAME_LEN); buf_len -= frame_start + KTV_FRAME_LEN;
// 3. 校验帧尾和校验和(原逻辑保留) if (frame.tail1 != 0xFF || frame.tail2 != 0x00) { fprintf(stderr, "warning: invalid frame tail (0x%x,0x%x)\n", frame.tail1, frame.tail2); continue; } if (frame.checksum != calc_ktv_checksum(&frame)) { fprintf(stderr, "warning: checksum error (calc:0x%x, recv:0x%x)\n", calc_ktv_checksum(&frame), frame.checksum); continue; }
// 4. 解析坐标+旋转+缩放+上报(原逻辑保留) raw_x = ((frame.xCoorH & 0x0F) << 8) | frame.xCoorL; raw_y = ((frame.yCoorH & 0x0F) << 8) | frame.yCoorL; rotate_coordinate(raw_x, raw_y, rotate, &rotated_x, &rotated_y); scale_coordinate(rotated_x, rotated_y, &screen_x, &screen_y);
switch (frame.status) { case STATUS_FIRST_TOUCH: case STATUS_CONTINUE_TOUCH: is_pressed = 1; break; case STATUS_RELEASE_TOUCH: is_pressed = 0; break; default: fprintf(stderr, "warning: unknown touch status (0x%x)\n", frame.status); continue; }
// 打印有效数据日志(调试用) printf("Valid touch: raw(x:%d,y:%d) → screen(x:%d,y:%d), pressed:%d\n", raw_x, raw_y, screen_x, screen_y, is_pressed);
// 上报数据 if (report_to_uarttp4(report_fd, screen_x, screen_y, is_pressed) < 0) { break; } } }
// 退出前上报释放状态(关键:解决异常退出无释放) report_to_uarttp4(report_fd, touch_cache.last_x, touch_cache.last_y, 0);
// 资源释放 close(uart_fd); close(report_fd); printf("UART touch report stopped\n"); return 0; }
|