orangepi_串口

1. 串口的基本认知

串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方 式的扩展接口。串行接口(Serial Interface)是指数据一位一位地顺序传送。其特点是通信线路简 单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成 本,特别适用于远距离通信,但传送速度较慢

面试的时候可能会问,有没有玩过spi,可以回答玩过串口和I2c

  • 是设备间接线通信的一种方式
  • 数据一位一位地顺序传送
  • 双向通信,全双工

    • 半双工:只能单向通讯
    • 全双工:可以双向通讯
  • 传送速度相对较慢
  • 串口通信(Serial Communication)是一种在计算机和外部设备或计算机之间进行的串行数据传输方式。它采用逐位(bit)顺序传输数据,相对于并行通信而言,串口通信使用的数据线较少,在远距离通信中可以节约通信成本。串口通信广泛应用于各种设备之间的数据传输,如计算机与外设(如打印机、鼠标、键盘等)、微控制器之间的通信等。
  • 串口通信的基本参数包括波特率(数据传输速率)、数据位(每个数据包的位数)、停止位(用于表示单个数据包的结束)、奇偶校验位(用于错误检测)等。这些参数需要在通信双方之间预先约定,以确保数据的正确传输和解析。
  • 串口通信的主要优点包括使用简单的线路结构、成本较低、适用于远距离通信等。然而,它也有一些缺点,如传输速度相对较慢、传输距离受限等。因此,在选择通信方式时,需要根据具体的应用场景和需求进行权衡。

1.1. 关于电器标准和协议:

串行接口按电气标准及协议来分包括RS-232-C、RS-422、RS485等。RS-232-C、RS-422与RS-485标准只对接口的电气特性做出规定,不涉及接插件、电缆或协议。

RS-232

