HAL库:串口 不定长数据接收 + 循环收发缓冲区 + 空闲中断 收发数据

news2024/9/21 0:33:34

目录

HAL库:不定长数据接收 + 循环收发缓冲区 + 空闲中断 收发数据

串口:多指针定位+收发循环使用缓冲区设计

文件架构:

程序部分大体思路

Uart.h

Uart.c

stm32fxx_It.c

main.c


HAL库:不定长数据接收 + 循环收发缓冲区 + 空闲中断 收发数据

串口:多指针定位+收发循环使用缓冲区设计

STM32C8T6单片机一共有20K的RAM

设计介绍:

  • 定义接收、发送缓冲区(2K 2048字节大小) 用于在接收时不频繁进入中断。

  • 定义结构体,成员为 :Start End 指针 用于在一次串口接收完毕,程序进入空闲中断时。使用Start、End 标记这一段字节的起始和节数地址。 用于读取

  • 定义结构体数组 可以存10段上述的一段字节的起始地址。[0:9]

  • 定义结构体指针 In Out 初始化时,In、Out都指向结构体数组的第一个元素。当接收到一段字节后。In跳到结构体数组的第二个元素。准备一下次接收。 当Out和In的地址不同时。代表接收到数据,此时Out可通过第一个元素的Start 和 End 读出这段 字节,再跳到下一个元素,等待下次不相同时读出。

  • 定义结构体指针 END END指针固定指向结构体数组的最后一个元素 [9] 当Out跳到最后一个成员,即Out 或 In = END时、 在写入、读取后回滚到结构体数组的第一个元素。

  • 接收回卷: 约定最大发送值为300。当2048字节的接收缓冲区余量小于最大发送值300时,回卷。重新从起始地址开始缓存

  • 发送回卷: 当发送字节数量 小于当前发送缓冲余量时,进行回卷。最大值为2048。

文件架构:

  • Uart.h :定义了与串口通信相关的数据结构和函数声明,用于管理串口通信中的接收和发送缓冲区,以及串口的初始化操作。
  • Uart.c :主要针对串口 1 进行配置为空闲中断 ,包括初始化参数、设置缓冲区指针、配置 IO 口和使能空闲中断等。 实现了数据收发、中断处理及缓冲区管理,确保串口 1 高效稳定地进行数据通信。
  • stm32fxx_It.c : 主要是 void USART1_IRQHandler(void) 的中断函数:该函数是串口 1 的中断服务函数。首先调用 HAL 库的中断处理函数,后续 检测到串口 1 进入空闲状态时,清除空闲中断标志位,计算接收字节数量并累加,然后终止接收,触发终止回调函数。终止函数在Uart.c中
  • main.c :在主循环中,通过判断接收和发送缓冲区的指针状态,实现数据的接收和发送,并在指针到达末尾时进行回卷操作。当接收缓冲区有数据时,将其拷贝到发送缓冲区并移动输出指针;当发送缓冲区有数据且处于空闲状态时,发送数据并移动输出指针。

程序部分大体思路

当接收到没有超过最大接收量256 - 1 时,会触发空闲中断。 (如果等于256 会同时触发接收完成中断和空闲中断。)

在接受到后, 会进入串口中断中

在中断判断空闲中断标志位,记录空闲前接收了多少的字节

然后终止当前的接收(否则会继续等到完成接收)。

进入终止接收回调函数。

在函数中会对每次传入的 一段 字节 的起始位和结束位做标记。并且会根据指针的位置进行回卷等操作。

在主函数中。会不断循环检查是否接收到一段字节。即Rx_In_ArrPtr 结构体指针的位置是否与Rx_Out_ArrPtr 的地址相同,如果不同就代表已经记录了一个接收到的一段字节的起始位置。就可以把他移动到 发送缓冲区。

在主函数中调用这个函数, 在往发送缓冲区移动的时候仍然会对这段字节 的起始位和结束位做标记。并且会根据指针的位置进行回卷等操作。

在检查到发送缓冲区的In和Out不同后,有会去判断并发送这一段的 字节。

这只是一个大概的顺序, 具体的 内容 去看 文件吧。里边的注释挺详细的

Uart.h

#ifndef __UART_H
#define __UART_H

