细说STM32F407单片机中断方式CAN通信
目录
一、工程配置
1、时钟、DEBUG、USART6、GPIO、CodeGenerator
2、CAN1
3、NVIC
二、软件设计
1、KEYLED
2、can.h
3、can.c
(1)CAN1中断初始化
(2)RNG初始化和随机数产生
(3) 筛选器组设置
(4)发送消息
(5)中断方式接收消息
4、main.c
三、下载与运行
在实际的CAN通信中,使用轮询方式发送消息,使用中断方式接收消息更加实用和普遍。本实例设计一个CAN通信,使用中断方式接收消息,并且测试在两个FIFO上使用不同的筛选器。
本实例仍然使用使用旺宝红龙开发板STM32F407ZGT6 KIT V1.0。需要参考本文作者的其他文章:
参考文章1: 细说STM32F407单片机轮询方式CAN通信_stm32f407can波特率配置表-CSDN博客 https://wenchm.blog.csdn.net/article/details/144852504
参考文章2:细说STM32F407单片机CAN基础知识及其HAL驱动程序-CSDN博客 https://wenchm.blog.csdn.net/article/details/144769950
示例仍然引用KEYLED文件夹中的文件。
示例的功能和使用流程如下:
示例工程中,使用开发板上的S2键,按下S2键后,发送一个随机数的数据帧。并用LED1显示工作状态,输出到串口助手上。按下S6键,开发板复位。示例的功能还包括:
- 使用CAN1的回环模式自发自收。
- 开启FIFO0的接收中断,开启FIFO1的接收中断。
- 为FIFO0设置筛选器,只接收标识符ID为奇数的消息;为FIFO1设置筛选器,接收所有消息。
- 使用随机数生成器(Random Number Generator,RNG),在发送消息时,用随机数作为帧的数据。
[S2]KeyUp = Send a Data Frame LED1 ON
一、工程配置
CAN接口原理图同参考文件1。
1、时钟、DEBUG、USART6、GPIO、CodeGenerator
与参考 文件1的设置相同或近似。
这里,设置HCLK=168MHz,PCLK1=42MHz,PCLK2=84MHz。
2、CAN1
CAN1模块的参数设置与参考文件1相似。
3、NVIC
使用CAN1模块的接收中断,打开CAN1 RX0中断和CAN1 RX1中断,两个中断的抢占优先级都设置为1,一个CAN模块有4个中断,RX0中断是FIFO0的中断,RX1中断是FIFO1的中断,每个中断有几个中断事件和对应的回调函数,详见参考文章2。
二、软件设计
1、KEYLED
本例的项目中要使用KEYLED,其中的keyled.c和keyled.h的使用方法与参考文章1相同。
2、can.h
/* USER CODE BEGIN Prototypes */
HAL_StatusTypeDef CAN_SetFilters();
void CAN_TestPoll(uint8_t msgID,uint8_t frameType);
/* USER CODE END Prototypes */
3、can.c
/* USER CODE BEGIN 0 */
#include "rng.h"
#include <stdio.h>
/* USER CODE END 0 */
/* USER CODE BEGIN 1 */
//设置筛选器,需要在完成CAN初始化之后调用此函数
HAL_StatusTypeDef CAN_SetFilters()
{CAN_FilterTypeDef canFilter;//1. 设置FIFO0的筛选器canFilter.FilterBank = 0; //筛选器组编号canFilter.FilterMode = CAN_FILTERMODE_IDMASK; //ID掩码模式canFilter.FilterScale = CAN_FILTERSCALE_32BIT; //32位长度//只接收stdID为奇数的帧canFilter.FilterIdHigh = 0x0020; //CAN_FxR1寄存器的高16位canFilter.FilterIdLow = 0x0000; //CAN_FxR1寄存器的低16位canFilter.FilterMaskIdHigh = 0x0020; //CAN_FxR2寄存器的高16位canFilter.FilterMaskIdLow = 0x0000; //CAN_FxR2寄存器的低16位canFilter.FilterFIFOAssignment = CAN_RX_FIFO0; //应用于FIFO0canFilter.FilterActivation = ENABLE; //使用筛选器canFilter.SlaveStartFilterBank = 14; //从CAN控制器筛选器起始的BankHAL_StatusTypeDef result=HAL_CAN_ConfigFilter(&hcan1, &canFilter);//2. 设置FIFO1的筛选器canFilter.FilterBank = 1; //筛选器组编号//接收所有帧canFilter.FilterIdHigh = 0x0000; //CAN_FxR1寄存器的高16位canFilter.FilterIdLow = 0x0000; //CAN_FxR1寄存器的低16位canFilter.FilterMaskIdHigh = 0x0000; //CAN_FxR2寄存器的高16位,所有位任意canFilter.FilterMaskIdLow = 0x0000; //CAN_FxR2寄存器的低16位,所有位任意canFilter.FilterFIFOAssignment = CAN_RX_FIFO1; //应用于FIFO 1result=HAL_CAN_ConfigFilter(&hcan1, &canFilter);return result;
}void CAN_SendMsg(uint8_t msgID,uint8_t frameType)
{CAN_TxHeaderTypeDef TxHeader;TxHeader.StdId = msgID; //stdIDTxHeader.RTR = frameType; //数据帧,CAN_RTR_DATATxHeader.IDE = CAN_ID_STD; //标准格式TxHeader.DLC =4; //数据长度TxHeader.TransmitGlobalTime = DISABLE;uint32_t rand;HAL_RNG_GenerateRandomNumber(&hrng, &rand); //产生32位随机数uint8_t TxData[8]; //最多8个字节TxData[3] = rand & 0x000000FF;TxData[2] = (rand & 0x0000FF00)>>8;TxData[1] = (rand & 0x00FF0000)>>16;TxData[0] = (rand & 0xFF000000)>>24;while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan1) < 1) {} //等待有可用的发送邮箱printf("Send MsgID= %X\r\n",msgID);uint32_t TxMailbox; //临时变量,用于返回使用的邮箱编号/* 发送到邮箱,由CAN模块负责发送到CAN总线 */if(HAL_CAN_AddTxMessage(&hcan1,&TxHeader,TxData,&TxMailbox) != HAL_OK)printf("Send to mailbox error.\r\n");
}//读取和显示FIFO0或FIFO1的消息
//参数FIFO_num是FIFO编号,CAN_RX_FIFO0或CAN_RX_FIFO1
void CAN_ReadMsg(uint32_t FIFO_num)
{CAN_RxHeaderTypeDef RxHeader;uint8_t RxData[8]; //接收数据缓存区,8个字节if(FIFO_num==CAN_RX_FIFO0){printf("Msg received by FIFO0.\r\n");if(HAL_CAN_GetRxMessage(&hcan1,CAN_RX_FIFO0,&RxHeader,RxData) != HAL_OK){printf("Read FIFO0 error.\r\n");return;}}else{printf("Msg received by FIFO1.\r\n");if(HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO1, &RxHeader, RxData) != HAL_OK){printf("Read FIFO1 error.\r\n");return;}}//显示接收到的消息printf("StdID= %lX\r\n",RxHeader.StdId);printf("RTR(0=Data,2=Remote)= %lX\r\n",RxHeader.RTR);printf("IDE(0=Std,4=Ext)= %lX\r\n",RxHeader.IDE);printf("FilterMatchIndex= %lX\r\n",RxHeader.FilterMatchIndex);printf("DLC(Data length)= %lX\r\n",RxHeader.DLC);for (uint8_t i=0; i<4; i++){printf("Data[0]= %X\r\n",RxData[0]);printf("Data[1]= %X\r\n",RxData[1]);printf("Data[2]= %X\r\n",RxData[2]);printf("Data[3]= %X\r\n",RxData[3]);}printf("** Reselect menu or reset **\r\n");
}
//FIFO0接收到新消息事件中断回调函数
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{CAN_ReadMsg(CAN_RX_FIFO0);
}//FIFO1接收到新消息事件中断回调函数
void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan)
{CAN_ReadMsg(CAN_RX_FIFO1);
}
/* USER CODE END 1 */
(1)CAN1中断初始化
函数MX_CAN1_Init()的代码与参考文章1完全相同,函数HAL_CAN_MspInit()中与参考文章1比较,增加了两个中断的初始化设置,开启了CAN1_RX0和CAN1_RX1中断:
/* CAN1 interrupt Init */HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 1, 0);HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);HAL_NVIC_SetPriority(CAN1_RX1_IRQn, 1, 0);HAL_NVIC_EnableIRQ(CAN1_RX1_IRQn);
(2)RNG初始化和随机数产生
RNG是MCU的一个内部单元,其初始化很简单,就是定义了RNG模块的外设对象变量,开启其时钟。相关代码如下:
#include "rng.h"
RNG_HandleTypeDef hrng;void MX_RNG_Init(void)
{hrng.Instance = RNG;if (HAL_RNG_Init(&hrng) != HAL_OK){Error_Handler();}
}void HAL_RNG_MspInit(RNG_HandleTypeDef* rngHandle)
{if(rngHandle->Instance==RNG){/* RNG clock enable */__HAL_RCC_RNG_CLK_ENABLE();}
}void HAL_RNG_MspDeInit(RNG_HandleTypeDef* rngHandle)
{if(rngHandle->Instance==RNG){/* Peripheral clock disable */__HAL_RCC_RNG_CLK_DISABLE();}
}
可以使用轮询方式或中断方式产生32位的随机数,分别对应两个函数。
HAL_RNG_GenerateRandomNumber() //轮询方式产生随机数。
HAL_RNG_GetRandomNumber_IT() //中断方式产生随机数。
HAL_RNG_GenerateRandomNumber()的函数原型如下:
HAL_StatusTypeDef HAL_RNG_GenerateRandomNumber(RNG_HandleTypeDef *hrng,uint32_t*random32bit)
RNG由时钟树中的时钟信号由PCLK1 42MHz驱动,典型值是42MHz,这个时钟频率不能太低,在本例中,这个时钟频率是42MHz。产生两个连续随机数的最小间隔是42个PLL42CLK周期。
(3) 筛选器组设置
在文件can.h中,自定义的函数CAN_SetFilters()用于对FIFO0和FIFO1进行筛选器设置。详细代码在can.c中实现。
为FIFO0设置的筛选器是只接收标识符ID为奇数的消息,为FIFO1设置的筛选器是可以接收任何消息。注意,可以为一个FIFO设置多个筛选器,但是一个筛选器只能用于一个FIFO,所以,这两个筛选器的FilterBank必须不同。结构体CAN_FilterTypeDef各成员变量的意义以及筛选器的设置原理详见参考文章1。
(4)发送消息
在main.c中调用函数CAN_SendMsg()发送数据帧,这个自定义函数的实现代码与参考文章1不同,并在can.c中实现这个函数的代码。
在其实现的程序中还调用函数HAL_RNG_GenerateRandomNumber(),产生一个32位随机数,然后分解为4字节存入发送数据缓冲区。程序仍然是用函数HAL_CAN_AddTxMessage()将需要发送的消息写入发送邮箱,由CAN模块自动将消息发送到CAN总线上。函数CAN_SendMsg()只管发送消息,接收消息由中断去处理。
(5)中断方式接收消息
由于开启了CAN1的RX0中断和RX1中断,在文件stm32f4xx_it.c中自动生成了这两个中断的ISR框架。
CAN1_RX0是FIFO0接收消息、满或上溢时产生的中断,接收消息中断事件对应的回调函数是HAL_CAN_RxFifo0MsgPendingCallback()。同样的,FIFO1接收消息中断事件对应的回调函数是HAL_CAN_RxFifo1MsgPendingCallback()。CAN1_RX0和CAN1_RX1中断事件与回调函数的对应关系详见参考文章1。所以,要使用中断方式处理FIFO0和FIFO1接收的消息,只需重新实现这两个回调函数即可。在文件can.c中重新实现这两个回调函数。
//FIFO0接收到新消息事件中断回调函数
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{CAN_ReadMsg(CAN_RX_FIFO0);
}//FIFO1接收到新消息事件中断回调函数
void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan)
{CAN_ReadMsg(CAN_RX_FIFO1);
}
两个回调函数都调用了同一个函数CAN_ReadMsg(),只是传递了相应的FIFO编号。函数CAN_ReadMsg()负责读取FIFO0或FIFO1的消息并显示。读取FIFO里面收到的消息仍然使用函数HAL_CAN_GetRxMessage(),消息头结构体CAN_RxHeaderTypeDef的意义见参考文章1。这里显示了一个成员变量FilterMatchIndex的值,这是接收消息的FIFO内接收了消息的筛选器的序号,是在一个FIFO内的筛选器的序号,而不是筛选器的FilterBank属性值。
4、main.c
/* USER CODE BEGIN Includes */
#include "keyled.h"
#include <stdio.h>
/* USER CODE END Includes */
/* USER CODE BEGIN 2 */printf("Demo18_2_CAN:CAN Interrupt.\r\n");printf("Test mode:Loopback.\r\n");if (CAN_SetFilters() == HAL_OK) //设置筛选器printf("ID Filter: Only Odd IDs.\r\n");if (HAL_CAN_Start(&hcan1) == HAL_OK) //启动CAN1模块printf("CAN is started.\r\n");printf("[S2]KeyUp = Send a Data Frame.\r\n");//必须开启FIFO接收到消息中断事件,否则不会响应中断事件__HAL_CAN_ENABLE_IT(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);__HAL_CAN_ENABLE_IT(&hcan1, CAN_IT_RX_FIFO1_MSG_PENDING);//HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);//HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO1_MSG_PENDING);// MCU output low level LED light is onLED1_OFF();/* USER CODE END 2 */
在外设初始化部分,函数MX_RNG_Init()用于RNG的初始化,函数MX_CAN1_Init()用于CAN1模块的初始化。
函数CAN_SetFilters()用于设置FIFO0和FIFO1的筛选器组,与参考文章1的同名函数代码不同。这里要使用中断方式进行消息接收,还需要开启FIFO0和FIFO1的接收新消息的中断事件,即
__HAL_CAN_ENABLE_IT(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);
__HAL_CAN_ENABLE_IT(&hcan1,CAN_IT_RX_FIFO1_MSG_PENDING);
其中的两个宏分别是FIFO0和FIFO1接收新消息的中断事件使能控制位的宏定义,也作为中断事件类型宏定义,详见参考文章2。
主程序的while()循环中调用自定义函数CAN_SendMsg()以轮询方式发送一个数据帧,接收数据帧在中断里处理。
/* USER CODE BEGIN WHILE */uint8_t msgID=1;while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */KEYS curKey = ScanPressedKey(KEY_WAIT_ALWAYS);if (curKey==KEY_UP){CAN_SendMsg(msgID++, CAN_RTR_DATA);LED1_ON();}elseLED1_OFF();HAL_Delay(500); //延时,消除按键抖动}/* USER CODE END 3 */
/* USER CODE BEGIN 4 */
//串口打印
int __io_putchar(int ch)
{HAL_UART_Transmit(&huart6,(uint8_t*)&ch,1,0xFFFF);return ch;
}
/* USER CODE END 4 */
三、下载与运行
下载后在串口助手上先显示菜单,或者按下S6复位键也显示菜单。
每次按下KeyUp键可以发送一个标准格式数据帧,msgID加1,msgID作为数据帧的标识符ID。
运行时会发现,msgID为奇数时,是由FIFO0接收消息,msgID为偶数时,是由FIFO1接收消息。因为在设置筛选器组时,设置FIFO0只能接收标识符ID为奇数的消息,FIFO1可以接收任意标识符ID的消息。当标识符ID为偶数时,只能由FIFO1接收,当标识符ID为奇数时,两个FIFO都可以接收,但是由FIFO0优先接收。
不管是哪个FIFO接收的消息,显示的FilterMatchIndex的值都是0,因为它们都只有一个筛选器。
相关文章:

细说STM32F407单片机中断方式CAN通信
目录 一、工程配置 1、时钟、DEBUG、USART6、GPIO、CodeGenerator 2、CAN1 3、NVIC 二、软件设计 1、KEYLED 2、can.h 3、can.c (1)CAN1中断初始化 (2)RNG初始化和随机数产生 (3) 筛选器组设置…...

Python应用指南:高德交通态势数据
在现代城市的脉络中,交通流量如同流动的血液,交通流量的动态变化对出行规划和城市管理提出了更高的要求。为了应对这一挑战,高德地图推出了交通态势查询API,旨在为开发者提供一个强大的工具,用于实时获取指定区域或道路…...

医学图像分析工具01:FreeSurfer || Recon -all 全流程MRI皮质表面重建
FreeSurfer是什么 FreeSurfer 是一个功能强大的神经影像学分析软件包,广泛用于处理和可视化大脑的横断面和纵向研究数据。该软件由马萨诸塞州总医院的Martinos生物医学成像中心的计算神经影像实验室开发,旨在为神经科学研究人员提供一个高效、精确的数据…...

.NET框架用C#实现PDF转HTML
HTML作为一种开放标准的网页标记语言,具有跨平台、易于浏览和搜索引擎友好的特性,使得内容能够在多种设备上轻松访问并优化了在线分享与互动。通过将PDF文件转换为HTML格式,我们可以更方便地在浏览器中展示PDF文档内容,同时也更容…...

