STM32的CAN总线调试经验分享
相关文章
CAN总线简易入门教程
CAN总线显性电平和隐性电平详解
STM32的CAN总线调试经验分享
文章目录
- 相关文章
- 背景
- CAN总线
- CAN控制器
- CAN收发器
- 调试过程
- 硬件排查
- CAN分析仪
- 芯片CAN控制器调试
- 总结
背景
最近负责的一个项目用的主控芯片是STM32F407IGT6
,需要和几个电机控制器进行通讯,有很多参数需要进行监控。负责固件开发的同事一直搞不定一个问题。就是开启CAN
的接收中断,接收不到数据,问题卡了很久,一直无法闭环。
CAN总线
CAN总线是一种串行通信协议,用于在微控制器和其他设备之间传输数据。CAN总线通常用于汽车、工业自动化和机器人等领域。
CAN总线的硬件通常由以下几个部分组成:
- 控制器区域:包括CAN控制器和CAN收发器;
- 总线电缆:用于连接CAN总线上的所有设备;
- 终端电阻:用于终止总线,以减少反射和信号干扰;
- 外部电源:用于为CAN总线提供电源;
CAN总线的控制器区域通常包括CAN控制器和CAN收发器。
- CAN控制器负责处理CAN总线上的数据传输,包括数据发送和接收、错误检测和纠正等;
- CAN收发器则负责将CAN控制器的信号转换为总线上的电信号,并将总线上的电信号转换为CAN控制器可以理解的信号。
CAN控制器
主板上的芯片STM32F407IGT6
中带有两路的CAN控制器,分别为CAN1
和 CAN2
,具体如下图所示;
CAN收发器
主板上使用的是芯片SN65HVD230
,这是TI公司的一款性能强大且具体低功耗功能的CAN收发器,具体的典型应用电路如下所示;
调试过程
硬件排查
设备的调试过程中,首先要确保硬件链路上是否正常。最常见的方法就是直接用示波器进行检查。
具体如下所示;
- 检查CAN控制器和CAN收发器之间是否正常;
- 检查CAN收发器的差分信号是否正常,这里可能要了解一下CAN总线电平的显性电平和隐性电平的特点,以及CAN底层协议的细节,会比较复杂;
个人比较推荐使用上述步骤检查硬件链路是否存在问题,那如何对数据进行分析呢?当然可以对着示波器的波形一点一点进行分析,但是这样是很低效的,这里我建议使用CAN分析仪进行数据抓包,下面我们继续进行介绍。
CAN分析仪
至于数据传输是否正确,可以使用CAN盒进行数据监听,下面是我使用的一款CAN分析仪,如图;
将CAN分析仪的CAN_H
和CAN_L
分别并联到CAN收发器的CAN_H
和CAN_L
上,然后打开CAN分析仪厂家提供的PC软件,就可以对CAN总线的数据进行监听;
- 将CAN分析仪接入到CAN总线;
- 将CAN分析仪连接到电脑(这里是USB接口),需要配置相同的波特率;
- 打开CAN分析仪配套的PC软件,进行数据的收发;
进行到这里,我在项目中遇到的问题是,发送正常,但是STM32F407
无法接收到连续的数据,可以接收到一次数据,后面便无法再进入中断。
这时候,只能再芯片端进行Debug
了。
芯片CAN控制器调试
这里的代码用的HAL库,库版本相对来说比较老,是V1.7.10
版本的,如下图所示;
当时我把项目升级到最新的HAL库,发现CAN部分的驱动改动比较大,另外,下文都是基于V1.7.10
版本的HAL库。
CAN控制器的初始化代码如下所示;
void MX_CAN_Init(void)
{CAN_FilterConfTypeDef sFilterConfig;/*CAN单元初始化*/hCAN.Instance = CANx; /* CAN外设 */hCAN.pTxMsg = &TxMessage;hCAN.pRxMsg = &RxMessage;hCAN.Init.Prescaler = 6; /* BTR-BRP 波特率分频器 定义了时间单元的时间长度 42/(1+6+7)/6 = 500Kbps */hCAN.Init.Mode = CAN_MODE_NORMAL; /* 正常工作模式 */hCAN.Init.SJW = CAN_SJW_1TQ; /* BTR-SJW 重新同步跳跃宽度 1个时间单元 */hCAN.Init.BS1 = CAN_BS1_6TQ; /* BTR-TS1 时间段1 占用了6个时间单元 */hCAN.Init.BS2 = CAN_BS2_7TQ; /* BTR-TS1 时间段2 占用了7个时间单元 */hCAN.Init.TTCM = DISABLE; /* MCR-TTCM 关闭时间触发通信模式使能 */hCAN.Init.ABOM = ENABLE; /* MCR-ABOM 自动离线管理 */hCAN.Init.AWUM = ENABLE; /* MCR-AWUM 使用自动唤醒模式 */hCAN.Init.NART = DISABLE; /* MCR-NART 禁止报文自动重传 DISABLE-自动重传 */hCAN.Init.RFLM = DISABLE; /* MCR-RFLM 接收FIFO 锁定模式 DISABLE-溢出时新报文会覆盖原有报文 */hCAN.Init.TXFP = DISABLE; /* MCR-TXFP 发送FIFO优先级 DISABLE-优先级取决于报文标示符 */HAL_CAN_Init(&hCAN);/*CAN过滤器初始化*/sFilterConfig.FilterNumber = 0; /* 过滤器组0 */sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; /* 工作在标识符屏蔽位模式 */sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; /* 过滤器位宽为单个32位。*//* 使能报文标示符过滤器按照标示符的内容进行比对过滤,扩展ID不是如下的就抛弃掉,是的话,会存入FIFO0。 */sFilterConfig.FilterIdHigh = 0x0000; //(((uint32_t)0x1314<<3)&0xFFFF0000)>>16; /* 要过滤的ID高位 */sFilterConfig.FilterIdLow = 0x0000; //(((uint32_t)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; /* 要过滤的ID低位 */sFilterConfig.FilterMaskIdHigh = 0x0000; /* 过滤器高16位每位必须匹配 */sFilterConfig.FilterMaskIdLow = 0x0000; /* 过滤器低16位每位必须匹配 */sFilterConfig.FilterFIFOAssignment = 0; /* 过滤器被关联到FIFO 0 */sFilterConfig.FilterActivation = ENABLE; /* 使能过滤器 */ sFilterConfig.BankNumber = 14;HAL_CAN_ConfigFilter(&hCAN, &sFilterConfig);}
根据注释,可以大概看懂,另外再简单分析一下关键的几点;
- 波特率设置为 500Kbps;
- 对报文不进行过滤,可以接收任何扩展ID的数据;
虽然不进行任何过滤,但是还是无法接收到CAN回传的数据,无法进入的接收中断;
从STM32F407的编程手册里了解到,
不难发现,CAN1
的FIFO0
产生接收中断需要满足三个条件中的任意一个;
FMPIE0
置1
且FMP0
置1
;FIFO不为空会产生中断FFIE0
置1
且FULL
置1
;FIFO满,会产生中断FOVIE0
置1
且FOVR0
置1
;FIFO溢出,会产生中断
手册里是这样描述的,如下图所示;
使用仿真器对芯片进行调试,设置断点,发现FMPIE0
被清空了,具体如下图所示;
FMPIE0
这一位是FIFO0中有挂起的消息会产生中断的中断使能标志位;
所以到这里,问题有点明朗了,为什么无法进入中断?是中断使能位被清空了。
那么下面就是检查代码,看看是哪里把中断给disable
了。
继续调试,发现在ESR
寄存器中,TEC
的值一直增加,然后EWGF
被值1
了;具体如下所示;
TEC
和REC
分别是发送错误计数器和接收错误计数器;
如 CAN 协议所述,错误管理完全由硬件通过发送错误计数器( CAN_ESR 寄存器中的 TEC
值)和接收错误计数器( CAN_ESR 寄存器中的 REC 值)来处理,这两个计数器根据错误
状况进行递增或递减。有关 TEC 和 REC 管理的详细信息,请参见 CAN 标准。
两者均可由软件读取,用以确定网络的稳定性。此外, CAN 硬件还将在 CAN_ESR 寄存器中
提供当前错误状态的详细信息。通过 CAN_IER 寄存器( ERRIE 位等),软件可以非常灵活
地配置在检测到错误时生成的中断。
当TEC
大于96的时候,硬件会将EWGF
置1
(错误警告标志位);在代码中找到了相应的宏定义;这下问题越来越清晰了。
全文搜索这个宏定义,在HAL_CAN_IRQHandler
中找到了__HAL_CAN_DISABLE_IT(CAN_IT_FMP0)
,关闭了FIFO0
的消息挂起中断,
整体代码如下;
/*** @brief Handles CAN interrupt request * @param hcan: pointer to a CAN_HandleTypeDef structure that contains* the configuration information for the specified CAN.* @retval None*/
void HAL_CAN_IRQHandler(CAN_HandleTypeDef* hcan)
{uint32_t tmp1 = 0U, tmp2 = 0U, tmp3 = 0U;uint32_t errorcode = HAL_CAN_ERROR_NONE;/* Check Overrun flag for FIFO0 */tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_FOV0);tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_FOV0);if(tmp1 && tmp2){/* Set CAN error code to FOV0 error */errorcode |= HAL_CAN_ERROR_FOV0;/* Clear FIFO0 Overrun Flag */__HAL_CAN_CLEAR_FLAG(hcan, CAN_FLAG_FOV0);}/* Check Overrun flag for FIFO1 */tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_FOV1);tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_FOV1);if(tmp1 && tmp2){/* Set CAN error code to FOV1 error */errorcode |= HAL_CAN_ERROR_FOV1;/* Clear FIFO1 Overrun Flag */__HAL_CAN_CLEAR_FLAG(hcan, CAN_FLAG_FOV1);}/* Check End of transmission flag */if(__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_TME)){tmp1 = __HAL_CAN_TRANSMIT_STATUS(hcan, CAN_TXMAILBOX_0);tmp2 = __HAL_CAN_TRANSMIT_STATUS(hcan, CAN_TXMAILBOX_1);tmp3 = __HAL_CAN_TRANSMIT_STATUS(hcan, CAN_TXMAILBOX_2);if(tmp1 || tmp2 || tmp3) {tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK0);tmp2 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK1);tmp3 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK2);/* Check Transmit success */if(tmp1 || tmp2 || tmp3){/* Call transmit function */CAN_Transmit_IT(hcan);}else /* Transmit failure */{/* Set CAN error code to TXFAIL error */errorcode |= HAL_CAN_ERROR_TXFAIL;}/* Clear transmission status flags (RQCPx and TXOKx) */SET_BIT(hcan->Instance->TSR, CAN_TSR_RQCP0 | CAN_TSR_RQCP1 | CAN_TSR_RQCP2 | \CAN_FLAG_TXOK0 | CAN_FLAG_TXOK1 | CAN_FLAG_TXOK2);}}tmp1 = __HAL_CAN_MSG_PENDING(hcan, CAN_FIFO0);tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_FMP0);/* Check End of reception flag for FIFO0 */if((tmp1 != 0U) && tmp2){/* Call receive function */CAN_Receive_IT(hcan, CAN_FIFO0);}tmp1 = __HAL_CAN_MSG_PENDING(hcan, CAN_FIFO1);tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_FMP1);/* Check End of reception flag for FIFO1 */if((tmp1 != 0U) && tmp2){/* Call receive function */CAN_Receive_IT(hcan, CAN_FIFO1);}/* Set error code in handle */hcan->ErrorCode |= errorcode;tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_EWG);tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_EWG);tmp3 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_ERR);/* Check Error Warning Flag */if(tmp1 && tmp2 && tmp3){/* Set CAN error code to EWG error */hcan->ErrorCode |= HAL_CAN_ERROR_EWG;}tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_EPV);tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_EPV);tmp3 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_ERR); /* Check Error Passive Flag */if(tmp1 && tmp2 && tmp3){/* Set CAN error code to EPV error */hcan->ErrorCode |= HAL_CAN_ERROR_EPV;}tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_BOF);tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_BOF);tmp3 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_ERR); /* Check Bus-Off Flag */if(tmp1 && tmp2 && tmp3){/* Set CAN error code to BOF error */hcan->ErrorCode |= HAL_CAN_ERROR_BOF;}tmp1 = HAL_IS_BIT_CLR(hcan->Instance->ESR, CAN_ESR_LEC);tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_LEC);tmp3 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_ERR);/* Check Last error code Flag */if((!tmp1) && tmp2 && tmp3){tmp1 = (hcan->Instance->ESR) & CAN_ESR_LEC;switch(tmp1){case(CAN_ESR_LEC_0):/* Set CAN error code to STF error */hcan->ErrorCode |= HAL_CAN_ERROR_STF;break;case(CAN_ESR_LEC_1):/* Set CAN error code to FOR error */hcan->ErrorCode |= HAL_CAN_ERROR_FOR;break;case(CAN_ESR_LEC_1 | CAN_ESR_LEC_0):/* Set CAN error code to ACK error */hcan->ErrorCode |= HAL_CAN_ERROR_ACK;break;case(CAN_ESR_LEC_2):/* Set CAN error code to BR error */hcan->ErrorCode |= HAL_CAN_ERROR_BR;break;case(CAN_ESR_LEC_2 | CAN_ESR_LEC_0):/* Set CAN error code to BD error */hcan->ErrorCode |= HAL_CAN_ERROR_BD;break;case(CAN_ESR_LEC_2 | CAN_ESR_LEC_1):/* Set CAN error code to CRC error */hcan->ErrorCode |= HAL_CAN_ERROR_CRC;break;default:break;}/* Clear Last error code Flag */ hcan->Instance->ESR &= ~(CAN_ESR_LEC);}/* Call the Error call Back in case of Errors */if(hcan->ErrorCode != HAL_CAN_ERROR_NONE){/* Clear ERRI Flag */ hcan->Instance->MSR = CAN_MSR_ERRI; /* Set the CAN state ready to be able to start again the process */hcan->State = HAL_CAN_STATE_READY;/* Disable interrupts: *//* - Disable Error warning Interrupt *//* - Disable Error passive Interrupt *//* - Disable Bus-off Interrupt *//* - Disable Last error code Interrupt *//* - Disable Error Interrupt *//* - Disable FIFO 0 message pending Interrupt *//* - Disable FIFO 0 Overrun Interrupt *//* - Disable FIFO 1 message pending Interrupt *//* - Disable FIFO 1 Overrun Interrupt *//* - Disable Transmit mailbox empty Interrupt */__HAL_CAN_DISABLE_IT(hcan, CAN_IT_EWG |CAN_IT_EPV |CAN_IT_BOF |CAN_IT_LEC |CAN_IT_ERR |CAN_IT_FMP0|CAN_IT_FOV0|CAN_IT_FMP1|CAN_IT_FOV1|CAN_IT_TME);/* Call Error callback function */HAL_CAN_ErrorCallback(hcan);}
}
最后,找到无法进入接收中断的原因,是CAN总线出现发送错误的情况,从而触发了错误警告标志位EWGF
,进而将关闭了消息挂起中断。
总结
本文简单介绍了在STM32F407上的CAN总线调试过程,解决了一个数据接收的问题,简单整理了一下调试的过程和思路。不过本人能力有限,难免存在错误和纰漏,请不吝赐教,如果文章帮到了你,点赞支持一下👍👍👍
相关文章:

STM32的CAN总线调试经验分享
相关文章 CAN总线简易入门教程 CAN总线显性电平和隐性电平详解 STM32的CAN总线调试经验分享 文章目录相关文章背景CAN总线CAN控制器CAN收发器调试过程硬件排查CAN分析仪芯片CAN控制器调试总结背景 最近负责的一个项目用的主控芯片是STM32F407IGT6,需要和几个电机控…...

深度剖析自定义类型(结构体、枚举、联合)——“C”
各位CSDN的uu们你们好呀,今天,小雅兰的内容是心心念念的结构体啦,其实在此之前,我也写过结构体的知识点,只是并没有很深入,那么,今天我会仔细来学习自定义类型的知识点,下面…...

《水经注地图服务》发布的全球影像数据在水经微图中调用
(本文首发于“水经注GIS”公号,订阅“水经注GIS”公号,为你分享更多GIS技术 )1、引言古人云:“工欲善其事,必先利其器。”意思是说:工匠想要使他的工作做好,一定要先让工具锋利&…...

MyBatis --- 缓存、逆向工程、分页插件
一、MyBatis的缓存 1.1、MyBatis的一级缓存 一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问 使一级缓存失效的四种情况: 1、…...

vue3自定义svg图标组件
可参考: 未来必热:SVG Sprites技术介绍 懒人神器:svg-sprite-loader实现自己的Icon组件 在Vue3项目中使用svg-sprite-loader 前置知识 在页面中,虽然可以通过如下的方式使用img标签,来引入svg图标。但是,…...

