当前位置: 首页 > news >正文

STM32F103_LL库+寄存器学习笔记12 - 提高串口通讯程序的健壮性:异常监控 + 超时保护机制

导言


首先,进行USART和DMA状态监测、记录异常状态并主动处理,是高健壮性嵌入式系统开发的核心思想之一。
这种机制看似复杂,实则能有效保障系统长期、稳定地运行:

  • 提升通讯可靠性。
  • 降低维护成本。
  • 增强系统自恢复能力。

因此,建议你在STM32项目中实现USART和DMA的健康监测机制。

另外,STM32串口+DMA通信中引入超时保护机制的主要目的是:

  • 防止通讯过程中数据不完整或缺失(数据量小于半传输阈值,导致DMA半满、满传输中断均未触发)。可能有人会问?不是有接收空闲中断吗?确实有。但是,有可能接收空闲中断因某些原因导致失效。
  • 利用定时器,定期检测DMA接收缓冲区数据变化情况。比如若在一定时间内未收到数据,触发处理或重启DMA。

因此,引入超时机制(Timeout),可有效提升通信实时性与健壮性。

项目地址:https://github.com/q164129345/MCU_Develop/tree/main/stm32f103_ll_library11_usart_dma_error_check

一、监控串口异常


1.1、寄存器USART_SR

在这里插入图片描述
在这里插入图片描述
如上所示,串口的状态寄存器ISR一共有4个错误标志。其中位0的校验错误是在奇偶校验功能开启后才会触发,然而,这个功能一般不会开启。
以上这4个错误的清除方式都是先读USART_SR再读USART_DR,跟清除DILE空闲中断标志一样。所以,有了清除IDLE标志的代码后,不用单独去清除这些错误标志了。

NE、FE、ORE中断都依赖RXNE中断。 如上所示,参考手册说到NE、FE、ORE中断都依赖RXNE中断。就是说,想及时获取NE、FE、ORE中断就需要让RXNEIE置1,开启RXNE中断。但是,如果数据流量很大,开启RXNE中断会使每个字节到达时都进入中断,这可能导致中断频繁且影响系统性能。利用DMA接收数据,开启空闲中断来判断数据帧的结束。在空闲中断处理函数中,除了处理数据,还可以检查错误标志。不过,前提是必须有数据到达,才会触发空闲中断。所以,更好的方案是USART1全局中断与主轮询都要监控串口异常,记录异常并及时恢复。

1.2、代码实现(LL库)

/* LL库 */
uint16_t usart1Error = 0; // 记录串口发生的错误次数static __inline uint8_t USART1_Error_Handler(void) {// 错误处理:检查USART错误标志(ORE、NE、FE、PE)if (LL_USART_IsActiveFlag_ORE(USART1) ||LL_USART_IsActiveFlag_NE(USART1)  ||LL_USART_IsActiveFlag_FE(USART1)  ||LL_USART_IsActiveFlag_PE(USART1)){// 通过读SR和DR来清除错误标志volatile uint32_t tmp = USART1->SR;tmp = USART1->DR;(void)tmp;return 1;} else {return 0;} 
}void USART1_IRQHandler(void)
{// 错误处理:检查USART错误标志(ORE、NE、FE、PE)if (USART1_Error_Handler()) {usart1Error++; // 记录事件} else if (LL_USART_IsActiveFlag_IDLE(USART1)) { // 空闲中断// 处理空闲中断} else {  // 其他中断// 处理其他中断}
}

1.3、代码实现(寄存器方式)

uint16_t usart1Error = 0; // 记录串口发生的错误次数static __inline uint8_t USART1_Error_Handler(void) {// 寄存器方式:检查错误标志(PE、FE、NE、ORE分别位0~3)if (USART1->SR & ((1UL << 0) | (1UL << 1) | (1UL << 2) | (1UL << 3))) {// 清除错误标志volatile uint32_t tmp = USART1->SR;tmp = USART1->DR;(void)tmp;return 1;} else {return 0;}
}void USART1_IRQHandler(void)
{// 寄存器方式:检查错误标志(PE、FE、NE、ORE分别位0~3)if (USART1_Error_Handler()) {usart1Error++; // 记录事件} else if (USART1->SR & (1UL << 4)) { // 空闲中断// 处理空闲中断} else { // 其他中断// 处理其他中断}
}

为了更好地排查问题,可以在检测到错误时记录错误日志或计数,便于后续进行调试和系统优化。 全局变量usart1Error的目的就是统计错误的次数,在适当的实际将它上报给上位机。上位机有一个GUI程序,为了了解单片机系统的串口通讯情况,可以将usart1Error的计数显示在GUI上。当计数经常递增,表明串口有问题,需要去排查硬件或者线束问题等等。

根据实际需求,可以设计一些自动恢复机制。 当检测到错误时,除了清除错误标志、统计错误的次数。可以考虑重启串口、重新配置DMA通道或者进行数据重传,从而提高系统的鲁棒性。

1.4、串口重启的方法(重新初始化)

当错误次数累积太多时,可以考虑重启串口。进一步提升系统的鲁棒性。

1.4.1、代码实现(LL库)