#include "string.h" //memry操作用的头文件

#define U1_RX_SIZE 2048 //接收、发送数组缓冲区长度 宏定义
#define U1_TX_SIZE 2048
#define U1_RX_MAX  256  //约定发送方单次发送字节 最大值 - 1。
                        //否则当接收满了,就会产生完成回调和空闲回调两个回调。

typedef struct
{
    //用于标记接收到的一段字节的起始和结束位置
    uint8_t* Start;
    uint8_t* End;
}LCB;    //location Ctrl Block 位置单元组

typedef struct
{
    /*接收缓冲区配置*/
    uint32_t    Rx_Counter;          //接收缓冲区 累计收到的字节
    LCB         Rx_LocationArr[10];  //接收缓冲区 字节段 位置单元组 数组
    LCB        *Rx_In_ArrPtr;           //用于保存 一段字节的 位置
    LCB        *Rx_Out_ArrPtr;          //用于根据位置 读出缓冲区中的值
    LCB        *Rx_End_ArrPtr;          //用于定义位置单元组 数组的终点位置,防止指针越界
    /*发送缓冲区配置*/
    uint32_t    Tx_Counter;          //发送缓冲区 累计发送的字节。用于计算余量
    LCB         Tx_LocationArr[10];  //发送缓冲区 字节段 位置单元组 数组
    LCB        *Tx_In_ArrPtr;        //用于保存 一段字节的 位置
    LCB        *Tx_Out_ArrPtr;       //用于根据位置 读出缓冲区中的值
    LCB        *Tx_End_ArrPtr;       //用于定义位置单元组 数组的终点位置,防止指针越界
    /*串口处理结构体配置*/
    UART_HandleTypeDef Uart;         //方便一次配置完成。
    
    /*发送忙状态*/
    uint8_t     Tx_State;
}UCB;    //UART Ctrl Block 串口缓冲区单元组

//串口1 初始化
void Uart1_Init(uint32_t Bandrate);
//串口1 接收缓冲区 规则  指针位置 初始化
void Uart1_Ptr_Init(void);
//串口1 发送数据
void Uart1_Tx_Data(uint8_t* data, uint8_t len);

//UART2初始化
void Uart2_Init(uint32_t Bandrate);
//UART3初始化
void Uart3_Init(uint32_t Bandrate);

//外部声明的总控结构体
extern UCB Uart1;
extern UART_HandleTypeDef Uart2;
extern UART_HandleTypeDef Uart3;

#endif

Uart.c

#include "stm32f1xx_hal.h"
#include "Uart.h"

//串口1 配置结构体
UCB Uart1;
UART_HandleTypeDef Uart2;
UART_HandleTypeDef Uart3;

//定义接收和发送的缓冲区
uint8_t U1_Rx_Buff[U1_RX_SIZE];
uint8_t U1_Tx_Buff[U1_TX_SIZE];