智能火焰与烟雾检测系统(Python+YOLOv5深度学习模型+清新界面)
摘要:智能火焰与烟雾检测系统用于智能日常火灾检测报警,利用摄像头画面实时识别火焰与烟雾,另外支持图片、视频火焰检测并进行结果可视化。本文详细介绍基于智能火焰与烟雾检测系统,在介绍算法原理的同时,给出Python的…...

Java实习生------JUC并发编程(多线程)10道面试题打卡⭐⭐⭐
目录 并行和并发有什么区别? 线程和进程有什么区别? 创建线程有哪几种方式? runnable和callable有什么区别? 线程的状态及转换? sleep()和wait()的区别? run()和start()有什么区别? 在…...

ChatGPT和百度文心一言写用例,谁更强?
文心一言发布的第一时间,就排队申请了邀请码,昨晚看了下,邀请码已经到手,索性就拿一个例子试了一下,看看哪个能够真正意义上的提高生产力,最简单的录制了个GIF动画如下:问题:你是一个…...

设计模式总结
设计模式的六大原则 开放-封闭原则(OCP) (总原则) Open-Close Principle:该对扩展开放,对修改关闭。 目的就是保证程序的扩展性好,易于维护和升级。 开放-封闭原则是面向对象设计的核心所在, 开闭原则是Java世界里最基础的设计原则。 开闭…...

