【正点原子STM32连载】 第二十九章 DMA实验 摘自【正点原子】APM32F407最小系统板使用指南

news2024/11/24 3:34:35

1)实验平台:正点原子stm32f103战舰开发板V4
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html#

第二十九章 DMA实验

本章,我们将介绍STM32F103的DMA。我们将利用DMA来实现串口数据传送,并在LCD模块上显示当前的传送进度。
本章分为如下几个小节:
29.1 DMA简介
29.2 硬件设计
29.3 程序设计
29.4 下载验证

29.1 DMA简介
DMA,全称为:Direct Memory Access,即直接存储器访问。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAM与I/O设备开辟一条直接传送数据的通路,能使CPU的效率大为提高。
STM32F103内部有2个DMA控制器(DMA2仅存大容量产品中),DMA1有7个通道。DMA2有5个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。
STM32F103的DMA有以下一些特性:
●每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置。
●在七个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),假如在相等优先权时由硬件决定(请求0优先于请求1,依此类推)。
●独立的源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。
●支持循环的缓冲器管理。
●每个通道都有3个事件标志(DMA半传输,DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。
●存储器和存储器间的传输。
●外设和存储器,存储器和外设的传输。
●闪存、SRAM、外设的SRAM、APB1、APB2和AHB外设均可作为访问的源和目标。
●可编程的数据传输数目:最大为65536。
29.1.1 DMA框图
STM32F103ZET6有两个DMA控制器,DMA1和DMA2,本章,我们仅针对DMA1进行介绍。
下面先来学习DMA控制器框图,通过学习DMA控制器框图会有一个很好的整体掌握,同时对之后的编程也会有一个清晰的思路。STM32F103的DMA控制器框图如图29.1.1.1所示:
在这里插入图片描述

图29.1.1.1 DMA控制器框图
图中,我们标记了3处位置,起作用分别是:
①DMA请求
如果外设想要通过DMA来传输数据,必须先给DMA控制器发送DMA请求,DMA收到请求信号之后,控制器会给外设一个应答信号,当外设应答后且DMA控制器收到应答信号之后,就会启动DMA的传输,直到传输完毕。
STM32F103共有DMA1和DMA2两个控制器,DMA1有7个通道,DMA2有5个通道,不同的DMA控制器的通道对应着不同的外设请求,这决定了我们在软件编程上该怎么设置,具体见表29.1.1.1DMA请求映像表。
在这里插入图片描述

表29.1.1.1 DMA1请求映像表
在这里插入图片描述

表29.1.1.2 DMA2请求映像
② 通道
DMA具有12个独立可编程的通道,其中DMA1有7个通道,DMA2有5个通道,每个通道对应不同的外设的DMA请求。虽然每个通道可以接收多个外设的请求,但是同一时间只能接收一个,不能同时接收多个。
③ 仲裁器
当发生多个DMA通道请求时,就意味着有先后响应处理的顺序问题,这个就由仲裁器管理。仲裁器管理DMA通道请求分为两个阶段。第一阶段属于软件阶段,可以在DMA_CCRx寄存器中设置,有4个等级:非常高,高,中和低四个优先级。第二阶段属于硬件阶段,如果两个或以上的DMA通道请求设置的优先级一样,则他们优先级取决于通道编号,编号越低优先权越高,比如通道0高于通道1。在大容量产品和互联型产品中,DMA1控制器拥有高于DMA2控制器的优先级。
29.1.2 DMA寄存器
 DMA中断状态寄存器(DMA_ISR)
DMA 中断状态寄存器描述如图29.1.2.1所示:
在这里插入图片描述

图29.1.2.1 DMA_ISR寄存器
该寄存器是查询当前DMA传输的状态,我们常用的是TCIFx位,即通道DMA传输完成与否的标志。注意此寄存器为只读寄存器,所以在这些位被置位之后,只能通过其他的操作来清除。
 DMA中断标志清除寄存器(DMA_IFCR)
DMA中断标志清除寄存器描述如图29.1.2.2所示:
在这里插入图片描述

图29.1.2.2 DMA_IFCR寄存器
该寄存器是用来清除DMA_ISR的对应位的,通过写0清除。在DMA_ISR被置位后,我们必须通过向该寄存器对应的位写0来清除。
 DMA通道x传输数量寄存器(DMA_CNDTRx)
DMA通道x传输数量寄存器描述如图29.1.2.3所示:
在这里插入图片描述

图29.1.2.3 DMA_CNDTR寄存器
该寄存器控制着DMA通道x的每次传输所要传输的数据量。其设置范围为0~65535。并且该寄存器的值随着传输的进行而减少,当该寄存器的值为0的时候就代表此次数据传输已经全部发送完成。可以通过这个寄存器的值来获取当前DMA传输的进度。
 DMA通道x配置寄存器(DMA_CCRx)
DMA通道x配置寄存器描述如图29.1.2.4所示:
在这里插入图片描述

图29.1.2.4 DMA_CCRx寄存器
该寄存器控制着DMA很多相关信息,包括数据宽度、外设及存储器宽度、通道优先级、增量模式、传输方向、中断允许、使能等,所以说DMA_CCRx是DMA传输的核心控制寄存器。
 DMA通道x外设地址寄存器(DMA_CPARx)
DMA通道x外设地址寄存器描述如图29.1.2.5所示:
在这里插入图片描述

图29.1.2.5 DMA_CPARx寄存器
该寄存器是用来存储STM32外设的地址,比如我们平常使用串口1,那么该寄存器必须写入0x40013804(其实就是&USART1_DR)。其他外设就可以修改成其他对应外设地址就好了。
 DMA通道x存储器地址寄存器(DMA_CMARx)
DMA通道x存储器地址寄存器用来存放存储器的地址,该寄存器和DMA_CPARx差不多,所以就不列出来了。举个应用的例子,在程序中,我们使用到一个g_sendbuf[5200]数组来做存储器,那么我们在DMA_CMARx中写入&g_sendbuf即可。
29.2 硬件设计

  1. 例程功能
    每按下按键KEY0,串口1就会以DMA方式发送数据,同时在LCD上面显示传送进度。打开串口调试助手,可以收到DMA发送的内容。LED0闪烁用于提示程序正在运行。
  2. 硬件资源
    1)LED灯
    LED0 – PB5
    2)独立按键 KEY0 – PE4
    3)串口1(PA9/PA10连接在板载USB转串口芯片CH340上面)
    4)正点原子 2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
  3. 原理图
    DMA属于STM32F103内部资源,通过软件设置好就可以了。
    29.3 程序设计
    29.3.1 DMA的HAL库驱动
    DMA在HAL库中的驱动代码在stm32f1xx_hal_dma.c文件(及其头文件)中。
  4. HAL_DMA_Init函数
    DMA的初始化函数,其声明如下:
    HAL_StatusTypeDef HAL_DMA_Init(DMA_HandleTypeDef *hdma);
    函数描述:
    用于初始化DMA1,DMA2。
    函数形参:
    形参1是DMA_HandleTypeDef结构体类型指针变量,其定义如下:
typedef struct __DMA_HandleTypeDef
{
  void                              *Instance;    	/* 寄存器基地址 */
  DMA_InitTypeDef                 Init;           	/* DAM通信参数 */
  HAL_LockTypeDef                 Lock;           	/* DMA锁对象 */
  __IO HAL_DMA_StateTypeDef     State;        	/* DMA传输状态 */
  void                              *Parent;    		/* 父对象状态,HAL库处理的中间变量 */
void (*XferCpltCallback)( struct __DMA_HandleTypeDef *hdma);/*DMA传输完成回调*/
/* DMA一半传输完成回调 */
void (* XferHalfCpltCallback)( struct __DMA_HandleTypeDef * hdma);  
  /* DMA传输完整的Memory1回调 */
void (* XferM1CpltCallback)( struct __DMA_HandleTypeDef * hdma);   
/* DMA传输半完全内存回调 */
void (* XferM1HalfCpltCallback)( struct __DMA_HandleTypeDef * hdma);  
/*DMA传输错误回调*/
void (* XferErrorCallback)( struct __DMA_HandleTypeDef * hdma);
/* DMA传输中止回调 */
  void (* XferAbortCallback)( struct __DMA_HandleTypeDef * hdma);
  __IO uint32_t                   ErrorCode;            		/* DMA存取错误代码 */
  DMA_TypeDef				   *DmaBaseAddress;		   	/* DMA通道基地址 */
uint32_t					   ChannelIndex;             	/* DMA通道索引 */
}DMA_HandleTypeDef;

