海思3559万能平台搭建:串口编程

简介: 海思3559万能平台搭建:串口编程

前言

 平常的工作使用中,总是免不了要和串口打交道,协议的收发也经常通过串口来实现,海思3559下的串口和标准的linux下串口大同小异,可以参考之前zynq的串口编程,也可以直接阅读本文

使能串口

 最直接的方式就是将设备树中对应uart的status修改为 status = “okay”。海思实际加载的串口驱动是PL011,menuconfig查看配置Device Drivers > Character devices > Serial drivers中的ARM AMBA PL011 serial port support 和 Support for console on AMBA serial port是否有选择上。重新编译内核烧入,在/dev 下可以查看是否有串口设备ttyAMA0~4。sdk2.0.3.1版本默认是有的

查看串口配置

 可以使用linux的stty命令查看串口的配置参数。比如:stty -F /dev/ttyAMA0 -a

/dev#cd /proc/tty/driver//proc/tty/driver # ls ttyAMA
/proc/tty/driver # cat ttyAMA serinfo:1.0 driver revision:
0:uart:PLell rev2 mmio:0x12100000 irq:13 tx:916928 rx:867 RTS|DTR|DSR|CD|RI1:uart:PLoll rev2 mm1o:0x12101000 irq:14 tx:0 rx:0 DSR|CD|RI2:uart:PLoll rev2 mm1o:0x12102000 1rq:15 tx:0 rx:0 DSR|CD|RI3:uart:PLell rev2 mm1o:0x12103000 irq:16 tx:0 rx:0 DSR|CD|RI4:uart:PLell rev2 mm1o:0x12104000 irg:17 tx:0 rx:0 DSR|CD|RI

同样可以使用stty命令设置串口参数,比如:stty -F /dev/ttyAMA0 ispeed 115200 ospeed 115200 cs8

查看串口数据收发

 一般调试串口,我们可以将TX与RT接在一起,自己发数据给自己接收,看数据是否正常。也可以使用cat 查看串口数据或是echo发送数据。

 发送数据:

echo "test 1234567890" > /dev/ttyAMA0

 接收数据:

cat /dev/ttyAMA0

查看串口硬件分配:

 串口的硬件分配状态,比如IO和中断使用情况可以在/proc/tty/driver下的ttyAMA 种查看:

/dev#cd /proc/tty/driver//proc/tty/driver # ls ttyAMA
/proc/tty/driver # cat ttyAMA serinfo:1.0 driver revision:
0:uart:PLell rev2 mmio:0x12100000 irq:13 tx:916928 rx:867 RTS|DTR|DSR|CD|RI1:uart:PLoll rev2 mm1o:0x12101000 irq:14 tx:0 rx:0 DSR|CD|RI2:uart:PLoll rev2 mm1o:0x12102000 1rq:15 tx:0 rx:0 DSR|CD|RI3:uart:PLell rev2 mm1o:0x12103000 irq:16 tx:0 rx:0 DSR|CD|RI4:uart:PLell rev2 mm1o:0x12104000 irg:17 tx:0 rx:0 DSR|CD|RI

注意

 如果串口的配置和数据的收发命令都能够正常,但是串口的引脚没有电平变化,这个可能是串口的复用功能没有设置,需要设置一下GPIO复用为串口功能。复用功能可以在设备树dts中设置,也可以使用海思的himm命令直接设置:

himm 0x120f0100 0x01 #UART2_RXD
himm 0x120f0104 0x01 #UART2_TXD

这些都是和标注的linux一致的,不一样的是海思的串口经常需要代码初始化后才能操作,或者使用海思自己的microcom

e8645132497c454793b936de91792f17.png

收发测试代码

操作库函数头文件

/************************************************************************************************
*****Describe: This program is writen to operate HI35xx serial devices.                     *****
*****Author: xin.han                        *****
*****Date: 2022-09-17                  *****
*************************************************************************************************/
#ifndef _UART_H_
#define _UART_H_
#include<stdio.h>       
#include<stdlib.h>     
#include<unistd.h>    
#include<sys/types.h>   
#include<sys/stat.h>     
#include<fcntl.h>     
#include<termios.h>    
#include<errno.h>      
#include<string.h> 
#include <signal.h>  
#include "platform.h"
#include "parser.h"
#include "queue.h"
//宏定义 
// #define HI_FALSE  -1  
// #define HI_TRUE     0   
#ifdef debugprintf
  #define debugpri(mesg, args...) fprintf(stderr, "[HI Serial print:%s:%d:] " mesg "\n", __FILE__, __LINE__, ##args) 
