细说STM32单片机FreeRTOS消息缓冲区及其应用实例

目录

一、消息缓冲区功能概述

二、消息缓冲区操作相关函数

1、相关函数概述

2、部分函数详解

(1)创建消息缓冲区

(2)写入消息

(3)读取消息

(4)消息缓冲区状态查询

三、消息缓冲区使用示例

1、示例功能与CubeMX项目设置

(1)RCC、SYS、Code Generator、USART3、TIM6

(2)RTC的设置

(3)FreeRTOS的设置

(4)NVIC

2、程序功能实现

(1)主程序

(2)FreeRTOS对象初始化

(3)RTC的唤醒中断

(4)任务Task_Show的功能

3、运行调试


一、消息缓冲区功能概述

        消息缓冲区(message buffer)是基于流缓冲区实现的,也就是它的实现使用了流缓冲区的技术,如同信号量是基于队列实现的。与流缓冲区的差异在于:消息缓冲区传输的是可变长度的消息,如10字节、20字节或35字节的消息。写入者向消息缓冲区写入一个10字节的消息,读取者也必须以10字节的消息读出,而不是像流缓冲区那样,按字节流读出。

        每个消息都有一个消息头,就是消息数据的字节数。在STM32 MCU上,消息头就是一个uint32_t类型的整数。消息头的写入和读取是由FreeRTOS的API函数自动处理的,例如,向消息缓冲区写入一个长度为20字节的消息,实际占用空间是24字节。

        消息缓冲区没有触发水平,写入和读取都是以一条消息为单位的,操作要么成功,要么失败。

        消息缓冲区的其他特性与流缓冲区一样。例如:在只有一个写入者和一个读取者的情况下,可以安全操作消息缓冲区;如果有多个写入者或多个读取者,读写消息缓冲区的代码必须在临界代码段内,且等待时间必须设置为0。

二、消息缓冲区操作相关函数

1、相关函数概述

        消息缓冲区相关函数的头文件message_buffer.h,源程序都在文件stream_buffer.c里,因为消息缓冲区是基于流缓冲区实现的,要在程序中使用消息缓冲区,需包含头文件message_buffer.h。

分组

函数

功能

创建


删除

xMessageBufferCreate()

创建一个消息缓冲区,只需设置缓冲区大小

xMessageBufferCreateStatic()

创建一个消息缓冲区,静态分配内存

vMessageBufferDelete()

删除一个消息缓冲区

xMessageBufferReset()

复位一个消息缓冲区,清空数据。只有没有任务在阻塞状态下读或写消息缓冲区时,才可以复位消息缓冲区

写入

xMessageBufferSend()

向消息缓冲区发送一个消息

xMessageBufferSendFromISR()

xMessageBufferSend()的ISR版本

读取

xMessageBufferReceive()

从消息缓冲区接收一条消息

xMessageBufferReceiveFromISR()

xMessageBufferReceive()的ISR版本

状态

查询

xMessageBufferIsEmpty()

查询消息缓冲区是否为空,返回值pdTRUE表示无任何消息

xMessageBufferIsFull()

查询消息缓冲区是否满了,返回值pdTRUE表示不能
再写入任何消息

xMessageBufferSpacesAvailable()

查询消息缓冲区的剩余存储空间

        与流缓冲区不同的是:消息缓冲区无须设置触发水平,在写入或读取消息超时的时候,实际写入或读取的数据字节数为0,不会只写入或读取部分数据。

2、部分函数详解

(1)创建消息缓冲区

        用于创建消息缓冲区的函数是xMessageBufferCreate(),这是个宏函数,其原型定义如下:

/**
* \defgroup xMessageBufferCreate xMessageBufferCreate
* \ingroup MessageBufferManagement
*/
#define xMessageBufferCreate( xBufferSizeBytes ) ( MessageBufferHandle_t ) xStreamBufferGenericCreate( xBufferSizeBytes, ( size_t ) 0, pdTRUE )

        调用函数xMessageBufferCreate()时,只需传递缓冲区大小xBufferSizeBytes。这个函数实际上调用了函数xStreamBufferGenericCreate(),传递的触发水平参数为0,因为消息缓冲区没有触发水平,最后的参数pdTRUE表示要创建的是消息缓冲区。

        函数xMessageBufferCreate()的返回值是MessageBufferHandle_t类型的,就是所创建的消息缓冲区对象指针。

(2)写入消息

        用于向消息缓冲区写入消息的函数是xMessageBufferSend(),这是个宏函数,其原型定义如下:

/**
* \defgroup xMessageBufferSend xMessageBufferSend
* \ingroup MessageBufferManagement
*/
#define xMessageBufferSend( xMessageBuffer, pvTxData, xDataLengthBytes, xTicksToWait ) xStreamBufferSend( ( StreamBufferHandle_t ) xMessageBuffer, pvTxData, xDataLengthBytes, xTicksToWait )

        实际上,它是执行了流缓冲区写入数据的函数xStreamBufferSend()。函数中各参数的意义如下。

  • xMessageBuffer,所操作的消息缓冲区的句柄。
  • pvTxData,准备写入的数据缓冲区指针。
  • xDataLengthBytes,消息数据的字节数,不包括消息头的4字节。
  • xTicksToWait,等待的节拍数,如果消息缓冲区没有足够的空间用于写入这条消息,任务可以进入阻塞状态等待。若设置为0,则表示不等待;若设置为portMAX_DELAY,则表示一直等待。

        函数xStreamBufferSend()内部会判断传递来的缓冲区对象的类型。如果是消息缓冲区,就在实际写入数据前面加上一个uint32_t类型的整数,表示消息的字节数;如果是流缓冲区,就直接写入数据。

        函数xMessageBufferSend()的返回值是实际写入消息的字节数,不包括消息头的4字节。如果函数是因为等待超时而退出的,则返回值为0;如果写入成功,返回值就是写入的消息数据的字节数。这是与流缓冲区不同的一个地方,使用函数xStreamBufferSend()向流缓冲区写入数据时,如果因等待超时而退出,仍然可能向流缓冲区写入了一些数据。

        在ISR中,向消息缓冲区写入消息的函数是xMessageBufferSendFromISR(),它是个宏函数,实际就是执行了函数xStreamBufferSendFromISR(),其原型定义如下:

/**
* \defgroup xMessageBufferSendFromISR xMessageBufferSendFromISR
* \ingroup MessageBufferManagement
*/
#define xMessageBufferSendFromISR( xMessageBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken ) xStreamBufferSendFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken )

(3)读取消息

        用于从消息缓冲区读取消息的函数是xMessageBufferReceive(),其原型定义如下:

/**
* \defgroup xMessageBufferReceive xMessageBufferReceive
* \ingroup MessageBufferManagement
*/
#define xMessageBufferReceive( xMessageBuffer, pvRxData, xBufferLengthBytes, xTicksToWait ) xStreamBufferReceive( ( StreamBufferHandle_t ) xMessageBuffer, pvRxData, xBufferLengthBytes, xTicksToWait )

        它就是执行了函数xStreamBufferReceive()。函数中各参数的意义如下。

  • xMessageBuffer,所操作的消息缓冲区的句柄。
  • pvRxData,保存读出数据的缓冲区指针。
  • xBufferLengthBytes,缓冲区pvRxData的长度,也就是最大能读取的字节数。
  • xTicksToWait,等待的节拍数。如果消息缓冲区里没有消息,任务可以进入阻塞状态等待。若设置为0,则表示不等待;若设置为portMAX_DELAY,则表示一直等待。

        函数xStreamBufferReceive()会自动区分参数xMessageBuffer是流缓冲区,还是消息缓冲区。如果是消息缓冲区,它会先读取表示消息长度的4字节消息头,然后按照长度读取后面的消息数据。

        函数xMessageBufferReceive()返回的是实际读取的消息的字节数,不包括消息头的4字节。如果函数是因为等待超时而退出的,则返回值为0。

        在ISR中从消息缓冲区读取消息的函数是xMessageBufferReceiveFromISR(),它是个宏函数,实际就是执行了函数xStreamBufferReceiveFromISR(),其原型定义如下:

/**
* \defgroup xMessageBufferReceiveFromISR xMessageBufferReceiveFromISR
* \ingroup MessageBufferManagement
*/
#define xMessageBufferReceiveFromISR( xMessageBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken ) xStreamBufferReceiveFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken )

(4)消息缓冲区状态查询

        以下几个查询消息缓冲区状态的函数,只需使用消息缓冲区的句柄作为函数的输入参数。

  • xMessageBufferIsEmpty()查询一个消息缓冲区是否为空,若返回pdTRUE,则表示缓冲区不包含任何消息。
  • xMessageBufferIsFull()查询一个消息缓冲区是否已满,若返回pdTRUE,则表示不能再写入任何消息。
  • xMessageBufferSpacesAvailable()查询一个消息缓冲区剩余的存储空间字节数,返回值类型为uint32_t。

