37 Linux 485收发切换延时导致接收数据错乱问题解决

485通讯数据帧结束标志位判断方法和485通讯主要函数 485通讯数据帧结束判断标志位如何判断: 在485通讯中,如何去判断一帧数据接收结束呢? 首先要知道的是,在每帧数据中,每个字节之间的数据发送间隔时间一定会小于每帧数据的之间的发送间隔时间。也就是说对于接收数据来说在字节之间每个数据帧之间的接收时间是不一样的,数据帧之间的接收时间要大于数据帧之中每个字节的接收时间的。那么就可以利用这两种数据的间隔时间的区别来进行区分是否一帧数据接收完毕。 在485通讯的接收过程中,所以看起来是一帧一帧数据接收的,但是实际情况还是每个字节每个字节来进行接收的,那么如果添加一个 阅读详情
AI摘要

本文深入探讨了Linux系统中485通讯的收发切换延时问题,详细分析了延时原因,并提出了一种内核模块解决方案,通过监控串口状态寄存器,确保数据发送完成后快速切换至接收状态,有效解决了数据错乱问题。

37.1 前言

这几天在对接Linux上位机与stm32开发板,通过485进行对接。Linux上位机端485是通过串口+485转换芯片+一个IO控制方向组成的。原本以为485这东西简简单单(之前有做过两块单片机的485驱动),没想到因为Linux系统调用延时导致485收发切换延时,导致Linux上位机始终不能正确接收stm32下位机回复的数据。

期间以为是硬件问题,然后一直找硬件的人帮忙查看485阻容匹配是否正确,后面硬件的确发现一些问题,并作了一些改进,但仍然未能解决收发数据错乱问题,没办法,拿示波器看了又看,最后发现原来Linux上位机这边485发送数据后没能快速切换为接收状态,而下位机却在切换之前回复了上位机,因此导致Linux 485接收数据乱码。

37.2 切换延时探析

从波形找出了问题所在,回归串口编程,如下图示。在发送数据前将485芯片控为发送状态,发送后等待串口数据发送完成,然后在将485芯片控为接收状态。如果发送完不调用tcdrain而直接控IO可能导致数据没有发送完成就中断了,因此必须要找个方式等待数据发送完成。直接sleep肯定不行,因为sleep函数本来就是不精准的,有时sleep少,有时sleep多,难搞。

 

         串口硬件发送数据应该很快不用那么25毫秒才把几个字节的数据发送出去啊!!什么问题呢?查阅资料是发现一个合理的解释是:Linux对硬件实时性高,对于用户请求的实时性较低。

这个得说说Linux工作队列相关机制,对于硬件操作Linux处理的很及时,但是对于数据Linux可能将其交给系统的下半部的内核线程去处理,这就可能导致用户的系统调用存在一定的延时。为此,如果想要解决这种影响,那么就换一种方式去等待数据发送完成而切换485的收发状态。

37.3 Linux 485收发切换延时解决方案选择

关于Linux485切换延时问题,成熟的解决方案有以下几种:

第一、硬件自控收发,要重新设计硬件不太现实,特别是对于已经发货很多的产品;

第二、更改串口驱动,使其增加对485 的支持,工作量太大,需要对tty子系统比较熟悉,难度有点大;

第三、编写Linux驱动模块,监控串口状态寄存器,有用户层触发监控再由内核线程自行判断控制方向,该方式实测可行,较第二更为简单。

本篇亦是基于第三种方式,解决Linux485切换延时的问题的。

 

37.4 Linux 485切换方案实现

目标:编写一个内核模块,模块内开一个内核线程(平时处于休眠状态),结合高精度定时器及条件变量查询串口状态寄存器的发送位标记,直到寄存器发送状态为置为空闲,此时将485切换为接收状态。该内核模块主要实现了iotcl函数,提供接口给用户层触发查询串口状态标志位情况,还有两个高精度内核定时器,用以精准延时或查询。

实现代码:

readStatus.h:

#ifndef _READSTATUS_H_
#define _READSTATUS_H_

struct stDataInfo
{
	int nOption;
};

#define READSTATUS_IOC_MAGIC 0XF157
#define READSTATUS_IOCSET   _IOW(READSTATUS_IOC_MAGIC, 0, struct stDataInfo)
#define READSTATUS_IOCGET	_IOR(READSTATUS_IOC_MAGIC, 1, struct stDataInfo)

