目录
一、OrangePi PC Plus的串口
1.开启OrangePi PC+用于通信的串口
🔖方法一 :修改/boot/orangepiEnv.txt
🔖方法二:通过orangepi-config
2.基于wiringPi的串口通信Demo1.c
二、Linux下的串口开发基础
1.Linux下的串口配置
2.Linux串口通信Demo2.c
一、OrangePi PC Plus的串口
根据官方的用户手册所描述,OrangePi PC+可用于设备通信的串口有三个,分别对应的设备文件是:/dev/ttyS1、/dev/ttyS2、/dev/ttyS3,且默认未开启(zero plus据说默认开启,总之测试即可得知)。之前提到到调试串口应该是对于/dev/ttyS0(也可做通信串口,效果较差不推荐)。
1.开启OrangePi PC+用于通信的串口
这里使用官方提供的examples里的serialTest.c程序来测试。默认未开启情况下,直接编译运行该程序,是满屏的-1和→。我们可以通过修改/boot/orangepiEnv.txt这个文件,或者orange-config打开配置菜单去开启ttyS1、ttyS2或ttyS3。
特别说明:这里使用的系统是官方提供的ubuntu系统。如果是Armbian系统,应该修改的文件是/boot/armbianEnv.txt,同时调出配置菜单命令是armbian-config
对应的针脚接线,可通过gpio readall查看。
🔖方法一 :修改/boot/orangepiEnv.txt
以开启ttyS1为例,修改如下内容:(保存修改后需要reboot命令重启)
修改好重启后,接上TTL转USB模块到UART1。再用测试程序测试,正确的运行结果如下
🔖方法二:通过orangepi-config
orangepi-config命令调出配置菜单来开启通信串口,按照以下步骤
System → Hardware → 选择对应串口,空格选中 → Save → reboot重启
2.基于wiringPi的串口通信Demo1.c
#include <stdio.h>
#include <string.h>
#include <wiringPi.h>
#include <wiringSerial.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
int fd;
/* 发送数据线程 */
void *sendDatas(){
char *sendBuffer = (char *)malloc(128);
while(1){
memset(sendBuffer,'\0',sizeof(sendBuffer));
printf("Send -> ");
scanf("%s",sendBuffer);
serialPuts(fd,sendBuffer);
}
}
int main ()
{
fd = serialOpen("/dev/ttyS1",115200);
if (fd < 0){
printf("serial open error\n");
return -1;
}
if (wiringPiSetup () == -1){
perror("wiringPi setup");
return -1;
}
pthread_t sendThread;
char recvBuffer[128];
int cnt;
pthread_create(&sendThread,NULL,sendDatas,NULL);
/* 主线程,接收数据 */
while(1){
cnt = serialDataAvail(fd);
if(cnt > 0){
memset(recvBuffer,'\0',128);
read(fd,recvBuffer,cnt);
printf("receive -> %s\n",recvBuffer);
}
}
}
二、Linux下的串口开发基础
1.Linux下的串口配置
在demo1中,仅是调用了wiringPi库提供打开串口的API,下面要在demo1的基础上,自己编写Linux应用层的串口通信代码,也就是不用调库实现类似serialOpen()的接口。在serialOpen()这一接口中,主要完成设置波特率(输入和输出)、设置奇偶校验位、停止位、数据位。(也就是调用Linux标准C库的接口,向内核传递串口通信必要的参数信息。)
首先需要大致了解Linux下串口配置需要做哪些事,这里参考wiringpi源码来了解Linux配置串口基本流程。(以下源码摘自官方wiringPi库,注释为自行添加,可能有误仅供参考):
int serialOpen (const char *device, const int baud)
{
struct termios options ;
speed_t myBaud ;
int status, fd ;
switch (baud)
{
case 50: myBaud = B50 ; break ;
case 75: myBaud = B75 ; break ;
case 110: myBaud = B110 ; break ;
case 134: myBaud = B134 ; break ;
case 150: myBaud = B150 ; break ;
case 200: myBaud = B200 ; break ;
case 300: myBaud = B300 ; break ;
case 600: myBaud = B600 ; break ;
case 1200: myBaud = B1200 ; break ;
case 1800: myBaud = B1800 ; break ;
case 2400: myBaud = B2400 ; break ;
case 4800: myBaud = B4800 ; break ;
case 9600: myBaud = B9600 ; break ;
case 19200: myBaud = B19200 ; break ;
case 38400: myBaud = B38400 ; break ;
case 57600: myBaud = B57600 ; break ;
case 115200: myBaud = B115200 ; break ;
default:
return -2 ;
}
if ((fd = open (device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) == -1)
return -1 ;
fcntl (fd, F_SETFL, O_RDWR) ; //设置串口阻塞办法
// Get and modify current options: 获取和修改当前选项
tcgetattr (fd, &options) ;
cfmakeraw (&options) ; //将终端设置为原始模式8N1无流控
cfsetispeed (&options, myBaud) ; //设置输入波特率
cfsetospeed (&options, myBaud) ; //设置输出波特率
/* 据说linux并没有将输入波特率和输出波特率分开
调用cfsetispeed和cfsetospeed任何一个会同时修改输入和输出波特率
但是也许传入要修改的波特率为0的时候这两个函数会有区别 */
options.c_cflag |= (CLOCAL | CREAD); //CLOCAL忽略modem状态线,CREAD使能设备接收(某教材所述)
options.c_cflag &= ~PARENB ; //设置奇偶校验位——这里是无校验位
/* 三种校验位的设置 */
/* 1.奇校验位设置 options.c_cflag |= (PARODD | PARENB);
options.c_cflag |= INPCK;
2.偶校验位设置 options.c_cflag |= PARENB;
options.c_cflag &= PARODD;
options.c_cflag |= INPCK;
3.无校验位设置 options.c_cflag &= ~PARENB; */
options.c_cflag &= ~CSTOPB; //设置1位的停止位
/*options.c_cflag |= CSTOPB; 设置2位的停止位*/
options.c_cflag &= ~CSIZE ; //用数据位掩码清空设置
options.c_cflag |= CS8 ; //设置8位的数据位
/* 设置输出模式*/
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
/* 设置等待时间和最小接收字符 */
options.c_cc [VMIN] = 0 ; //读取字符最少个数
options.c_cc [VTIME] = 100 ; //读取一个字符等待100分秒
tcsetattr (fd, TCSANOW, &options) ; //使上面新的设置生效
ioctl (fd, TIOCMGET, &status); //获取(具体未查到,不深究)
status |= TIOCM_DTR ;
status |= TIOCM_RTS ;
ioctl (fd, TIOCMSET, &status); //设置(具体未查到,不深究)
usleep (10000) ; // 10mS
return fd ;
}
2.串口通信Demo2.c
demo2主要是在demo1的基础上,自己实现了wiringPi库中的serialOpen()这一接口,以及用Linux下标准的文件I/O操作函数read()、write()实现串口通信。文章来源:https://www.toymoban.com/news/detail-402551.html
#include <stdio.h>
#include <stdlib.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>
#include <pthread.h>
int uartOpen (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:
return -2 ;
}
fd = open(device,O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
if(fd == -1){
perror("open");
return -1;
}
fcntl (fd, F_SETFL, O_RDWR) ; //设置串口阻塞办法
//获取和修改当前选项
tcgetattr (fd, &options) ;
cfmakeraw (&options) ; //将终端设置为原始模式8N1无流控
cfsetispeed (&options, myBaud) ; //设置输入波特率
cfsetospeed (&options, myBaud) ; //设置输出波特率
options.c_cflag |= (CLOCAL | CREAD); //CLOCAL 忽略modem状态线,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_cc [VMIN] = 0 ; //读取字符最少个数
options.c_cc [VTIME] = 100 ; //读取一个字符等待100分秒
tcsetattr (fd, TCSANOW, &options) ; //使上面新的设置生效
ioctl (fd, TIOCMGET, &status);
status |= TIOCM_DTR ;
status |= TIOCM_RTS ;
ioctl (fd, TIOCMSET, &status);
usleep (10000) ; //10ms
return fd ;
}
int uartfd;
/* 向串口发送数据的线程 */
void *sendDatas(){
int cnt;
while(1){
char *buffer = (char *)malloc(64);
memset(buffer,'\0',sizeof(buffer));
printf("send -> ");
scanf("%s",buffer);
/*向串口1对应的设备文件写入buffer的数据*/
cnt = write(uartfd,buffer,strlen(buffer));
if(cnt < 0)printf("Serial send datas error\n");
}
}
/* 读取串口数据的线程 */
void *recvDatas(){
int cnt,readSize;
char *buffer = (char *)malloc(64);
while(1){
/* 判断串口是否有数据 */
if(ioctl(uartfd,FIONREAD,&cnt) == -1){
perror("ioctl");
return 0;
}else{
readSize = read(uartfd,buffer,cnt); //读取数据到buffer中
if(readSize > 0)printf("recv -> %s",buffer);//读取成功再打印
}
memset(buffer,'\0',sizeof(buffer));
}
}
int main(int argc,char **argv){
pthread_t sendThread,recvThread;
if(argc < 2){
printf("syntax error.Usage:%s /dev/ttyS*\n",argv[0]);
return -1;
}
/* 以波特率115200打开串口,其他相关配置已在uartOpen接口中已默认 */
uartfd = uartOpen(argv[1],115200);
if(uartfd == -1){
printf("%s open error\n",argv[1]);
return -1;
}else{
printf("open %s succeed.\n",argv[1]);
}
/* 主函数中定义出两个线程用于接收和发送数据 */
pthread_create(&sendThread,NULL,sendDatas,NULL);
pthread_create(&recvThread,NULL,recvDatas,NULL);
/* 主线程每10秒发送心跳包 */
while(1){
char alive[] = "I am alive\r\n";
write(uartfd,alive,strlen(alive));
sleep(10);
}
return 0;
}
说明:由于笔者水平有限,文中难以避免有所错漏,敬请各读者斧正
版权声明:转载请附上原文出处链接及本声明。
文章来源地址https://www.toymoban.com/news/detail-402551.html
到了这里,关于第三篇 香橙派的外设开发基础(中)— 串口篇的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!