#else
  #define debugpri(mesg, args...)
/*
*描述  : 打开串口
*参数  : HiSerDevice串口设备名 串口设备举例: /dev/ttyAMA1 /dev/ttyAMA2
*返回值: 成功返回fd,失败返回-1
*注意  :无
*/
int HI_Serial_Open(char* HiSerDevice);  
/*
*描述  : 关闭串口
*参数  : fd文件描述符 
*返回值: 成功返回fd,失败返回-1
*注意  :无
*/
void HI_Serial_Close(int fd); 
/*
*描述  : 串口参数设置
*参数  : fd: 文件描述符  
*        speed: 波特率.115200,19200,9600...
*        flow_ctrl: 流控
*        databits: 数据位 取值为5,6,7,8
*        stopbits: 停止位 取值为1,2
*        parity: 奇偶校验位  取值为N,W,O,S
*返回值: 成功返回0,失败返回-1
*注意  :无
*/ 
int HI_Serial_Set(int fd,int speed,int flow_ctrl,int databits,int stopbits,int parity) ;
/*
*描述  : 串口初始化,实际还是串口参数设置
*参数  : fd: 文件描述符  
*        speed: 波特率.115200,19200,9600...
*        flow_ctrl: 流控
*        databits: 数据位 取值为5,6,7,8
*        stopbits: 停止位 取值为1,2
*        parity: 奇偶校验位  取值为N,W,O,S
*返回值: 成功返回0,失败返回-1
*注意  :无
*/ 
int HI_Serial_Init(int fd, int speed,int flow_ctrl,int databits,int stopbits,int parity);
/*
*描述  : 串口发送
*参数  : fd:文件描述符 
*        send_buf:发送buf
*        data_len:数据长度
*返回值: 成功返回数据长度,失败返回-1
*注意  :无
*/
int HI_Serial_Send(int fd, char *send_buf,int data_len) ;
/*
*描述  : 串口接受
*参数  : fd:文件描述符 
*        send_buf:接收buf
*        data_len:数据长度
*返回值: 成功返回数据长度,失败返回-1
*注意  :无
*/
int HI_Serial_Recv(int fd, char *rcv_buf,int data_len) ;
/*
*描述  : 串口接受函数线程
*参数  : 无
*返回值: 无
*注意  :无
*/
HI_VOID *uart_recv_task(HI_VOID *arg);
/*
*描述  : 串口发送函数线程
*参数  : 无
*返回值: 无
*注意  :无
*/
HI_VOID *uart_send_task(HI_VOID *arg);
#endif
#endif

操作库函数