也称标准串口,最常用的一种[串行通讯接口,比如我们的电脑主机的9针串口 ,最高速率为20kb/s,RS-232是为点对点(即只用一对收、发设备)通讯而设计的,其传送距离最大为约15米。所以RS-232适合本地设备之间的通信

RS-422

由于接收器采用高输入阻抗和发送驱动器比RS232更强的驱动能力,故允许在相同传输线上连接多个接收节点,最多可接10个节点。即一个主设备(Master),其余为从设备(Slave),从设备之间不能通信,所以RS-422支持点对多的双向通信。

RS-422的最大传输距离为1219米,最大传输速率为10Mb/s。平衡双绞线的长度与传输速率成反比

RS-485

是从RS-422基础上发展而来的,无论四线还是二线连接方式总线上可多接到32个设备

1.2. 关于串口的电平:

经常听到的UART:

异步串行是指UART(Universal Asynchronous Receiver/Transmitter),通用异步接收/发送。UART包含TTL电平的串口和RS232电平的串口

RS232电平:

逻辑1为-3~-15V的电压,逻辑0为 3~15V的电压

笔记本通过RS232电平和单片机通信

TTL电平:

TTL是(Transistor-Transistor Logic),即晶体管-晶体管逻辑的简称,它是计算机处理器控制的设备 内部各部分之间通信的标准技术。TTL电平信号应用广泛,是因为其数据表示采用二进制规定, +5V等价于逻辑”1”,0V等价于逻辑”0”。

数字电路中,由TTL电子元器件组成电路的电平是个电压范围,规定: 输出高电平>=2.4V,输出低电平<=0.4V; 输入高电平>=2.0V,输入低电平<=0.8V

笔记本电脑通过TTL电平与单片机进行通信,需要将电脑的USB转成TTL电平,需要用到CH340工具将USB转换成TTL电平实现电脑与MCU之间的通信

1.3. 串口通信引脚接线:

RXD:数据接收引脚,

TXD:数据发送引脚

VCC:电源正极

GND:电源负极

1.4. 串口的通信协议:

串口通信协议中的波特率、奇偶检验位和停止位等参数是非常重要的,它们决定了数据的传输方式和数据的完整性检查。以下是一些常见的串口通信参数:

波特率(Baud Rate):

波特率是串口通信中的传输速率,它表示每秒传输的比特数。常见的波特率包括 9600、115200、57600 等。

通信的双方必须使用相同的波特率。波特率过高可能会导致数据传输错误,因此需要根据硬件和通信距离来选择适当的波特率。

数据位(Data Bits):

数据位指定每个数据字节中的位数,通常为 5、6、7 或 8 位。大多数情况下,使用 8 位数据位以支持 8 位的二进制数据。

奇偶检验位(Parity):

奇偶检验位用于检测数据传输中的错误。通常有以下几种选项: 无校验位:不使用奇偶检验位。 奇校验位:确保数据位中有奇数个 “1”。 偶校验位:确保数据位中有偶数个 “1”。 奇偶检验位通常用于检测单比特错误。

停止位(Stop Bits):

停止位表示数据字节的结束。通常有 1 位和 2 位停止位选项,其中 1 位停止位是最常见的选择。 这些参数通常一起组合,以确定数据的帧格式。例如,常见的串口通信设置是:

波特率:9600 数据位:8 无奇偶检验位 1 位停止位 这意味着每个数据帧由 10 位组成:1 位起始位、8 位数据位、无奇偶检验位和 1 位停止位。

正确配置这些参数对于串口通信非常重要,因为通信的双方必须使用相同的参数,否则会导致数据传输错误。通常,串口设备的规格表明了所需的通信参数设置。在编程中,你需要使用相应的库函数来设置这些参数,以确保正确的数据传输和校验。

2. Orangepi Zero 2串口通信开发

2.1. Orangepi Zero 2的两组串口:

Orangepi Zero2开发板有两组串口第一种串口是开发板刚开始登录的时候用到的,还有一组可以通过输入:gpio readall来查询,都是用的TTL针脚型接口,串口接线:TXD和RXD交叉接线

2.2. 串口调试工具:

使用安信可串口调试助手,亲测好用,可以调节波特率,可以设置快捷键等。

2.3. 查看串口驱动文件:
/* 在配套的wiringPi库里面有官方给的对应的原码给它复制过来	*/
cp .. /home/orangepi/wiringPiFromwindows/wiringOP-next/examples/serialTest.c .
 
/* LINUX里面一切皆文件每个硬件设备对应一个文件,查询串口文件 */   
ls /dev/ttyS*
 
/*dev串口S0用来调试信息,当然S0串口禁用掉默认串口的系统消息,也可以当成通信串口来使用,网上有各种命令和配置方式,供大家各自去研究*/
/*dev串口S1这个板子是不支持的,dev串口S5用来信息交互*/ 
//  如何看orangepi支持S几,可使用gpio readall命令查看 TXD,RXD支持.几
/dev/ttyS0  /dev/ttyS1  /dev/ttyS2  /dev/ttyS3  /dev/ttyS4  /dev/ttyS5

2.4. 修改配置文件:
如果使用的为Linux5.16内核的系统,uart5默认是关闭的,需要手动打开才能使用。
    
在/boot/orangepiEnv.txt中加入uart5配置,保存退出然后输入指令reboot,重启 Linux系统就可以打开uart5。
orangepi@orangepizero2:~/waishe$ sudo vim /boot/orangepiEnv.txt  

2.5. 基于wiringPi库的串口开发:
#include <stdio.h>         // 包含标准输入输出库头文件
#include <string.h>        // 包含字符串处理库头文件
#include <errno.h>         // 包含错误号头文件
#include <wiringPi.h>      // 包含wiringPi库头文件,用于GPIO控制
#include <wiringSerial.h>  // 包含wiringSerial库头文件,用于串口通信
#include <stdlib.h>        // 包含标准库头文件
#include <pthread.h>       // 包含线程库头文件
#include <unistd.h>        // 包含unistd库头文件,用于延时
 
int fd;     
 
/* 发送线程函数 */
void* Sendhandler()
{
    char *sendbuf = NULL;
    
    sendbuf = (char *)malloc(32 * sizeof(char));                           // 开辟内存空间
    while(1){
        memset(sendbuf, '\0', strlen(sendbuf));                           // 清空缓冲区
        scanf("%s",sendbuf);                                              // 输入数据 
        while(*sendbuf != '\0'){                                          // 循环发送数据   
            serialPutchar(fd, *sendbuf++);                                // 发送数据
        }   
    }
}
 
/* 接收线程函数 */
void* Recvhandler()
{
    while(1){
        while(serialDataAvail(fd)){                                  // 循环接收数据
            printf("%c",serialGetchar(fd));                          // 接收数据并打印
            fflush(stdout);                                          // 清空缓冲区  
        }
    }
}
 
int main()
{
    pthread_t idSend;                                       // 定义线程发送id
    pthread_t idRecv;                                       // 定义线程接收id
 
    fd = serialOpen("/dev/ttyS5", 115200);          // 打开串口设备 "/dev/ttyS5",设置波特率为 115200
    if(fd < 0){
        printf("Error opening serial port\n");              // 输出错误信息
        return -1;
    }
 
    pthread_create(&idSend, NULL, Sendhandler, NULL);        // 创建发送线程
    pthread_create(&idRecv, NULL, Recvhandler, NULL);        // 创建接收线程
 
    if (wiringPiSetup () == -1)                             // 初始化wiringPi
    {
        fprintf (stdout, "Unable to start wiringPi: %s\n", strerror (errno)) ;  // 输出错误信息
        return 1 ;
    }
 
    while(1){                                               // 循环发送数据       
        sleep(10);                                          // 等待10秒
    }
    putchar('\n');                                          // 输出换行符
    return 0;
}

这段代码的主要功能是通过串口进行双向通信。它使用两个线程分别处理发送和接收数据。发送线程从标准输入读取数据并通过串口发送,接收线程从串口读取数据并打印到标准输出。主函数负责初始化串口和wiringPi库,并创建发送和接收线程。

  • fd = serialOpen("/dev/ttyS5", 115200);
fd 是一个文件描述符,表示成功打开的串口设备。
    这个文件描述符在后续的串口通信中用来标识和访问该串口。

serialOpen("/dev/ttyS5", 115200) 是调用 wiringSerial 库中的 serialOpen 函数,其参数包括:

"/dev/ttyS5":指定要打开的串口设备。在 Linux 系统中,/dev/ttyS* 通常指代串口设备,
    ttyS5 表示第六个串口(从 0 开始计数)。
115200:设置串口的波特率(数据传输速率),单位为比特每秒(bps)。
    115200 是一个常用的波特率,适用于许多串口通信应用。
成功打开串口后,程序可以使用返回的文件描述符 fd 进行串口数据的发送和接收。
    如果打开失败(例如设备不存在或权限问题),fd 会返回一个负值,程序会输出错误信息并终止。
  • serialPutchar(fd, *sendbuf++);
serialPutchar(fd, *sendbuf++) 
    使用 wiringSerial 库中的 serialPutchar 函数,将字符发送到串口,其中:
    fd 是之前打开串口时返回的文件描述符,表示要通过哪个串口进行通信。
    *sendbuf 是当前指向的字符(即发送缓冲区中的第一个字符)。
    sendbuf++ 在发送字符后,会将指针 sendbuf 向前移动一个位置,以指向下一个字符。
    这个循环发送的过程会持续到字符串的末尾(即遇到字符 \0),实现逐字符地将消息发送到串口。
  • serialDataAvail(fd)
serialDataAvail(fd) 是 wiringSerial 库中的一个函数,用于检查指定串口中是否有可用的数据。
具体来说:

fd 是打开串口时返回的文件描述符,指明要检查哪个串口。

该函数返回一个整数值,表示串口缓冲区中可读取的字节数。
如果返回值大于零,则表示有数据可以读取;如果返回值为零,则表示没有可用数据。

通过调用这个函数,接收线程可以在进入接收循环前检查是否有数据可读,从而避免空读操作,提高效率。
  • printf("%c",serialGetchar(fd)); // 接收数据并打印
这行代码的作用是从串口接收一个字符并将其打印到标准输出。具体来说:

serialGetchar(fd) 是 wiringSerial 库中的一个函数,用于从指定的串口读取一个字符。

fd 是之前打开串口时返回的文件描述符,指示要从哪个串口读取数据。

printf("%c", ...) 将读取到的字符格式化为字符类型并输出到标准输出(通常是终端或控制台)。

     因此,这行代码的作用是实时接收串口数据,并将每个接收到的字符直接显示出来,
允许用户看到从串口发送过来的信息。

这段代码的主要功能是通过串口进行双向通信。它使用两个线程分别处理发送和接收数据。发送线程从标准输入读取数据并通过串口发送,接收线程从串口读取数据并打印到标准输出。主函数负责初始化串口和wiringPi库,并创建发送和接收线程。

2.6. 不使用wiringPi库自己实现串口通信:
/*mySerial.h*/

//@Author: <NAME> 打开指定的串口设备,并设置波特率

int mySerialOpen(const char *device, const int baud);
/*
* @param device 串口设备名称,如"/dev/ttyUSB0"
* @param baud 波特率,如9600、115200等
* @return 成功返回文件描述符,失败返回-1
*/

// @Author: 向指定的串口设备发送字符串

void mySerialSendString(const int fd, const char *str);
 /*
* @param fd 串口设备文件描述符
* @param str 要发送的字符串 
* @return 无
*/
// @Author: 从指定的串口设备读取字符串

int mySerialReadString(const int fd, char *buffer);
/*
* @param fd 串口设备文件描述符              
* @param buffer 读取到的字符串存放的缓冲区      
* @return 读取到的字符串的长度,失败返回-1
*/
/*mySerialTool.c*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
 
/*打开指定的串口设备,并设置波特率*/
int SerialOpen(const char *device, const int baud)
{
    struct termios options; // 串口配置参数
    speed_t myBaud;         // 波特率
    int status, fd;         // 状态和文件描述符
 
    //根据传入的波特率参数设置相应的波特率
    switch(baud){
        case 9600:
            myBaud = B9600;
            break;
        case 115200:
            myBaud = B115200;
            break;
        default:
            printf("不支持的波特率!\n");
            return -2;
    }  
 
    //打开串口设备
    if( (fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY)) == -1){
        printf("无法打开串口设备\n");
        return -1;
    }
 
     // 设置文件描述符的标志为读写模式
    fcntl(fd, F_SETFL, O_RDWR);
 
    // 获取当前串口配置
    tcgetattr(fd, &options);
 
    // 设置串口为原始模式,无特殊处理
    cfmakeraw(&options);
    // 设置输入波特率
    cfsetispeed(&options, myBaud);
    // 设置输出波特率
    cfsetospeed(&options, myBaud);
 
    // 清除标志位并设置数据格式
    options.c_cflag |= (CLOCAL | CREAD);
    options.c_cflag &= ~PARENB; // 无奇偶校验位
    options.c_cflag &= ~CSTOPB; // 1个停止位
    options.c_cflag &= ~CSIZE; // 清除数据位
    options.c_cflag |= CS8; // 设置为8位数据位
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 非规范模式,不执行输入处理
    options.c_oflag &= ~OPOST; // 输出处理
 
    // 设置读取时的超时和最小接收字符数
    options.c_cc[VMIN] = 0;
    options.c_cc[VTIME] = 100; // 10秒超时
 
    // 应用串口配置
    tcsetattr(fd, TCSANOW, &options);
 
    // 使用ioctl设置串口的DTR和RTS信号
    ioctl(fd, TIOCMGET, &status);
    status |= TIOCM_DTR;
    status |= TIOCM_RTS;
    ioctl(fd, TIOCMSET, &status);
 
    // 短暂延时
    usleep(10000); // 10毫秒延时
 
    return fd; // 返回文件描述符
}
 