这个结构体内容比较多,,下面列出几个成员说明一下。
Instance:是用来设置寄存器基地址,例如要设置的对象是串口1的发送,那么就要参考表29.1.1.1,串口1的DMA传输需要用到的是DMA1的通道4,即DMA1_Channel4。
Parent:是HAL库处理中间变量,用来指向DMA通道外设句柄。
XferCpltCallback(传输完成回调函数),XferHalfCpltCallback(半传输完成回调函数),XferM1CpltCallback(Memory1传输完成回调函数)和XferErrorCallback(传输错误回调函数)是四个函数指针,用来指向回调函数入口地址。
其他成员变量是HAL库处理过程状态标识变量,这里就不做过多讲解。
接下来我们重点介绍Init,它是DMA_InitTypeDef结构体类型变量,该结构体定义如下:

typedef struct
{   
  uint32_t Direction;            	/* 传输方向,例如存储器到外设DMA_MEMORY_TO_PERIPH */ 
  uint32_t PeriphInc;            	/* 外设(非)增量模式,非增量模式DMA_PINC_DISABLE */  
  uint32_t MemInc;               	/* 存储器(非)增量模式,增量模式DMA_MINC_ENABLE */  
  uint32_t PeriphDataAlignment;	/* 外设数据大小:8/16/32位 */
  uint32_t MemDataAlignment;    	/* 存储器数据大小:8/16/32位 */
  uint32_t Mode;                 	/* 模式:循环模式/普通模式 */    
  uint32_t Priority;            	/* DMA优先级:低/中/高/非常高 */ 
}DMA_InitTypeDef;

该结构体成员变量非常多,但每个成员变量的配置基本都是DMA_CCRx寄存器的相关位。
函数返回值:
HAL_StatusTypeDef枚举类型的值。
以DMA的方式传输串口数据的配置步骤
1)使能DMA时钟。
DMA的时钟使能是通过AHB1ENR寄存器来控制的,这里我们要先使能时钟,才可以配置DMA相关寄存器。HAL库方法为:

__HAL_RCC_DMA1_CLK_ENABLE(); /* DMA1时钟使能 */
__HAL_RCC_DMA2_CLK_ENABLE(); /* DMA2时钟使能 */

2)初始化DMA。
调用HAL_DMA_Init函数初始化DMA的相关参数,包括配置通道,外设地址,存储器地址,传输数据量等。
HAL库为了处理各类外设的DMA请求,在调用相关函数之前,需要调用一个宏定义标识符,来连接DMA和外设句柄。例如要使用串口DMA发送,所以方式为:
__HAL_LINKDMA(&g_uart1_handler, hdmatx, g_dma_handle);
其中g_uart1_handler是串口初始化句柄,我们在usart.c中定义过了。g_dma_handle是DMA初始化句柄。hdmatx是外设句柄结构体的成员变量,在这里实际就是g_uart1_handler的成员变量。在HAL库中,任何一个可以使用DMA的外设,它的初始化结构体句柄都会有一个DMA_HandleTypeDef指针类型的成员变量,是HAL库用来做相关指向的。hdmatx就是DMA_HandleTypeDef结构体指针类型。
这句话的含义就是把g_uart1_handler句柄的成员变量hdmatx和DMA句柄g_dma_handle连接起来,是纯软件处理,没有任何硬件操作。
这里我们就点到为止,如果大家要详细了解HAL库指向关系,请查看本实验宏定义标识符__HAL_LINKDMA的定义和调用方法,就会很清楚了。
3)使能串口的DMA发送,启动传输。
串口1的DMA发送实际是串口控制寄存器CR3的位7来控制的,在HAL库中操作该寄存器来使能串口DMA发送的函数为HAL_UART_Transmit_DMA。
这里大家需要注意,调用该函数后会开启相应的DMA中断,对于本章实验,我们是通过查询的方法获取数据传输状态,所以并没有做中断相关处理,也没有编写中断服务函数。
HAL库还提供了对串口的DMA发送的停止,暂停,继续等操作函数:

HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart);   	/* 停止 */
HAL_StatusTypeDef HAL_UART_DMAPause(UART_HandleTypeDef *huart);  	/* 暂停 */
HAL_StatusTypeDef HAL_UART_DMAResume(UART_HandleTypeDef *huart); 	/* 恢复 */

4)查询DMA传输状态。
在DMA传输过程中,我们要查询DMA传输通道的状态,使用的方法是通过检测DMA寄存器的相关位实现:
__HAL_DMA_GET_FLAG(&g_dma_handle, DMA_FLAG_TC4);
获取当前传输剩余数据量:
__HAL_DMA_GET_COUNTER(&g_dma_handle);
同样,我们也可以设置对应的DMA数据流传输的数据量大小,函数为:
__HAL_DMA_SET_COUNTER (&g_dma_handle, 1000);
DMA相关的库函数我们就讲解到这里,大家可以查看HAL库中文手册详细了解。
5)DMA中断使用方法。
DMA中断对于每个通道都有一个中断服务函数,比如DMA1_Channel4的中断服务函数为DMA1_Channel4_IRQHandler。HAL库提供了通用DMA中断处理函数HAL_DMA_IRQHandler,在该函数内部,会对DMA传输状态进行分析,然后调用相应的中断处理回调函数:

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);     /* 发送完成回调函数 */
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);/* 发送一半回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);     /* 接收完成回调函数 */
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);/* 接收一半回调函数 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);      /* 传输出错回

调函数 */
29.3.2 程序流程图
图30.3.2.1 DMA实验程序流程图
29.3.3 程序解析

  1. DMA驱动代码
    这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。DMA驱动源码包括两个文件:dma.c和dma.h。
    dma.h头文件只有函数得声明,就不解释了,我们直接介绍dma.c的程序。下面是与DMA初始化相关的函数,其定义如下:
/**
 * @brief    	串口TX DMA初始化函数
 *   @note     	这里的传输形式是固定的, 这点要根据不同的情况来修改
 *              	从存储器 -> 外设模式/8位数据宽度/存储器增量模式
 *
 * @param    	dmax_chy    : DMA的通道, DMA1_Channel1 ~ 7, DMA2_Channel1 ~ 5
 *              	某个外设对应哪个DMA, 哪个通道, 请参考<<STM32中文参考手册>> 10.3.7节
 *              	必须设置正确的DMA及通道, 才能正常使用! 
 * @retval     	无
 */
void dma_init(DMA_Channel_TypeDef* DMAx_CHx)
{	/* 大于DMA1_Channel7, 则为DMA2的通道了 */
    if ((uint32_t)DMAx_CHx > (uint32_t)DMA1_Channel7)     
    {
        __HAL_RCC_DMA2_CLK_ENABLE();                      /* DMA2时钟使能 */
    }
    else 
    {
        __HAL_RCC_DMA1_CLK_ENABLE();                      /* DMA1时钟使能 */
    }
    /* 将DMA与USART1联系起来(发送DMA) */
    __HAL_LINKDMA(&g_uart1_handle, hdmatx, g_dma_handle); 
    
    /* Tx DMA配置 */
    g_dma_handle.Instance = DMAx_CHx;	/* USART1_TX的DMA通道: DMA1_Channel4 */
    g_dma_handle.Init.Direction = DMA_MEMORY_TO_PERIPH; 	/* 存储器到外设模式 */
    g_dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;    		/* 外设非增量模式 */
    g_dma_handle.Init.MemInc = DMA_MINC_ENABLE;           	/* 存储器增量模式 */
    g_dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; /* 外设位宽 */
    g_dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;  	   /* 存储器位宽 */
    g_dma_handle.Init.Mode = DMA_NORMAL;                 		/* DMA模式:正常模式 */
    g_dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM; 		/* 中等优先级 */

    HAL_DMA_Init(&g_dma_handle);
}

该函数是一个通用的DMA配置函数,DMA1/DMA2的所有通道,都可以利用该函数配置,不过有些固定参数可能要适当修改(比如位宽、传输方向等)。该函数在外部只能修改DMA数据通道,更多的其他设置只能在该函数内部修改。对照前面的配置步骤的详细讲解来分析这部分代码即可。
2. main.c代码
在main.c里面编写如下代码:
/* 要循环发送的字符串 */

