海思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 0x13options.c_cflag |= CREAD; //清bit位 关闭流控字符 0x11 0x13options.c_iflag &= ~(INLCR|ICRNL);//清bit位 关闭字符映射 0x0a 0x0doptions.c_iflag &= ~(IXON);//清bit位 关闭流控字符 0x11 0x13//set flow controlswitch(flow_ctrl) { case 0 ://none options.c_cflag &= ~CRTSCTS; break; case 1 ://use hard ware options.c_cflag |= CRTSCTS; break; case 2 ://use sofwareoptions.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 fdsetfs_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也会立即返回。
海思3559万能平台搭建:串口编程相关推荐
- 海思3559万能平台搭建:DDR移植的一些问题
前言: 开发板是绝对无误的硬件环境,但是我们平时的开发肯定会接触自己搭建的硬件环境,难免会有这样那样的小问题,这里给出一次DDR的调试过程 问题描述 海思3559开发板可以用默认配置表格生成的 ...
- 海思3559万能平台搭建:OSD实时叠加的支持1SDL库 FREETYPE库 SDL_TTF库的移植
前言 万能平台字符叠加的功能自然少不了,但海思默认支持的都是静态位图,如果实时刷新或者我们向在屏幕上显示一些中文信息就捉襟见肘了,所以这里需要参考移植开源项目SDL的库来帮我们实现这一想法 网 ...
- 海思3559万能平台搭建:获取数据帧修改后编码
前言 有了这么长的铺垫和反复的啃sample,现在开始搭建自己的平台就底气多了,倒也不至于万能平台哈哈,只是在完成配置文件的功能后,可以不用改代码重新编译,就可以实现多场景多平台多功能下的使用了 ...
- 海思3559万能平台搭建:在截获的YUV图像上画框
前言 万里长征第二步,YUV的认识和编码还在进行中,熟悉了YUV格式的原理和储存方式后,我们就可以结合第一步中从vpss通道截获的YUV图像上尝试修改,叠加自己的算法,先简单粗暴的改改,后续在替换 ...
- 海思3559万能平台搭建:添加一个新的sensor
前言 海思对摄像头的支持是只有固定的,如果我们想更换新的相机,让fpga接入通过lvds接口给3559提供视频源的话,除了相关配置,还需要添加新的sensor库,编译新的isp库,而且,如果移植不 ...
- 海思3559万能平台搭建:OSD功能的优化
前言: 功能测试的OSD使用还是比较简单的,随便找个位置做个时间戳,背景还是黑色,且只能显示一行,很明显效果并不是那么理想,这里做一个升级,对海思区域叠加的配置以及osd窗口的创建等都在本文一并写 ...
- 海思3559万能平台搭建:OSD的自动反色
前言 OSD功能在之前两篇中已经满足了大部分的应用场景,为了进一步提升效率和自适应环境亮度和反色,这里介绍改进方法 效率提升 我们之前整体的流程框架是这样的:TTF初始化,打开字体,区域初始化 ...
- 海思3559万能平台:VGS的画线处理
前言 海思的OSD功能除了之前提到的第三方库,自己的VGS也可以做到一些简单的诸如画线之类的操作,这里介绍下假如类似识别时需要画矩形框的时候可以的一种做法 海思VGS简介 海思的VGS 是视频 ...
- 海思3559开发常识储备:相关名词全解
前言 接连啃了两个sample,还是觉得笼笼统统模模糊糊,没有达到想要的一目了然的程度,那就再整理整理资料,补些硬货吧 图像和像素格式 颜色: (1)颜色是主观还是客观存在? 颜色的本质是光 ...
最新文章
- linux 文件查找与文件中注释去除
- linux中rm删除的文件是否可以恢复,Linux下用rm删除的文件的恢复方法
- Graph Embedding学习笔记(3):Graph Convolution Networks
- jquery 绑定动态元素
- tableau实战系列(三十五)-教你画个不一样的圆角条形图
- Android日期对话框NumberPicker的用法教程
- 数列分块入门 5(LibreOj-6281)
- 都是宝宝:北京孩子3成不玩电子游戏睡眠状况最好 江苏孩子起得最早
- c++内存优化:二级间接索引模式内存池
- python生成10个随机数字符串_python生成随机数、随机字符串
- ppp协议 服务器,详解PPP及PPPoE协议
- c语言图像的简单叠加,第10章C语言图形编程.ppt
- PFC颗粒6.0软件模拟---工程案例
- 关于VGA接口连接1080p显示器模糊多半是线的问题
- 【微信支付】微信支付之 Native 支付
- Java第一天笔记01——jdk8的安装与环境变量的配置
- 微信SDK删除支付模块流程
- 电信aep平台是什么意思_亚马逊电商平台是什么意思?亚马逊注册技巧
- Xmind2021安装激活破解
- ReactOS研究班