/************************************************************************************************
*****Describe: This program is writen to operate HI35xx serial devices.                     *****
*****Author: xin.han                        *****
*****Date: 2022-09-17                  *****
*************************************************************************************************/
#include "uart.h"
int HiSerfd; 
void HI_Serial_Close(int fd);
void Hi_sigsegv(int dummy)
{
  if(HiSerfd > 0)
  HI_Serial_Close(HiSerfd);
  fprintf(stderr, "Hi Serial Caught SIGSEGV, Abort!\n");
  fclose(stderr);
  abort();
}
void Hi_sigterm(int dummy)
{
  if(HiSerfd > 0)
  HI_Serial_Close(HiSerfd);
  fprintf(stderr, "Hi Serial Caught SIGTERM, Abort!\n");
  fclose(stderr);
  exit(0);
}
void Hi_init_signals(void)
{
  struct sigaction sa;
  sa.sa_flags = 0;
  sigemptyset(&sa.sa_mask);
  sigaddset(&sa.sa_mask, SIGSEGV);
  sigaddset(&sa.sa_mask, SIGTERM);
  sigaddset(&sa.sa_mask, SIGPIPE);
  sa.sa_handler = Hi_sigsegv;
  sigaction(SIGSEGV, &sa, NULL);
  sa.sa_handler = Hi_sigterm;
  sigaction(SIGTERM, &sa, NULL);
  sa.sa_handler = SIG_IGN;
  sigaction(SIGPIPE, &sa, NULL);
}
void HI_Serial_Usage(void)
{
    printf("Usage:\n");
    printf("\tmyhicom [-d] <HiSerialDevice> [-s] get netdeviece info [-rw] read or wite select\n");
    printf("\tmyhicom [-h] for more usage\n");
    printf("\tmyhicom [-v] the verson of the sofware\n");
  printf("\tExample:\n\tmyhicom -d /dev/ttyAMA1 -s 115200 -w HiSerial:HelloWorld\n");
}
int HI_Serial_Open(char* HiSerDevice)  
{  
    int fd;
  fd = open( HiSerDevice, O_RDWR|O_NOCTTY);//  O_RDWR : 可读可写 
             //  O_NOCTTY :该参数不会使打开的文件成为该进程的控制终端。如果没有指定这个标志,那么任何一个 输入都将会影响用户的进程。
              //  O_NDELAY :这个程序不关心DCD信号线所处的状态,端口的另一端是否激活或者停止。如果用户不指定了这个标志,则进程将会一直处在睡眠状态,直到DCD信号线被激活。
  // fd = open(HiSerDevice, O_RDWR|O_NOCTTY|O_NDELAY);  
  if (-1 == fd)  
  {  
  perror("HiSerial Can't Open Serial HiSerDevice");  
  return(-1);  
  }  
  //恢复串口为阻塞状态                                 
  if(fcntl(fd, F_SETFL, 0) < 0)  
  {  
  debugpri("fcntl failed!\n");  
  return(-1);  
  }       
  else  
  {  
  debugpri("fcntl=%d\n",fcntl(fd, F_SETFL,0));  
  }  
  //测试是否为终端设备      
  if(0 == isatty(STDIN_FILENO))  
  {  
  debugpri("standard input is not a terminal device\n");  
  return(-1);  
  }  
  else  
  {  
  debugpri("isatty success!\n");  
  }                
  printf("fd->open=%d\n",fd);  
  return fd;  
}  
void HI_Serial_Close(int fd)  
{  
  if(fd > 0)
  close(fd); 
  return; 
}  
int HI_Serial_Set(int fd,int speed,int flow_ctrl,int databits,int stopbits,int parity)  
{  
  int   i;  
  // int   status;  
  int   speed_arr[] = { B115200, B19200, B9600, B4800, B2400, B1200, B300};  
  int   name_arr[] = {115200,  19200,  9600,  4800,  2400,  1200,  300};  
  struct termios options;  
  if( tcgetattr( fd,&options)  !=  0)  
  {  
  perror("SetupSerial 1");      
  return(-1);   
  }  
    //set buater rate 
  for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++)  
  {  
  if  (speed == name_arr[i])  
  {               
    cfsetispeed(&options, speed_arr[i]);   
    cfsetospeed(&options, speed_arr[i]);    
  }  
  }       
    //set control model 
    options.c_cflag |= CLOCAL;  //清bit位 关闭流控字符 0x11 0x13
    options.c_cflag |= CREAD;  //清bit位 关闭流控字符 0x11 0x13
    options.c_iflag &= ~(INLCR|ICRNL);//清bit位 关闭字符映射 0x0a 0x0d
    options.c_iflag &= ~(IXON);//清bit位 关闭流控字符 0x11 0x13
    //set flow control
    switch(flow_ctrl)  
    {  
  case 0 ://none  
              options.c_cflag &= ~CRTSCTS;  
              break;     
  case 1 ://use hard ware 
              options.c_cflag |= CRTSCTS;  
              break;  
  case 2 ://use sofware
              options.c_cflag |= IXON | IXOFF | IXANY;  
              break;  
    }  
    //select data bit   
    options.c_cflag &= ~CSIZE;  
    switch (databits)  
    {    
  case 5    :  
                     options.c_cflag |= CS5;  
                     break;  
  case 6    :  
                     options.c_cflag |= CS6;  
                     break;  
  case 7    :      
                 options.c_cflag |= CS7;  
                 break;  
  case 8:      
                 options.c_cflag |= CS8;  
                 break;    
  default:     
                 fprintf(stderr,"Unsupported data size\n");  
                 return (-1);   
    }  
    //select parity bit 
    switch (parity)  
    {    
  case 'n':  
  case 'N'://无奇偶校验位  
                 options.c_cflag &= ~PARENB;   
                 options.c_iflag &= ~INPCK;      
                 break;   
  case 'o':    
  case 'O'://设置为奇校验    
                 options.c_cflag |= (PARODD | PARENB);   
                 options.c_iflag |= INPCK;               
                 break;   
  case 'e':   
  case 'E'://设置为偶校验  
                 options.c_cflag |= PARENB;         
                 options.c_cflag &= ~PARODD;         
                 options.c_iflag |= INPCK;        
                 break;  
  case 's':  
  case 'S'://设置为空格    
                 options.c_cflag &= ~PARENB;  
                 options.c_cflag &= ~CSTOPB;  
                 break;   
        default:    
                 fprintf(stderr,"Unsupported parity\n");      
                 return (-1);   
    }   
    // set stopbit  
    switch (stopbits)  
    {    
  case 1:     
                 options.c_cflag &= ~CSTOPB; break;   
  case 2:     
                 options.c_cflag |= CSTOPB; break;  
  default:     
                       fprintf(stderr,"Unsupported stop bits\n");   
                       return (-1);  
    }  
  //修改输出模式,原始数据输出    
  options.c_oflag &= ~OPOST;  
  options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);  
  //options.c_lflag &= ~(ISIG | ICANON);  
    //set wait time   主要影响read函数
  /* options.c_cc[VTIME] = X;   //设置从获取到1个字节后开始计时的超时时间
  options.c_cc[VMIN] = Y;   //设置要求等待的最小字节数
  在原始模式下对read()函数的影响:
  1、X=0,Y!=0。函数read()只有在读取了Y个字节的数据或者收到一个信号的时候才返回;
  2、X!=0,Y=0。即使没有数据可以读取,read()函数等待X时间量后返回;
  3、X!=0,Y!=0。第一个字节数据到时开始,最先满足收到Y个字节或达超时时间X任意一个条件,read()返回;
  4、X=0,Y=0。即使读取不到任何数据,函数read也会立即返回。 */
    options.c_cc[VTIME] = 1;    /* 读取一个字符等待1*(1/10)s */      
    options.c_cc[VMIN] = 32;    /* 读取字符的最少个数为xx,单位是字节 */  
    tcflush(fd,TCIFLUSH);  
    // //激活配置 (将修改后的termios数据设置到串口中)  
    if (tcsetattr(fd,TCSANOW,&options) != 0)    
  {  
  perror("com set error!\n");    
  return -1;   
  }  
    return 0;   
}  
int HI_Serial_Init(int fd, int speed,int flow_ctrl,int databits,int stopbits,int parity)  
{  
    // int err;  
    //设置串口数据帧格式  
    if (HI_Serial_Set(fd,speed,flow_ctrl,databits,stopbits,parity) == -1)  
  {                                                           
  return -1;  
  }  
    else  
  {  
  return  0;  
  }  
}  
int HI_Serial_Send(int fd, char *send_buf,int data_len)  
{  
    int len = 0;  
    len = write(fd,send_buf,data_len);  
    if (len == data_len )  
  {  
  debugpri("send data is %s\n",send_buf);
  return len;  
  }       
    else     
  {                   
  tcflush(fd,TCOFLUSH);  
  return -1;  
  }  
} 
int HI_Serial_Recv(int fd, char *rcv_buf,int data_len)  
{  
  int len,fs_sel;  
    fd_set fs_read;  
    struct timeval time;  
    FD_ZERO(&fs_read);  
    FD_SET(fd,&fs_read);  
    time.tv_sec = 30;  
    time.tv_usec = 0;  
    // len = read(fd,rcv_buf,data_len);  
    // select fdset
    fs_sel = select(fd+1,&fs_read,NULL,NULL,&time);  
    if(fs_sel)  
  {  
  len = read(fd,rcv_buf,data_len);  
  debugpri("HiSeral Receive Data = %s len = %d fs_sel = %d\n",rcv_buf,len,fs_sel);  
  return len;  
  }  
    else  
  {  
  debugpri("Hiserial haven't data receive!");  
  return -1;  
  }       
} 
HI_VOID *uart_recv_task(HI_VOID *arg)
{
  int len;
  char HiSerialDev[32]="/dev/ttyAMA1";
  // char sendbuf[1024]={0};
  char recvbuf[1024]={0};
  int SerialSpeed = 115200;
    int HiSerfd;
    int i;
  // Hi_init_signals();
  // HI_Serial_Usage();  
    HiSerfd = HI_Serial_Open(HiSerialDev);
    HI_Serial_Init(HiSerfd,SerialSpeed,0,8,1,'N');
    while(1)
    {
    len = HI_Serial_Recv(HiSerfd, recvbuf,sizeof(recvbuf)); 
    // printf("serial_recv length is %d\n",len); 
    if(len > 0)  
    {  
        printf(" ttyAMA1:recv origin data \n");
        // recvbuf[len] = '\0';  
        for (i = 0;i < len ;i++)
        {
            printf("%02x  ",recvbuf[i]);  
        }
        printf("\n");  
        // memset(recvbuf,0,sizeof(recvbuf));
        //break;  
    }  
    else  
    {  
    //     printf("Hiserial haven't data receive \n");  
    }  
    // sleep(2);        
    }
    // debugpri("myHicom write %s\n",optarg);
    // HiSerfd = HI_Serial_Open(HiSerialDev);
    // printf("fd = %d device = %s speed = %d\n",HiSerfd,HiSerialDev,SerialSpeed);
    // HI_Serial_Init(HiSerfd,SerialSpeed,0,8,1,'N');      
    // sprintf(sendbuf,"%s\n",optarg);      
    // HI_Serial_Send(HiSerfd, sendbuf, strlen(sendbuf)+1); 
    // if(HiSerfd > 0)
    //     HI_Serial_Close(HiSerfd);      
    // break;
  return 0;
}
HI_VOID *uart_send_task(HI_VOID *arg)
{
  char HiSerialDev[32]="/dev/ttyAMA1";
  char sendbuf[1024]="HelloWorld1234567890";
  // char recvbuf[1024]={0};
  // char sendbuf[] = {0};
  int SerialSpeed = 115200;
    int HiSerfd;
  // Hi_init_signals();
  // HI_Serial_Usage();  
    HiSerfd = HI_Serial_Open(HiSerialDev);
    HI_Serial_Init(HiSerfd,SerialSpeed,0,8,1,'N');
    while(1)
    {
    HI_Serial_Send(HiSerfd, sendbuf, strlen(sendbuf)+1); 
        sleep(1);    
    }
    // debugpri("myHicom write %s\n",optarg);
    // HiSerfd = HI_Serial_Open(HiSerialDev);
    // printf("fd = %d device = %s speed = %d\n",HiSerfd,HiSerialDev,SerialSpeed);
    // HI_Serial_Init(HiSerfd,SerialSpeed,0,8,1,'N');      
    // sprintf(sendbuf,"%s\n",optarg);      
    // HI_Serial_Send(HiSerfd, sendbuf, strlen(sendbuf)+1); 
    // if(HiSerfd > 0)
    //     HI_Serial_Close(HiSerfd);      
    // break;
  return 0;
}

