Clion开发STM32——移植FreeModbus
STM32型号 :STM32H743VIT6
FreeModbus版本 :1.6
使用工具:stm32cubeMX,Clion
使用STM32作从机,模式:RTU
网上用keil的比较多,用Clion的比较少,如果你也用Clion,那么希望本文可以给你提供些许参考。
1 下载官网源码
官网地址:https://www.embedded-experts.at/en/freemodbus/about/
demo是移植例程,但是里面没有stm32的。
modbus是源码.
看一下modbus文件,我这里使用的模式是RTU模式,所以ascii和tcp文件夹就不需要了,如果你也只用RTU模式,那么只需要标红的文件夹。
在freemodbus-v1.6\demo\BARE\port这个文件夹中的文件全部需要,这些是接口文件。
把上面的文件放在一个文件夹里。
在自己的工程目录下新建FreeModbus(自己起名就好),并添加上面的文件,如下:
2 修改CMakeLists_template
可以参考这篇文章Clion开发STM32——添加自己文件
增加头文件路径
增加编译文件
只关注蓝色框框起来的即可,其他文件可以忽略,那是我这个工程的其他文件。
3 cubeMX配置串口和定时器
3.1串口
3.2 定时器
3.3 NVIC
中断回调函数比较繁琐,所以我这里取消掉了,自己写中断内的内容。
4 修改接口 port
4.1 port.h
首先是port.h文件
#ifndef _PORT_H
#define _PORT_H#include <assert.h>
#include <inttypes.h>/* ----------------------- Platform includes --------------------------------*/
#include "stm32h7xx_hal.h"
#include "main.h"/* ----------------------- Defines ---------*/#define INLINE inline
#define PR_BEGIN_EXTERN_C extern "C" {
#define PR_END_EXTERN_C }#define ENTER_CRITICAL_SECTION( ) __set_PRIMASK(1)//禁止中断
#define EXIT_CRITICAL_SECTION( ) __set_PRIMASK(0)//允许中断typedef uint8_t BOOL;typedef unsigned char UCHAR;
typedef char CHAR;typedef uint16_t USHORT;
typedef int16_t SHORT;typedef uint32_t ULONG;
typedef int32_t LONG;#ifndef TRUE
#define TRUE 1
#endif#ifndef FALSE
#define FALSE 0
#endif#endif
包含平台,定义进出临界区
4.2 portserial.c
这个文件是留给用户写串口函数接口的。
使能串口中断(串口接收中断,串口发送中断)
这里串口发送中断,可以选择两种
1 发送寄存器空中断 UART_IT_TXE
2 发送完成中断 UART_IT_TC
两种都可以 ,只是UART_IT_TC不会主动触发中断,必须得发送完成,发送完成是只数据被写入到发送寄存器,然后串口会把数据送到移位寄存器,等到移位寄存器发送完成,才会置发送完成中断。所以后面发送函数里,得收到加一个发送函数来触发,才能进中断。
而UART_IT_TXE使能后就可以进入中断,因为发送寄存器本就空的。往发送寄存器里写数据之后,数据也会被送到移位寄存器。
我这里使用的事发生寄存器空中断UART_IT_TXE。
#include "port.h"/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"/* ----------------------- static functions ---------------------------------*/void prvvUARTTxReadyISR( void );//被UART发送空中断调用的函数,注意是空中断,通知modbus数据可以发送void prvvUARTRxISR( void );//被UART接收中断调用的函数,通知modbus有数据到来/* ----------------------- Start implementation -----------------------------*/
/*使能串口中断*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{/* If xRXEnable enable serial receive interrupts. If xTxENable enable* transmitter empty interrupts.*/if (xRxEnable) {/*设置485为接收模式*/__HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);//使能接收中断} else {__HAL_UART_DISABLE_IT(&huart2, UART_IT_RXNE);}if (xTxEnable) {/*设置485为发送模式*/__HAL_UART_ENABLE_IT(&huart2, UART_IT_TXE);//使能发送寄存器为空中断} else {__HAL_UART_DISABLE_IT(&huart2, UART_IT_TXE);}
}
/*初始化串口*/
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{/*MX_USART2_UART_Init*/return TRUE;
}
/*发送一个字节*/
BOOL
xMBPortSerialPutByte( CHAR ucByte )
{/* Put a byte in the UARTs transmit buffer. This function is called* by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been* called. */huart2.Instance->TDR = ucByte;//HAL_UART_Transmit(&huart2, (uint8_t*)&ucByte, 1, 0);return TRUE;
}
/*接收一个字节*/
BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{/* Return the byte in the UARTs receive buffer. This function is called* by the protocol stack after pxMBFrameCBByteReceived( ) has been called.*/*pucByte = (uint8_t)(huart2.Instance->RDR & (uint8_t)0x00FF);return TRUE;
}/* Create an interrupt handler for the transmit buffer empty interrupt* (or an equivalent) for your target processor. This function should then* call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that* a new character can be sent. The protocol stack will then call * xMBPortSerialPutByte( ) to send the character.*/void prvvUARTTxReadyISR( void )
{pxMBFrameCBTransmitterEmpty( );
}/* Create an interrupt handler for the receive interrupt for your target* processor. This function should then call pxMBFrameCBByteReceived( ). The* protocol stack will then call xMBPortSerialGetByte( ) to retrieve the* character.*/void prvvUARTRxISR( void )
{pxMBFrameCBByteReceived( );
}
这两个函数需要取消static定义,这两个函数要在中断里被调用,而static就限制了它只能在本文件中使用,所以取消掉。(当然你把中断函数写在这个文件也可以,我个人习惯把中断都放在stm32h7xx_it.c文件中)
4.3 portimer.c
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"/* ----------------------- static functions ---------------------------------*/void prvvTIMERExpiredISR( void );//被定时器溢出中断调用的函数,通知modbus3.5个字符等待时间到/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{TIM_MasterConfigTypeDef sMasterConfig = {0};htim7.Instance = TIM7;htim7.Init.Prescaler = 11999;htim7.Init.CounterMode = TIM_COUNTERMODE_UP;htim7.Init.Period = usTim1Timerout50us - 1;htim7.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;if (HAL_TIM_Base_Init(&htim7) != HAL_OK){return FALSE;}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim7, &sMasterConfig) != HAL_OK){return FALSE;}return TRUE;
}/*使能定时器*/
inline void
vMBPortTimersEnable( )
{__HAL_TIM_CLEAR_IT(&htim7,TIM_IT_UPDATE);__HAL_TIM_SET_COUNTER(&htim7,0);HAL_TIM_Base_Start_IT(&htim7);/* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
}/*禁止定时器*/
inline void
vMBPortTimersDisable( )
{HAL_TIM_Base_Stop_IT(&htim7);__HAL_TIM_SET_COUNTER(&htim7,0);__HAL_TIM_CLEAR_IT(&htim7,TIM_IT_UPDATE);/* Disable any pending timers. */
}/* Create an ISR which is called whenever the timer has expired. This function* must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that* the timer has expired.*/void prvvTIMERExpiredISR( void )
{( void )pxMBPortCBTimerExpired( );
}
这个函数同样取消static,它被定时器溢出中断调用
关于定时器参数为什么要这么设置,可以看一下FreeModbus学习——eMBInit初始化
4.4 添加port.c文件
这个文件中存放的都是功能码处理函数调用的回调函数,当然你也可以放在别的文件,比如main.c里都可以。
这个文件中的函数怎么调用的,可以看一下FreeModbus学习——读输入寄存器eMBFuncReadInputRegister
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"/* ----------------------- Defines ------------------------------------------*/
//输入寄存器
#define REG_INPUT_START 3000
#define REG_INPUT_NREGS 4//保持寄存器
#define REG_HOLD_START 4000
#define REG_HOLD_NREGS 10//线圈
#define REG_COILS_START 0
#define REG_COILS_NREGS 4//开关寄存器
#define REG_DISCRETE_START 1000
#define REG_DISCRETE_NREGS 4
/* ----------------------- Static variables ---------------------------------*/
static USHORT usRegInputStart = REG_INPUT_START;
static USHORT usRegInputBuf[REG_INPUT_NREGS] = {0x01, 0x02, 0x03, 0x04};static USHORT usRegHoldStart = REG_HOLD_START;
static USHORT usRegHoldBuf[REG_HOLD_NREGS];static USHORT usRegCoilsStart = REG_COILS_START;
static uint8_t usRegCoilsBuf[REG_COILS_NREGS];static USHORT usRegDiscreteStart = REG_DISCRETE_START;
static uint8_t usRegDiscreteBuf[REG_DISCRETE_NREGS];
/****************************************************************************
* 名 称:eMBRegInputCB
* 功 能:读取输入寄存器,对应功能码是 04 eMBFuncReadInputRegister
* 入口参数:pucRegBuffer: 数据缓存区,用于响应主机
* usAddress: 寄存器地址
* usNRegs: 要读取的寄存器个数
* 出口参数:
* 注 意:上位机发来的 帧格式是: SlaveAddr(1 Byte)+FuncCode(1 Byte)
* +StartAddrHiByte(1 Byte)+StartAddrLoByte(1 Byte)
* +LenAddrHiByte(1 Byte)+LenAddrLoByte(1 Byte)+
* +CRCAddrHiByte(1 Byte)+CRCAddrLoByte(1 Byte)
* 3 区
****************************************************************************/
eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{eMBErrorCode eStatus = MB_ENOERR;int iRegIndex;//寄存器数组索引usAddress = usAddress - 1;//传进来的地址+1了,这里要减1//判断地址是否在输入寄存器范围内if( ( usAddress >= REG_INPUT_START ) && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) ){iRegIndex = ( int )( usAddress - usRegInputStart );//地址 - 开始地址 = 索引while( usNRegs > 0 ){*pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );//寄存器值高位*pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );//寄存器值低位iRegIndex++;usNRegs--;}}else{eStatus = MB_ENOREG;}return eStatus;
}
/****************************************************************************
* 名 称:eMBRegHoldingCB
* 功 能:对应功能码有:06 写保持寄存器 eMBFuncWriteHoldingRegister
* 16 写多个保持寄存器 eMBFuncWriteMultipleHoldingRegister
* 03 读保持寄存器 eMBFuncReadHoldingRegister
* 23 读写多个保持寄存器 eMBFuncReadWriteMultipleHoldingRegister
* 入口参数:pucRegBuffer: 数据缓存区,用于响应主机
* usAddress: 寄存器地址
* usNRegs: 要读写的寄存器个数
* eMode: 功能码
* 出口参数:
* 注 意:4 区
****************************************************************************/
eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
{eMBErrorCode eStatus = MB_ENOERR;int iRegIndex;usAddress = usAddress - 1;if((usAddress >= REG_HOLD_START) && ((usAddress+usNRegs) <= (REG_HOLD_START + REG_HOLD_NREGS))){iRegIndex = (int)(usAddress - usRegHoldStart);switch(eMode){case MB_REG_READ://读寄存器while(usNRegs > 0){*pucRegBuffer++ = (uint8_t)(usRegHoldBuf[iRegIndex] >> 8);*pucRegBuffer++ = (uint8_t)(usRegHoldBuf[iRegIndex] & 0xFF);iRegIndex++;usNRegs--;}break;case MB_REG_WRITE://写寄存器while(usNRegs > 0){usRegHoldBuf[iRegIndex] = *pucRegBuffer++ << 8;usRegHoldBuf[iRegIndex] |= *pucRegBuffer++;iRegIndex++;usNRegs--;}}}else//错误{eStatus = MB_ENOREG;}return eStatus;
}/****************************************************************************
* 名 称:eMBRegCoilsCB
* 功 能:对应功能码有:01 读线圈 eMBFuncReadCoils
* 05 写线圈 eMBFuncWriteCoil
* 15 写多个线圈 eMBFuncWriteMultipleCoils
* 入口参数:pucRegBuffer: 数据缓存区,用于响应主机
* usAddress: 线圈地址
* usNCoils: 要读写的线圈个数
* eMode: 功能码
* 出口参数:
* 注 意:如继电器
* 0 区
****************************************************************************/
eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
{eMBErrorCode eStatus = MB_ENOERR;USHORT iRegIndex;USHORT usCoilGroups = ((usNCoils - 1) / 8 + 1);UCHAR ucStatus = 0;UCHAR ucBits = 0;UCHAR ucDisp = 0;usAddress = usAddress - 1;if((usAddress >= REG_COILS_START) && ((usAddress + usNCoils) <= (REG_COILS_START + REG_COILS_NREGS))){iRegIndex = (int)(usAddress - usRegCoilsStart);switch(eMode){case MB_REG_READ://读线圈while(usCoilGroups--){ucDisp = 0;ucBits = 8;while((usNCoils--) != 0 && (ucBits--) != 0){ucStatus |= (usRegCoilsBuf[iRegIndex++] << (ucDisp++));}*pucRegBuffer++ = ucStatus;}break;case MB_REG_WRITE://写线圈while(usCoilGroups--){ucStatus = *pucRegBuffer++;ucBits = 8;while((usNCoils--) != 0 && (ucBits--) != 0){usRegCoilsBuf[iRegIndex++] = ucStatus & 0X01;ucStatus >>= 1;}}}}else//错误{eStatus = MB_ENOREG;}return eStatus;
}
/****************************************************************************
* 名 称:eMBRegDiscreteCB
* 功 能:读取离散寄存器,对应功能码有:02 读离散寄存器 eMBFuncReadDiscreteInputs
* 入口参数:pucRegBuffer: 数据缓存区,用于响应主机
* usAddress: 寄存器地址
* usNDiscrete: 要读取的寄存器个数
* 出口参数:
* 注 意:1 区
****************************************************************************/
eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{eMBErrorCode eStatus = MB_ENOERR;USHORT iRegIndex;USHORT usDiscreteGroups = ((usNDiscrete - 1) / 8 + 1);UCHAR ucStatus = 0;UCHAR ucBits = 0;UCHAR ucDisp = 0;usAddress = usAddress - 1;if((usAddress >= REG_DISCRETE_START) && ((usAddress + usNDiscrete) <= (REG_DISCRETE_START + REG_DISCRETE_NREGS))){iRegIndex = (int)(usAddress - usRegDiscreteStart);while(usDiscreteGroups--){ucDisp = 0;ucBits = 8;while((usNDiscrete--) != 0 && (ucBits--) != 0){if(usRegDiscreteBuf[iRegIndex]){ucStatus |= (1 << ucDisp);}ucDisp++;}*pucRegBuffer++ = ucStatus;}}else//错误{eStatus = MB_ENOREG;}return eStatus;
}
4.5 写中断服务函数
void USART2_IRQHandler(void)
{/* USER CODE BEGIN USART2_IRQn 0 */if((__HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE) != RESET)&& (__HAL_UART_GET_IT_SOURCE(&huart2, UART_IT_RXNE)!= RESET)) {__HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_RXNE);prvvUARTRxISR();return;}if ((__HAL_UART_GET_IT_SOURCE(&huart2, UART_IT_TXE)!= RESET)&& (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TXE)!= RESET)){__HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_TXE);prvvUARTTxReadyISR();return;}}void TIM7_IRQHandler(void)
{/* USER CODE BEGIN TIM7_IRQn 0 */if(__HAL_TIM_GET_FLAG(&htim7, TIM_FLAG_UPDATE) != RESET&& __HAL_TIM_GET_IT_SOURCE(&htim7, TIM_IT_UPDATE) !=RESET) {__HAL_TIM_CLEAR_FLAG(&htim7, TIM_FLAG_UPDATE);prvvTIMERExpiredISR();}}
5 调用协议栈
依次调用
eMBInit( MB_RTU, 1, 3, 9600, MB_PAR_NONE );
eMBEnable();
然后把eMBPoll();放在循环里
6 测试
以读输入寄存器为例
功能码04
使用软件Modbus Poll
软件配置:
效果:
OK !
移植结束。
想深入了解FreeModbus源码,可以看我写的其笔记FreeModbus,写的不好,对源码理解上以及写的时候,难免有瑕疵纰漏,如有错误还请大佬指出。您原谅着瞧,原谅着看。
相关文章:

Clion开发STM32——移植FreeModbus
STM32型号 :STM32H743VIT6 FreeModbus版本 :1.6 使用工具:stm32cubeMX,Clion 使用STM32作从机,模式:RTU 网上用keil的比较多,用Clion的比较少,如果你也用Clion,那么希望…...
c++栈笔记
一种常见的数据结构,遵循后进先出,先进后出的原则。地址不连续,栈顶(top) 1.常见函数 stack<int> s;定义一个参数类型为int 的栈 名为ss.push()向栈中插入元素s.emplace()压栈,无返回值s.pop()删除…...

Oracle配置TCPS加密协议测试
文章目录 一、环境信息二、配置过程1.创建证书2.监听配置2.1.配置sqlnet.ora2.2.配置listener.ora文件2.3.配置tnsnames.ora文件2.4.重载监听 3.数据库本地测试3.1. tcps登录测试3.2.日志监控 一、环境信息 操作系统:Linux 版本信息:Oracle 19c 参考文档…...

Jetpack Compose 通过 OkHttp 发送 HTTP 请求的示例
下面是一个使用 Kotlin 和 Jetpack Compose 来演示通过 OkHttp 发送 HTTP 请求的示例。这个示例包括在 Jetpack Compose 中发送一个 GET 请求和一个 POST 请求,并显示结果。 添加okhttp依赖 首先,在你的 build.gradle.kts 文件中添加必要的依赖…...

Pytorch使用教学3-特殊张量的创建与类型转化
1 特殊张量的创建 与numpy类似,PyTorch中的张量也有很多特殊创建的形式。 zeros:全0张量 # 形状为2行3列 torch.zeros([2, 3]) # tensor([[0., 0., 0.], # [0., 0., 0.]])ones:全1张量 # 形状为2行3列 torch.ones([2, 3]) # tensor([[1., 1., 1.], # …...

【日记】办个护照不至于有这种刑事罪犯一样的待遇吧……(737 字)
正文 暴晒,中午出去骑共享单车,座垫都不敢坐。 至于为什么,中午觉都不睡跑出去,是因为今天他们办承兑汇票的业务,搞了一天,中午不休息,说可能还会用到我的指纹,让我 on call。我心想…...
【矩阵微分】在不涉及张量的前提下计算矩阵对向量的导数并写出二阶泰勒展开
本篇内容摘自CMU 16-745最优控制的第10讲 “Nonlinear Trajectory Optimization”。 如何在不涉及张量运算的前提下,计算矩阵对向量的导数并写出二阶泰勒展开 在多维微积分中,计算矩阵对向量的导数和二阶泰勒展开是一项重要的任务。本文将介绍如何在不涉…...

数据结构之判断平衡二叉树详解与示例(C,C++)
文章目录 AVL树定义节点定义计算高度获取平衡因子判断是否为平衡二叉树完整示例代码结论 在计算机科学中,二叉树是一种非常重要的数据结构。它们被广泛用于多种算法中,如排序、查找等。然而,普通的二叉树在极端情况下可能退化成链表ÿ…...
深入解析仓颉编程语言:函数式编程的核心特性
摘要 仓颉编程语言以其独特的语法和功能,为开发者提供了强大的编程工具。本文将深入探讨仓颉语言中的嵌套函数、Lambda 表达式和闭包等函数式编程的核心特性,帮助开发者更好地理解和利用这些工具。 引言 在现代编程语言中,函数式编程范式越…...

springboot惠农服务平台-计算机毕业设计源码50601
目录 1 绪论 1.1 研究背景 1.2研究意义 1.3论文结构与章节安排 2 惠农服务平台app 系统分析 2.1 可行性分析 2.2 系统功能分析 2.3 系统用例分析 2.4 系统流程分析 2.5本章小结 3 惠农服务平台app 总体设计 3.1 系统功能模块设计 3.2 数据库设计 表access_token (…...

Lua脚本简单理解
目录 1.安装 2.语法 2.1Lua数据类型 2.2变量 2.3lua循环 2.4流程控制 2.5函数 2.6运算符 2.7关系运算符 3.lua脚本在redis中的使用 3.1lua脚本再redis简单编写 3.2普通锁Lua脚本 3.3可重入锁lua脚本 1.安装 centos安装 安装指令: yum -y update yum i…...
AutoSAR自适应平台架构总览--AP的初认识
AutoSAR自适应平台架构总览:AP 基础设施层(Foundation Layer)核心操作系统(Core OS)通信管理(Communication Management) 服务层(Services Layer)诊断服务(Diagnostics S…...

GPT-4o Mini:探索最具成本效益的小模型在软件开发中的应用
随着人工智能技术的迅猛发展,自然语言处理(NLP)领域也取得了显著的进步。OpenAI 最新发布的 GPT-4o Mini 模型,以其卓越的性能和极具竞争力的价格,成为了广大开发者关注的焦点。作为一名长期关注人工智能及其在软件开发…...

{Spring Boot 原理篇} Spring Boot自动装配原理
SpringBootApplication 1,Spring Boot 应用启动,SpringBootApplication标注的类就是启动类,它去实现配置类中的Bean的自动装配 SpringBootApplication public class SpringbootRedis01Application {public static void main(String[] args)…...

QEMU源码全解析 —— CPU虚拟化(10)
接前一篇文章: 本文内容参考: 《趣谈Linux操作系统》 —— 刘超,极客时间 《QEMU/KVM》源码解析与应用 —— 李强,机械工业出版社 《深度探索Linux系统虚拟化原理与实现》—— 王柏生 谢广军, 机械工业出版社 特此致谢! 二、x86架构CPU虚拟化 3. VMX 上一回讲解了支…...
46、PHP实现矩阵中的路径
题目: PHP实现矩阵中的路径 描述: 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。 路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向…...

c++笔记2
目录 2.2 栈底(bottom) } 大数乘大数 节点:包含一个数据元素及若干指向子树分支的信息 。 节点的度:一个节点拥有子树的数目称为节点的度 。 叶子节点:也称为终端节点,没有子树的节点或者度为零的节点…...
通过Lua脚本手写redis分布式锁
1、手写 Redis 分布式锁,包括上锁、解锁、自动续期。 此功能实现采用 Lua脚本实现,Lua脚本可以保证原子性。 setnx可以实现分布式锁,但是无法实现可重入锁,所以用hset来代替setnx实现可重入的分布式锁。 -- lock if redis.call…...
解析银行个人征信系统
银行个人征信系统,也被称为个人信用信息基础数据库或金融信用信息基础数据库,是我国社会信用体系的重要基础设施。该系统由中国人民银行组织国内相关金融机构建立,旨在依法采集、整理、保存、加工自然人(法人)及其他组…...

AttributeError: ‘list‘ object has no attribute ‘text‘
AttributeError: ‘list‘ object has no attribute ‘text‘ 目录 AttributeError: ‘list‘ object has no attribute ‘text‘ 【常见模块错误】 【解决方案】 示例代码 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页,我是博主英…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving
地址:LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂,正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...
深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向
在人工智能技术呈指数级发展的当下,大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性,吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型,成为释放其巨大潜力的关键所在&…...
深入浅出WebGL:在浏览器中解锁3D世界的魔法钥匙
WebGL:在浏览器中解锁3D世界的魔法钥匙 引言:网页的边界正在消失 在数字化浪潮的推动下,网页早已不再是静态信息的展示窗口。如今,我们可以在浏览器中体验逼真的3D游戏、交互式数据可视化、虚拟实验室,甚至沉浸式的V…...