三、消息缓冲区使用示例

1、示例功能与CubeMX项目设置

        本示例演示消息缓冲区的使用,实例的功能和使用流程如下。

  • 创建一个消息缓冲区和一个任务Task_Show。
  • 使用RTC的唤醒中断,唤醒周期为1s。在RTC的唤醒中断里读取当前时间,转化为字符串后,作为消息写入消息缓冲区,每次写入的消息长度不一样。
  • 在任务Task_Show里读取消息缓冲区的消息,并在串口助手上显示。
  • 继续使用旺宝红龙开发板STM32F407ZGT6 KIT V1.0。

  • 一些设置可以参考本文作者写的其他文章:

        细说STM32单片机FreeRTOS流缓冲区及其应用实例-CSDN博客  https://wenchm.blog.csdn.net/article/details/148168854?spm=1011.2415.3001.5331

(1)RCC、SYS、Code Generator、USART3、TIM6

        配置时钟树,将APB1定时器时钟频率设置为84MHz,APB2定时器时钟频率设置为168MHz ;设置TIM6作为基础时钟源;其它设置可见参考文章。

(2)RTC的设置

        启用LSE,启用RTC,在时钟树上将LSE作为RTC的时钟源。启用周期唤醒功能,设置唤醒周期为1s,其他参数用默认值即可。

 

(3)FreeRTOS的设置

        设置FreeRTOS接口为CMSIS_V2,所有“Config”和“Include”参数保持默认值。在FreeRTOS里创建一个任务Task_Show,其主要参数如图所示。

(4)NVIC

        在NVIC里开启RTC唤醒中断,设置其中断优先级为5,因为要在其ISR里使用FreeRTOS API函数。

2、程序功能实现