/*向指定的串口设备发送字符串*/
void SerialWriteString(const int fd, const char *str)
{
    int ret;
 
    ret = write(fd, str, strlen(str));            // 发送字符串
    if(ret == -1){
        printf("串口发送失败!\n");
        exit(-1);                                 // 发送失败,退出程序
    }
}
 
/*从指定的串口设备读取字符串*/
int SerialReadString(const int fd, char *buffer)
{
    int n_read;
 
    n_read = read(fd, buffer, 32);                // 读取串口数据
    if(n_read == -1){
        printf("串口读取失败!\n");
        exit(-1);                                 // 读取失败,退出程序
    }
}

uartTool.h


int SerialOpen(const char *device, const int baud);

void SerialWriteString(const int fd, const char *str);

int SerialReadString(const int fd, char *buffer);

uart.c

/*mySerialTest.c*/
#include <stdio.h>         // 包含标准输入输出库头文件
#include <string.h>        // 包含字符串处理库头文件
#include <errno.h>         // 包含错误号头文件
#include <stdlib.h>        // 包含标准库头文件
#include <pthread.h>       // 包含线程库头文件
#include <unistd.h>        // 包含unistd库头文件,用于延时
#include "mySerial.h"      // 包含自定义串口通信头文件
 
int fd;                                                                                      // 串口文件描述符
 