【K8S系列】深入解析Pod对象(一)
目录 序言 1.问题引入 1.1 问题描述 2 问题解答 2.1 pod 属性 2.1.1 NodeSelector 2.1.2 HostAliases 2.1.3 shareProcessNamespace 2.1.4 NodeName 2.1.5 其他pod属性 2.2 容器属性 2.2.1 ImagePullPolicy 2.2.2 Lifecycle 3 总结 4. 投票 序言 任何一件事情&am…...

JVM学习.02 内存分配和回收策略
1、前言《JVM学习.01 内存模型》篇讲述了JVM的内存布局,其中每个区域是作用,以及创建实例对象的时候内存区域的工作流程。上文还讲到了关于对象存货后,会被回收清理的过程。今天这里就着重讲一下对象实例是如何被清理回收的,以及清…...

logstash+elasticsearch+Kibana(ELK)日志收集
文章目录一.安装ELK 7.17二.为Elasticsearch设置密码三.配置logstash四.springboot整合logstash五.spring整合Elastic Search一.安装ELK 7.17 不要一股脑执行以下语句,请观察修改要修改的地方 安装logstash # logstash安装docker run -d --name logstash \-p 5043:5043 -p 5…...

今天面试了一个2年Java经验的
今天去面试了一个26岁的程序员,看了简历,2年经验,本科,写得很牛叉。 Spring cloud alibaba全家桶、redis,分布式锁,服务调用,数据库事务,线程,Zookeeper、Dubbo 、Rabbi…...

