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

OrangePi Zero 3学习笔记(Android篇)3 - 串口

目录

1. 找到串口号

2. 修改串口权限

3. 串口类

3.1 serialport.hpp

3.2 serialport.cpp

3.2.1 构造函数

3.2.2 Open函数

3.2.3 Close函数

3.2.4 Write函数

3.2.5 Read函数

3.2.6 SetFlowCtrl函数

4. 测试程序

5. 编译

6. 运行验证


除了默认的UART用于shell,Zero 3的24pin接口还有一路UART5:

将这个脚用线短路起来测试,即红色排针(2个5V接口)那排排针往下数4-5脚短路。

1. 找到串口号

可以在shell中运行命令:

1|apollo-p2:/ $ ls -l /dev/tty*

查看系统本身的串口信息:

crw-rw-rw- 1 root      root           5,   0 1970-01-01 08:00 /dev/tty
crw------- 1 root      root         247,   0 1970-01-01 08:00 /dev/ttyAS0
crw-rw---- 1 bluetooth net_bt_admin 247,   1 1970-01-01 08:00 /dev/ttyAS1
crw------- 1 root      root         247,   5 1970-01-01 08:00 /dev/ttyAS5
crw-rw---- 1 bluetooth net_bt_admin 236,   0 2023-08-10 23:01 /dev/ttyBT0
crw------- 1 root      root         236,   1 1970-01-01 08:00 /dev/ttyBT1
crw------- 1 root      root           4,  64 1970-01-01 08:00 /dev/ttyS0
crw------- 1 root      root           4,  65 1970-01-01 08:00 /dev/ttyS1
crw------- 1 root      root           4,  66 1970-01-01 08:00 /dev/ttyS2
crw------- 1 root      root           4,  67 1970-01-01 08:00 /dev/ttyS3

从原理图上看,AS1对应UART1,标识也是bluetooth

猜测ttyAS5对应UART5,不过尝试读写ttyAS5提示权限不够。

apollo-p2:/ $ whoami
shell
apollo-p2:/ $ echo hello >/dev/ttyAS5
/system/bin/sh: can't create /dev/ttyAS5: Permission denied
1|apollo-p2:/ $ echo hello >/dev/tty
hello

 修改权限也不行

127|apollo-p2:/ $ chmod 666 /dev/ttyAS5
chmod: chmod '/dev/ttyAS5' to 0666: Operation not permitted

2. 修改串口权限

找到文件longan/kernel/linux-5.4/scripts/dtc/include-prefixes/arm64/sunxi/sun50iw9.dtsi

		serial0 = &uart0;serial1 = &uart1;serial2 = &uart2;serial3 = &uart3;serial4 = &uart4;serial5 = &uart5;

可以看到实际配置了6个Uart。说明不是这里配置。

进入Ubuntu文件系统,进入longan/kernel/linux-5.4文件夹,执行

make menuconfig

在Device Drivers找一圈也没有找到配置。

尝试SELinux方式修改权限。

在device/softwinner/apollo/common/sepolicy/public/file_contexts添加:

/dev/ttyAS5                 u:object_r:ttyAS5_device:s0

这样改也不行。

在device/softwinner/apollo/common/system/init.sun50iw9p1.rc中添加修改权限命令:

on post-fs-data# create file for audio dump datamkdir /data/vendor/hardware/audio_d 0777 audio audiomkdir /data/audio_d 0777 media mediachown system system /dev/nsichmod 0660 /dev/nsichmod 0755 /product/bin/HelloWorldchmod 0666 /dev/ttyAS5
apollo-p2:/ $ ls -l /dev/tty*
crw-rw-rw- 1 root      root           5,   0 1970-01-01 08:00 /dev/tty
crw------- 1 root      root         247,   0 1970-01-01 08:00 /dev/ttyAS0
crw-rw---- 1 bluetooth net_bt_admin 247,   1 1970-01-01 08:00 /dev/ttyAS1
crw-rw-rw- 1 root      root         247,   5 1970-01-01 08:00 /dev/ttyAS5
crw-rw---- 1 bluetooth net_bt_admin 236,   0 2023-08-10 23:01 /dev/ttyBT0
crw------- 1 root      root         236,   1 1970-01-01 08:00 /dev/ttyBT1
crw------- 1 root      root           4,  64 1970-01-01 08:00 /dev/ttyS0
crw------- 1 root      root           4,  65 1970-01-01 08:00 /dev/ttyS1
crw------- 1 root      root           4,  66 1970-01-01 08:00 /dev/ttyS2
crw------- 1 root      root           4,  67 1970-01-01 08:00 /dev/ttyS3