const uint8_t TEXT_TO_SEND[] ={"正点原子 STM32 DMA 串口实验"};
#define SEND_BUF_SIZE       (sizeof(TEXT_TO_SEND) + 2) * 200	/* 发送数据长度*/
uint8_t g_sendbuf[SEND_BUF_SIZE];   /* 发送数据缓冲区 */

int main(void)
{
    uint8_t  key = 0;
    uint16_t i, k;
    uint16_t len;
    uint8_t  mask = 0;
    float pro = 0;          					/* 进度 */

    HAL_Init();            					/* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);		/* 设置时钟, 72Mhz */
    delay_init(72);                       		/* 延时初始化 */
    usart_init(115200);                 		/* 串口初始化为115200 */
    led_init();                          		/* 初始化LED */
    lcd_init();                             		/* 初始化LCD */
    key_init();                        			/* 初始化按键 */
dma_init(DMA1_Channel4);            		/* 初始化串口1 TX DMA */

    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "DMA TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "KEY0:Start", RED);

    len = sizeof(TEXT_TO_SEND);
    k = 0;
    
    for (i = 0; i < SEND_BUF_SIZE; i++) 	/* 填充ASCII字符集数据 */
    {
        if (k >= len)   							/* 入换行符 */
        {
            if (mask)
            {
                g_sendbuf[i] = 0x0a;
                k = 0;
            }
            else
            {
                g_sendbuf[i] = 0x0d;
                mask++;
            }
        }
        else     									/* 复制TEXT_TO_SEND语句 */
        {
            mask = 0;
            g_sendbuf[i] = TEXT_TO_SEND[k];
            k++;
        }
    }
 
    i = 0;

    while (1)
    {
        key = key_scan(0);

        if (key == KEY0_PRES)       				/* KEY0按下 */
        {
            printf("\r\nDMA DATA:\r\n");
            lcd_show_string(30, 130, 200, 16, 16, "Start Transimit....", BLUE);
            lcd_show_string(30, 150, 200, 16, 16, "   %", BLUE);    /* 显示百分号 */

HAL_UART_Transmit_DMA(&g_uart1_handle, g_sendbuf, SEND_BUF_SIZE);
            /* 等待DMA传输完成,此时我们来做另外一些事情,比如点灯  
             * 实际应用中,传输数据期间,可以执行另外的任务 
             */
            while (1)
            {
/* 等待 DMA1_Channel4 传输完成 */
                if ( __HAL_DMA_GET_FLAG(&g_dma_handle, DMA_FLAG_TC4))  
                {
                    __HAL_DMA_CLEAR_FLAG(&g_dma_handle, DMA_FLAG_TC4);
                    HAL_UART_DMAStop(&uartx_handler);    /* 传输完成以后关闭串口DMA */
                    break;
                } 

                pro = __HAL_DMA_GET_COUNTER(&g_dma_handle);
                len = SEND_BUF_SIZE;        	/* 总长度 */
                pro = 1 - (pro / len);      	/* 得到百分比 */
                pro *= 100;                 		/* 扩大100倍 */
                lcd_show_num(30, 150, pro, 3, 16, BLUE);
            } 

            lcd_show_num(30, 150, 100, 3, 16, BLUE);    /* 显示100% */
/* 提示传送完成 */
            lcd_show_string(30, 130, 200, 16, 16, "Transimit Finished!", BLUE); 
        }
        i++;
        delay_ms(10);

        if (i == 20)
        {
            LED0_TOGGLE();  /* LED0闪烁,提示系统正在运行 */
            i = 0;
        }
    }
}

main函数的流程大致是:先初始化发送数据缓冲区g_sendbuf的值,然后通过KEY0开启串口DMA发送,在发送过程中,通过__HAL_DMA_GET_COUNTER(&g_dma_handle)获取当前还剩余的数据量来计算传输百分比,最后在传输结束之后清除相应标志位,提示已经传输完成。
29.4 下载验证
将程序下载到开发板后,可以看到LED0不停的闪烁,提示程序已经在运行了。LCD显示的内容如图29.4.1所示:
在这里插入图片描述

