当前位置: 首页 > article >正文

linux c串口应用编程,参照golang里面的json.Marshal/json.Unmarshal

protocol.h代码:

root@iZwz99zhkxxl5h6ecbm2xwZ:~/serial-ipc# cat protocol.h 
#ifndef __PROTOCOL_H__
#define __PROTOCOL_H__

#include <stdint.h>
#include <stdbool.h>

enum PACKAGE_TYPE {
	TYPE_HEART_BEAT = 0x01,
	TYPE_SOMKE = 0x02,
};

typedef struct __package {
	uint8_t type;                // 命令类型
	uint8_t waterNum;            // 流水号,递增,避免命令重传时重复执行
	uint8_t checksum;            // crc
	uint8_t len;                 // 内容长度
	//void * content;
	uint8_t content[256];        256byte足够串口使用,如果高波特率,请用上面的*content和malloc/free
} uart_pack;

extern bool marshal(uint8_t * buf, uint8_t len, uart_pack * packObj);
extern bool unmarshal(uint8_t * buf, uint8_t len, uart_pack * packObj);
extern void packageSchedule(uart_pack * packObj);

#endif

root@iZwz99zhkxxl5h6ecbm2xwZ:~/serial-ipc#

protocol.c 代码,实现二进制流序列化、反序列化。

root@iZwz99zhkxxl5h6ecbm2xwZ:~/serial-ipc# cat protocol.c 
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "protocol.h"

bool marshal(uint8_t * buf, uint8_t len, uart_pack * packObj)
{
	/*if (len < sizeof(uart_pack) + packObj->len) {
		printf("package head fail\r\n");
		return false;
	}*/

	buf[0] = packObj->type;
	buf[1] = packObj->waterNum;
	buf[2] = packObj->checksum;
	buf[3] = packObj->len;
	if (packObj->len) {
		memcpy(&buf[4], packObj->content, packObj->len);
	}

	return true;
}

bool unmarshal(uint8_t * buf, uint8_t len, uart_pack * packObj)
{
	/*if (len < sizeof(uart_pack) + packObj->len) {
		return false;
	}*/

	packObj->type = buf[0];
	packObj->waterNum = buf[1];
	packObj->checksum = buf[2];
	packObj->len = buf[3];
	if (packObj->len) {
		memcpy(packObj->content, &buf[4], packObj->len);
	}

	return true;
}

void packageSchedule(uart_pack * packObj)
{
	switch (packObj->type) {
		case TYPE_HEART_BEAT:
			printf("received one heart beat package\r\n");
			break;
	
		case TYPE_SOMKE:
			printf("received one smoke package\r\n");
			break;
		default:
			break;
	}
}

root@iZwz99zhkxxl5h6ecbm2xwZ:~/serial-ipc#

main.c代码:

root@iZwz99zhkxxl5h6ecbm2xwZ:~/serial-ipc# cat main.c 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <signal.h>
#include <sys/select.h>

#include "protocol.h"

#define SERIAL_PORT "/dev/ttyUSB0"  // 替换为您的串口设备
//#define SERIAL_PORT "/dev/ttyCH341USB0"  // 替换为您的串口设备
#define BUFFER_SIZE 256

volatile sig_atomic_t stop = 0;

void sigint_handler(int signum) {
	stop = 1;
}

