欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

玩转Linux串口编程(RS485详解) - 1. 简介" 在Linux中,所有串口都被映射成TTY终端模式,因此进行串口编程时,关键在于找到并启用对应平台的TTY。以Nuclei平台的轩辕91030M芯片为例,在设备树配置文件中: ```markdown uart0: - compatible: "sifive,uart0" address: 0x10013000 interrupt: plic0 (2) clock: hfclk2 status: okay uart1: - compatible: "sifive,uart0" address: 0x10012000 interrupt: plic0 (3) clock: hfclk2 status: okay ``` 确保上述 UART 设备的 `status` 都设为 "okay" 后,在 "/dev/" 目录下会出现 ttySIF0 和 ttySIF1 两个串

最编程 2024-07-20 11:30:37
...
查看代码
static int io_rs485_to_send(void)
{
    int ret;
#if KERNEL_RS485_CTRL
	int fd;
	char *tl485_ctl = "/dev/tl485_ctl_pin";
	
	fd = open(tl485_ctl, O_RDWR);
	if(fd < 0)
    {
        printf("Open %s failed\n", tl485_ctl);
        close(fd);
		return -1;
    }
	
    ret = ioctl(fd, 1, 0);
    if(ret<0)
    {
        printf("tl485 set ctl to high failed!\r\n");
		close(fd);
        return -1;
    }

	close(fd);
    return 0;
#else
	if(devGpioSet(8, 1) < 0) /* 用户态驱动接口 */
		return -1;
	
	return 0;
#endif
}

static int io_rs485_to_recv(void)
{
    int ret;
#if KERNEL_RS485_CTRL
	int fd;
	char *tl485_ctl = "/dev/tl485_ctl_pin";
	
	fd=open(tl485_ctl, O_RDWR);
	if(fd < 0)
    {
        printf("Open %s failed\n", tl485_ctl);
        close(fd);
		exit(1);
    }
	
    ret = ioctl(fd, 0, 0);
    if(ret<0)
    {
    	close(fd);
        printf("tl485 set ctl to low failed!\r\n");
        return -1;
    }
	
    close(fd);
    return 0;
#else
	if(devGpioSet(8, 0) < 0) /* 用户态驱动接口 */
		return -1;

	return 0;
#endif
}

 3、串口初始化

查看代码
int devRS485InitPort(int com)
{
	int fd;
	int rv;
	int flags = 0;
	
    /*USART_RS485_DEV就是 /dev/ 下的tty设备,如本例中就是"/dev/ttySIF1" */
	fd = open(USART_RS485_DEV, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd < 0) {
        perror("Open error:\n");
		exit(1);
    }

	/* 恢复串口为阻塞状态 */
	if (fcntl(fd, F_SETFL, 0) < 0) {
		printf("fcntl failed.\n");
		return -1;
	}

    /* 测试该设备是否为tty设备 */
	if (isatty(fd) == 0) {
		printf("not tty device.\n");
		return -1;
	}

    rv = io_uart_set_opt(fd, 9600, 8, 'N', 1);
    if (rv < 0) {
        printf("Set uart faild\n");
		return -1;
    }

	return fd;
}

  4、串口发送函数

查看代码
STATUS devRS485SendDatas(int ttyfd, const char *buf, int len)
{
	int rv = 0;
	ssize_t wlen = 0;
	int sendflag = 0;
	
	rv = io_rs485_to_send();
	if(rv < 0)
	{
		printf("set 485 to send failed\n");
		return -1;
	}

	wlen = write(ttyfd, buf, len);
	if (wlen != len) {
        tcflush(ttyfd, TCOFLUSH);
		printf("write 485 failed\n");
        return -1;
    }
    
    /* write只是将数据从文件写到了发送缓存区,
     * tcdrain是等待发送缓存区发送完成,完成之前阻塞。
     * 从很多教程都说这个函数会在这里阻塞,但是我测试波形时发现:
       收发控制管脚总是在RS485数据线刚出波形就置为接收状态了
       !!!!!!!这里没搞懂,所以在后面加了延时。
     */
	rv = tcdrain(ttyfd); 
	if(rv < 0)
	{
		printf("Wite 485 send failed\n");
		return -1;
	}

	usleep(len * 1000); /*9600发送1BYTE数据大约1ms*/

	rv = io_rs485_to_recv();
	if(rv < 0)
	{
		printf("Set 485 recv failed\n");
		return -1;
	}

	return 0;
}

  5、接收函数

查看代码
int g485fd;

int open485(void)
{
    int retval;
    fd_set rfds;
    struct timeval tv;
    int nread;
    char gRcvBuf[256];
    int gRcvLen = 0;
    
    g485fd = devRS485InitPort(0);
    if (g485fd < 0) {
        printf("error: open console error.\r\n");
        close(g485fd);
        return ERROR;
    }
    
    // wait 2.5s ,select最后一个参数
    tv.tv_sec = 2;      //阻塞时间(秒)
    tv.tv_usec = 500;   //阻塞时间(毫秒)
	
    while (1)
    {	
        FD_ZERO(&rfds);
        FD_SET(g485fd, &rfds);

        retval = select(g485fd + 1 , &rfds, NULL, NULL, NULL); /*最后一个参数为NULL表示有数据前一直阻塞*/
        if (retval == -1) {
            perror("select()");
            break;
        }
        else if (retval) { // pan duan shi fou hai you shu ju
            if(!FD_ISSET(g485fd,&rfds)) /*判断是不是这个串口触发的*/
                continue;
            
            /*测试时发现这里每次调用read()只收到一个byte*/
            nread = read(g485fd, gRcvBuf + gRcvLen, 256); 
            gRcvLen += nread;

            /*缓存区越界处理,这里只是随便写的,需要修改*/
            if (gRcvLen >= sizeof(gRcvBuf))
                gRcvLen = 0; 
			
            //printf("gRcvLen = %d ", gRcvLen);
	
            if (gRcvBuf[gRcvLen - 2] == '\r' && gRcvBuf[gRcvLen-1] == '\n') {
                FD_ZERO(&rfds);
                FD_SET(g485fd, &rfds);
	
                retval = select(g485fd + 1 , &rfds, NULL, NULL, NULL);
                if (!retval) continue;// no datas, break
			}
            
            /*
             *包解析流程
             ......
             */

            //for (int i = 0; i < 19; i++) {
            //	printf("%02x ", gRcvBuf[i]); 
            //}

            //printf("gRcvLen=%d\r\n", gRcvLen);
        }
        else {
			continue;
        };
    }

  创建一个任务处理串口的接收即可。

 

参考:

  https://blog.****.net/weixin_45003868/article/details/130263090

  https://zhuanlan.zhihu.com/p/521283753?utm_id=0&wd=&eqid=b51aeee60009016800000003647efd5b