mamba-ssm安装
注意1:mamba-ssm要与casual-conv1d一起安装。 注意2:mamba-ssm与cuda、pytorch版本要对应。需要看你下载的代码的requirements.txt causal-conv1d与mamba的whl包官网下载: https://github.com/Dao-AILab/causal-conv1d/releases?page3 htt…...

网络IP协议
IP(Internet Protocol,网际协议)是TCP/IP协议族中重要的协议,主要负责将数据包发送给目标主机。IP相当于OSI(图1)的第三层网络层。网络层的主要作用是失陷终端节点之间的通信。这种终端节点之间的通信也叫点…...

双指针算法详解
目录 一、双指针 二、双指针题目 1.移动零 解法: 代码: 2.复写零 编辑 解法: 代码: 边界情况处理: 3.快乐数 编辑 解法:快慢指针 代码: 4.盛水最多的容器 解法:(对撞指针)…...

MySQL的最左匹配原则是什么
最左匹配原则是应用于联合索引的规则。 对于以下表F:f1,f2,f3;建立了联合索引(f2,f3),那么我们在查询的时候如果是: select * from F where f2 ? and f3 ?; 或 sele…...

LeetCode:106.从中序与后序遍历序列构造二叉树
跟着carl学算法,本系列博客仅做个人记录,建议大家都去看carl本人的博客,写的真的很好的! 代码随想录 LeetCode:106.从中序与后序遍历序列构造二叉树 给定两个整数数组 inorder 和 postorder ,其中 inorder …...