可以看到已经改动了。

apollo-p2:/ $ echo hello </dev/ttyAS5
hello

3. 串口类

参考上一节helloworld的方式(device/softwinner/apollo/apollo-p2)添加SerialPort的文件夹,在这个文件夹里面新增2个文件:serialport.cpp和serialport.hpp。

3.1 serialport.hpp

需要包含的头文件:

#include	<stdio.h>      /*标准输入输出定义*/
#include	<stdlib.h>     /*标准函数库定义*/
#include	<unistd.h>     /*Unix 标准函数定义*/
#include	<sys/types.h> 
#include	<sys/stat.h>
#include	<fcntl.h>      /*文件控制定义*/
#include	<errno.h>      /*错误号定义*/
#include	<termios.h>    /*PPSIX 终端控制定义*/
#include	<string.h> 

增加类: 

using namespace std;
class CSerialport
{private:int fd;const int baudrateSetting[14] = { B1000000, B576000, B500000, B460800, B230400, B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300,};const int baudrate[14] = {1000000,  576000,  500000,  460800,  230400,  115200,  57600, 38400,  19200,  9600, 4800,  2400,  1200,  300, };public:CSerialport();bool Open(const char *dev); bool Open(const char *dev, int baud);bool Open(const char *dev, int baud, int databits, int stopbits, char parity);void Close(void);int Write(unsigned char *buf, int len); int Read(unsigned char *buf, int len);  bool SetFlowCtrl(bool enable);
};
  • fd:串口的句柄。
  • baudrateSetting :底层设置波特率的值,这些参数是底层驱动固定的值。
  • baudrate:app层设置波特率的范围,和实际值一致。
  • Open:打开串口设备,重载了3个函数,返回值都是true或false,参数的含义分别是
    • dev:设备名,例如打开"/dev/ttyAS1", Open("/dev/ttyAS1")即可。
    • baud:波特率,有效值看数组baudrate
    • databits:数据位长度,有效值7或8,默认8。
    • stopbits:停止位长度,有效值1或2,默认1。
    • parity:校验方式,有效值'O'(奇校验)、'E'(偶校验)、'N'(无校验),默认'N'。
  • Close:关闭串口设备
  • Write:写数据到串口,buf表示缓冲,写出的数据,len表示数据长度,返回值为实际写出的数据长度,为-1时表示写错误发生。
  • Read:从串口读数据,buf表示缓冲,读入的数据,len表示缓冲长度,返回值为实际读入的数据长度,为0则表示无数据。
  • SetFlowCtrl:设置流控功能是否使能,参数enable为true时打开流控功能。返回true或false。

3.2 serialport.cpp

在cpp中实现类的成员函数。

需要包含的头文件:

#include "serialport.hpp"

添加命名空间:

using namespace std;

3.2.1 构造函数

CSerialport::CSerialport()
{fd = -1;
}

初始化串口句柄为-1。

3.2.2 Open函数

这里重载了3个Open函数。

第一个只是打开,没有设置串口的其他关键参数。

bool CSerialport::Open(const char *dev)
{char* _dev=new char[256];strcpy(_dev, dev);fd = open(_dev, O_RDWR | O_NOCTTY | O_NDELAY);if (-1 == fd)   {           perror("Can't Open Serial Port\n");return false;      }return true;  
}

第二个是设置了波特率。

bool CSerialport::Open(const char *dev, int baud)
{struct termios options;int i;if(Open(dev) == false)return false; if (tcgetattr(fd, &options) !=  0){perror("SetupSerial fail\n");return false;}//设置串口输入波特率和输出波特率  for (i = 0; i < (int)(sizeof(baudrate) / sizeof(int)); i++)  {if (baud == baudrate[i])  {cfsetispeed(&options, baudrateSetting[i]);   cfsetospeed(&options, baudrateSetting[i]); tcflush(fd, TCIFLUSH);if ((tcsetattr(fd, TCSANOW, &options)) != 0){perror("Serialport set error\n");return false;}  return true; }}perror("Open Serial fail: baudrate is invalid\n");return false;
}

第三个函数包括设置串口的常用参数。

