Jetson Nano 入坑之路 ---- (10)C/C++语言读写UART或USB串口数据

news/2024/7/8 11:52:28

在上一篇“Jetson Nano 入坑之路 ”系列博客中笔者讲到笔者封装的一个串口函数,本篇博客,笔者会简单易懂的讲解C语言读写串口的方法。

优化:其他博主的博客在这个串口部分大部分都有个问题,就是十六进制读取的时候,会发现,0x7F以上后,第八位被吃了。你发0xFF,但是程序始终读出来的是0x7F。因为最高位“被吃”了。本博客的程序是博主亲测实用程序。


环境准备

Jetson自带了ch34x的驱动,所以可以直接使用CH34x系列的USB-TTL作为USB串口与Jetson进行交互。在SYN6288语言播报模块笔者也用USB-TTL尝试过。

为了测试,可以安装个mincom,然后把TX和RX短接一下。


代码实现

就不吊胃口了,直接上正菜。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <iconv.h>
#include <string>
#include <iostream>

// 函数声明部分
int open_port(int com_port);
int set_uart_config(int fd, int baud_rate, int data_bits, char parity, int stop_bits);

// 使用实例
int main()
{
    // begin::第一步,串口初始化
    int UART_fd = open_port(0);
	if (set_uart_config(UART_fd, 115200, 8, 'N', 1) < 0)
	{
		perror("set_com_config");
		exit(1);
	}
    // end::串口初始化
    
    // begin::第二步,读下位机上发的一行数据
    char str[128];
	char buff[1];
	int len = 0;
	while (1)
	{
		if (read(UART_fd, buff, 1)) {
			if (buff[0] == '\n') {
				break;
			}else {
				str[len++] = buff[0];
			}
		}
	}
    printf("content:%s\n",str);
    // end::读下位机上发的一行数据

    // begin::第三步,向下位机发送数据
    write(UART_fd, str, len);
    // end::向下位机发送数据

    return 0;
}

/*
* 打开串口
*/
int open_port(int com_port)
{
    int fd;
    /* 使用普通串口 */
    // TODO::在此处添加串口列表
    char* dev[] = { "/dev/ttyTHS1", "/dev/ttyUSB0" };

    //O_NDELAY 同 O_NONBLOCK。
    fd = open(dev[com_port], O_RDWR | O_NOCTTY);
    if (fd < 0)
    {
        perror("open serial port");
        return(-1);
    }

    //恢复串口为阻塞状态 
    //非阻塞:fcntl(fd,F_SETFL,FNDELAY)  
    //阻塞:fcntl(fd,F_SETFL,0) 
    if (fcntl(fd, F_SETFL, 0) < 0)
    {
        perror("fcntl F_SETFL\n");
    }
    /*测试是否为终端设备*/
    if (isatty(STDIN_FILENO) == 0)
    {
        perror("standard input is not a terminal device");
    }

    return fd;
}

/*
* 串口设置
*/
int set_uart_config(int fd, int baud_rate, int data_bits, char parity, int stop_bits)
{
    struct termios opt;
    int speed;
    if (tcgetattr(fd, &opt) != 0)
    {
        perror("tcgetattr");
        return -1;
    }

    /*设置波特率*/
    switch (baud_rate)
    {
    case 2400:  speed = B2400;  break;
    case 4800:  speed = B4800;  break;
    case 9600:  speed = B9600;  break;
    case 19200: speed = B19200; break;
    case 38400: speed = B38400; break;
    default:    speed = B115200; break;
    }
    cfsetispeed(&opt, speed);
    cfsetospeed(&opt, speed);
    tcsetattr(fd, TCSANOW, &opt);

    opt.c_cflag &= ~CSIZE;

    /*设置数据位*/
    switch (data_bits)
    {
    case 7: {opt.c_cflag |= CS7; }break;//7个数据位  
    default: {opt.c_cflag |= CS8; }break;//8个数据位 
    }

    /*设置奇偶校验位*/
    switch (parity) //N
    {
    case 'n':case 'N':
    {
        opt.c_cflag &= ~PARENB;//校验位使能     
        opt.c_iflag &= ~INPCK; //奇偶校验使能  
    }break;
    case 'o':case 'O':
    {
        opt.c_cflag |= (PARODD | PARENB);//PARODD使用奇校验而不使用偶校验 
        opt.c_iflag |= INPCK;
    }break;
    case 'e':case 'E':
    {
        opt.c_cflag |= PARENB;
        opt.c_cflag &= ~PARODD;
        opt.c_iflag |= INPCK;
    }break;
    case 's':case 'S': /*as no parity*/
    {
        opt.c_cflag &= ~PARENB;
        opt.c_cflag &= ~CSTOPB;
    }break;
    default:
    {
        opt.c_cflag &= ~PARENB;//校验位使能     
        opt.c_iflag &= ~INPCK; //奇偶校验使能          	
    }break;
    }

    /*设置停止位*/
    switch (stop_bits)
    {
    case 1: {opt.c_cflag &= ~CSTOPB; } break;
    case 2: {opt.c_cflag |= CSTOPB; }   break;
    default: {opt.c_cflag &= ~CSTOPB; } break;
    }

    /*处理未接收字符*/
    tcflush(fd, TCIFLUSH);

    /*设置等待时间和最小接收字符*/
    opt.c_cc[VTIME] = 1000;
    opt.c_cc[VMIN] = 0;

    /*关闭串口回显*/
    opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | NOFLSH);

    /*禁止将输入中的回车翻译为新行 (除非设置了 IGNCR)*/
    opt.c_iflag &= ~ICRNL;
    /*禁止将所有接收的字符裁减为7比特*/
    opt.c_iflag &= ~ISTRIP;

    /*激活新配置*/
    if ((tcsetattr(fd, TCSANOW, &opt)) != 0)
    {
        perror("tcsetattr");
        return -1;
    }

    return 0;
}