图29.4.1 DMA实验测试图
我们打开串口调试助手,然后按KEY0,可以看到串口显示如图29.4.2所示的内容:
在这里插入图片描述

图29.4.2 串口收到的数据内容
可以看到串口收到了开发板发送过来的数据,同时可以看到TFTLCD上显示了进度等信息,如图29.4.3所示:
在这里插入图片描述

图29.4.3 DMA串口数据传输中
至此,我们整个DMA实验就结束了,希望大家通过本章的学习,掌握STM32F103的DMA使用。DMA是个非常好的功能,它不但能减轻CPU负担,还能提高数据传输速度,合理的应用DMA,往往能让你的程序设计变得简单。

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

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

相关文章

【PowerQuery】PowerQuery学习路径

PowerQuery这么好,怎么去学习呢?相信很多初读本书的朋友迫切的希望了解整个PowerQuery全景知识和它提供的相应的功能。但是对于PowerQuery来说,一开始就会进行自定义函数的构建当然也是不可能的,这里有相应的学习路径来进行由浅入深的学习,帮助读者更好的理解PowerQuery的…

12V/24V/48V 直流DC电源浪涌保护方案图 超齐全

直流DC电源端口浪涌过压防护一直都是很多新老电子工程师关注的方案之一。不管是电源端口浪涌防护还是信号接口静电保护&#xff0c;浪涌静电防护&#xff0c;找东沃&#xff0c;电路保护不迷路&#xff01;东沃电子专注于研发、生产、销售静电保护二极管&#xff08;ESD&#x…

natapp 内网穿透 获取网页信息相关内容简介

1 首选介绍natapp 内网穿透进入官网 https://natapp.cn/ 购买或者使用免费隧道 协议使用web ,填写本地地址和web端可访问的端口 进入exe文件的目录 运行 natapp -authtokenxxxxxx 剩下获取微信用户信息 第一步获取code 这里需要设置授权域名 这里可以设置上 natapp给分配的地…

WebSocket原理简介

慢聊Go之GoLang中使用Gorilla Websocket&#xff5c;Go主题月 - 掘金 (juejin.cn) 【Go项目】24. WebSocket 基本原理_哔哩哔哩_bilibili 1.http和socket的区别 1&#xff09; http要先给服务器发请求&#xff0c;然后才会得到响应&#xff0c;基本是一问一答式。 而socke…

纯css实现奥运五环、3D平移、旋转、扭曲

文章目录 前言效果图htmlcss 前言 1、不是真正的五环&#xff0c;因为通过形变得来。 2、不同电脑显示器的像素不同&#xff0c;显现的效果不同。 3、不推荐使用此方法。 4、主要通过旋转加平移的方式实现。 效果图 html <div class"olympic_rings"><span …

医美小程序怎么做

医疗美容小程序商城功能明细&#xff1a; 1. 商品展示&#xff1a; - 商品分类&#xff1a;根据不同的医疗美容产品进行分类展示&#xff0c;方便用户查找和浏览。 - 商品详情&#xff1a;展示商品的详细信息&#xff0c;包括价格、规格、功效、成分等&#xff0c;以及用户评价…

Java反射:探索对象创建与类信息获取

文章目录 1. 对象的创建2. 类的初始化2.1 类的加载2.2 类的连接2.3 类的初始化 3. 反射是什么&#xff1f;4. 获取Class类对象4.1 使用类名.class4.2 使用对象的getClass()方法4.3 使用Class.forName() 5. 获取构造器对象5.1 使用getConstructors()和getDeclaredConstructors()…

C标准输入与标准输出——stdin,stdout

&#x1f517; 《C语言趣味教程》&#x1f448; 猛戳订阅&#xff01;&#xff01;&#xff01; ​—— 热门专栏《维生素C语言》的重制版 —— &#x1f4ad; 写在前面&#xff1a;这是一套 C 语言趣味教学专栏&#xff0c;目前正在火热连载中&#xff0c;欢迎猛戳订阅&#…

【iVX】iVX的低代码未来发展趋势:加速应用开发的创新之路

简介&#xff1a; 随着数字化转型的飞速发展&#xff0c;企业和组织对快速开发和交付高质量应用的需求越来越迫切。低代码开发平台作为一种创新的解决方案&#xff0c;极大地简化了应用程序的开发过程。在这一领域&#xff0c;iVX低代码平台作为领先的创业公司&#xff0c;正在…