#endif

readStatus.c:

/*****************************************************************
* 包含头文件
******************************************************************/
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/hrtimer.h>
#include <linux/jiffies.h>
#include <linux/device.h>	//class_createnikey
#include <linux/sched.h>	//wake_up_process()
#include <linux/kthread.h>	//kthread_create()、kthread_run()
#include <asm/uaccess.h>
#include <linux/err.h> 
#include <linux/time.h>
#include <linux/delay.h> //for msleep

#include "readStatus.h"

/*****************************************************************
* 宏定义(仅在当前C文件使用的宏定义写在当前C文件中,否则需写在H文件中)
******************************************************************/
#define I_DEVICE_NAME			"iTestDev"

#define I_UART0_REGFR			0x120a0018 //Uart状态寄存器地址

#define I_GPIO0_CTLREG			0x112F0034 //控制寄存器地址
#define I_GPIO0_DIRREG			0x120D0400 //方向寄存器地址
#define I_GPIO0_DATAREG			0x120D03FC //数据寄存器地址

/*****************************************************************
* 结构定义(仅在当前C文件使用的结构体写在当前C文件中,否则需写在H文件中)
******************************************************************/
struct _stMngrInfo{
	int major;
	struct class *iTest_class;
	struct device *iTest_device;

	unsigned int *pUartFR;				/*串口状态寄存器指针*/
	unsigned int *p485CtlReg;			/*GPIO0_6控制寄存器指针*/
	unsigned int *p485DirReg;			/*GPIO0_6方向寄存器指针*/
	unsigned int *p485DataReg;			/*GPIO0_6数据寄存器指针*/

	int nKPthreadWorkFlag;				/*内核线程运行标记*/
	struct task_struct *pkThreadTask; /*内核线程指针*/
	
	int nQueWaiting;					/*等待队列条件*/	
	wait_queue_head_t iwaitQue; 		/*等待队列*/  
	struct hrtimer ihrtimer;			/*高精度定时器*/	

	int SSwitchDelayFlag;				/*发送后接收延时*/
	struct hrtimer Sihrtimer;			/*高精度定时器*/	
};

/*****************************************************************
* 全局变量定义
******************************************************************/
struct _stMngrInfo gstMngrInfo;
static DEFINE_MUTEX(gReadStatusMutex); /*互斥锁*/

/*****************************************************************
* 静态变量定义
******************************************************************/

/*****************************************************************
* 外部变量声明(如果全局变量没有在其它的H文件声明,引用时需在此处声明,
*如果已在其它H文件声明,则只需包含此H文件即可)
******************************************************************/


static int iTest_HwInit(struct _stMngrInfo *pgstMngrInfo)
{
	pgstMngrInfo->pUartFR = ioremap(I_UART0_REGFR, 4);
	if(pgstMngrInfo->pUartFR==NULL){
		printk(KERN_EMERG "ioremap %x faild\n",I_UART0_REGFR);
		return -1;
	}

	pgstMngrInfo->p485CtlReg = ioremap(I_GPIO0_CTLREG, 4);
	if(pgstMngrInfo->p485CtlReg==NULL){
		printk(KERN_EMERG "ioremap %x faild\n",I_GPIO0_CTLREG);
		goto iExit1;
	}
	
	pgstMngrInfo->p485DirReg = ioremap(I_GPIO0_DIRREG, 4);
	if(pgstMngrInfo->p485DirReg==NULL){
		printk(KERN_EMERG "ioremap %x faild\n",I_GPIO0_DIRREG);
		goto iExit2;
	}
	
	pgstMngrInfo->p485DataReg = ioremap(I_GPIO0_DATAREG, 4);
	if(pgstMngrInfo->p485DataReg==NULL){
		printk(KERN_EMERG "ioremap %x faild\n",I_GPIO0_DATAREG);
		goto iExit3;
	}
	
	printk(KERN_EMERG "iTest_HwInit OK.\n");
	return 0;

iExit3:
	iounmap(pgstMngrInfo->p485DataReg);
iExit2:
	iounmap(pgstMngrInfo->p485CtlReg);
iExit1:
	iounmap(pgstMngrInfo->pUartFR);
	
	printk(KERN_EMERG "iTest_HwInit Fail.\n");
	return -1;
}