还是新建两个线程,一个收一个发

HI_VOID *uart_recv_task(HI_VOID *arg)
{
  int len;
  char HiSerialDev[32]="/dev/ttyAMA1";
  // char sendbuf[1024]={0};
  char recvbuf[1024]={0};
  int SerialSpeed = 115200;
    int HiSerfd;
    int i;
    HiSerfd = HI_Serial_Open(HiSerialDev);
    HI_Serial_Init(HiSerfd,SerialSpeed,0,8,1,'N');
    while(1)
    {
  len = HI_Serial_Recv(HiSerfd, recvbuf,sizeof(recvbuf)); 
  // printf("serial_recv length is %d\n",len); 
  if(len > 0)  
  {  
    printf(" ttyAMA1:recv origin data \n");
    // recvbuf[len] = '\0';  
    for (i = 0;i < len ;i++)
    {
    printf("%02x  ",recvbuf[i]);  
    }
    printf("\n");  
    // memset(recvbuf,0,sizeof(recvbuf));
    //break;  
  }  
  else  
  {  
  //     printf("Hiserial haven't data receive \n");  
  }  
    }
  return 0;
}
HI_VOID *uart_send_task(HI_VOID *arg)
{
  char HiSerialDev[32]="/dev/ttyAMA1";
  char sendbuf[1024]="HelloWorld1234567890";
  // char recvbuf[1024]={0};
  // char sendbuf[] = {0};
  int SerialSpeed = 115200;
    int HiSerfd;
    HiSerfd = HI_Serial_Open(HiSerialDev);
    HI_Serial_Init(HiSerfd,SerialSpeed,0,8,1,'N');
    while(1)
    {
    HI_Serial_Send(HiSerfd, sendbuf, strlen(sendbuf)+1); 
        sleep(1);    
    } 
  return 0;
}

 还是比较简单的

 其实也不是海思的坑,做驱动的时候不影响,做应用的时候会稍微恶心一点

 每次接收的时候,如果内容短还好,一旦大于8个字,就会分成几次接收,倒也不会丢,现在自己要写应用了,为了方便肯定不能这么用了呀,一番检查发现是options.c_cc[VMIN] 的原因,默认是1,单位是字节,表示最少收到的字节数,select收到就会返回了

 options.c_cc[VTIME] = X;   //设置从获取到1个字节后开始计时的超时时间

 options.c_cc[VMIN] = Y;   //设置要求等待的最小字节数

 在原始模式下对read()函数的影响:

 1、X=0,Y!=0。函数read()只有在读取了Y个字节的数据或者收到一个信号的时候才返回;

 2、X!=0,Y=0。即使没有数据可以读取,read()函数等待X时间量后返回;

 3、X!=0,Y!=0。第一个字节数据到时开始,最先满足收到Y个字节或达超时时间X任意一个条件,read()返回;

 4、X=0,Y=0。即使读取不到任何数据,函数read也会立即返回。