bool CSerialport::Open(const char *dev, int baud, int databits, int stopbits, char parity)
{struct termios options;int i;if (Open(dev) == false)return false; if (tcgetattr(fd, &options) != 0){perror("SetupSerial fail\n");return false;}bzero(&options, sizeof(options));for (i = 0; i < (int)(sizeof(baudrate) / sizeof(int)); i++)  {if (baud == baudrate[i])  {cfsetispeed(&options, baudrateSetting[i]);cfsetospeed(&options, baudrateSetting[i]);break;}}options.c_cflag |= CLOCAL | CREAD;options.c_cflag &= ~CSIZE;switch (databits){case 7:options.c_cflag |= CS7;break;case 8:default:options.c_cflag |= CS8;break;}switch (parity){case 'O':                     //奇校验options.c_cflag |= PARENB;options.c_cflag |= PARODD;options.c_iflag |= (INPCK | ISTRIP);//printf("parity is Odd\n");break;case 'E':                     //偶校验options.c_iflag |= (INPCK | ISTRIP);options.c_cflag |= PARENB;options.c_cflag &= ~PARODD;//printf("parity is Even\n");break;case 'N':                    //无校验default:options.c_cflag &= ~PARENB;//printf("parity is None\n");break;}if (stopbits == 2){options.c_cflag |= CSTOPB;}else{options.c_cflag &= ~CSTOPB;}options.c_cc[VTIME] = 0;options.c_cc[VMIN] = 0;tcflush(fd, TCIFLUSH);if ((tcsetattr(fd, TCSANOW, &options)) != 0){perror("Serialport set error\n");return false;}return true;
}

3.2.3 Close函数

void CSerialport::Close(void)
{if (-1 == fd){return;}close(fd);
}

3.2.4 Write函数

int CSerialport::Write(unsigned char *buf, int len)
{ssize_t ret;ret = write(fd, buf, len);if(ret == -1)perror("Serialport send fail\n");return ret;
}

3.2.5 Read函数

int CSerialport::Read(unsigned char *buf, int len)
{#define TimeOut     10  //if no data in 10ms, returnint retval;fd_set rfds;struct timeval tv;int ret, pos;tv.tv_sec = TimeOut / 1000;  //set the rcv wait time  tv.tv_usec = TimeOut % 1000 * 1000;  //100000us = 0.1spos = 0;while (1){FD_ZERO(&rfds);FD_SET(fd, &rfds);retval = select(fd + 1, &rfds, NULL, NULL, &tv);if (retval == -1){perror("Serialport no data\n");break;}else if (retval){ret = read(fd, buf + pos, 1);if (-1 == ret){printf("Serialport read no data\n");break;}pos++;if (len <= pos){break;}}else{//printf("retval:%d\n", retval);break;}}return pos;
}

读函数是在10ms内判断设备是否有数据,如果没数据就返回,如果有就继续读。宏定义TimeOut就是定义时间间隔,需要自己根据实际情况设定,一般情况10ms应该就够了。

3.2.6 SetFlowCtrl函数

bool CSerialport::SetFlowCtrl(bool enable)
{struct termios options;if (tcgetattr(fd, &options) != 0){perror("SetupSerial fail\n");return false;}if(enable == true){options.c_cflag |= CRTSCTS;}else{options.c_cflag &= ~CRTSCTS;}tcflush(fd, TCIFLUSH);if ((tcsetattr(fd, TCSANOW, &options)) != 0){perror("Serialport set error\n");return false;}return true;
}

4. 测试程序

新建文件main.cpp

包含的头文件

#include "serialport.hpp"

添加main函数

int main(int argc, char **argv)
{}

定义一个串口类实体

CSerialport sp1;

把main参数打印出来

int i = 0;
unsigned char buf[256];
int len = 0;
printf("The num of parameter:%d\n", argc);
for (i = 0; i < argc; i++)
{printf("%s\n", argv[i]);
}

根据不同的参数个数调用Open函数

if(argc == 1)
{printf("Open default serial port: tty\n");sp1.Open("/dev/tty");
}  
else if(argc == 2)
{printf("Open serial port:%s\n", argv[1]);sp1.Open(argv[1]);
}
else if(argc == 3)
{printf("Open serial port:%s, baudrate:%d\n", argv[1], atoi(argv[2]));sp1.Open(argv[1], atoi(argv[2]));
}
else if(argc == 6)
{printf("Open serial port:%s, baudrate:%d, data bits:%d, stop bits:%d, parity:%s\n", argv[1], atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), argv[5]);sp1.Open(argv[1], atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), argv[5]);
}

