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

海思3559万能平台搭建:串口编程 一休摸鱼 2024-01-02 16:51:33 225

前言

  平常的工作使用中,总是免不了要和串口打交道,协议的收发也经常通过串口来实现,海思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也会立即返回。

文章来源CSDN,如有侵权联系删除:https://blog.csdn.net/qq_42330920/article/details/127075475?spm=1001.2014.3001.5502

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包 点赞 收藏 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
一休摸鱼
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区