static unsigned int iTest_Set485Dir(int nOption)
{
	struct _stMngrInfo *pgstMngrInfo = &gstMngrInfo;
	unsigned int nReg485Value = 0;

	//选为GPIO6模式
	nReg485Value = readl(pgstMngrInfo->p485CtlReg);
	nReg485Value &= ~0xF;
	writel(nReg485Value,pgstMngrInfo->p485CtlReg);

	//设为输出
	nReg485Value = readb(pgstMngrInfo->p485DirReg);
	nReg485Value |= (1<<6);
	writeb((char)nReg485Value,pgstMngrInfo->p485DirReg);

	//设置输出电平
	if(nOption==1)
	{
		nReg485Value = readb(pgstMngrInfo->p485DataReg);
		nReg485Value |= (1<<6);
		writeb((char)nReg485Value,pgstMngrInfo->p485DataReg);
	}
	else
	{
		nReg485Value = readb(pgstMngrInfo->p485DataReg);
		nReg485Value &= ~(1<<6);
		writeb((char)nReg485Value,pgstMngrInfo->p485DataReg);
	}
	
	return 0;
}

static int iTest_wakeUpKThread(void)
{
	struct _stMngrInfo *pgstMngrInfo = &gstMngrInfo;

	mutex_lock(&gReadStatusMutex);
	pgstMngrInfo->nQueWaiting = 1;
	mutex_unlock(&gReadStatusMutex);

	wake_up(&pgstMngrInfo->iwaitQue);
	return 0;
}

static unsigned short iTest_scanUARTFRBusyBit(struct _stMngrInfo *pgstMngrInfo)
{
	unsigned short sRegUartValue = 0;
	sRegUartValue = readw(pgstMngrInfo->pUartFR);

	sRegUartValue = (sRegUartValue &(1<<3));

	return sRegUartValue;
}

static enum hrtimer_restart iTest_ShrtimerHander(struct hrtimer *timer)  
{
	iTest_Set485Dir(0);//设为接收状态
	return HRTIMER_NORESTART;
}

static enum hrtimer_restart iTest_hrtimerHander(struct hrtimer *timer)  
{
	mutex_lock(&gReadStatusMutex);
	gstMngrInfo.nQueWaiting = 1;
	mutex_unlock(&gReadStatusMutex);
	
	wake_up(&gstMngrInfo.iwaitQue);
	return HRTIMER_NORESTART;
}

static int iTest_kthread(void *arg)
{
	int nRet = 0;
	struct _stMngrInfo *pgstMngrInfo = (struct _stMngrInfo *)arg;
	
	while(!kthread_should_stop()) 
	{
		/*添加这个是为了能退出线程*/
		if(pgstMngrInfo->nKPthreadWorkFlag == 0)
		{
			msleep(1000);
			continue;
		}

		/*等待唤醒,然后查看串口发送位状态*/
		wait_event(pgstMngrInfo->iwaitQue,pgstMngrInfo->nQueWaiting);		
		mutex_lock(&gReadStatusMutex);
		pgstMngrInfo->nQueWaiting = 0;
		mutex_unlock(&gReadStatusMutex);
		
		nRet = iTest_scanUARTFRBusyBit(pgstMngrInfo);
		if(nRet==0)
		{
			hrtimer_start(&pgstMngrInfo->Sihrtimer,ktime_set(0,5000*1000),HRTIMER_MODE_REL);  
		}
		else
		{
			/*启动高精度定时器,1ms后唤醒线程*/
			hrtimer_start(&pgstMngrInfo->ihrtimer,ktime_set(0,1000*1000),HRTIMER_MODE_REL);  
		}
	}
	
	printk(KERN_EMERG "iTest_kthread exit OK!\n");
	return 0;
}