22. 【.NET 8 实战--孢子记账--从单体到微服务】--记账模块--切换主币种
这篇文章我们将结合主币种设置以及收支记录实现切换主币种后重新计算以前记录的转换后的金额。那么,为什么要在切换主币种后要重新计算转换后的金额呢?有以下两个原因: 统一的币种,方便我们统计数据方便用户按照当地的币种查看收…...

01.02周四F34-Day43打卡
文章目录 1. 地是湿的。昨晚估计下雨了。2. 你可能把包丢在餐厅里了吧?3. 她说他可能误了航班。4. 我本来应该早点来的,但路上特别堵。5. 约翰可能在那次事故中受了重伤。6. 这是一个情景对话7. 我本可以走另一条路的。8. 我准是瘦了不少,你看我这裤子现在多肥。9. 钱没了!会…...

行业商机信息付费小程序系统开发方案
行业商机信息付费小程序系统,主要是整合优质行业资源,实时更新的商机信息。在当今信息爆炸的时代,精准、高效地获取行业商机信息对于企业和个人创业者而言至关重要。 一、使用场景 日常浏览:用户在工作间隙或闲暇时间,…...

cut-命令详解
一、命令 1.cut列截取命令 cut命令的默认分隔符是制表符 2.参数: -f 列号 #提取第几列-d 分隔符 #按照指定分隔符分割列-c 字符范围 #不依赖分隔符来区分列,而是通过字符范围(行首为0)来进行字段提取。“n-”表…...