/*** @brief  当检测到USART1异常时,重新初始化USART1及其相关DMA通道。* @note   此函数首先停止DMA1通道4(USART1_TX)和通道5(USART1_RX),确保DMA传输终止,*         然后禁用USART1及其DMA接收请求,并通过读SR和DR清除挂起的错误标志,*         经过短暂延时后调用MX_USART1_UART_Init()重新配置USART1、GPIO和DMA,*         MX_USART1_UART_Init()内部已完成USART1中断的配置,无需额外使能。* @retval None*/
void USART1_Reinit(void)
{/* 禁用DMA,如果USART1启用了DMA的话 */LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_4); // 禁用DMA1通道4while(LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_4)) { // 等待通道完全关闭(可以添加超时机制以避免死循环)// 空循环等待}LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_5); // 禁用DMA1通道5while(LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_5)) {  // 等待通道完全关闭(可以添加超时机制以避免死循环)// 空循环等待}/* USART1 */NVIC_DisableIRQ(USART1_IRQn); // 1. 禁用USART1全局中断,避免重启过程中产生新的中断干扰LL_USART_DisableDMAReq_RX(USART1); // 2. 禁用USART1的DMA接收请求(如果使用DMA接收)LL_USART_Disable(USART1); // 3. 禁用USART1// 4. 读SR和DR以清除挂起的错误标志(例如IDLE、ORE、NE、FE、PE)volatile uint32_t tmp = USART1->SR;tmp = USART1->DR;(void)tmp;for (volatile uint32_t i = 0; i < 1000; i++); // 5. 可选:短暂延时,确保USART完全关闭tx_dma_busy = 0; // 复位发送标志!!!!!!!!!!!!!!!!!!!MX_USART1_UART_Init(); // 重新初始化USART1DMA1_Channel5_Configure(); // 重新初始化DMA1通道5
}

1.4.2、代码实现(寄存器方式)