static long iTest_unioctl(struct file *pfilp, unsigned int cmd, unsigned long arg)
{
	int nRet = 0;
	struct stDataInfo iDataInfo;

	if (copy_from_user(&iDataInfo, (const void *)arg, sizeof(struct stDataInfo))) {
		nRet = -EFAULT;
		goto iExit;
	}

	switch (cmd) {
		case READSTATUS_IOCGET: //get 内核态数据
			{
				if (copy_to_user((void *)arg, &iDataInfo, sizeof(struct stDataInfo))) {
					nRet = -EFAULT;
				}
			}
			break;
		case READSTATUS_IOCSET:
			{
				if(iDataInfo.nOption == 1)
				{
					//设为发送
					iTest_Set485Dir(1);
				}
				else if(iDataInfo.nOption == 0)
				{
					//唤醒线程
					iTest_wakeUpKThread();
				}
			}
			break;
		default:
			{
				pr_err("wrong ioctl cmd[%d]\n", cmd);
				nRet = -EINVAL;
			}
		break;
	}

iExit:

	return nRet;
}

static const struct file_operations iTest_fops = {
	.owner	= THIS_MODULE,
	.unlocked_ioctl = iTest_unioctl,
};

static int __init iTest_Init(void)
{
	int nRet = -1;
	memset(&gstMngrInfo,0,sizeof(gstMngrInfo));
	
	/* 主设备号设置为0表示由系统自动分配主设备号 */
	gstMngrInfo.major = register_chrdev(0, I_DEVICE_NAME, &iTest_fops);

	/* 创建iTest_class类 */
	gstMngrInfo.iTest_class = class_create(THIS_MODULE, "iTestClass");

	/* 在iTest_class类下创建设备,并在/dev/目录下创建iTestDevice节点*/
	gstMngrInfo.iTest_device = device_create(gstMngrInfo.iTest_class, NULL, MKDEV(gstMngrInfo.major, 0), NULL, "iTestDevice");

	nRet = iTest_HwInit(&gstMngrInfo);
	if(nRet<0)
		return -1;

	/*高精度定时器初始化*/
	 /*初始化等待队列*/  
	gstMngrInfo.nQueWaiting = 0;  
	init_waitqueue_head(&gstMngrInfo.iwaitQue);  

	/*初始化高精度定时器*/  
	hrtimer_init(&gstMngrInfo.ihrtimer,CLOCK_MONOTONIC,HRTIMER_MODE_REL);
	gstMngrInfo.ihrtimer.function = iTest_hrtimerHander;    /*唤醒查询线程回调函数*/

	hrtimer_init(&gstMngrInfo.Sihrtimer,CLOCK_MONOTONIC,HRTIMER_MODE_REL);
	gstMngrInfo.Sihrtimer.function = iTest_ShrtimerHander;    /*切换接收回调*/

	/*创建内核线程*/
	gstMngrInfo.nKPthreadWorkFlag = 1;
	gstMngrInfo.pkThreadTask = kthread_create(iTest_kthread,&gstMngrInfo,"iTest_task");
	if (IS_ERR(gstMngrInfo.pkThreadTask))
	{
		nRet = PTR_ERR(gstMngrInfo.pkThreadTask);
		printk(KERN_EMERG "kthread_create fail.\n");
		return nRet;
	}
	//开始线程
	wake_up_process(gstMngrInfo.pkThreadTask);

	printk(KERN_EMERG "iTest_Init OK.\n");
	return nRet;
}


static void __exit iTest_Exit(void)
{

	if (gstMngrInfo.pkThreadTask!=NULL)
	{
		gstMngrInfo.nKPthreadWorkFlag = 0;
		iTest_wakeUpKThread();
		kthread_stop(gstMngrInfo.pkThreadTask);
		gstMngrInfo.pkThreadTask = NULL;
	}
	
	hrtimer_cancel(&gstMngrInfo.ihrtimer);
	hrtimer_cancel(&gstMngrInfo.Sihrtimer);

	iounmap(gstMngrInfo.p485DataReg);
	iounmap(gstMngrInfo.p485CtlReg);
	iounmap(gstMngrInfo.pUartFR);
	
	unregister_chrdev(gstMngrInfo.major, I_DEVICE_NAME);
	device_unregister(gstMngrInfo.iTest_device);
	class_destroy(gstMngrInfo.iTest_class);	
	
	printk(KERN_EMERG "iTest_Exit OK.\n");
}
 
module_init(iTest_Init);
module_exit(iTest_Exit);

MODULE_AUTHOR("ljc");
MODULE_LICENSE("GPL");
 

