BeagleBoneBlack学习之串口通信

2019-07-12 23:33发布

有关Beaglebone的资料请参考官网:BeagleBone资源简介
它的串口资源如下:
BeagleBone串口引脚表
一共四个半串口,其中一个串口的rx被系统使用了没引出来,留给用户的还是4个。下面例程以UART1即P9.24/P9.26为例 串口使用方法分为两大步:
1.加载UARTcape
2.访问串口设备 是这样的,BeagleBone官方把所有扩展设备(插在扩展槽上的)都叫cape。无论这个cape是真实的还是虚拟的。本例中只是访问串口资源,就相当于一个虚拟cape(因为用到了官方的扩展功能) 一、加载UARTcape
先说几个相关路径
“/lib/firmware” —— beaglebone 相关设备树文件
“/sys/devices/bone_capemgr.9” —— beaglebone cape配置路径
“/dev” —— linux 设备目录
我们需要在/sys/devices/bone_capemgr.9里加载UART,然后在/dev里就会多出一个ttyO*设备。以UART1为例:
1.先要找到UART1的ID号。
进入/lib/firmware目录,执行如下代码: ls -l | grep UART 结果如下:
/lib/firmware
可以看到一堆关于UART的dtbo文件。这些文件就是linux的设备树文件(dts、dtb、dtbo)。更多资料请参考使用BBB的device tree和cape,在此不做多讲。
我们需要从dts(device tree source)文件中找寻UART的编号,但是该文件夹下并没有dts文件。可以用以下命令获取: dtc -I dtb -O dts ADAFRUIT-UART1-00A0.dtbo > ADAFRUIT-UART1-00A0.dts 该命令是设备树编辑命令,可以正向逆向编辑dts dtbo文件
执行后如下图:
dtboTOdts
可以看到有dts文件了。
再用cat读取该文件: cat ADAFRUIT-UART1-00A0.dts dts文件
这个part-number就是所要找到UART1的ID号。PS有关设备树请参考使用BBB的device tree和cape 找到UART1的ID号后在/sys/devices/bone_capemgr.9目录下执行: echo ADAFRUIT-UART1 > slots 那么UART1的设备树驱动便加载完毕,打开/dev目录会发现多了一个ttyO1的设备,该设备便是串口设备。如下图:
ttyO1
如果出现ttyO1则证明配置成功,如果未出现则失败。
PS.ttyO0是系统默认的那个串口,可以通过它以终端方式访问beaglebone 以上是通过bash命令的方式加载UARTcape,下面另附一c语言版本; #include #include #include #define SLOTS "/sys/devices/bone_capemgr.9/slots" int main() { int fd, count; //mount the Drive of Uart if ((fd = open(SLOTS, O_WRONLY)) < 0) { perror("SLOTS: Failed to open the file. "); return -1; } if ((count_t = write(fd, "ADAFRUIT-UART1",14))<0) { perror("SLOTS:Failed to write to the file Failed to mount the UART1"); return -1; } close(fd); //mount successful return 0; } 二、访问串口设备 当串口Cape挂载完毕后,便能像普通文件一样访问串口了。有几个地方需要注意下:在linux眼里一切皆是文件,所有的设备都能像文件一样读写,这和单片机、windows里的设备观不一样,需注意。 配置串口有个结构体需要了解下:termios。具体信息执行百度“linux 串口 termios”一大堆资料 在此不再多说
有几个需要注意的地方需留意下:
1.关于串口的读写方式(阻塞和非阻塞)
阻塞模式:读数据时读串口设备没有数据,那么系统会阻塞在这里,一直到串口有数据或者等待超时为止
写数据时,如果缓冲区剩余空间不够,那么系统会阻塞在这里,直到空间足够或等待超时为止
非阻塞模式:读数据如果设备没数据,返回-1,如果有返回读取个数。写数据时如果空间不够,则能写多少写多少,返回写进去的个数
因此推荐使用非阻塞模式,非阻塞模式可通过文件打开方式设置: fd = open("/dev/ttyO1", O_RDWR | O_NOCTTY | O_NDELAY) O_RDWR :读写方式打开;
O_NOCTTY :不以终端模式打开,以防键盘对它有影响,比如“ctl+c”,在终端里是结束进程的意思
O_NDELAY : 非阻塞模式打开
2.串口的配置: tcgetattr(fd, &opt); // get the configuration of the UART // config UART opt.c_cflag = B9600 | CS8 | CREAD | CLOCAL; // 9600 baud, 8-bit, enable receiver, no modem control lines opt.c_iflag = IGNPAR | ICRNL; // ignore partity errors, CR -> newline opt.c_iflag &= ~(IXON | IXOFF | IXANY); //turn off software stream control opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //关闭回显功能,关闭经典输入 改用原始输入 tcflush(fd,TCIOFLUSH); // 清理输入输出缓冲区 tcsetattr(fd, TCSANOW, &opt); // changes occur immmediately c_cflag 、c_iflag 、c_lflag 都是termios结构体的成员,用以配置串口,具体信息自行百度,在此仅解释用到的内容
tcgetattr(fd,opt)函数:用以获得fd所指向文件的配置参数,保存在opt变量内。
然后修改opt的参数 再调用tcsetattr重设串口参数,如上述代码所示。其中注意串口配置前需要用tcflush()清理下输入输出缓冲区。
关于回显功能:回显就是串口设备接收到什么数据,立马原封不动通过串口发回去,这个咱不需要,关掉就行。 3.输入方式的原始输入和标准输入
标准输入是把串口设备用作终端登录beaglebone时用的,里面有些控制符,比如遇到回车换行read才能读取一次等等。如果该串口不是用以终端登录用的,改成原始模式就是。
原始模式没啥规矩,收到数据就存起来,有read访问时就把数据送出去,没有就存在等着。一般用串口传数据就需要这种方式。 4.关于串口中断啥的
做单片机开发时,串口收数据一般用中断来完成。可能是linux的缘故 并没有在beaglebone上找到有关串口中断的事(linux把一切设备当做文件来处理,文件是没有中断的了)。所以 只能用查询的办法来查看接收串口数据了(1Ghz的主频开个小进程查询串口数据还是小case了) 最后附完整代码如下:
加载UART cape
每5S读取一次串口,如有数据则通过串口传回去,如没有,继续运行。一共循环10次退出 #include #include #include #include // using the termios.h library #define SLOTS "/sys/devices/bone_capemgr.9/slots" int main() { int fd, count_r,count_t,i; unsigned char buff[100]; // the reading & writing buffer struct termios opt; //uart confige structure //mount the Drive of Uart if ((fd = open(SLOTS, O_WRONLY)) < 0) { perror("SLOTS: Failed to open the file. "); return -1; } if ((count_t = write(fd, "ADAFRUIT-UART1",14))<0) { perror("SLOTS:Failed to write to the file Failed to mount the UART1"); return -1; } close(fd); //mount successful //open the UART1: read&write, No block and doesn't serve as an terminal if ((fd = open("/dev/ttyO1", O_RDWR | O_NOCTTY | O_NDELAY)) < 0) { perror("UART: Failed to open the UART device:ttyO1. "); return -1; } tcgetattr(fd, &opt); // get the configuration of the UART // config UART opt.c_cflag = B9600 | CS8 | CREAD | CLOCAL; // 9600 baud, 8-bit, enable receiver, no modem control lines opt.c_iflag = IGNPAR | ICRNL; // ignore partity errors, CR -> newline opt.c_iflag &= ~(IXON | IXOFF | IXANY); //turn off software stream control opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //关闭回显功能,关闭经典输入 改用原始输入 tcflush(fd,TCIOFLUSH); // 清理输入输出缓冲区 tcsetattr(fd, TCSANOW, &opt); // changes occur immmediately if ((count_t = write(fd, "Hello BeagleBone ",18))<0) { perror("ERR:Failed to write to the Device:ttyO1 "); return -1; } for(i = 0;i < 10;i++)//查询法 读十次 { if ((count_r = read(fd,(void*)buff,100))<0) perror("ERR:No data is ready to be read "); else if (count_r == 0) printf("ERR:No data is ready to be read "); else { buff[count_r] = 0; printf("The following was read in [%d]: %s ",count_r,buff); if ((count_t = write(fd, "DEBUG:",6))<0) { perror("ERR:Failed to write to the Device:ttyO1 "); return -1; } if ((count_t = write(fd, buff,count_r))<0) { perror("ERR:Failed to write to the Device:ttyO1 "); return -1; } if ((count_t = write(fd, " ",1))<0) { perror("ERR:Failed to write to the Device:ttyO1 "); return -1; } } usleep(5000000); // 延时 5s } close(fd); return 0; } that’s all。据说有select方法查询串口有无数据,下次试试再更。
over