Apache MINA 反序列化漏洞CVE-2024-52046
漏洞描述: Apache MINA 是一个功能强大、灵活且高性能的网络应用框架。它通过抽象网络层的复杂性,提供了事件驱动架构和灵活的 Filter 链机制,使得开发者可以更容易地开发各种类型的网络应用。 Apache MINA 框架的 ObjectSerializationDeco…...

二、AI知识(神经网络)
二、AI知识(神经网络) 1.常用算法 FNN CNN RNN LSTM DNN GRU 2.深度学习中概念及算法 1. 感知机 感知机(Perceptron)是一种最早的人工神经网络模型之一,通常用来解决二分类问题。它由弗兰克罗森布拉特&#…...

node.js之---子线程(child_process)模块
为什么需要子线程(child_process)模块 Worker Threads 的基本概念 如何使用 Worker Threads Worker Threads 的性能 Worker 线程的优势和限制 进阶用法:共享内存 为什么需要子线程(child_process)模块 在 Node.js…...

Json字符串解析失败
通过第三方服务,拿到响应体的data对象(拿到的时候对象是有值的) 通过JSON.parseObject方法,拿到的对象,值为null 通过查看对应的json字符串,发现命名不一样... JSONField SeriealizedName注解是用来解析j…...

LeetCode算法题——螺旋矩阵ll
题目描述 给你一个正整数n,生成一个包含1到n2所有元素,且元素按顺时针顺序螺旋排列的n x n正方形矩阵matrix 。 示例 输入:n 3 输出:[[1,2,3],[8,9,4],[7,6,5]]题解 思路: 将整个过程分解为逐圈填充的过程…...