linux下485通信调试记录 linux下可以安装并使用下述串口调试工具进行串口测试 阅读详情

相关推荐

linux上面使用ftdi芯片模拟485串口详解

linux上面使用ftdi芯片模拟485串口详解 一、设备信息 cpu:AR9344 switch:QCA8337 ftdi:FT4232HL 内核版本:Linux version 2.6.31--LSDK-9.2.0_U11.14 ps:我这里使用FT4232HL芯片模拟485串口的功能 FT4232HL芯片功能介绍,这些都能在ftdi官网找到相关手册 1.单芯片USB到四串行端口与各种配置。 2.整个USB协议由芯片处理,USB不需特定固件...

zengyaowu1988的博客 4890

485通信要点

1. 双绞线 485物理线缆跟CAN一样,使用双绞线,目的是抑制共模干扰 2. 485芯片 485可以理解为一个uart+方向控制,485属于半双工,同时只能发送或者接收,因此需要一根引脚控制数据方向,这跟引脚连接到485芯片,由芯片内部电路实现具体控制。 3. 485收发切换 注意,由于485芯片各个厂家的差异,响应速度也会不同,在通信时要根据物理层硬件特性和链路层协议不一样而合理地,实现收法切换延时。比如刚发送完不能理解切换到接收状态,要延时基本...

fengel_cs的专栏 4848

WiFi温湿度传感器与RS-485传感器选型实战指南

温湿度传感器是工业物联网与智能环境监测的基础感知单元,其通信方式直接决定系统可靠性、部署成本与运维寿命。WiFi方案依托TCP/IP协议栈实现云端直连,优势在于免布线与快速组网,但受信道干扰、天线效率、电源纹波及NTP依赖等物理层限制;RS-485方案基于差分信号与主从总线架构,以确定性延迟、强抗扰性和低功耗见长,却对布线规范、终端匹配和拓扑设计极为敏感。二者并非性能高低之分,而是适用边界的本质差异:WiFi适合小规模、网络可控、运维资源充足的场景;485则胜任高电磁干扰、长距离、多节点及严苛实时性要求的工

weixin_33898233的博客 395

RS485基本知识

RS-485

asd45694的博客 2万+

嵌入式Linux下Modbus RTU工业通信实战:从串口配置到传感器数据落地

Modbus RTU是工业现场最主流的串行通信协议,其本质是在RS-485物理层上运行的轻量级主从式数据链路协议。它依赖精确的字符时间间隔(如3.5字符静默期)、CRC16校验(LSB先行)和确定性状态机解析,以应对电磁干扰、晶振偏差与长距离信号衰减等严苛环境。在嵌入式Linux平台实现时,关键不在协议本身,而在于内核串口驱动适配(如ttyS0/ttymxc0设备名识别)、termios底层参数调优(VMIN=0/VTIME=1非阻塞读)、硬件级RS-485收发控制(Auto DE芯片或LSR寄存器轮询),

weixin_34221112的博客 424

工业串口扫描工具ScomKit:协议级故障定位与多协议调试实战

工业串口通信是现场设备联网的基础环节,涉及RS-232/485物理层适配、Modbus TCP/RTU等协议解析及电磁干扰应对等核心挑战。其原理涵盖波特率自适应、CRC校验、功能码语义识别与设备行为建模,技术价值在于将传统‘能通’升级为‘懂设备、可诊断、能闭环’。典型应用场景包括PLC集成调试、能源监控系统交付、水处理中控联调及国产IO模块健壮性测试。ScomKit作为面向工业协议现场交付的扫描工具,深度融合物理层探测、协议帧模板库与语义行为图谱,特别强化对Modbus TCP/RTU和DL/T645等主流

weixin_34183910的博客 209

PJ85718DM+ATmega324PA工业温度监测系统设计