int main() {
	int serial_fd;
	struct termios serial_options;

	fd_set rfds;
	struct timeval tv;
	int retval;
	char buffer[BUFFER_SIZE];
	int n;


	// 打开串口
	serial_fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY | O_NDELAY);
	if (serial_fd == -1) {
		perror("Failed to open serial port");
		return -1;
	}

	// 配置串口选项
	tcgetattr(serial_fd, &serial_options);		// 读取串口默认配置
	
	cfsetispeed(&serial_options, B115200);		// set io speed
	cfsetospeed(&serial_options, B115200);
	//
	serial_options.c_cflag |= (CLOCAL | CREAD);
	serial_options.c_cflag &= ~CSIZE;
	serial_options.c_cflag |= CS8;
	serial_options.c_cflag &= ~PARENB;
	serial_options.c_cflag &= ~CSTOPB;
	// 设置最少字符和等待时间
	serial_options.c_cc[VMIN] = 1;     // 读数据的最小字节数
	serial_options.c_cc[VTIME]  = 0;   //等待第1个数据,单位是10s
	serial_options.c_cflag &= ~CRTSCTS;
	serial_options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
	serial_options.c_iflag &= ~(IXON | IXOFF | IXANY);
	serial_options.c_oflag &= ~OPOST;
	tcsetattr(serial_fd, TCSANOW, &serial_options);

	signal(SIGINT, sigint_handler);

	while (!stop) {
		FD_ZERO(&rfds);
		FD_SET(serial_fd, &rfds);

		tv.tv_sec = 5;
		tv.tv_usec = 0;

		// 使用 select 等待串口可读
		retval = select(serial_fd + 1, &rfds, NULL, NULL, &tv);
		if (retval == -1) {
			perror("Select error");
			break;
		}
		else if (retval) {	
			printf("Data is available now.\n");

			/* FD_ISSET(0, &rfds) will be true. */
			if (FD_ISSET(serial_fd, &rfds)) {
				n = read(serial_fd, buffer, BUFFER_SIZE);
				if (n > 0) {
					buffer[n] = '\0';
					printf("Received: %s\n", buffer);

					write(serial_fd, "ack:", 3);
					write(serial_fd, buffer, n);

					uart_pack packObj;
					bool ret = unmarshal(buffer, n, &packObj);
					if (ret) {
						packageSchedule(&packObj);
					}

				}
			}
		}
		else {
			printf("No data within five seconds.\n");		
		}
	}

	// 关闭串口
	close(serial_fd);

	return 0;
}
root@iZwz99zhkxxl5h6ecbm2xwZ:~/serial-ipc#

Makefile

root@iZwz99zhkxxl5h6ecbm2xwZ:~/serial-ipc# cat Makefile 

.PHONY:all

all:
	gcc -g main.c protocol.c -o uart-ipc

clean:
	rm -rf uart-ipc
root@iZwz99zhkxxl5h6ecbm2xwZ:~/serial-ipc# 

http://www.kler.cn/a/417344.html

相关文章:

  • 鸿蒙UI(ArkUI-方舟UI框架)- 使用文本
  • vite共享配置之---css相关
  • 本地化部署deepseek r1,包含web部署
  • PostgreSQL函数自动Commit/Rollback所带来的问题
  • 人工智能赋能企业系统架构设计:以ERP与CRM系统为例
  • R语言 | 使用 ComplexHeatmap 绘制热图,分区并给对角线分区加黑边框
  • Python中的实用工具JSON解析
  • SpringMVC工作原理【流程图+文字详解SpringMVC工作原理】
  • 前海湾地铁的腾通数码大厦背后的临时免费停车点探寻
  • Ps:存储 Adobe PDF - 一般
  • 区分 Hive on Spark 和 Spark on Hive
  • 大数据 MapReduce基础实战
  • 基于Java Springboot Vue3图书管理系统
  • 港科夜闻 |香港科大推出 InvestLM生成式人工智能平台,支持金融中小企应用AI技术潜力...
  • 【docker】docker常用命令汇总
  • SpringCloud 详解
  • 数据分析的尽头是web APP?
  • 使用C#开发VTK笔记(二)Part1-VTK系统结构解析
  • 使用Github Action将Docker镜像转存到阿里云私有仓库,供国内服务器使用,免费易用
  • TouchGFX源码分析1---(Event类 和Click Event类)
  • C++多态的实现原理
  • 最短距离和路径问题 ford
  • 数据结构-图-领接表存储
  • HDLCPPP原理与配置
  • 关于最近win11不能使用ie,而不能使用考试客户端的解决方法
  • 人工智能 实验2 jupyter notebook平台 打印出分类器的正确率