UCB uarb1;
//初始化串口1
void Uart1_Init(uint32_t Bandrate)
{
    Uart1.Uart.Instance = USART1;//选择串口1
    Uart1.Uart.Init.BaudRate = Bandrate;//波特率
    Uart1.Uart.Init.WordLength = UART_WORDLENGTH_8B;//数据长度
    Uart1.Uart.Init.StopBits = UART_STOPBITS_1;//停止位
    Uart1.Uart.Init.Parity = UART_PARITY_NONE;//奇偶校验
    Uart1.Uart.Init.Mode = UART_MODE_TX_RX;//收发模式
    Uart1.Uart.Init.HwFlowCtl = UART_HWCONTROL_NONE;//流控
    
    HAL_UART_Init(&Uart1.Uart);
    
    /*初始化串口1 缓冲区 规则  指针位置*/
    Uart1_Ptr_Init();
    /*使能空闲中断*/
    __HAL_UART_ENABLE_IT(&Uart1.Uart,UART_IT_IDLE);
    HAL_UART_Receive_IT(&Uart1.Uart, Uart1.Rx_In_ArrPtr->Start, U1_RX_MAX);
    //开启接收中断。缓冲区位置为刚才记录的Start。接受的数量为U1_RX_MAX
}
//初始化串口1 缓冲区 规则  指针位置函数
void Uart1_Ptr_Init(void)
{
    Uart1.Rx_In_ArrPtr  = &Uart1.Rx_LocationArr[0];     //输入 位置数组对 指向 第一个元素
    Uart1.Rx_Out_ArrPtr = &Uart1.Rx_LocationArr[0];     //输出 位置数组对 指向 第一个元素
    Uart1.Rx_End_ArrPtr = &Uart1.Rx_LocationArr[9];     //结束 位置指向 最后一个元素
    Uart1.Rx_Counter = 0;   //用于累加收到的字节数量
    Uart1.Rx_In_ArrPtr->Start = U1_Rx_Buff;             //记录接收缓冲区的起始地址
    
    Uart1.Tx_In_ArrPtr  = &Uart1.Tx_LocationArr[0];     //输入 位置数组对 指向 第一个元素
    Uart1.Tx_Out_ArrPtr = &Uart1.Tx_LocationArr[0];     //输出 位置数组对 指向 第一个元素
    Uart1.Tx_End_ArrPtr = &Uart1.Tx_LocationArr[9];     //结束 位置指向 最后一个元素
    Uart1.Tx_Counter = 0;   //用于累加收到的字节数量
    Uart1.Tx_In_ArrPtr->Start = U1_Tx_Buff;             //记录接收缓冲区的起始地址
}

//串口1 发送数据
void Uart1_Tx_Data(uint8_t* data, uint8_t len)//参数为数据的起始位置,和发送多少
{
    //判断这次要发送的数据量是不是 小于 剩余缓冲区的大小.是:还能放
    if( len < (U1_TX_SIZE - Uart1.Tx_Counter) )
    {
        Uart1.Tx_In_ArrPtr->Start = &U1_Tx_Buff[Uart1.Tx_Counter]; //记录下一次 开始记录的位置
    }
    else    //不够了。回卷
    {
        Uart1.Tx_Counter = 0;   //重置计数
        Uart1.Tx_In_ArrPtr->Start = U1_Tx_Buff; //标记起始地址为缓冲区起始位置
    }
    
    
    memcpy(Uart1.Tx_In_ArrPtr->Start, data, len);   //拷贝源地址到标记的Start位置
    Uart1.Tx_Counter += len;    //累加,为下次拷贝做准备
    Uart1.Tx_In_ArrPtr->End = &U1_Tx_Buff[Uart1.Tx_Counter - 1];//标记End
    
    Uart1.Tx_In_ArrPtr++;//准备记录下一次的 起始位置
    
    //判断是否指向最后一个元素
    if(Uart1.Tx_In_ArrPtr == Uart1.Tx_End_ArrPtr)
    {
        Uart1.Tx_In_ArrPtr = &Uart1.Tx_LocationArr[0];   //回到0号位置
    }
}

//初始化串口2
void Uart2_Init(uint32_t Bandrate)
{
    Uart2.Instance = USART2;//选择串口2
    Uart2.Init.BaudRate = Bandrate;//波特率
    Uart2.Init.WordLength = UART_WORDLENGTH_8B;//数据长度
    Uart2.Init.StopBits = UART_STOPBITS_1;//停止位
    Uart2.Init.Parity = UART_PARITY_NONE;//奇偶校验
    Uart2.Init.Mode = UART_MODE_TX_RX;//收发模式
    Uart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;//流控
    
    HAL_UART_Init(&Uart2);
}

//初始化串口3
void Uart3_Init(uint32_t Bandrate)
{
    Uart3.Instance = USART3;//选择串口3
    Uart3.Init.BaudRate = Bandrate;//波特率
    Uart3.Init.WordLength = UART_WORDLENGTH_8B;//数据长度
    Uart3.Init.StopBits = UART_STOPBITS_1;//停止位
    Uart3.Init.Parity = UART_PARITY_NONE;//奇偶校验
    Uart3.Init.Mode = UART_MODE_TX_RX;//收发模式
    Uart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;//流控
    
    HAL_UART_Init(&Uart3);
}