// 配置DMA1的通道4:普通模式,内存到外设(flash->USART1_TX),优先级高,存储器地址递增、数据大小都是8bit
__STATIC_INLINE void DMA1_Channel4_Configure(void) {// 开启时钟RCC->AHBENR |= (1UL << 0UL); // 开启DMA1时钟// 设置并开启全局中断NVIC_SetPriority(DMA1_Channel4_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),2, 0));NVIC_EnableIRQ(DMA1_Channel4_IRQn);// 数据传输方向DMA1_Channel4->CCR &= ~(1UL << 14UL); // 外设到存储器模式DMA1_Channel4->CCR |= (1UL << 4UL); // DIR位设置1(内存到外设)// 通道优先级DMA1_Channel4->CCR &= ~(3UL << 12UL); // 清除PL位DMA1_Channel4->CCR |= (2UL << 12UL);  // PL位设置10,优先级高// 循环模式DMA1_Channel4->CCR &= ~(1UL << 5UL);  // 清除CIRC位,关闭循环模式// 增量模式DMA1_Channel4->CCR &= ~(1UL << 6UL);  // 外设存储地址不递增DMA1_Channel4->CCR |= (1UL << 7UL);   // 存储器地址递增// 数据大小DMA1_Channel4->CCR &= ~(3UL << 8UL);  // 外设数据宽度8位,清除PSIZE位,相当于00DMA1_Channel4->CCR &= ~(3UL << 10UL); // 存储器数据宽度8位,清除MSIZE位,相当于00// 中断DMA1_Channel4->CCR |= (1UL << 1UL);   // 开启发送完成中断
}__STATIC_INLINE void USART1_Configure(void) {/* 1. 使能外设时钟 */// RCC->APB2ENR 寄存器控制 APB2 外设时钟RCC->APB2ENR |= (1UL << 14UL); // 使能 USART1 时钟 (位 14)RCC->APB2ENR |= (1UL << 2UL);  // 使能 GPIOA 时钟 (位 2)/* 2. 配置 GPIO (PA9 - TX, PA10 - RX) */// GPIOA->CRH 寄存器控制 PA8-PA15 的模式和配置// PA9: 复用推挽输出 (模式: 10, CNF: 10)GPIOA->CRH &= ~(0xF << 4UL);        // 清零 PA9 的配置位 (位 4-7)GPIOA->CRH |= (0xA << 4UL);         // PA9: 10MHz 复用推挽输出 (MODE9 = 10, CNF9 = 10)// PA10: 浮空输入 (模式: 00, CNF: 01)GPIOA->CRH &= ~(0xF << 8UL);        // 清零 PA10 的配置位 (位 8-11)GPIOA->CRH |= (0x4 << 8UL);         // PA10: 输入模式,浮空输入 (MODE10 = 00, CNF10 = 01)// 开启USART1全局中断NVIC_SetPriority(USART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0)); // 优先级1(优先级越低相当于越优先)NVIC_EnableIRQ(USART1_IRQn);/* 3. 配置 USART1 参数 */// (1) 设置波特率 115200 (系统时钟 72MHz, 过采样 16)// 波特率计算: USART_BRR = fPCLK / (16 * BaudRate)// 72MHz / (16 * 115200) = 39.0625// 整数部分: 39 (0x27), 小数部分: 0.0625 * 16 = 1 (0x1)USART1->BRR = (39 << 4UL) | 1;      // BRR = 0x271 (39.0625)// (2) 配置数据帧格式 (USART_CR1 和 USART_CR2)USART1->CR1 &= ~(1UL << 12UL);      // M 位 = 0, 8 位数据USART1->CR2 &= ~(3UL << 12UL);      // STOP 位 = 00, 1 个停止位USART1->CR1 &= ~(1UL << 10UL);      // 没奇偶校验// (3) 配置传输方向 (收发双向)USART1->CR1 |= (1UL << 3UL);        // TE 位 = 1, 使能发送USART1->CR1 |= (1UL << 2UL);        // RE 位 = 1, 使能接收// (4) 禁用硬件流控 (USART_CR3)USART1->CR3 &= ~(3UL << 8UL);       // CTSE 和 RTSE 位 = 0, 无流控// (5) 配置异步模式 (清除无关模式位)USART1->CR2 &= ~(1UL << 14UL);      // LINEN 位 = 0, 禁用 LIN 模式USART1->CR2 &= ~(1UL << 11UL);      // CLKEN 位 = 0, 禁用时钟输出USART1->CR3 &= ~(1UL << 5UL);       // SCEN 位 = 0, 禁用智能卡模式USART1->CR3 &= ~(1UL << 1UL);       // IREN 位 = 0, 禁用 IrDA 模式USART1->CR3 &= ~(1UL << 3UL);       // HDSEL 位 = 0, 禁用半双工// (6) 中断USART1->CR1 |= (1UL << 4UL);        // 使能USART1空闲中断 (IDLEIE, 位4)// (7) 关联DMA的接收请求USART1->CR3 |= (1UL << 6UL);        // 使能USART1的DMA接收请求(DMAR,位6) // (7) 启用 USARTUSART1->CR1 |= (1UL << 13UL);       // UE 位 = 1, 启用 USART
}// 配置USART1_RX的DMA1通道5
__STATIC_INLINE void DMA1_Channel5_Configure(void) {// 时钟RCC->AHBENR |= (1UL << 0UL); // 开启DMA1时钟// 开启全局中断NVIC_SetPriority(DMA1_Channel5_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));NVIC_EnableIRQ(DMA1_Channel5_IRQn);/* 1. 禁用DMA通道5,等待其完全关闭 */DMA1_Channel5->CCR &= ~(1UL << 0);  // 清除EN位while(DMA1_Channel5->CCR & 1UL);    // 等待DMA通道5关闭/* 2. 配置外设地址和存储器地址 */DMA1_Channel5->CPAR = (uint32_t)&USART1->DR;  // 外设地址为USART1数据寄存器DMA1_Channel5->CMAR = (uint32_t)rx_buffer;    // 存储器地址为rx_bufferDMA1_Channel5->CNDTR = RX_BUFFER_SIZE;        // 传输数据长度为缓冲区大小/* 3. 配置DMA1通道5 CCR寄存器- DIR   = 0:外设→存储器- CIRC  = 1:循环模式- PINC  = 0:外设地址不自增- MINC  = 1:存储器地址自增- PSIZE = 00:外设数据宽度8位- MSIZE = 00:存储器数据宽度8位- PL    = 10:优先级设为高- MEM2MEM = 0:非存储器到存储器模式*/DMA1_Channel5->CCR = 0;  // 清除之前的配置DMA1_Channel5->CCR |= (1UL << 5);       // 使能循环模式 (CIRC,bit5)DMA1_Channel5->CCR |= (1UL << 7);       // 使能存储器自增 (MINC,bit7)DMA1_Channel5->CCR |= (3UL << 12);      // 设置优先级为非常高 (PL置为“11”,bit12-13)// 增加传输完成与传输过半中断DMA1_Channel5->CCR |= (1UL << 1);       // 传输完成中断 (TCIE)DMA1_Channel5->CCR |= (1UL << 2);       // 传输过半中断 (HTIE)/* 4. 使能DMA通道5 */DMA1_Channel5->CCR |= 1UL;  // 置EN位启动通道
}/*** @brief  当检测到USART1异常时,重新初始化USART1及其相关DMA通道。* @note   此函数首先停止DMA1通道4(USART1_TX)和通道5(USART1_RX),确保DMA传输终止,*         然后禁用USART1及其DMA接收请求,并通过读SR和DR清除挂起的错误标志,*         经过短暂延时后调用MX_USART1_UART_Init()重新配置USART1、GPIO和DMA,*         MX_USART1_UART_Init()内部已完成USART1中断的配置,无需额外使能。* @retval None*/
void USART1_Reinit(void)
{/* 禁用DMA,如果USART1启用了DMA的话 */DMA1_Channel4->CCR &= ~(1UL << 0); // 禁用DMA1通道4:清除CCR的EN位(位0)while (DMA1_Channel4->CCR & 1UL) { // 等待DMA1通道4完全关闭(建议增加超时处理)// 空循环等待}DMA1_Channel5->CCR &= ~(1UL << 0); // 禁用DMA1通道5:清除CCR的EN位(位0)while (DMA1_Channel5->CCR & 1UL) { // 等待DMA1通道5完全关闭(建议增加超时处理)// 空循环等待}NVIC_DisableIRQ(USART1_IRQn); // 禁用USART1全局中断,避免重启过程中产生新的中断干扰USART1->CR3 &= ~(1UL << 6); // 禁用USART1的DMA接收请求:清除CR3的DMAR位(位6)USART1->CR1 &= ~(1UL << 13); // 禁用USART1:清除CR1的UE位(位13)// 读SR和DR以清除挂起的错误标志(例如IDLE、ORE、NE、FE、PE)volatile uint32_t tmp = USART1->SR;tmp = USART1->DR;(void)tmp;for (volatile uint32_t i = 0; i < 1000; i++); // 可选:短暂延时,确保USART1完全关闭tx_dma_busy = 0; // 复位发送标志!!!!!/* 重新初始化USART1、DMA1 */USART1_Configure(); // USART1DMA1_Channel4_Configure(); // DMA1通道4DMA1_Channel5_Configure(); // DMA1通道5
}

