Pixhawk固件PX4之串口通讯

本文详细介绍如何通过Pixhawk的串口(SERIAL4:/dev/ttyS6)连接外部设备,实现数据读取与发送功能。重点介绍了串口初始化、波特率设置、数据读写的具体代码实现,并分享了实验结果及遇到的问题,如TELEM2(USART3)串口发送数据冲突解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、目的

为进一步扩展pixhawk的接口及功能,通过pixhawk现有接口(串口、I2C等)连接外部设备来实现,本节内容主要介绍串口通讯方式。

2、测试平台

硬件:pixhawk 2.4.8

px4固件版本:1.9.0

pixhawk串口采用SERIAL4: /dev/ttyS6,连接图如下:

3、功能实现

主要实现pixhawk从STM32串口端读取数据,并将数据发给PC,通过串口调试助手查看。

pixhawk端代码参考网上pixhawk串口通讯代码,具体如下:

/**
 * @file px4_uorb_subs.c

 */

#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#include <drivers/drv_hrt.h>
#include <systemlib/err.h>
#include <fcntl.h>
#include <systemlib/mavlink_log.h>

#include <px4_defines.h>
#include <px4_config.h>
#include <px4_posix.h>
#include <px4_shutdown.h>
#include <px4_tasks.h>
#include <px4_time.h>

static bool thread_should_exit = false;		/**< px4_uorb_subs exit flag */
static bool thread_running = false;		/**< px4_uorb_subs status flag */
static int rw_uart_task;				/**< Handle of px4_uorb_subs task / thread */

static int uart_init(char * uart_name);
static int set_uart_baudrate(const int fd, unsigned int baud);


/**
 * daemon management function.
 */
__EXPORT int rw_uart_main(int argc, char *argv[]);

/**
 * Mainloop of daemon.
 */
int rw_uart_thread_main(int argc, char *argv[]);

/**
 * Print the correct usage.
 */
static void usage(const char *reason);

static void
usage(const char *reason)
{
	if (reason) {
		warnx("%s\n", reason);
	}

	warnx("usage: px4_uorb_adver {start|stop|status} [-p <additional params>]\n\n");
}

int set_uart_baudrate(const int fd, unsigned int baud)
{
	int speed;

	switch (baud) {
	case 9600:   speed = B9600;   break;
	case 19200:  speed = B19200;  break;
	case 38400:  speed = B38400;  break;
	case 57600:  speed = B57600;  break;
	case 115200: speed = B115200; break;
	default:
		warnx("ERR: baudrate: %d\n", baud);
		return -EINVAL;
	}

	struct termios uart_config;

	int termios_state;

	/* 以新的配置填充结构体 */
	/* 设置某个选项,那么就使用"|="运算,
	 * 如果关闭某个选项就使用"&="和"~"运算
	 * */
	tcgetattr(fd, &uart_config); // 获取终端参数

	/* clear ONLCR flag (which appends a CR for every LF) */
	uart_config.c_oflag &= ~ONLCR;// 将NL转换成CR(回车)-NL后输出。

	/* 无偶校验,一个停止位 */
	uart_config.c_cflag &= ~(CSTOPB | PARENB);// CSTOPB 使用两个停止位,PARENB 表示偶校验

	 /* 设置波特率 */
	if ((termios_state = cfsetispeed(&uart_config, speed)) < 0) {
		warnx("ERR: %d (cfsetispeed)\n", termios_state);
		return false;
	}

	if ((termios_state = cfsetospeed(&uart_config, speed)) < 0) {
		warnx("ERR: %d (cfsetospeed)\n", termios_state);
		return false;
	}
	// 设置与终端相关的参数,TCSANOW 立即改变参数
	if ((termios_state = tcsetattr(fd, TCSANOW, &uart_config)) < 0) {
		warnx("ERR: %d (tcsetattr)\n", termios_state);
		return false;
	}

	return true;
}


int uart_init(char * uart_name)
{
	int serial_fd = open(uart_name, O_RDWR | O_NOCTTY);
	/*Linux中,万物皆文件,打开串口设备和打开普通文件一样,使用的是open()系统调用*/
	// 选项 O_NOCTTY 表示不能把本串口当成控制终端,否则用户的键盘输入信息将影响程序的执行
	if (serial_fd < 0) {
		err(1, "failed to open port: %s", uart_name);
		printf("failed to open port: %s\n", uart_name);
		return false;
	}
	printf("Open the %s\n",serial_fd);
	return serial_fd;
}


/**
消息发布进程,会不断的接收自定义消息
 */
int rw_uart_main(int argc, char *argv[])
{
	if (argc < 2) {
		usage("missing command");
		return 1;
	}

	if (!strcmp(argv[1], "start")) {

		if (thread_running) {
			warnx("px4_uorb_subs already running\n");
			/* this is not an error */
			return 0;
		}

		thread_should_exit = false;//定义一个守护进程
		rw_uart_task = px4_task_spawn_cmd("rw_uart",
			SCHED_DEFAULT,
			SCHED_PRIORITY_DEFAULT,//调度优先级
			2000,//堆栈分配大小
			rw_uart_thread_main,
			(argv) ? (char *const *)&argv[2] : (char *const *)NULL);
		return 0;
	}

	if (!strcmp(argv[1], "stop")) {
		thread_should_exit = true;
		return 0;
	}

	if (!strcmp(argv[1], "status")) {
		if (thread_running) {
			warnx("\trunning\n");

		}
		else {
			warnx("\tnot started\n");
		}

		return 0;
	}

	usage("unrecognized command");
	return 1;
}