//定义 强声明 的IO口和时钟打开的 回调函数
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    //判断传入的结构体中第一个成员  来判断是串口1 、 2、 3
    if(huart->Instance == USART1)
    {
        __HAL_RCC_GPIOA_CLK_ENABLE();   //打开GPIOA时钟
        __HAL_RCC_USART1_CLK_ENABLE();  //打开串口1 的时钟
        
        GPIO_InitTypeDef GPIO_InitStructure;
        //TX初始化 PA9
        GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;//输出
        GPIO_InitStructure.Pin = GPIO_PIN_9;
        GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM;//速度中等为10M
        HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
        
        //RX初始化 PA10
        GPIO_InitStructure.Mode = GPIO_MODE_AF_INPUT;//输入
        GPIO_InitStructure.Pin = GPIO_PIN_10;
        GPIO_InitStructure.Pull = GPIO_NOPULL;//浮空输入
        HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
        
        HAL_NVIC_SetPriority(USART1_IRQn,3,0);
        HAL_NVIC_EnableIRQ(USART1_IRQn);    //配置优先级和使能中断
    }
    else if(huart->Instance == USART2)//如果是串口2
    {
        __HAL_RCC_GPIOA_CLK_ENABLE();//打开GPIOA(PA2 PA3)时钟
        __HAL_RCC_USART2_CLK_ENABLE();//打开串口2 的时钟
    
        GPIO_InitTypeDef GPIO_InitStructure;
        //TX初始化 PA2
        GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;//输出
        GPIO_InitStructure.Pin = GPIO_PIN_2;
        GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM;//速度中等为10M
        HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
        
        //RX初始化 PA3
        GPIO_InitStructure.Mode = GPIO_MODE_AF_INPUT;//输入
        GPIO_InitStructure.Pin = GPIO_PIN_3;
        GPIO_InitStructure.Pull = GPIO_NOPULL;//浮空输入
        HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
    }
    else if(huart->Instance == USART3)//如果是串口3
    {
        __HAL_RCC_GPIOB_CLK_ENABLE();//打开GPIOB(PB10 PB11)时钟
        __HAL_RCC_USART3_CLK_ENABLE();//打开串口3 的时钟
    
        GPIO_InitTypeDef GPIO_InitStructure;
        //TX初始化 PB10
        GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;//输出
        GPIO_InitStructure.Pin = GPIO_PIN_10;
        GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM;//速度中等为10M
        HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);
        
        //RX初始化 PB11
        GPIO_InitStructure.Mode = GPIO_MODE_AF_INPUT;//输入
        GPIO_InitStructure.Pin = GPIO_PIN_11;
        GPIO_InitStructure.Pull = GPIO_NOPULL;//浮空输入
        HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);
    }
}

//定义 强声明的 UART 接收完成回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    //判断
    if(huart->Instance == USART1)
    {
        
    }
}

//定义 强声明的 UART中断错误回调函数
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
    //判断串口
    if(huart->Instance == USART1)
    {
        
    }
}

//定义 强声明的 UART发送完成回调函数
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART1)
    {
        Uart1.Tx_State = 0; //退出忙状态
    }
}

//强定义的 串口终止 回调函数
void HAL_UART_AbortReceiveCpltCallback(UART_HandleTypeDef *huart)
{
    //判断串口
    if(huart->Instance == USART1)
    {
        // 标记这次字节段的结束的地址
        Uart1.Rx_In_ArrPtr->End = &U1_Rx_Buff[Uart1.Rx_Counter - 1]; //从0开始存,所以要减1
        
        Uart1.Rx_In_ArrPtr++;   //In指向下一个 位置对结构体 数组的下一个元素
        //判断是否指向最后一个元素
        if(Uart1.Rx_In_ArrPtr == Uart1.Rx_End_ArrPtr)
        {
            Uart1.Rx_In_ArrPtr = &Uart1.Rx_LocationArr[0];   //回到0号位置
        }
        
        //判断缓冲区余量是否小于U1_RX_MAX
        if( (U1_RX_SIZE - Uart1.Rx_Counter) < U1_RX_MAX )
        {   
            //如果小于就回滚
            Uart1.Rx_Counter = 0;   //计数清零
            Uart1.Rx_In_ArrPtr->Start = U1_Rx_Buff; //开始指针指向缓冲区起始地址
        }
        else//余量还够
        {
            //从第一个字节段末尾的下一个地址开始存
            Uart1.Rx_In_ArrPtr->Start = &U1_Rx_Buff[Uart1.Rx_Counter];   
            //重新打开中断接收
            HAL_UART_Receive_IT(&Uart1.Uart, Uart1.Rx_In_ArrPtr->Start, U1_RX_MAX);
        }
    }

}