void* readSerial()
{
    char buffer[32];
 
    while(1){
        memset(buffer, '\0', sizeof(buffer));                                                  // 清空缓冲区
        SerialReadString(fd, buffer);                                                        // 读取串口数据
        printf("从串口中读取到的数据是:%s\n",buffer);                                          // 打印读取到的数据
    }
}
 
void* sendSerial()
{
    char buffer[32] = {'\0'};
 
    while(1){
        memset(buffer, '\0', sizeof(buffer));                                                 // 清空缓冲区
        scanf("%s",buffer);                                                               // 输入要发送的数据
        SerialWriteString(fd, buffer);                                                  // 发送数据到串口
    }
}
 
int main(int argc, char **argv)
{
    char deviceName[32] = {'\0'};                                                                  // 设备名
    pthread_t readThread;                                                                         // 读取线程
    pthread_t sendThread;                                                                         // 发送线程   
 
    if(argc < 2){
        printf("Usage: %s /dev/ttyS?\n", argv[0]);
        return -1;
    }
 
    strcpy(deviceName, argv[1]);                                                       // 拷贝设备名
    if( (fd = SerialOpen(deviceName, 115200)) == -1){                                     // 打开串口
        printf("open %s failed\n", deviceName);
    }
 
    pthread_create(&readThread, NULL, readSerial, NULL);                                // 创建读取线程
    pthread_create(&sendThread, NULL, sendSerial, NULL);                                // 创建发送线程
 
    while(1){
        sleep(10);                                                                       // 延时10秒
    }
    return 0;
}

 好了。看到这里,我们orangepi基础入门就结束啦!书写不易。给个点赞,关注吧!!! 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值