海思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也会立即返回。
文章来源CSDN,如有侵权联系删除:https://blog.csdn.net/qq_42330920/article/details/127075475?spm=1001.2014.3001.5502
- 分享
- 举报
-
浏览量:1073次2024-01-04 17:47:00
-
浏览量:411次2023-09-11 11:42:09
-
浏览量:1682次2023-10-12 16:48:04
-
浏览量:1131次2023-07-26 13:44:05
-
浏览量:1621次2020-08-10 19:19:56
-
浏览量:910次2024-01-05 10:33:11
-
浏览量:1448次2019-06-13 11:39:09
-
浏览量:3369次2018-03-28 16:56:08
-
浏览量:6691次2022-02-19 16:25:21
-
浏览量:5672次2020-08-26 14:15:06
-
浏览量:3988次2021-12-03 16:40:57
-
浏览量:2858次2020-07-31 16:50:14
-
浏览量:1114次2024-01-06 10:33:06
-
浏览量:4267次2019-12-16 13:54:11
-
浏览量:3118次2020-05-07 19:02:31
-
浏览量:3244次2019-02-14 17:57:04
-
浏览量:4446次2020-07-27 16:34:42
-
浏览量:3250次2020-08-10 19:30:45
-
浏览量:2172次2022-03-15 09:00:49
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
一休摸鱼
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明