海思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

在这里插入图片描述
  同样可以使用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 种查看:
在这里插入图片描述

注意

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

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

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

收发测试代码

操作库函数头文件

/************************************************************************************************
*****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也会立即返回。

猜你喜欢

转载自blog.csdn.net/qq_42330920/article/details/127075475