写字符串

const char *wrData = "Serial Port Write String\n";
sp1.Write((unsigned char *)(wrData), strlen(wrData));

因为此时串口的TxD和RxD是短路的, 此时可以读到写出去的字符串

usleep(50000);
len = sp1.Read(buf, 256);
if(len > 0)
{//buf[len] = 0;printf("Serial Read String:%s", buf);
}
elseprintf("Serial Read String Fail\n");

注意需要delay一段时间再去读。

最后关闭设备

sp1.Close();
return 0;

5. 编译

新建一个Android.bp文件。

cc_binary {name: "SerialPort",srcs: ["main.cpp","serialport.cpp"],product_specific: true
}

在apollo_p2.mk里面添加SerialPort:

PRODUCT_PACKAGES += SerialPort

在Ubuntun中运行lunch后执行:

mmm device/softwinner/apollo/apollo-p2/serialport/

6. 运行验证

通过adb把生成的执行文件push到目标板中。

首先连接目标板:

adb connect 192.168.3.81:5555

IP地址根据自己的板子地址修改。

将文件push到目标板:

adb root
adb remount
adb push out/target/product/apollo-p2/product/bin/SerialPort /product/bin

然后adb shell,进入目标板的product/bin下运行:

apollo-p2:/product/bin # ./SerialPort /dev/ttyAS5 115200
The num of parameter:3
./SerialPort
/dev/ttyAS5
115200
Open serial port:/dev/ttyAS5, baudrate:115200
Serial Read String:Serial Port Write StringSerial Port Write String

将RxD和TxD断开短路,接到一个USB转串口板子上验证。

apollo-p2:/ # ./product/bin/SerialPort /dev/ttyAS5 115200 8 1 N
The num of parameter:6
./product/bin/SerialPort
/dev/ttyAS5
115200
8
1
N
Open serial port:/dev/ttyAS5, baudrate:115200, data bits:8, stop bits:1, parity:N
Serial Read String Fail

电脑上接收到字符串,由于没有发送数据到串口,所以显示Serial Read String Fail。

http://www.xdnf.cn/news/334153.html

相关文章:

  • Qt实现车载多媒体项目,包含天气、音乐、视频、地图、五子棋功能模块,免费下载源文件!
  • Linux/AndroidOS中进程间的通信线程间的同步 - 消息队列
  • nputop:交互式 Ascend NPU 进程查看器(nvitop昇腾版)
  • 视觉图像处理及多模态融合初探
  • MyBatis(进阶)(xml标签)
  • 代码随想录算法训练营第三十七天-2|动态规划part2
  • [5-2] 对射式红外传感器计次旋转编码器计次 江协科技学习笔记(38个知识点)
  • 服务器数据恢复—Linux操作系统服务器意外断电导致部分文件丢失的数据恢复
  • 力扣刷题Day 41:除自身以外数组的乘积(238)
  • 【Linux】Linux工具(1)
  • 基于Centos7的DHCP服务器搭建
  • 【MySQL】存储引擎 - MyISAM详解
  • ARM 芯片上移植 Ubuntu 操作系统详细步骤
  • 云原生架构下的企业数字化转型之路:理念、挑战与落地实战
  • 2. Windows+Msys2+QGis3.36.1编译运行
  • 【Python开源】深度解析:一款高效音频封面批量删除工具的设计与实现
  • Axios替代品Alova
  • nutui-uniapp项目:弹框、弹出层的组件选择问题(组件对比)
  • 基于腾讯云MCP广场的AI自动化实践:爬取小红书热门话题
  • STM32系统定时器以及微秒延时函数分析
  • 电池自动分选机:新能源时代的“质检卫士”
  • Excel学习笔记
  • 蓝桥杯第十六届c组c++题目及个人理解
  • C++入门(下)--《Hello C++ World!》(2)(C/C++)
  • 【C++】手搓一个STL风格的string容器
  • 【开源解析】基于Python的智能文件备份工具开发实战:从定时备份到托盘监控
  • 键盘固件刷写详解:Bootloader
  • AppInventor2如何实现写文件不覆盖,而是在文件尾部追加?
  • 使用 React 实现语音识别并转换功能
  • Java游戏服务器开发流水账(2)开发中Maven的管理