int rw_uart_thread_main(int argc, char *argv[])
{
	char data = '0';
	//char out_buf[5]="endl";
	char buffer[5] = "";	
	/*
	 * TELEM1 : /dev/ttyS1
	 * TELEM2 : /dev/ttyS2
	 * GPS    : /dev/ttyS3
	 * NSH    : /dev/ttyS5
	 * SERIAL4: /dev/ttyS6
	 * N/A    : /dev/ttyS4
	 * IO DEBUG (RX only):/dev/ttyS0
	 */
	int uart_read = uart_init("/dev/ttyS6");
	if (false == uart_read)
		return -1;
	if (false == set_uart_baudrate(uart_read, 9600)) {
		printf("[JXF]set_uart_baudrate is failed\n");
		return -1;
	}
	printf("[JXF]uart init is successful\n");
	//warnx("[rw_uart] starting\n");
	
	thread_running = true;
	//write(uart_read, &out_buf[0],4);

	while (!thread_should_exit) {
		read(uart_read, &data, 1);
		if (data == 'R') {
			for (int i = 0; i < 4; ++i) {
				read(uart_read, &data, 1);
				buffer[i] = data;
				write(uart_read, &buffer[i], sizeof(buffer[i]));
				data = '0';
				usleep(5000);//5ms pause
			}
			printf("%s\n", buffer);
		}
		
		printf("rw_uart TX-test:running!\n");
		
		usleep(1000000);
	}
	//write(uart_read, &out_buf[0],4);
	warnx("[rw_uart] exiting.\n");
	thread_running = false;
	int fd=close(uart_read);
	printf("close stauts: %d\n",fd);
	return 0;
}

4、实验结果

QGC端的Mavlink Console端输出结果:

PC串口调试助手:

结果与期望的功能一致。

5、问题

网上搜索的pixhawk串口通讯基本上实现的是读取功能,为实现发送功能,采用write函数。

参考网上例程,串口采用TELEM2(USART3),发送数据时,存在只能在了进程启动后发送一段时间数据的问题。

后来更改为SERIAL4: /dev/ttyS6,程序正常,故初步判断TELEM2(USART3)串口发送数据可能其他进程也在调用,导致存在冲突。

 

最后附上参考的博客链接

 

Pixhawk---通过串口方式添加一个自定义传感器(超声波为例)

Pixhawk原生固件PX4之串口读取信息

 

### PX4 串口驱动开发与配置 #### 背景概述 PX4 是一款开源的自动驾驶仪软件框架,广泛应用于无人机和其他自主机器人系统。其串口驱动主要用于连接外部设备,例如 GPS、数传模块以及其他传感器。这些设备通过串口通信协议实现数据交换。 --- #### 任务调度机制 PX4串口驱动在任务调度上有两种主要情况[^1]: - **轮询模式**:适用于低延迟需求的任务,在固定的时间间隔内主动查询串口状态并处理接收到的数据。 - **中断模式**:当有新数据到达时触发中断信号,随后执行相应的回调函数来处理数据。 这两种方式的选择取决于具体应用场景的需求以及硬件性能支持的情况。 --- #### 常见串口号对应关系 对于 Pixhawk2.4.8 飞控板而言,它通常配备六个可用的串口,它们在 PX4 中分别被赋予特定的设备编号[^2]。以下是常见的映射表: | 序号 | 设备名称 | 对应功能 | |------|----------------|------------------| | 1 | `/dev/ttyS0` | 主要用于调试 | | 2 | `/dev/ttyS1` | 默认分配给 GPS | | 3 | `/dev/ttyS2` | 可作为备用通道 | | 4 | `/dev/ttyS3` | 数传电台常用端口 | | 5 | `/dev/ttyS4` | 用户自定义用途 | | 6 | `/dev/ttyS5` | 测距传感器接入 | 实际应用中可以根据项目需求重新调整各串口的功能绑定。 --- #### 编写与配置流程 以测距传感器 TeraRanger 的串口驱动为例说明整个过程[^3]: ##### 文件结构分析 假设目标是为新型测距传感器创建专属驱动,则需关注以下几个核心文件及其作用: - `AP_RangeFinder_TeraRanger_Serial.h`: 定义类声明部分,包含成员变量和方法原型; - `AP_RangeFinder_TeraRanger_Serial.cpp`: 实现具体的逻辑操作,比如初始化设置波特率、校验帧格式等; ##### 关键代码片段展示 下面给出一段简化版的 C++ 初始化模板供参考: ```cpp #include <drivers/drv_hrt.h> #include <px4_platform_common/defines.h> class AP_RangeFinder_TeraRanger { public: explicit AP_RangeFinder_TeraRanger(const char *port_name, uint32_t baud_rate); private: int _fd; // File descriptor for serial communication bool init(); }; // Constructor implementation AP_RangeFinder_TeraRanger::AP_RangeFinder_TeraRanger(const char *port_name, uint32_t baud_rate) : _fd(-1){ struct termios options; _fd = open(port_name, O_RDWR | O_NOCTTY | O_NDELAY); if (_fd != -1){ tcgetattr(_fd, &options); cfsetispeed(&options, baud_rate); cfsetospeed(&options, baud_rate); options.c_cflag |= (CLOCAL | CREAD); options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; tcsetattr(_fd, TCSANOW, &options); } } bool AP_RangeFinder_TeraRanger::init(){ return _fd != -1 ? true : false; } ``` 上述代码展示了如何打开指定串口,并对其进行基本参数设定的过程。 --- #### 注意事项 - 在多线程环境下访问共享资源时务必加入同步锁保护措施以防竞态条件发生。 - 不同型号飞控可能具备差异化的硬件特性,请仔细查阅官方文档确认兼容性信息。 - 若遇到异常状况无法正常工作,建议逐步排查物理连线牢固程度、固件版本匹配度等因素。 ---
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值