stm32fxx_It.c

/*-------------------------------------------------*/
/*                                                 */
/*          实现各种中断服务函数的源文件           */
/*                                                 */
/*-------------------------------------------------*/

#include "stm32f1xx_hal.h"   
#include "stm32f1xx_it.h"

#include "Uart.h"

void EXTI15_10_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_10);
}
void EXTI0_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}

void USART1_IRQHandler(void)
{
    HAL_UART_IRQHandler(&Uart1.Uart);
    
    if( __HAL_UART_GET_FLAG(&Uart1.Uart,UART_FLAG_IDLE) )//如果进入空闲状态
    {
        __HAL_UART_CLEAR_IDLEFLAG(&Uart1.Uart); //清除空闲中断标志位
        Uart1.Rx_Counter += (U1_RX_MAX - Uart1.Uart.RxXferCount);   //提前读取Count,计算此次字节数量并累加
        HAL_UART_AbortReceive_IT(&Uart1.Uart);  //终止接收(会清除记录的接收量:Count。有终止回调函数)
    }
}

/*-------------------------------------------------*/
/*函数名:不可屏蔽中断处理函数                     */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void NMI_Handler(void)
{

}

/*-------------------------------------------------*/
/*函数名:硬件出错后进入的中断处理函数             */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void HardFault_Handler(void)
{

}
/*-------------------------------------------------*/
/*函数名:软中断,SWI 指令调用的处理函数           */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void SVC_Handler(void)
{
    
}
/*-------------------------------------------------*/
/*函数名:可挂起的系统服务处理函数                 */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void PendSV_Handler(void)
{
    
}
/*-------------------------------------------------*/
/*函数名:SysTic系统嘀嗒定时器处理函数             */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void SysTick_Handler(void)
{  
    HAL_IncTick();  
}

main.c

#include "stm32f1xx_hal.h"

#include "rcc.h"
#include "led.h"
#include "key.h"

#include "Uart.h"
/*函 数 名:HAL库:中断方式。实现UART串口数据收发。 定长  20字节 */
 

int main (void)
{
    HAL_Init();//初始化HAL库
    
    RccClock_Init();//配置时钟树72M

    LED_Init();
    
    Uart1_Init(921600);//初始化串口1
    
    while(1)
    {   
        //如果接收缓冲区 In指针和Out指针不是一个地址。那么就说明接收缓冲区有数据了
        if(Uart1.Rx_Out_ArrPtr != Uart1.Rx_In_ArrPtr)
        {
            //拷贝这次接收到的字节 到发送缓冲区 .注意是Out 的地址,不是in
            Uart1_Tx_Data( Uart1.Rx_Out_ArrPtr->Start, (Uart1.Rx_Out_ArrPtr->End - Uart1.Rx_Out_ArrPtr->Start +1));
            Uart1.Rx_Out_ArrPtr++;  //向后挪
            //如果等于END  则回卷 
            if( Uart1.Rx_Out_ArrPtr == Uart1.Rx_End_ArrPtr )
            {
                Uart1.Rx_Out_ArrPtr = &Uart1.Rx_LocationArr[0]; //记录下一次 开始记录的位置
            }
        }
        
        //如果发送缓冲区 In指针和Out指针不是一个地址。那么就说明发送缓冲区有数据了。并且此时处于空闲状态
        if( (Uart1.Tx_Out_ArrPtr != Uart1.Tx_In_ArrPtr) && (Uart1.Tx_State == 0) )//0为空闲。1 为忙
        {
            Uart1.Tx_State = 1; //进入忙状态
            HAL_UART_Transmit_IT( &Uart1.Uart, Uart1.Tx_Out_ArrPtr->Start, (Uart1.Tx_Out_ArrPtr->End - Uart1.Tx_Out_ArrPtr->Start + 1) );
            Uart1.Tx_Out_ArrPtr++;  //向后挪
            //如果等于END  则回卷 
            if( Uart1.Tx_Out_ArrPtr == Uart1.Tx_End_ArrPtr )
            {
                Uart1.Tx_Out_ArrPtr = &Uart1.Tx_LocationArr[0]; //记录下一次 开始记录的位置
            }
        }
    }
}

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

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