(1)主程序

        完成设置后,CubeMX自动生成代码。在CubeIDE中打开项目,添加用户功能代码后,主程序代码如下:

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"
#include "rtc.h"
#include "usart.h"
#include "gpio.h"/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void MX_FREERTOS_Init(void);/*** @brief  The application entry point.* @retval int*/
int main(void)
{/* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* Configure the system clock */SystemClock_Config();/* Initialize all configured peripherals */MX_GPIO_Init();MX_RTC_Init();MX_USART3_UART_Init();/* USER CODE BEGIN 2 *///Start Menuuint8_t startstr[] = "Demo9_2:Using Message Buffer.\r\n\r\n";HAL_UART_Transmit(&huart3,startstr,sizeof(startstr),0xFFFF);/* USER CODE END 2 *//* Init scheduler */osKernelInitialize();/* Call init function for freertos objects (in cmsis_os2.c) */MX_FREERTOS_Init();/* Start scheduler */osKernelStart();/* We should never get here as control is now taken by the scheduler *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}

(2)FreeRTOS对象初始化

        自动生成的函数MX_FREERTOS_Init()只创建了任务,在CubeMX里不能可视化地创建消息缓冲区,需要在CubeMX生成的CubeIDE初始代码的基础上,编程创建消息缓冲区。在文件freertos.c中定义两个常量和消息缓冲区对象,在函数MX_FREERTOS_Init()中增加创建消息缓冲区对象的代码。完成后的代码如下:

        自动生成includes,并手动添加私有includes: 

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "message_buffer.h"
#include "usart.h"
#include <stdio.h>		//用到函数sprintf()
#include <string.h>		//用到函数strlen()
/* USER CODE END Includes */

        手动添加私有宏定义:

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define	MSG_BUFFER_LEN	50		//消息缓存区长度,单位:字节
#define	MSG_MAX_LEN		20		//消息最大长度,单位:字节
/* USER CODE END PD */

        手动添加创建消息缓冲区句柄变量代码;

        自动生成任务函数句柄变量代码:

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
MessageBufferHandle_t  msgBuffer;		//消息缓存区句柄变量
/* USER CODE END Variables */
/* Definitions for Task_Show */
osThreadId_t Task_ShowHandle;
const osThreadAttr_t Task_Show_attributes = {.name = "Task_Show",.stack_size = 256 * 4,.priority = (osPriority_t) osPriorityNormal,
};/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes *//* USER CODE END FunctionPrototypes */void AppTask_Show(void *argument);void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) *//*** @brief  FreeRTOS initialization* @param  None* @retval None*/
void MX_FREERTOS_Init(void) 
{/* Create the thread(s) *//* creation of Task_Show */Task_ShowHandle = osThreadNew(AppTask_Show, NULL, &Task_Show_attributes);/* USER CODE BEGIN RTOS_THREADS *//* add threads, ... */msgBuffer=xMessageBufferCreate(MSG_BUFFER_LEN);		//创建消息缓存区/* USER CODE END RTOS_THREADS */
}

(3)RTC的唤醒中断

        在RTC的唤醒中断里读取当前时间,将其转换为字符串后写入消息缓冲区。RTC唤醒中断的回调函数是HAL_RTCEx_WakeUpTimerEventCallback()。为便于使用消息缓冲区句柄变量msgBuffer,在文件freertos.c中重新实现这个回调函数:

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{RTC_TimeTypeDef sTime;RTC_DateTypeDef sDate;if (HAL_RTC_GetTime(hrtc, &sTime,  RTC_FORMAT_BIN) != HAL_OK)return;if (HAL_RTC_GetDate(hrtc, &sDate,  RTC_FORMAT_BIN) !=HAL_OK)return;char dtArray[MSG_MAX_LEN];   						//存储消息的数组, MSG_MAX_LEN=20if ((sTime.Seconds % 2)==0)  						//分奇偶秒,发送不同长度的消息字符串siprintf(dtArray,"Seconds = %u",sTime.Seconds);	//转换为字符串,自动加'\0'elsesiprintf(dtArray,"Minute= %u",sTime.Minutes);	//转换为字符串,自动加'\0'uint8_t bytesCount=strlen(dtArray);					//字符串长度,不带最后的结束符BaseType_t  highTaskWoken=pdFALSE;if (msgBuffer != NULL){uint16_t  realCnt=xMessageBufferSendFromISR(msgBuffer,dtArray, bytesCount+1, &highTaskWoken);  // bytesCount+1,带结束符'\0'printf("Write bytes=   %d\r\n", realCnt);		 //实际写入消息长度portYIELD_FROM_ISR(highTaskWoken);				 //申请进行一次任务调度}
}int __io_putchar(int ch)
{HAL_UART_Transmit(&huart3,(uint8_t*)&ch,1,0xFFFF);return ch;
}
/* USER CODE END Application */

        上述程序首先读取RTC的时间和日期,根据当前时间的秒数是奇数还是偶数,生成不同长度的字符串数据并保存到数组dtArray里。这里用到了C语言标准库中的两个函数siprintf()和strlen()。siprintf()与printf()类似,只是把字符串写入一个数组,并且在字符串最后自动添加结束符\0。strlen()用于得到字符串的长度,但是不包括最后的结束符。

        在使用函数xMessageBufferSendFromISR()向消息缓冲区写入消息时,执行的代码如下:

uint16_t realCnt = xMessageBufferSendFromISR(msgBuffer,dtArray,bytesCount+1,&highTaskwoken);

        这里传递的第3个参数值是bytesCount+1,也就是加上了字符串的结束符,否则,读取者读出的消息字符串将不带结束符,串口助手将无法正常显示字符串。bytesCount+1的值必须小于或等于MSG_MAX_LEN。

        函数的返回值realCnt是实际写入的消息长度,不带消息头的4个字节。如果消息写入成功,那么realCnt等于bytesCount+1。

        这里写入消息的数据是字符串,这只是为了演示方便,实际写入消息的数据可以是任意类型的数据,而不一定是字符串。

(4)任务Task_Show的功能

        在任务Task_Show里读取消息缓冲区里的消息,并在串口助手上显示,其任务函数代码如下:

/* USER CODE BEGIN Header_AppTask_Show */
/*** @brief  Function implementing the Task_Show thread.* @param  argument: Not used* @retval None*/
/* USER CODE END Header_AppTask_Show */
void AppTask_Show(void *argument)
{/* USER CODE BEGIN AppTask_Show *//* Infinite loop */uint8_t dtArray[MSG_MAX_LEN];								//读出的数据临时保存数组for(;;){uint16_t realCnt=xMessageBufferReceive(msgBuffer, dtArray,MSG_MAX_LEN, portMAX_DELAY);					//读取消息printf("Read message bytes  =  %d\r\n", realCnt);		//实际读出字节数printf("message string Read =  %s\r\n", dtArray);		//显示读出的消息字符串}/* USER CODE END AppTask_Show */
}

        上述程序用函数xMessageBufferReceive()读取消息缓冲区里的消息,然后在串口助手上显示实际读取的消息长度和消息字符串。调用函数xMessageBufferReceive()的代码如下:

uint16_t realCnt = xMessageBufferReceive(msgBuffer,dtArray,MSG_MAX_LEN,portMAX_DELAY);

        其中,dtArray是用于存储读出数据的uint8_t类型数组,传递的第3个参数是MSG_MAX_LEN,也就是最大可以读取的消息的长度。函数返回值realCnt是实际读取的消息的长度,不包括消息头的4个字节。MSG_MAX_LEN应该大于或等于realCnt,否则,会导致无法读出一条完整的消息。

3、运行调试

        构建项目后,下载到开发板上并运行测试,会发现显示的写入消息长度和读出消息长度是一致 的,串口助手上显示的消息字符串也是正确的,说明可以写入和读出不同长度的消息。在实际使用消息缓冲区时,写入者和读取者之间应该定义好消息的格式,如同串口通信一样定义通信协议。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/84323.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【缓存】JAVA本地缓存推荐Caffeine和Guava

&#x1f31f; 引言 在软件开发过程中&#xff0c;缓存是提升系统性能的常用手段。对于基础场景&#xff0c;直接使用 Java集合框架&#xff08;如Map/Set/List&#xff09;即可满足需求。然而&#xff0c;当面对更复杂的缓存场景时&#xff1a; 需要支持多种过期策略&#x…

IDA插件 MIPSROP的安装和使用方法

前言 笔者的IDA版本为9.0&#xff0c;刚开始根据一些博客描述以为将mipsrop.py拷贝到IDA的plugins目录即可&#xff0c;可操作后发现事情好像没这么简单&#xff0c;复制进去后就发现没有博客中所说的 MIPS ROP Finder &#xff0c;笔者在网上搜索了很多博客后在 https://bbs.…

(1)转置后,行列式的值不变 (2)将行列式的任意两行互换位置后,行列式改变符号

以下是对原始内容在不改变内容本身的前提下进行的格式优化&#xff0c;以提升可读性和逻辑清晰度&#xff1a; ✅ 行列式的几何意义 行列式&#xff08;determinant&#xff09;是线性代数中一个非常重要的概念&#xff0c;它的几何含义可以从以下几个方面理解&#xff1a; &a…

最大似然估计(Maximum Likelihood Estimation, MLE)详解

一、定义 最大似然估计 是一种参数估计方法&#xff0c;其核心思想是&#xff1a; 选择能使观测数据出现概率最大的参数值作为估计值。 具体来说&#xff0c;假设数据 D x 1 , x 2 , … , x n D{x_1,x_2,…,x_n} Dx1​,x2​,…,xn​独立且服从某个概率分布 P ( x ∣ θ ) P(…

用go从零构建写一个RPC(3)--异步调用+多路复用实现

在前两个版本中&#xff0c;我们实现了基础的客户端-服务端通信、连接池、序列化等关键模块。为了进一步提升吞吐量和并发性能&#xff0c;本版本新增了 异步发送机制 和 多路复用支持&#xff0c;旨在减少资源消耗、提升连接利用率。 代码地址&#xff1a;https://github.com/…

FFmpeg 安装包全攻略:gpl、lgpl、shared、master 区别详解

这些 FFmpeg 安装包有很多版本和变种&#xff0c;主要区别在于以下几个方面&#xff1a; ✅ 一、从名称中看出的关键参数&#xff1a; 1. 版本号 master&#xff1a;开发版&#xff0c;最新功能&#xff0c;但可能不稳定。n6.1 / n7.1&#xff1a;正式版本&#xff0c;更稳定…

深度学习实战:从图像分类到文本生成的完整案例解析

1 图像分类案例 1.1 CIFAR10数据集介绍 cifar数据是torchvision第三方包提供的数据集 训练集5w 测试集1w y标签 10个类别 10分类问题 一张图形状 (32, 32, 3) import torch import torch.nn as nn from torchvision.datasets import CIFAR10 from torchvision.transforms i…

Android 添加系统服务的完整流程

[应用程序] (应用进程)│↓ 调用简单API [SoundManager] │ ├─ 代理模式门面模式&#xff08;应用进程&#xff09;│ ├─ 缓存数据 ←─ 装饰器模式&#xff08;应用进程&#xff09;│ └─ 转换异常 ←─ 适配器模式&#xff08;应用进程&#xff09;│↓ 通过Bind…

wan2.1代码笔记

GPU内存不够&#xff0c;可以先运行umt5&#xff0c;然后再运行wanpipeline&#xff0c;参考FLUX.1代码笔记&#xff0c;或者使用ComfyUI。 下面使用随机数代替umt5 embedding。 import torch from diffusers.utils import export_to_video from diffusers import Autoencoder…

环境搭建与工具配置

3.1 本地环境搭建 3.1.1 WAMP环境搭建漏洞靶场&#xff08;一、二&#xff09; WAMP&#xff08;Windows Apache MySQL PHP&#xff09;是搭建本地Web漏洞靶场的基础环境。 安装步骤&#xff1a; Apache&#xff1a;下载并安装最新版Apache HTTP Server&#xff0c;配置监…

STM32F446主时钟失效时DAC输出异常现象解析与解决方案

—### 现象概述 在STM32F446微控制器应用中&#xff0c;若主时钟&#xff08;HSE&#xff09;的晶体信号对地短路&#xff0c;但DAC&#xff08;数模转换器&#xff09;仍能输出变化信号&#xff0c;这一现象看似矛盾&#xff0c;实则与系统时钟切换机制密切相关。本文将从硬件…

React 如何封装一个可复用的 Ant Design 组件

文章目录 前言一、为什么需要封装组件&#xff1f;二、 仿antd组件的Button按钮三、封装一个可复用的表格组件 (实战)1. 明确需求2. 设计组件 API3. 实现组件代码4. 使用组件 三、封装组件的最佳实践四、进阶优化 总结 前言 作为一名前端开发工程师&#xff0c;在日常项目中&a…

STC89C52RC/LE52RC

STC89C52RC 芯片手册原理图扩展版原理图 功能示例LED灯LED灯的常亮效果LED灯的闪烁LED灯的跑马灯效果&#xff1a;从左到右&#xff0c;从右到左 数码管静态数码管数码管计数mian.cApp.cApp.hCom.cCom.hDir.cDir.hInt.cInt.hMid.cMid.h 模板mian.cApp.cApp.hCom.cCom.hDir.cDir…

踩坑记录:RecyclerView 局部刷新notifyItemChanged多次调用只触发一次 onBindViewHolder 的原因

1. 问题背景 在做项目的时候&#xff0c;RecyclerView需要使用局部刷新&#xff0c;使用 notifyItemChanged(position, payload) 实现局部刷新&#xff0c;但发现调用多次只执行了一次&#xff0c;第二个刷新不生效。 2. 错误示例&#xff08;只处理 payloads.get(0)&#xff…

OpenLayers 加载鹰眼控件

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图控件是一些用来与地图进行简单交互的工具&#xff0c;地图库预先封装好&#xff0c;可以供开发者直接使用。OpenLayers具有大部分常用的控件&#x…

WPF···

设置启动页 默认最后一个窗口关闭,程序退出,可以设置 修改窗体的icon图标 修改项目exe图标 双击项目名会看到代码 其他 在A窗体点击按钮打开B窗体,在B窗体设置WindowStartupLocation=“CenterOwner” 在A窗体的代码设置 B.Owner = this; B.Show(); B窗体生成在A窗体中间…

github公开项目爬取

import requestsdef search_github_repositories(keyword, tokenNone, languageNone, max_results1000):"""通过 GitHub API 搜索仓库&#xff0c;支持分页获取所有结果&#xff08;最多 1000 条&#xff09;:param keyword: 搜索关键词:param token: GitHub To…

防震基座在半导体晶圆制造设备抛光机详细应用案例-江苏泊苏系统集成有限公司

在半导体制造领域&#xff0c;晶圆抛光作为关键工序&#xff0c;对设备稳定性要求近乎苛刻。哪怕极其细微的振动&#xff0c;都可能对晶圆表面质量产生严重影响&#xff0c;进而左右芯片制造的成败。以下为您呈现一个防震基座在半导体晶圆制造设备抛光机上的经典应用案例。 企…

S32K开发环境搭建详细教程(一、S32K IDE安装注册)

一、S32K IDE安装注册 1、进入恩智浦官网https://www.nxp.com.cn/&#xff08;需要在官网注册一个账号&#xff09; 2、直接搜索 “Standard Software”&#xff0c;找到S32K3 Standard Software&#xff0c;点击进入 3、下载 (1)Automotive SW - S32K3 - S32 Design Studio…

Spring Cloud Gateway 微服务网关实战指南

上篇文章简单介绍了SpringCloud系列OpenFeign的基本用法以及Demo搭建&#xff08;Spring Cloud实战&#xff1a;OpenFeign远程调用与服务治理-CSDN博客&#xff09;&#xff0c;今天继续讲解下SpringCloud Gateway实战指南&#xff01;在分享之前继续回顾下本次SpringCloud的专…