简单讲解

将头文件和两个串口初始化和配置函数封装到uart.c或cpp里后,就可以随意使用write和read方法进行串口读写了。

第一步的串口配置方法很重要哦!!!建议也将其封装到uart库里。方便日后使用。


建议结合minicom教程和SYN6288语音合成模块一起看看,这样会有更牢固的理解。

结语:喜欢本系列博客的朋友一键三连支持一下吧。想要了解相关的内容可以评论文章或私信,笔者有空就写。


http://www.niftyadmin.cn/n/2278873.html

相关文章

Liunx下用C语言实现MQTT的接收与发送(下)

继续上一篇关于MQTT的话题&#xff0c;本篇呢&#xff0c;我们将脱离paho的库环境&#xff0c;进行MQTT搭建。 环境准备 在paho.mqtt库中&#xff0c;简单翻阅一下&#xff0c;可以看到他的核心库的路径为 paho.mqtt.c-1.3.0\src 经过笔者的手撕源码&#xff0c;排除掉了许多…

Liunx下用C++实现MQTT的接收与发送

继续上次《Liunx下用C语言实现MQTT的接收与发送&#xff08;上&#xff09;&#xff08;下&#xff09;》&#xff0c;本人探寻了如何基于paho.mqtt.c库开发C版本的MQTT。事实上&#xff0c;paho官网有paho.mqtt.c库&#xff0c;不过笔者进去后玩不怎么转&#xff0c;可能是笔者…

Linux下使用C/C++获取可用串口

最近在Jetson Nano上发现个问题&#xff0c;插多个USB-TTL后&#xff0c;串口号会发生变化。看来笔者之前 C/C语言读写UART或USB串口数据https://blog.csdn.net/qq_25662827/article/details/122581819的同学应该都知道&#xff0c;笔者在代码中是将端口号写死了的。 面对多变…

面向APP抓包方法

最近呢&#xff0c;准备做个自动打卡的东西&#xff0c;不过呢&#xff0c;那APP是通过H5实现的。于是乎。就打算采用个蠢办法。 就是抓包&#xff0c;通过解析抓包&#xff0c;将包发过去就可以实现打卡了撒。 准备工作 本教程采用Fiddler软件进行抓包。在各大应用商城APP里应…

winliunx下C语言获取时间戳方法(秒级,毫秒级)

最近呢&#xff0c;项目需要获取时间戳&#xff0c;作为数据包中的内容。因此才有了此篇。 我们通常所用的时间戳呢&#xff0c;是Unix时间戳。Unix时间戳是从1970年1月1日&#xff08;UTC/GMT的午夜&#xff09;开始所经过的秒数&#xff0c;不考虑闰秒。 有个实用的时间戳获…

面向C/C++的json解析和合成的第三方库cJson

我们在项目中经常要用到json格式数据进行通讯&#xff0c;特别是还要做ARM开发板上实现&#xff0c;处理JSON&#xff0c;自己手撕数据处理是件麻烦的事情&#xff0c;不过现在我们有第三方库了&#xff01;那就是cJson&#xff01; 环境准备 cJson官方库&#xff1a; https:/…

C++ STL list详解

写这个博客专栏就是为了以后备用。 简介 list是一种序列式容器。list容器完成的功能实际上和数据结构中的双向链表是极其相似的&#xff0c;list中的数据元素是通过链表指针串连成逻辑意义上的线性表&#xff0c;list不仅是一个双向链表&#xff0c;而其还是一个环状双向链表。…

C++ STL vector详解

写这个博客专栏就是为了以后备用。 简介 vector(向量):是一种顺序容器&#xff0c;事实上和数组差不多&#xff0c;但它比数组更优越。一般来说数组不能动态拓展&#xff0c;因此在程序运行的时候不是浪费内存&#xff0c;就是造成越界。而vector正好弥补了这个缺陷&#xff0c…