逻辑覆盖测试用例设计
逻辑覆盖测试用例设计 实验目标 能够依据程序画出程序流程图理解常用覆盖方法的内涵理解常用覆盖方法的强弱关系能够使用常用覆盖方法设计测试用例 背景知识 白盒测试通常采用静态测试方法和动态测试方法开展。动态测试是参照系统需求或测试规则,通过预先设计一…...

面试官:说一下MySQL中的锁机制吧
5. 1MySQL有哪些锁? 为保证数据的一致性,需要对并发操作进行控制,因此产生了锁。同时锁机制也为实现MySQL的各个隔离级别提供了保证。 锁冲突 也是影响数据库并发访问性能的一个重要因素。所以锁对数据库而言显得尤其重要,也更加…...

STL库中list的迭代器实现痛点分析
前文本篇文章准备换个模式,之前都是先详解模拟实现,但是模拟实现的基本逻辑大多数老铁都是明白的,所以我们这次主要讲解STL库中list的独特性,也就是模拟实现中的重难点文末有模拟实现的源码一,list实现的特殊类list实现…...

字符编码对比(GBK、Unicode、UTF-8)
摘要我们在网上能看到各种文字和符号,那么它们是怎么存储和转化的,还有我们常常提及的UTF-8,为什么都要设置这种编码方式,这里就探讨下。字符集字符集:就是各国文字、符号、数字的集合。常见的字符集有:ASC…...