1.4.3、测试

测试方法:当串口在频繁收发时,中途让串口重启,看看串口收发能不能恢复正常。
在这里插入图片描述
如上所示,一开始串口正在正常收发大量的数据。接着,设置全局变量rebootUsart1等于1后,在主循环里就会执行函数USART1_Reinit()将串口与DMA重新初始化。可以看到,重新初始化后,串口能正常恢复收发!!

二、监控DMA异常


2.1、寄存器DMA_ISR

在这里插入图片描述
DMA每一个通道都只有一个错误标志TEIFx。 如上所示,例如,当TEIF5被置1时,表示DMA1通道5的传输发生了错误。
在这里插入图片描述
清除DMA通道的传输错误,使用寄存器DMA_IFCR。 如上所示,例如,当DMA1通道5发生传输错误之后,寄存器DMA_ISR的位19-TEIF5会被单片机系统置1。此时,通过软件将寄存器DMA_IFCR的位19-CTEIF置1,单片机系统就会清除错误。

2.2、代码实现(LL库)

/* LL库 */
uint16_t dma1Channel4Error = 0;
uint16_t dam1Channel5Error = 0;/* DMA1通道4,USART1发送 */
static __inline uint8_t DMA1_Channel4_Error_Handler(void) {// 检查通道4是否发生传输错误(TE)if (LL_DMA_IsActiveFlag_TE4(DMA1)) {// 清除传输错误标志LL_DMA_ClearFlag_TE4(DMA1);// 禁用DMA通道4,停止当前传输LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_4);// 清除USART1的DMA发送请求(DMAT位)LL_USART_DisableDMAReq_TX(USART1);// 清除发送标志!!tx_dma_busy = 0;return 1;} else {return 0;}
}void DMA1_Channel4_IRQHandler(void) {if (DMA1_Channel4_Error_Handler()) { // 传输错误中断dma1Channel4Error++; // 统计错误次数tx_dma_busy = 0; // 复位发送标志} else if () {// 传输完成中断处理}
}/* DMA1通道5,USART1接收 */
static __inline uint8_t DMA1_Channel5_Error_Hanlder(void) {// 检查通道5是否发生传输错误(TE)if (LL_DMA_IsActiveFlag_TE5(DMA1)) {// 清除传输错误标志LL_DMA_ClearFlag_TE5(DMA1);// 禁用DMA通道5,停止当前传输LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_5);// 重新设置传输长度,恢复到初始状态LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_5, RX_BUFFER_SIZE);// 重新使能DMA通道5,恢复接收LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_5);return 1;} else {return 0;}
}void DMA1_Channel5_IRQHandler(void) {if (DMA1_Channel5_Error_Hanlder()) {dam1Channel5Error++; // 统计错误次数} else if () {// 传输过半、传输完成中断处理}
}

2.3、代码实现(寄存器方式)

uint16_t dma1Channel4Error = 0;
uint16_t dam1Channel5Error = 0;/* DMA1通道4,USART1发送 */
static __inline uint8_t DMA1_Channel4_Error_Handler(void) {// 检查传输错误(TE)标志,假设TE对应位(1UL << 15)(请根据具体芯片参考手册确认)if (DMA1->ISR & (1UL << 15)) {// 清除TE错误标志DMA1->IFCR |= (1UL << 15);// 禁用DMA通道4DMA1_Channel4->CCR &= ~(1UL << 0);// 清除USART1中DMAT位USART1->CR3 &= ~(1UL << 7);// 清除发送标志!!tx_dma_busy = 0;return 1;} else {return 0;}
}void DMA1_Channel4_IRQHandler(void) {if (DMA1_Channel4_Error_Handler()) { // 传输错误中断dma1Channel4Error++; // 统计错误次数tx_dma_busy = 0; // 复位发送标志} else if () {// 传输完成中断处理}
}/* DMA1通道5,USART1接收 */
static __inline uint8_t DMA1_Channel5_Error_Hanlder(void) {// 检查传输错误(TE)标志,假设TE对应位(1UL << 19)(请确认具体位)if (DMA1->ISR & (1UL << 19)) {// 清除错误标志DMA1->IFCR |= (1UL << 19);// 禁用DMA通道5DMA1_Channel5->CCR &= ~(1UL << 0);// 重置传输计数DMA1_Channel5->CNDTR = RX_BUFFER_SIZE;// 重新使能DMA通道5DMA1_Channel5->CCR |= 1UL;return 1;} else {return 0;}
}void DMA1_Channel5_IRQHandler(void) {if (DMA1_Channel5_Error_Hanlder()) {dam1Channel5Error++; // 统计错误次数} else if () {// 传输过半、传输完成中断处理}
}

DMA传输错误,需立即清除,以免影响数据收发。 统计错误次数跟串口统计错误次数的目的是一样的。将信息传递给上位机的GUI程序,提供发生单片机系统问题的一些参考信息。

三、超时保护机制


超时保护机制,实际就是超时重启机制。 这里只简单聊一下实现思路,不提供代码,因为实现的方式太多了,需要根据项目的实际情况来修改。比如,某个项目的STM32控制板跟上位机有很频繁的串口通讯。正常情况下,每1S至少有10个消息包下发给STM32控制板。那么,此时可以在一个软件定时器(回调周期5S)里,监控消息包的数量(可以在中断回调里用一个变量来统计收到的串口消息包)。如果数量没有变化,可以认为有通讯超时异常。可以调用函数USART1_Reinit()去重启USART、DMA等外设。如果超过20S都还没有收到任何消息,可以调用函数NVIC_SystemReset()重启单片机系统等等。

相关文章:

STM32F103_LL库+寄存器学习笔记12 - 提高串口通讯程序的健壮性:异常监控 + 超时保护机制

导言 首先&#xff0c;进行USART和DMA状态监测、记录异常状态并主动处理&#xff0c;是高健壮性嵌入式系统开发的核心思想之一。 这种机制看似复杂&#xff0c;实则能有效保障系统长期、稳定地运行&#xff1a; 提升通讯可靠性。降低维护成本。增强系统自恢复能力。 因此&…...

搜索-BFS

马上蓝桥杯了&#xff0c;最近刷了广搜&#xff0c;感觉挺有意思的&#xff0c;广搜题类型都差不多&#xff0c;模板也一样&#xff0c;大家写的时候可以直接套模板 这里给大家讲一个比较经典的广搜题-迷宫 题目问问能否走到 (n,m) 位置&#xff0c;假设最后一个点是我们的&…...

Keil调试(RTT Debug 断点)

调试 打印操作 方式接口优缺点串口打印TXRX简单,但是占用串口,速度慢,重定向fputc简单RTT打印SWDIOSWCLK速度快,不占额外接口,直接移植RTT库断点打印SWDIOSWCLKDebug的时候断点操作SWOSWDIOSWCLKSWO需要连接SWO引脚,重定向fputc简单 这里我只介绍RTT打印和断点打印; 一. RT…...

【jQuery】插件

目录 一、 jQuery插件 1. 瀑布流插件&#xff1a; jQuery 之家 http://www.htmleaf.com/ 2. 图片懒加载&#xff1a; jQuery 插件库 http://www.jq22.com/ 3. 全屏滚动 总结不易~ 本章节对我有很大收获&#xff0c;希望对你也是~~~ 一、 jQuery插件 jQuery 功能…...

leetcode 28 Find the Index of the First Occurrence in a String

直接用kmp算法 class Solution { public:int strStr(string haystack, string needle) {return kmp(haystack,needle);}int kmp(std::string &text,std::string &pattern){int n text.size();int m pattern.size();if(m 0)return 0;std::vector<int> next;ne…...

nginx 动静分离

一.动静分离 1.动静分离的好处 Apache Tocmat 严格来说是一款java EE服务器&#xff0c;主要是用来处理 servlet请求。处理css、js、图片这些静态文件的IO性能不够好&#xff0c;因此&#xff0c;将静态文件交给nginx处理&#xff0c;可以提高系统的访问速度&#xff0c;减少…...

1.2 斐波那契数列模型:LeetCode 面试题 08.01. 三步问题

动态规划解三步问题&#xff1a;LeetCode 面试题 08.01. 三步问题 1. 题目链接 LeetCode 面试题 08.01. 三步问题 题目要求&#xff1a;小孩上楼梯&#xff0c;每次可以走1、2或3步&#xff0c;计算到达第 n 阶台阶的不同方式数&#xff0c;结果需对 1e9 7 取模。 2. 题目描述…...

关于AutoMapper

AutoMapper 概述 AutoMapper 是一个基于约定的对象 - 对象映射库&#xff0c;主要用于在不同对象类型之间自动映射属性值。它能根据配置的映射规则&#xff0c;将源对象的属性值填充到目标对象中&#xff0c;避免了手动编写大量繁琐的对象映射代码。 作用 提升开发效率&…...

是否每一层之间都要线性变换和激活函数?

1. 神经网络层的基本组成 一个典型的神经网络层通常包含两个步骤&#xff1a; 线性变换&#xff08;加权求和&#xff09;&#xff1a; z Wx} b 其中W 是权重矩阵&#xff0c;b是偏置向量&#xff0c;是输入&#xff0c;z 是线性输出。激活函数&#xff1a; 其中&#xff0c…...

golang 的reflect包的常用方法

目录 reflect 包方法总结 类型 (Type) 方法 值 (Value) 方法 代码示例&#xff1a; reflect 包方法总结 p : Person{Name: "小明", Age: 22}t : reflect.TypeOf(&p)v : reflect.ValueOf(p) 类型 (Type) 方法 方法名描述示例               Na…...

CentOS 7 安装 EMQX (MQTT)

CentOS 7 安装 EMQX 通过 Yum 源安装 EMQX 支持通过 Yum 源安装&#xff0c;您可通过以下 Yum 命令从中自动下载和安装 EMQX。 通过以下命令配置 EMQX Yum 源&#xff1a; curl -s https://assets.emqx.com/scripts/install-emqx-rpm.sh | sudo bash安装以下依赖项&#xff…...

Flask项目部署:Flask + uWSGI + Nginx

目录 1,网络架构 2,环境安装 2.1,安装yum:Shell软件包管理器 2.2 安装python 2.3 安装uWSGI 2.4 安装Flask 3,上传工程包到服务器,打包Flask项目 4,创建和配置 uwsgi 配置文件 uwsgi.ini 4.1配置文件 4.2配置文件注释详解 5,启动服务 6,安装nginx 7,nginx配置 8,…...

软件工程面试题(十五)

1、servlet 创建过程以及ruquest,response,session的生命周期? Servlet的创建过程: 第一步 public class AAA extends HttpServlet{ 实现对应的doxxx方法 } 第二步: 在web.xml中配置 <servlet> <servlet-name></servlet-name> <servlet-c…...

当Kafka化身抽水马桶:论组件并发提升与系统可用性的量子纠缠关系

《当Kafka化身抽水马桶&#xff1a;论组件并发提升与系统可用性的量子纠缠关系》 引言&#xff1a;一场OOM引发的血案 某个月黑风高的夜晚&#xff0c;监控系统突然发出刺耳的警报——我们的数据发现流水线集体扑街。事后复盘发现&#xff1a;Kafka集群、Gateway、Discovery服…...

python和Java的区别

Python和Java是两种流行的编程语言&#xff0c;它们之间有一些重要的区别&#xff1a; 语法&#xff1a;Python是一种动态类型的脚本语言&#xff0c;语法简洁明了&#xff0c;通常使用缩进来表示代码块。Java是一种静态类型的编程语言&#xff0c;语法更为严格&#xff0c;需要…...

QFlightInstruments飞行仪表控件库

QFlightInstruments 是一个开源的飞行仪表控件库&#xff0c;专为基于 Qt 的应用程序设计。它提供了一系列仿真实飞机仪表的组件&#xff0c;适用于飞行模拟软件、航空电子系统或任何需要高仿真飞行仪表显示的项目。 主要功能 高仿真飞行仪表&#xff1a;包括空速表、高度表、…...

可发1区的超级创新思路(python\matlab实现):MPTS+Lconv+注意力集成机制的Transformer时间序列模型

首先声明,该模型为原创!原创!原创!且该思路还未有成果发表,感兴趣的小伙伴可以借鉴! 应用场景 该模型主要用于时间序列数据预测问题,包含功率预测、电池寿命预测、电机故障检测等等。 一、模型整体架构(本文以光伏功率预测为例) 本模型由多尺度特征提取模块(MPTS)…...

Nginx — Nginx版本升级

例如&#xff1a;将10.224.11.220、10.224.11.221、10.208.11.220 三台服务器上的Nginx从1.21.1版本升级到1.23.3版本。 一、Nginx升级步骤 步骤一&#xff1a;备份老版本的Nginx&#xff08;10.224.11.220、10.224.11.221、10.208.11.220&#xff09; #关闭Nginx cd /usr/l…...

CSS学习笔记6——网页布局

目录 一、元素的浮动属性、清除浮动 清除浮动的其他方法 1、使用空标签清除浮动影响 2、使用overflow属性清除浮动 3、使用伪元素清除浮动影响 原理 overflow属性 二、元素的定位 1、相对定位 2、绝对定位 ​编辑 3、固定定位 z-index层叠等级属性 一、元素的浮动…...

C语言【指针二】

引言 介绍&#xff1a;const修饰指针&#xff0c;野指针 应用&#xff1a;指针的使用&#xff08;strlen的模拟实现&#xff09;&#xff0c;传值调用和传指调用 一、const修饰指针 1.const修饰变量 简单回顾一下前面学过的const修饰变量&#xff1a;在变量前面加上const&…...

第十六届蓝桥杯模拟二(串口通信)

由硬件框图可以知道我们要配置LED 和按键 一.LED 先配置LED的八个引脚为GPIO_OutPut,锁存器PD2也是,然后都设置为起始高电平,生成代码时还要去解决引脚冲突问题 二.按键 按键配置,由原理图按键所对引脚要GPIO_Input 生成代码,在文件夹中添加code文件夹,code中添加fun.…...

Java List 集合取交集、并集、差集、补集

在Java中&#xff0c;集合操作是编程中非常常见的需求&#xff0c;尤其是在处理数据集合时&#xff0c;如List、Set等。本文将详细介绍如何在Java中实现List集合的交集、并集、差集和补集操作&#xff0c;并提供代码示例和实现方法。 1. 交集操作 交集是指两个集合中都存在的元…...

SkyWalking+Springboot实战

1、下载SkyWalking APM 1.手动下载 Downloads | Apache SkyWalkinghttps://skywalking.apache.org/downloads/ 2.链接下载 https://dlcdn.apache.org/skywalking/10.2.0/apache-skywalking-apm-10.2.0.tar.gzhttps://dlcdn.apache.org/skywalking/10.2.0/apache-skywalking-…...

【小兔鲜】day01 项目、Vue3介绍、组合式API、小案例

【小兔鲜】day01 项目、Vue3介绍、组合式API、小案例 0. 市场上Vue前端工程师用到的技术1. Vue3小兔鲜先导课1.1 技术栈1.2 项目规模1.3 项目亮点1.4 课程安排 2. 认识Vue32.1 Vue3组合式API体验 3. create-vue创建Vue3项目3.1 新建项目结构3.2 小节3.3 补充说明npm init vuela…...

【Pandas DataFrame】

以下是 Pandas DataFrame 的核心知识点总结&#xff0c;用结构化分类帮你高效记忆关键操作和概念&#xff1a; 1. 基础操作 创建DataFrame 方法代码示例说明从字典创建df pd.DataFrame({A: [1,2], B: [3,4]})字典键为列名&#xff0c;值为数据从列表创建df pd.DataFrame([[…...

华为OD机试2025A卷 - 生成回文素数(Java Python JS C++ C )

最新华为OD机试 真题目录:点击查看目录 华为OD面试真题精选:点击立即查看 题目描述 求出大于或等于 N 的最小回文素数。 如果一个数大于 1,且其因数只有 1 和它自身,那么这个数是素数。 例如,2,3,5,7,11 以及 13 是素数。 如果一个数从左往右读与从右往左读是一…...

Jenkins教程(自动化部署)

Jenkins教程(自动化部署) 1. Jenkins是什么&#xff1f; Jenkins是一个开源的、提供友好操作界面的持续集成(CI)工具&#xff0c;广泛用于项目开发&#xff0c;具有自动化构建、测试和部署等功能。Jenkins用Java语言编写&#xff0c;可在Tomcat等流行的servlet容器中运行&…...

C#里使用libxl的对齐/边框/颜色

一份好的EXCEL文件,通道会有不同的颜色和边框来表示。 以便表示一些重要的信息,这样才能让人们一眼就看到需要关注的信息。 如下面所示: 要显示上面的内容,需要使用下面的例子: private void button12_Click(object sender, EventArgs e){var book = new ExcelBook();if…...

虚拟pinctrl驱动

之前呢&#xff0c;我们讲解了在内核中pinctrl子系统是怎么实现的&#xff0c;今天我们来尝试一下自己去写一个pinctrl子系统&#xff1a; 首先呢&#xff0c;我们来看看一个pinctrl子系统需要做的事情: 上面的话&#xff0c;我们看了一个pinctrl子系统需要的三大功能以及在驱…...

pycharm虚拟环境项目转移后配置解释器

添加解析器提示&#xff1a;无效的 Python SDK 解决方法 在到电脑安装python解析器&#xff0c;复制&#xff1a;python.exe和pythonw.exe 项目虚拟环境venv/Scripts Python解释器添加 项目现有虚拟环境&#xff0c;就可以正常使用...

蓝桥杯嵌入式学习笔记

用博客来记录一下参加蓝桥杯嵌入式第十六届省赛的学习经历 工具环境准备cubemx配置外部高速时钟使能设置串口时钟配置项目配置 keil配置烧录方式注意代码规范头文件配置 模块ledcubemx配置keil代码实现点亮一只灯实现具体操作的灯&#xff0c;以及点亮还是熄灭 按键cubemx配置k…...

0201-jsx语法基础-jsx-仿低代码平台项目

文章目录 1.jsx标签2.jsx属性3.jsx 事件3.1 声明事件3.2 使用事件&#xff08;对象&#xff09; 4. typescript类型基础4.1 类型声明4.2 事件函数传递自定义参数 5.插入js变量6. 条件判断7. 循环结语 1.jsx标签 jsx标签与html标签区别&#xff1a; 首字母大小写 大写是自定义组…...

在MCU工程中优化CPU工作效率的几种方法

在嵌入式系统开发中&#xff0c;优化 CPU 工作效率对于提升系统性能、降低功耗、提高实时性至关重要。Keil 作为主流的嵌入式开发工具&#xff0c;提供了多种优化策略&#xff0c;包括 关键字使用、内存管理、字节对齐、算法优化 等。本文将从多个方面介绍如何在 Keil 工程中优…...

Elasticsearch 的搜索功能

Elasticsearch 的搜索功能 建议阅读顺序&#xff1a; Elasticsearch 入门Elasticsearch 搜索&#xff08;本文&#xff09;Elasticsearch 搜索高级Elasticsearch 高级 1. 介绍 使用 Elasticsearch 最终目的是为了实现搜索功能&#xff0c;现在先将文档添加到索引中&#xff0c…...

【鸿蒙5.0】向用户申请麦克风授权

#效果图 步骤 在 config.json 里声明权限&#xff1a;在项目的 config.json 文件中添加麦克风权限的声明&#xff0c;告知系统应用需要使用该权限。检查权限状态&#xff1a;在代码里检查应用是否已经获得了麦克风权限。请求权限&#xff1a;若应用未获得麦克风权限&#xff0…...

数据结构与算法分析:树与哈希表(一)

遇到的问题&#xff0c;都有解决方案&#xff0c;希望我的博客能为你提供一点帮助。 一、概述 背景&#xff1a;链表处理大量数据时&#xff0c;线性访问耗时多。二叉查找树多数操作平均运行时间为 O (log N)&#xff0c;相对于链表树更加高效。 1.预备知识 1.1. 树的定义与…...

VBA第三十四期 VBA中怎么用OnKey事件

我们在VBA设计中经常需要使用到OnKey方法&#xff0c;特别是在窗口设计中比如我们想用到翻页按键&#xff0c;则就可以来建立一个OnKey事件。Setup_OnKey过程运行以后&#xff0c;就会达到最终效果&#xff0c;按PgDn键会将指针下移一行&#xff0c;按PgUp键会将指针上移一行。…...

HarmonyOS NEXT开发进阶(十五):日志打印 hilog 与 console.log 的区别

文章目录 一、前言二、两者区别对比三、HiLog 详解四、拓展阅读 一、前言 在日常开发阶段&#xff0c;日志打印是调试程序非常常用的操作&#xff0c;在鸿蒙的官方文档中介绍了hilog这种方式&#xff0c;前端转过来的开发者发现console.log也可以进行日志打印&#xff0c;而且…...

Skynet 框架中 gateserver、gate、watchdog 的关系

一、概述 在 Skynet 框架的网络通信架构中&#xff0c;gateserver、gate、watchdog 是三个核心组件&#xff0c;共同实现客户端连接的监听、管理和业务逻辑的分发。其设计目标是通过分层解耦&#xff0c;提升网络层的稳定性与业务逻辑的灵活性。 二、组件职责 1. ‌gateserve…...

第11章:优化I/O_《C++性能优化指南》_notes

第十一章核心知识点详解 11.1 读取文件的优化技巧 重点&#xff1a;减少内存分配、使用大缓冲区、优化函数调用链。 难点&#xff1a;理解系统调用开销与缓冲区大小的权衡。 代码示例与详解 示例1&#xff1a;使用高效函数签名和减少内存分配 #include <fstream> #inc…...

【C++初阶】--- 内存管理

1.C/C内存分布 我们一般说的32位机器和64位机器指的是虚拟空间的大小&#xff0c;也就是进程地址空间的大小&#xff0c;32位机器下&#xff0c;进程地址空间的大小是232个字节&#xff0c;也就是4G&#xff0c;64位机器下&#xff0c;进程地址空间的大小是264个字节,大概160亿…...

使用 Ansys Discovery 可视化液体池中的水流

了解 ANSYS Discovery&#xff1a;设计领域的变革者 ANSYS Discovery 是一款功能强大的软件工具&#xff0c;能够彻底改变设计流程。借助其先进的仿真功能&#xff0c;工程师现在可以在设计投入生产之前更深入地了解其设计。通过使用 ANSYS Discovery&#xff0c;设计师可以快…...

网络安全-网络安全基础

一、网络安全概述 TCP/IP协议定义了一个对等的开放性网络&#xff0c;使得连接到这个网络中的所有用户都可能面临来自网络中的恶意的破坏和攻击。这些攻击通过网络通信协议、网络应用协议甚至物理传输链路来实现。主要针对于软件和硬件进行攻击。那在互联网上如何保证自己的安…...

YOLOv8+ Deepsort+Pyqt5车速检测系统

该系统通过YOLOv8进行高效的目标检测与分割&#xff0c;结合DeepSORT算法完成目标的实时跟踪&#xff0c;并利用GPU加速技术提升处理速度。系统支持模块化设计&#xff0c;可导入其他权重文件以适应不同场景需求&#xff0c;同时提供自定义配置选项&#xff0c;如显示标签和保存…...

QML中的附加属性和附加信号处理程序

QML中的附加属性和附加信号处理程序 在QML中&#xff0c;附加属性(Attached Properties)和附加信号处理程序(Attached Signal Handlers)是特殊类型的属性和信号&#xff0c;它们由附加类型(Attached Types)提供&#xff0c;而不是由对象本身直接提供。 什么是附加的(Attached…...

爱普生FC-135晶振5G手机的极端温度性能守护者

在5G时代&#xff0c;智能手机不仅需要高速率与低延迟&#xff0c;更需在严寒、酷暑、振动等复杂环境中保持稳定运行。作为 5G 手机的核心时钟源&#xff0c;爱普生32.768kHz晶振FC-135凭借其宽温适应性、高精度稳定性与微型化设计&#xff0c;成为5G手机核心时钟源的理想选择&…...

GPT Workspace体验

GPT Workspace是一款将强大的自然语言处理模型&#xff08;如 ChatGPT 和 Gemini&#xff09;集成到 Google Workspace 应用&#xff08;如 Google Docs, Sheets, Slides, Gmail 和 Drive&#xff09;中的工具或插件。它的目标是提升用户在日常办公中的效率和创造力。 以下是对…...

Teleport 和 Set Actor location的区别

Teleport 和 Set Actor Location 都可以用于移动一个 Actor 的位置&#xff0c;但它们在底层逻辑和适用场景上有显著区别. 1. Set Actor Location 功能&#xff1a; 直接设置 Actor 的位置&#xff0c;不重置物理状态&#xff08;如速度、动量&#xff09;。 行为特点&#xf…...

“GPU 挤不动了?”——聊聊基于 GPU 的计算资源管理

“GPU 挤不动了?”——聊聊基于 GPU 的计算资源管理 作者:Echo_Wish “老板:为什么 GPU 服务器卡得跟 PPT 一样?” “运维:我们任务队列爆炸了,得优化资源管理!” 在 AI 训练、深度学习、科学计算的场景下,GPU 计算资源已经成为香饽饽。但 GPU 服务器贵得离谱,一台 A…...

解决前端项目中无法识别 .node 文件的依赖安装问题

解决前端项目中无法识别 .node 文件的依赖安装问题 问题描述 在 macOS 系统&#xff08;M1 Pro 芯片&#xff09;&#xff0c;使用 Node.js 版本 20.9.0 和 Vue 3 的环境下&#xff0c;项目启动过程中遇到了以下错误&#xff1a; [ERROR] No loader is configured for "…...