海思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月前
|
Unix Linux iOS开发
振南技术干货集:各大平台串口调试软件大赏(4)
振南技术干货集:各大平台串口调试软件大赏(4)
|
6月前
|
Linux 网络安全 Android开发
振南技术干货集:各大平台串口调试软件大赏(1)
振南技术干货集:各大平台串口调试软件大赏(1)
|
4月前
|
算法 数据处理 开发者
LabVIEW开发为何仿制(致敬)经典成熟软件
LabVIEW开发为何仿制(致敬)经典成熟软件
28 2
|
6月前
|
监控 网络协议 Linux
振南技术干货集:各大平台串口调试软件大赏(7)
振南技术干货集:各大平台串口调试软件大赏(7)
|
6月前
|
Unix Linux Shell
振南技术干货集:各大平台串口调试软件大赏(3)
振南技术干货集:各大平台串口调试软件大赏(3)
|
6月前
|
监控 Linux Android开发
振南技术干货集:各大平台串口调试软件大赏(6)
振南技术干货集:各大平台串口调试软件大赏(6)
|
物联网 数据处理
嵌入式系统与硬件设计:连接物联世界的智慧之源
本篇深入研究了物联网中嵌入式系统与硬件设计的关键内容。我们探讨了嵌入式系统的概述,介绍了微控制器与嵌入式开发板在物联网应用中的应用,以及硬件设计的基本原则和接口。通过Arduino示例代码,读者可以了解如何使用嵌入式开发板控制LED灯。设计原则和硬件接口部分帮助读者更好地理解硬件设计的关键考虑因素,包括电路设计、电源管理和接口选择。通过本篇内容,读者将更加了解嵌入式系统与硬件设计在物联网中的作用,为创造智能化的嵌入式应用提供了有益的指导。
184 0
|
存储 数据挖掘
带你读《天猫精灵:如何在互联网公司做硬件》——2.5 电子设计相关工具简介
带你读《天猫精灵:如何在互联网公司做硬件》——2.5 电子设计相关工具简介