【开源社区openEuler实践】hpcrunner
title: 探索 Hpcrunner:高性能计算的得力助手 date: ‘2024-12-31’ category: blog tags: Hpcrunner高性能计算任务调度资源优化 sig: HPC archives: ‘2024-12’ author:way_back summary: Hpcrunner 作为高性能计算领域的一款实用工具,专注于优化任务…...

linux下安装达梦数据库v8详解
目录 操作系统、数据库 1、下载达梦数据库 2、安装前准备 2.1、建立数据库用户和组 2.2、修改文件打开最大数 2.3、挂载镜像 2.4、新建安装目录 3、数据库安装 4、配置环境变量 5、初始化数据库实例 6、注册服务 7、使用数据库 8、卸载数据库 9、多实例管理 10、…...

Redis的常用命令
Redis中文字典网站 redis 命令手册https://redis.com.cn/commands.html Keys * 查看当前库所有的key exists ke 判断某个key是否存在 type key查看你的key是什么类型 Del key删除执行的key数据 unlink key非阻塞删除,仅仅将keys从keyspace元数据中删除…...

Docker入门常用命令总结
1.从远程仓库拉取一个纯净的镜像 docker pull docker .io/centos 2.创建并进入容器(左外右内) docker run --name xxx -dit 镜像id(镜像名称:Tag) /bin/bash 【参数必须放在镜像ID之前】 -i 让Docker分配一个伪终端,并…...

【Qt】容器控件、布局管理控件
目录 容器控件 QGroupBox QTabWidget 布局管理控件 QVBoxLayout 例子: QHBoxLayout 例子: QGridLayout 例子: 例子: QFormLayout 例子: QSpacerItem 例子: 容器控件 QGroupBox 表示一个带有…...

cesium小知识:常见的20多种property详解
要详细解释 Cesium 中所有的 Property 类,内容确实会非常丰富且详尽。 Property 基础 Property 是 Cesium 中用于表示随时间或条件变化的值的基础类。它允许你定义属性值如何根据时间、用户交互或其他逻辑动态改变。Property 的设计使得你可以创建复杂的动画和交互效果,而…...

图数据库 | 17、高可用分布式设计(上)
我们在前面的文章中,探索了多种可能的系统扩展方式,以及每种扩展方式的优劣。 本篇文章将通过具体的架构设计方案来对每一种方案的设计、投入产出比、各项指标与功能,以及孰优孰劣等进行评价。 在设计高性能、高可用图数据库的时候…...

1.运控概述
以下并不是我原创(包括图片),都是来源于网络收集。如CSDN博主,朝夕教育,AI等。 什么是运动控制 运控是指“控制移动”之意,可以利用各种电机进行位置控制等操作,让机器听懂你的指令。 什么是…...

DuckDB:密钥管理器及其应用
密钥管理器(Secrets Manager)为所有使用密钥的后端提供了统一的用户界面。密钥信息可以被限定范围,因此不同的存储前缀可以有不同的密钥信息,例如允许在单个查询中连接跨组织的数据。密钥也可以持久化,这样就不需要在每次启动DuckDB时都指定它…...

单元测试4.0+思路总结
Jmockit使用笔记_增加代码覆盖率_覆盖try catch_使用new MockUp私有方法-CSDN博客 一般使用new MockUp模拟被测试代码中的私有方法(常用) 使用new Expetations模拟被测试代码中的方法?...

epoll 水平ET跟边缘LT触发的区别是什么
epoll默认的是水平触发 意思就是当我们depoll默认的是水平触发 LT 模式(水平触发) 工作机制:在 LT 模式下,只要文件描述符(例如套接字)对应的 I/O 缓冲区中有数据可读或者可写空间(对于写操作…...

设计模式 创建型 单例模式(Singleton Pattern)与 常见技术框架应用 解析
单例模式(Singleton Pattern)是一种创建型设计模式,旨在确保某个类在应用程序的生命周期内只有一个实例,并提供一个全局访问点来获取该实例。这种设计模式在需要控制资源访问、避免频繁创建和销毁对象的场景中尤为有用。 一、核心…...