【百面成神】Redis基础11问,你能坚持到第几问
前 言 🍉 作者简介:半旧518,长跑型选手,立志坚持写10年博客,专注于java后端 ☕专栏简介:纯手打总结面试题,自用备用 🌰 文章简介:Redis最基础、重要的11道面试题 文章目录…...

十大排序算法极简汇总篇
说明 十大排序算法可以说是每个程序员都必须得掌握的了,如果你们像从 0 详细学习每一篇,那么你们可以看前面的文章。 但是呢,有些人可能已经学过,想要快速复习一下,看看代码怎么写的,那么可以看这篇十大排…...

数据结构笔记
文章目录第一章:数据结构与算法第二章:稀疏数组和队列一 、稀疏sparsearray 数组(一)案例需求(二)稀疏数组介绍(三)应用实列(四)代码实现二、队列(…...

web前端框架——Vue的特性
目录 前言: 一.vue 二.特性 1.轻量级 2.数据绑定 3.指令 4.插件 三.比较Angular 、React 、Vue 框架之间的比较 1. Angular Angular的优点: 2. React React 的优点: 3.vue 3.Vue的优点: 前言: 本篇文章…...

提权工具推荐(PEASS-ng、linpeas_linux_amd64、winPEASany_ofs)
介绍 在这里,您可以找到适用于Windows、Linux/Unix*和MacOS的权限提升工具。 这些工具搜索您可以利用的可能的本地权限提升路径,并用漂亮的颜色打印给您,这样您就可以很容易地识别错误配置。 查看book.hacktricks.xyz中的本地Windows权限提升检查表WinPEAS-Windows本地权限…...

Spark - 继承 FileOutputFormat 实现向 HDFS 地址追加文件
目录 一.引言 二.源码浅析 1.RDD.saveAsTextFile 2.TextOutputFormat 3.FileOutputFormat 三.源码修改 1.修改文件生成逻辑 - getRecordWriter 2.允许目录存在 - checkoutputSpecs 3.全部代码 - TextOutputFormatV2 四.追加存储代码实战 五.总结 一.引言 Output d…...

树莓派编程控制继电器及继电器组
目录 一,继电器说明 ● 继电器接口说明 ① 继电器输入端: ② 继电器输出端: 二,树莓派控制继电器 三,树莓派控制继电器组 一,继电器说明 通俗点讲,可以把继电器理解成是一些功能设备的控制开关。 ● LOW&#…...

oracle和mysql的区别
Oracle与MySQL的区别以及优缺点 MySQL的特点 1、性能卓越,服务稳定,很少出现异常宕机; 2、开放源代码无版本制约,自主性及使用成本低; 3、历史悠久,社区和用户非常活跃,遇到问题及时寻求帮助…...

<Linux开发> linux应用开发-之-uart通信开发例程
一、简介 串口全称叫做串行接口,串行接口指的是数据一个一个的按顺序传输,通信线路简单。使用两条线即可. 实现双向通信,一条用于发送,一条用于接收。串口通信距离远,但是速度相对会低,串口是一种很常用的工…...

基于深度学习的安全帽检测系统(YOLOv5清新界面版,Python代码)
摘要:安全帽检测系统用于自动化监测安全帽佩戴情况,在需要佩戴安全帽的场合自动安全提醒,实现图片、视频和摄像头等多种形式监测。在介绍算法原理的同时,给出Python的实现代码、训练数据集,以及PyQt的UI界面。安全帽检…...

Linux - 进程控制(进程替换)
0.引入创建子进程的目的是什么?就是为了让子进程帮我执行特定的任务让子进程执行父进程的一部分代码如果子进程想执行一个全新的程序代码呢? 那么就要使用进程的程序替换为什么要有程序替换?也就是说子进程想执行一个全新的程序代码ÿ…...

Java中 ==和equals的区别是什么?
作用: 基本类型,比较值是否相等引用类型,比较内存地址值是否相等不能比较没有父子关系的两个对象equals()方法的作用: JDK 中的类一般已经重写了 equals(),比较的是内容自定义类如果没有重写 equals(),将…...

Linux(网络基础---网络层)
文章目录0. 前言1. IP协议1-1 基本概念1-2 协议头格式2. 网段划分2-1 基本概念2.2 IP地址分五大类2-3 特殊的IP地址2-4 IP地址的数量限制2-5 私有IP地址和公网IP地址2-6 路由0. 前言 前面我们讲了,应用层、传输层;本章讲网络层。 应用层:我…...