【kafka】kafka介绍

https://kafka.apachecn.org/intro.html Apache Kafka是一个分布式流处理平台。这到底意味着什么呢? 我们知道流处理平台需要具有以下三种特性&#xff1a; 可以发布和订阅流式的记录。这一方面与消息队列或者企业消息系统类似。可以储存流式的记录&#xff0c;并且有较好的…

初识自动驾驶技术之旅 第一课 学习笔记

​ &#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! ​ 目录 &#x1f4da; 前言 &#x1f4d8; 1. 自动驾驶人才需求与挑战 &#x1f4d8; 2. Apollo …

什么合同管理系统?4类合同管理软件评测

说到合同管理系统&#xff0c;前提还是弄清楚合同有哪些类型&#xff0c;合同管理有那些痛点&#xff0c;才好对症下药。 一、合同的类型和合同管理的痛点 从法律角度来说&#xff0c;合同可以分为&#xff1a;有名合同与无名合同、单务合同与双务合同、有偿合同与无偿合同、…

Redis7安装配置

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Java从入门到精通 ✨特色专栏&#xf…

硬件系统工程师宝典(39)-----如何使用ESD防护器件?

各位同学大家好&#xff0c;欢迎继续做客电子工程学习圈&#xff0c;今天我们继续来讲这本书&#xff0c;硬件系统工程师宝典。 上篇我们介绍了一些常用的视频接口DisplayPort、DVI和HDMI接口以及它们的特点。今天我们来讲一讲ESD防护器件。 1.ESD概念 ESD&#xff08;Electr…

约会怎么走到目的地最近呢?一文讲清所有最短路算法问题

&#x1f680;&#x1f680;&#x1f680;&#x1f680;&#x1f680;订阅专栏&#x1f449; 趣学算法(dog) &#x1f448; 带你学习算法原理 算法模板&#x1f680;&#x1f680;&#x1f680;&#x1f680;&#x1f680; write in front 朋友们好啊&#xff0c;好久没写过…

Android Studio开发入门教程:如何更改APP的图标?

更改APP的图标&#xff08;安卓系统&#xff09; 环境&#xff1a;Windows10、Android Studio版本如下图、雷电模拟器。 推荐图标库 默认APP图标 将新图标拉进src/main/res/mipmap-hdpi文件夹&#xff08;一般app的icon图标是存放在mipmap打头的文件夹下的&#xff09; 更改sr…

Java File类和IO流

1. File类 1.1 File对象创建 注意&#xff1a; 路径中"\"要写成"\\"&#xff0c; 路径中"/"可以直接用 File对象可以指代一个不存在的文件路径路径中带盘符是绝对路径&#xff0c;不带盘符是相对路径 1.2 File判断和获取方法 1.3 创建和删除方…

Kafka3.0.0版本——增加副本因子

目录 一、服务器信息二、启动zookeeper和kafka集群2.1、先启动zookeeper集群2.2、再启动kafka集群 三、增加副本因子3.1、增加副本因子的概述3.2、增加副本因子的示例3.2.1、创建topic(主题)3.2.2、手动增加副本存储 一、服务器信息 四台服务器 原始服务器名称原始服务器ip节点…

企业架构LNMP学习笔记15

客户端缓存&#xff1a; B/S架构里&#xff0c;Browser是浏览器&#xff0c;就是客户端。 客户端缓存告知浏览器获取服务段的信息是在某个区间时间段是有效的。 每次请求从服务器拿一遍数据&#xff0c;数据没有变化&#xff0c;影响带宽&#xff0c;影响时间。刷新又要去加载…

百度飞桨(武汉)人工智能产业赋能中心签约,推动AI技术与汉阳“1+6”产业深度融合

9月1日&#xff0c;“文心中国行”首站落地武汉汉阳。活动现场&#xff0c;武汉市汉阳区与百度正式签约&#xff0c;共同打造百度飞桨&#xff08;武汉&#xff09;人工智能产业赋能中心&#xff0c;助力武汉产业高质量跨越式发展。活动围绕“深入解读大模型产业实践&#xff0…