相关文章

SOCKS5代理配置中的常见问题:故障排除指南

SOCKS5代理是一种灵活的网络代理协议&#xff0c;被广泛用于提高网络隐私、访问受限内容、绕过网络限制等。然而&#xff0c;用户在配置SOCKS5代理时经常会遇到各种问题&#xff0c;这些问题可能会影响网络连接的稳定性和安全性。 了解这些常见问题及其解决方案可以帮助用户更…

水陆双雄:赛艇与VELO Angel Revo Halo坐垫的平衡共舞~

在广阔的水域上&#xff0c;赛艇运动员们以划桨为笔&#xff0c;书写着速度与激情的篇章。每一桨的挥动&#xff0c;都是力量与技巧的完美结合。而在自行车运动中&#xff0c;VELO Angel Revo Halo坐垫则如同骑行者的守护神&#xff0c;以科技与环保之名&#xff0c;为长途跋涉…

【开学季】你需要这样一个桌面倒计时 时刻提醒你不负韶华

【开学季】你需要这样一个桌面倒计时 时刻提醒你不负韶华。又到开学季了&#xff0c;莘莘学子陆续的回归到校园&#xff0c;开启人生价值的提升。 时光荏苒&#xff0c;岁月如梭&#xff0c;转眼间又是开学季。这个季节&#xff0c;总是承载着太多的期待与梦想&#xff0c;它像…

推荐2024年新手友好的4款音乐剪辑软件!

最近我开始接触音乐剪辑&#xff0c;想把一些歌曲进行剪辑创作&#xff1b;于是在网上好多了很多的音频剪辑软件进行试用&#xff0c;一番下来&#xff0c;发现了4款使用起来体验感比较好的专业剪辑工具&#xff0c;在这里跟大家分享分享。这些工具都可以被应用于歌曲创作&…

样式(3)----修改主题颜色

楔子&#xff1a;做软件时&#xff0c;经常有这样的需求&#xff0c;这样配色不合适&#xff1f;或者像动态的修改样式&#xff1f;那问题来了&#xff1a;怎样修软件界面的主题、修改皮肤&#xff1f; 方法1&#xff1a;使用第三方控件&#xff0c;直接更换主题&#xff0c;p…

电商库存API:商家数字化转型的加速器

在电商行业&#xff0c;库存管理是运营的核心之一。随着业务的扩展和消费者需求的多样化&#xff0c;传统的库存管理方法已经难以满足现代电商的需求。这时&#xff0c;电商库存API应运而生&#xff0c;为商家提供了一种高效、灵活的库存管理手段。 一、对接电商库存API的重要…

如何用SpringBoot搭建摇滚乐鉴赏网站?一步步教你实现音乐分享与社区互动!

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

高效产品设计:十大关键经验教程

在当今这个竞争异常激烈的市场环境中&#xff0c;产品系统设计的效率已经迅速成为企业能否取得成功的关键因素之一。随着消费者需求的不断变化和技术发展的日新月异&#xff0c;企业必须快速响应市场动态&#xff0c;以便在激烈的竞争中保持优势。而一套高效的产品系统设计方法…

js逆向——异步栈分析(上)

案例1&#xff1a; 受害者网站&#xff1a;https://m.ctyun.cn/wap/main/auth/login 首先抓包分析一波 发现是xhr请求&#xff0c;可以跟栈调试&#xff0c;相对还是容易一些 前面三个一般是send,ajax,xmlhttprequest这种封装好的发包的库&#xff0c;一般不用在意&#xff…

图像数据处理25

六、 图像分割 6.2区域生长 6.2.1区域生长的基本概念&#xff1a;可以理解成先选择一个像素区域&#xff0c;然后将其邻接区域中相似程度高的像素合并进去&#xff0c;直到找不到可以合并的像素为止。其的主要优点是能够较为精准地将具有相同特征的联通区域分割出来。 6.2.2…