相关文章
|
6月前
|
监控 Linux Android开发
振南技术干货集:各大平台串口调试软件大赏(5)
振南技术干货集:各大平台串口调试软件大赏(5)
|
6月前
|
Linux 网络安全 Android开发
振南技术干货集:各大平台串口调试软件大赏(2)
振南技术干货集:各大平台串口调试软件大赏(2)
|
6月前
|
Linux 网络安全 Android开发
振南技术干货集:各大平台串口调试软件大赏(1)
振南技术干货集:各大平台串口调试软件大赏(1)
|
6月前
|
Unix Linux iOS开发
振南技术干货集:各大平台串口调试软件大赏(4)
振南技术干货集:各大平台串口调试软件大赏(4)
|
4月前
|
算法 数据处理 开发者
LabVIEW开发为何仿制(致敬)经典成熟软件
LabVIEW开发为何仿制(致敬)经典成熟软件
31 2
|
6月前
|
监控 网络协议 Linux
振南技术干货集:各大平台串口调试软件大赏(7)
振南技术干货集:各大平台串口调试软件大赏(7)
|
6月前
|
Unix Linux Shell
振南技术干货集:各大平台串口调试软件大赏(3)
振南技术干货集:各大平台串口调试软件大赏(3)
|
6月前
|
监控 Linux Android开发
振南技术干货集:各大平台串口调试软件大赏(6)
振南技术干货集:各大平台串口调试软件大赏(6)
|
6月前
|
Linux Android开发
全志平台A40I : I2C通讯问题调试笔记
全志平台A40I : I2C通讯问题调试笔记
182 0
|
6月前
|
数据可视化 安全 数据挖掘
搬运5款不为人知的小众软件,简单实用
电脑上的各类软件有很多,除了那些常见的大众化软件,还有很多不为人知的小众软件,专注于实用功能,简洁干净、功能强悍。
71 0