数字温度传感器是工业物联网(IIoT)边缘感知的核心器件,其精度、抗干扰性与协议兼容性直接决定系统可靠性。PJ85718DM凭借±0.1℃出厂校准、可配置FIR数字滤波和单总线CRC校验能力,显著优于DS18B20等通用型号;ATmega324PA则以-40℃~+105℃工业级宽温运行、双时钟RTC、硬件EEPROM写保护等特性,支撑断电数据不丢、长期免维护的严苛需求。二者协同构成面向HVAC、楼宇自控与设备状态监测的高鲁棒性最小可行单元,天然适配Modbus RTU工业通信标准,并为边缘智能(如温度趋势预

weixin_34174422的博客 210

PLC对接扫码支付:串口与Modbus RTU实战指南

PLC对接扫码支付本质上是工业控制系统与消费级终端的协议桥接问题,核心在于理解串口通信基础、Modbus RTU帧结构及数据映射原理。它要求开发者掌握RS485物理层接线规范、ASCII到BCD的链路层解析逻辑,并将支付状态转化为确定性控制信号。该技术广泛应用于智能售货机、无人仓储分拣线和定制化包装设备等场景,尤其适合需在严苛时序下实现扫码触发动作的电气工程师与设备制造商。文中重点剖析了串口调试、Modbus RTU校验、台达PLC 485从站配置等高频实操难点,为工业现场快速落地提供可复用的技术路径。

aofan9566的博客 385

远程串口透传方案:解决RS232/RS485超距通信与工业抗扰难题

串口通信(RS232/RS485/RS422)是工业设备互联的基础物理层协议,其本质是基于电平差或差分信号的异步串行传输,受限于信号衰减、地电位差、电磁干扰及半双工时序等固有特性,实际可靠传输距离远低于理论值。传统中继、光端机或通用串口服务器方案常因电气适配缺失、DE/RE控制失准、TCP与无连接串口协议语义错配等问题导致通讯不稳定。霜蝉方案通过硬件级RS485自动收发时序控制、SLP链路层心跳与应用层超时机制、零拷贝缓冲架构,实现字节级透明透传下的高可靠远程连接。适用于PLC远程监控、分布式传感器组网、嵌

weixin_34319111的博客 368

stm32控制舵机_使用URT-1控制飞特舵机上手教程(SM40BL舵机为例)

使用URT-1控制飞特舵机上手教程(SM40BL舵机为例):1,参阅URT-1使用说明.pdf2, 材料清单:① URT-1驱动板② 连接驱动板与电脑的MINI USB 连接线③ 给舵机供电的电源④ 舵机与舵机连接线⑤ 杜邦线(可选)3,将URT-1驱动板与电脑连接4,安装驱动,参阅文件CH340驱动,检查设备管理器串口号。5,舵机连接URT-1驱动板,驱动板接电源,参考下图:6,打开FD软件进行...

weixin_39564510的博客 3432

Ruoyi-Cloud集成Seata2.0实现分布式事务

在网络上,关于Spring Cloud和Ruoyi-Cloud整合Seata的教程有很多,针对不同版本的 Seata 也有不少相关教程。事实上,无论是 Spring Cloud 还是 Ruoyi-Cloud,其整合 Seata 的原理基本相同。这篇博文将详细介绍如何在本地开发环境下,在中实现Seata 2.0的整合,希望能为大家在分布式项目中实现 Seata 的整合提供实用的帮助,提供一个清晰的操作思路。

SR003的博客 2246

K230串口通信深度实战:从硬件电平到YbUart工业级调试

串口通信是嵌入式系统最基础也最关键的数据通路,其本质是硬件时序、电平匹配、波特率精度与驱动机制的协同结果。理解UART底层原理,才能应对RISC-V平台如K230在工业场景中面临的高实时性、低延迟、抗干扰等严苛要求。本文聚焦K230双核RISC-V架构下的UART控制器特性,深入解析TTL电平适配、寄存器级波特率误差计算、DMA+环形缓冲协同机制,并对比pyserial与原生YbUart库在中断响应、CPU占用、产线稳定性等方面的工程差异。内容覆盖硬件连接陷阱、Linux驱动层调试、Python应用层防错设

weixin_34008933的博客 359

pi-star图形界面安装指南

pi-star图形界面安装指南,MMDVM热点安装,树莓派3B+热点

blueisman的专栏 1909

TTL串口舵机原理与实战:电平兼容、总线控制与STM32/Arduino驱动

TTL串口舵机是一种基于标准UART通信、内置运动控制MCU的智能执行单元,其核心在于TTL电平(0V/3.3V或5V)与主流单片机原生匹配,无需电平转换即可直连;串口协议支持多节点寻址与状态反馈,显著提升多关节系统可靠性。相比传统PWM舵机,它将PID运算、加速度规划、堵转保护等逻辑下沉至本地,释放主控资源;相比CAN或RS485方案,又具备布线简洁、成本低、开发门槛低的技术优势。广泛应用于机械臂、教育机器人、云台稳定器及医疗康复设备等需高精度、可反馈、易扩展的嵌入式场景。本文聚焦TTL电平兼容性、串行总

weixin_34224941的博客 251

手把手教你用ROS和Gazebo搭建Unitree A1机器狗仿真环境(含URDF/Xacro详解)

本文详细介绍了如何使用ROS和Gazebo搭建Unitree A1机器狗的仿真环境,涵盖URDF/Xacro文件编写、Gazebo物理引擎集成及ROS控制实现。通过实战指南,帮助开发者掌握四足机器人仿真技术,提升开发效率。

bush的博客 278

【DaVinci Configurator专题】-2-将CAN 2.0或CANFD Matrix的Arxml/DBC文件导入到CFG

共9页 目录 1 打开DaVinci Configurator,导入Arxml文件 2 解决导入Arxml文件后可能会出现的一些错误 3 验证CFG的配置 4 根据CFG的配置生成代码 5 结尾 <--返回「实战篇」博文-总目录--> <--返回「Autosar从入门到精通-实战篇」专栏主页--> 关键词: Open the Input Files Assistant Complete update process Update ECU configurati

「汽车电子助手」的博客 6125

全志T153工业网关双网口与RS485抗干扰设计实战

工业通信接口设计的核心在于可靠性工程,而非单纯功能实现。RS485和以太网接口作为工业现场最基础的物理层通道,其抗干扰能力直接决定系统可用性。原理上,干扰本质是共模噪声通过接地路径、电源耦合与信号链串扰形成闭环;技术价值体现在宽温运行、EMC合规(IEC61000-4-3/4-5)、故障自愈等硬指标;典型应用场景包括PLC联网、智能仪表集抄、边缘网关等对连续运行要求严苛的领域。本文聚焦全志T153平台,深入解析双MAC直驱PHY的冗余架构、RS485信号链全程隔离策略及PCB级抗扰布局方法,覆盖从芯片电气特

weixin_34294649的博客 315

Python 爬虫实战:付费 IP 代理使用技巧与爬虫稳定性优化

付费IP代理实战指南:突破反爬与优化稳定性 本文系统讲解付费IP代理在爬虫项目中的高效应用方法。首先对比分析静态代理、动态代理和隧道代理的优缺点及适用场景,推荐高并发项目优先选择隧道代理。然后详细演示了主流代理服务商的集成方法,包括代理池的自动更新和健康检测机制。核心部分重点介绍了爬虫稳定性优化策略,如智能重试、频率控制和请求间隔优化等关键技术。最后通过压力测试验证优化效果,并提供成本控制方案,包括代理复用、错峰爬取等实用技巧。通过本教程,开发者可有效解决高并发爬虫中的IP封禁问题,显著提升系统稳定性和抗风

2503_91057718的博客 2450

Android车载串口开发:UART协议、RS485硬件适配与SELinux权限全链路解析

UART作为基础异步串行通信协议,广泛应用于车载电子系统中传感器、控制器与ADAS模块的数据交互;其实际落地依赖RS232/RS485等电气标准的正确选型与硬件设计,并需穿透Linux内核驱动、Android HAL抽象层、SELinux安全策略及用户空间权限管控四层技术栈。理解UART帧结构、波特率计算原理与差分抗扰机制,是保障车载RS485长距多节点稳定通信的前提;而解决/dev/ttySx不可见、USB串口枚举失败、乱码丢包等典型问题,本质在于打通从设备树配置、内核编译选项到init.rc权限设置的完

weixin_34055787的博客 224

endNote X9 增加 / 删除参考文献 文献编号自动更新

EndNote X9 插入参考文献常见问题总结。增删参考文献 文献编号自动更新

程序员,他们想的是什么?他们想的永远都是技术,他们崇尚的也永远都是技术。 7496

避坑指南:新手爬百度图片3个致命错,改完批量保存本地(亲测有效)

新手爬百度图片的核心是“绕开静态HTML,抓准API接口+处理反爬+提取原图”,改完本文提到的3个核心错,就能稳定批量保存高清图片。这套思路可复用在其他图片网站(如必应图片、360图片),只需修改API接口和参数即可。如果想进一步优化,可添加代理IP池、多线程下载(新手不建议,容易触发反爬)、图片去重等功能!

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1587

RS232/RS422/RS485与Modbus真实关系:电气层与协议层解耦实战指南

串口通信是工业自动化数据交互的基础,而RS232、RS422、RS485作为主流物理层标准,并非版本迭代,而是面向不同距离、抗扰性与拓扑需求的独立电气规范;Modbus则是一套应用层协议族,包含RTU、ASCII和TCP三种互不兼容的帧格式。理解其分层本质——物理层负责信号传输(电压、差分、终端电阻),协议层定义数据语义(功能码、寄存器、CRC校验)——才能避免‘接线即通’的误判。本文聚焦现场高频问题:RS485总线无响应、Modbus RTU CRC校验失败、自动收发时序丢帧等,结合示波器波形、串口原始帧

weixin_33951761的博客 373

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

STM32F407实现MODBUS RTU从站实战指南

MODBUS RTU是一种轻量级、无状态的工业串行通信契约,依赖确定性帧间隔与寄存器内存映射实现可靠交互。其核心原理在于二进制编码、CRC16校验及RS485半双工时序约束,技术价值体现在资源占用低、实时性强、兼容裸机与RTOS环境。典型应用场景包括PLC数据采集、温控模块协议对接及蓝桥杯嵌入式竞赛开发。本文聚焦STM32F407平台,结合SSCOM串口助手、Modbus Poll和逻辑分析仪,详解RTU帧解析、DE/RE收发切换、CRC16查表实现及超时帧接收机制,直击‘Response timeout’

weixin_33991418的博客 224

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

手写UART RTL:嵌入式与FPGA工程师的时序入门实战

UART(通用异步收发器)是嵌入式系统和FPGA开发中最基础、最典型的异步通信接口,其本质是无共享时钟下的时序契约实现。理解起始位同步、波特率分频、采样点定位与跨时钟域处理,直接关联数字电路中的时序本质与硬件可综合思维。它虽协议简单,却完整涵盖边沿检测、亚稳态防护、FIFO设计、状态机健壮性等关键工程能力,成为验证Verilog实践能力的黄金标尺。在工业控制、汽车电子及SoC调试中,稳定UART IP是软硬协同落地的前提。本文聚焦RTL级实现,覆盖115200bps高可靠设计、FIFO深度规划、中断机制与上

weixin_34413103的博客 348

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Modbus RTU稳定通信实战:物理层、时序与地址映射避坑指南

Modbus RTU作为工业自动化最基础的串行通信协议,其‘能通’不等于‘稳通’——真正制约可靠性的核心在于物理层链路质量、帧间隔时序精度和跨平台寄存器地址映射一致性。RS485终端电阻匹配、DB9引脚极性定义、总线拓扑结构共同构成信号完整性基础;7.5ms最小帧间隔实为电磁波传播延迟的工程折中,需结合从站响应时间动态调整;而‘40001’等地址在西门子、汇川、三菱等设备中的内存偏移差异,本质是Modbus地址空间与厂商寄存器映射规则的错位。本文聚焦现场高频故障根因,覆盖接线验证、示波器眼图判读、串口助手精

weixin_34416754的博客 239

Modbus数据模拟:工控调试的前置必修课

Modbus是一种广泛应用于工业自动化领域的通用通信协议,其核心在于主从架构下的寄存器读写机制;通过软件或轻量硬件模拟Modbus从站行为,可实现对线圈、保持寄存器、输入寄存器等数据的可控响应,从而解耦上位机开发与下位机部署节奏;该技术显著提升SCADA系统联调效率、HMI逻辑验证可靠性及产线故障复现能力,尤其适用于PLC未就绪、设备停机维护、新员工实训及招投标演示等典型场景;modbus和数据模拟已成为工控工程师快速交付与精准排障的关键能力。

weixin_33882452的博客 152
上一篇: 35 Linux 485串口编程及接收数据错乱解决
下一篇: 38 如何根据地址获取函数名
Chasing_Chasing
博客等级 码龄10年 233粉丝 87原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值