农产品品牌优化:软硬兼施,全网突围成焦点

农产品品牌想要在全网范围内脱颖而出&#xff0c;‌单靠传统的“硬实力”——如优质的产品质量、‌丰富的种植经验或是先进的生产技术&#xff0c;‌已经远远不够。‌在这个多元化的市场竞争中&#xff0c;‌软实力成为了决定品牌能否远航的关键。‌ 农产品讲究品牌软实力&…

【大模型】chatglm系列模型基础

前言&#xff1a;2022年11月&#xff0c;斯坦福大学大模型中心对全球30个主流大模型进行了全方位的评测2&#xff0c;GLM-130B 是亚洲唯一入选的大模型。GLM-130B 在准确性和恶意性指标上与 GPT-3 175B持平。chatglm1&#xff0c;2&#xff0c;3代在模型架构上基本一样&#xf…

day-43 有效的数独

思路 暴力解法&#xff1a;1.判断每行是否有重复的数字;2.判断每列是否有重复的数字&#xff1b;3.判断每一个以粗实线分隔的 3x3 宫内是否有重复的数字 解题过程 如果以上三种情况都不存在重复的数字&#xff0c;则返回true Code class Solution {public boolean isValidSu…

4G手机智能遥控开关

什么是4G手机智能遥控开关 4G手机智能遥控开关作为现代智能家居与工业自动化的重要组成部分&#xff0c;提供了便捷、高效的远程控制方案。它利用4G通信技术&#xff0c;允许用户随时随地通过智能手机或其他移动设备控制电器设备的开关状态&#xff0c;适用于家庭、办公、工业等…

读《Visual Whole-Body for Loco-Manipulation》①train

这个新项目比那些强化学习入门项目好配置多了。。。 不过需要搞个wandb账号&#xff0c;一开始运行时需要api key args Namespace(checkpoint-1,colsNone,compute_device_id0,debugFalse,experiment_nameNone,exptidSOME_YOUR_DESCRIPTION_debug,flat_terrainFalse,flexFalse…

认知杂谈43

今天分享 有人说的一段争议性的话 I I 年轻&#xff1a;潜力如海&#xff0c;挑战重重&#xff0c;绝非轻易挥霍的资本 I 咱都觉得年轻好像一张白纸&#xff0c;能在上面画出精彩人生呢。可实际上&#xff0c;年轻可不是啥简单的好事儿&#xff0c;它更像是个宝库&#xff0…

以太坊恐慌!支出不透明,不喜欢DeFi?创始人V神亮资辟谣!个人净资产90%为ETH!

以太坊&#xff08;Ethereum&#xff09;作为区块链领域的佼佼者&#xff0c;一直吸引着全球投资者和开发者的目光。然而&#xff0c;近期围绕以太坊基金会&#xff08;Ethereum Foundation&#xff09;及其创始人V神&#xff08;Vitalik Buterin&#xff09;的一系列争议&…

Word文档被锁定无法编辑怎么办?一键快速移除Word编辑限制

有没有遇到这种情况&#xff1f;打开Word文档后&#xff0c;准备对Word软件进行文档的编辑时&#xff0c;发现文档有部分内容无法进行编辑了&#xff0c;不知道怎么回事&#xff0c;其实这是因为无法编辑区域被锁定了&#xff0c;所以无法进行编辑&#xff0c;那么应该怎么解除…

Kotlin 泛型小知识: `<T>`, `<out T>`, `<in T>` 的区别

引言 我们是不是常常在 Kotlin 的代码中看到一些奇怪的符号&#xff0c;比如 <out T> 或者 <T>&#xff1f;如果我们对这些泛型&#xff08;Generics&#xff09;符号还不太了解&#xff0c;没关系&#xff01;今天我们就来聊一聊它们的区别&#xff0c;以及如何在…

c语言(二叉树)

第4章 二叉树和BST 树与二叉树 基本概念 树是一种非线性结构&#xff0c;其严格的数学定义是&#xff1a;如果一组数据中除了第一个节点&#xff08;第一个节点称为根节点&#xff0c;没有直接前驱节点&#xff09;之外&#xff0c;其余任意节点有且仅有一个直接前驱&#xff…