STM32HAL库 -- 8.串口UART通信并开启printf功能

目录

1.简介

2.串口和UART 

2.1串口的简介

2.2UART的简介

2.3UART通信协议

2.3.1波特率

2.3.2空闲位

2.3.3起始位

2.3.4数据位

2.3.5校验位

2.3.6停止位

3.STM32的UART

4.HAL库中常用的操作UART的函数

4.1UART初始化函数 -- HAL_UART_Init

4.2硬件初始化回调函数 -- HAL_UART_MspInit

4.3发送数据函数 -- HAL_UART_Transmit 

4.4接收数据的函数 -- HAL_UART_Receive

4.5使能中断源 -- __HAL_UART_ENABLE_IT

4.6获取UART的状态标志 -- __HAL_UART_GET_FLAG

4.7清除指定的 UART 状态标志位 -- __HAL_UART_CLEAR_FLAG

5.发送接收数据实验 并 打开串口printf功能

5.1硬件连接

5.2代码编写

5.2.1配置UART

5.2.2中断函数

5.2.3重定义fputc -- 支持printf函数

6.整体代码 && 实验现象

6.1uart1.h

6.2uart1.c

6.3main.c

6.4实验现象


1.简介

        这个文章会介绍串口通信,然后编写代码完成单片机通过串口发送到电脑,电脑发送数据到单片机的过程。

        作者使用的开发板是正点原子的精英版,写文章用于记录学习和经验分享。

2.串口和UART 

2.1串口的简介

        串口(Serial Port) 是一种常见的数据通信接口,用于实现设备之间的串行通信(Serial Communication)。所谓“串行”,是指数据是按 一位一位的顺序进行发送或接收的,与并行通信(一次性传输多个比特)相对。

2.2UART的简介

        UART(Universal Asynchronous Receiver/Transmitter),UART 全称是通用异步收发器,是一种硬件模块,用于实现异步串行通信。它是实现串口通信的核心逻辑电路。

        UART也是一种广泛应用于嵌入式系统中的异步串行通信协议,通过定义数据帧格式和波特率实现设备之间的点对点数据传输。它仅需两根信号线(TXD发送、RXD接收),无需共享时钟,具有结构简单、使用方便、兼容性强等优点,常用于调试输出、传感器通信、模块交互等场景。

        设备1的TX(发送端)接入设备2的RX(接收端),设备1的RX(接收端)接入设备2的(发送端),这样就实现了全双工通信。也可以单工通信,就是一发一收,接通一路TX、RX即可。

2.3UART通信协议

        UART的一个完整的数据帧通常包括起始位、数据位、可选的校验位和停止位,通信双方必须保持波特率和数据格式一致才能正常工作。

2.3.1波特率

        波特率(Baud Rate) 是串行通信中的一个关键参数,用于表示每秒传输的信号变化次数,单位是 bps(bits per second) 。在 UART 通信中,波特率决定了数据传输的速度。

        在 UART 通信中,发送端和接收端必须设置相同的波特率,否则会导致数据接收不到或者接收内容与发送的内容不一致的问题。

2.3.2空闲位

        空闲位(Idle State) 是指当串口未发送或接收任何数据时,数据线(通常是 TXD 或 RXD)处于高电平状态。数据帧以一个起始位(Start Bit,低电平)开始,空闲位作为帧与帧之间的间隔,确保接收方能正确识别下一帧数据。

2.3.3起始位

        起始位(Start Bit) 是 UART 数据帧中的第一个信号位,用于通知接收端:一帧数据即将开始传输。电平状态固定为低电平(0);长度为 1 位。

2.3.4数据位

        数据位(Data Bits) 是 UART 数据帧中承载实际信息的部分,表示一次通信中传输的有效数据内容。数据的长度通常为 5 到 8 位,8位用的最广泛,因为正好是1字节的大小。

        发送的顺序是低位先发(LSB First)。例如字符 'A' 的 ASCII 码为:0x41(十六进制)二进制表示为:01000001     UART 发送顺序(LSB 先发):1 0 0 0 0 0 1 0

2.3.5校验位

        校验位(Parity Bit)是 UART 数据帧中一个可选的错误检测位,用于在通信过程中简单判断数据是否出错。校验位的位置在数据位之后、停止位之前。可以选择无校验(None)、偶校验(Even)、奇校验(Odd)。

校验方式要求总“1”个数为...示例(数据位:01000001 → 有两个“1”)
偶校验(Even)偶数个“1”校验位 = 0(使总数仍为偶数)
奇校验(Odd)奇数个“1”校验位 = 1(使总数变为奇数)

        接收端收到后会重新计算并比对校验位,若不一致则判定为传输错误。即使数据吃错,也不会纠正错误,就仅仅简单的校验而已。

2.3.6停止位

        停止位(Stop Bit) 是 UART 数据帧中的最后一个部分,用于标识一次数据传输的结束,同时为下一次通信提供时间间隔。电平状态固定为高电平(1);长度可选为 1 位、1.5 位 或 2 位。

3.STM32的UART

        作者使用的是STM32F103ZET6为微控制器的开发板,该微控制器内置了3个通用同步/异步收发器(USART1USART2USART3),和2个通用异步收发器(UART4UART5)。

        UART 只支持异步串行通信,而USART 是 UART 的“升级版”,既支持异步也支持同步通信,功能更强大。在这里就把USART当作UART用就好。

        附上框图,虽然我没咋看懂。

4.HAL库中常用的操作UART的函数

        打开HAL库的stm32f1xx_hal_uart.c文件。

4.1UART初始化函数 -- HAL_UART_Init

HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);

        这个函数是 STM32 HAL 库中用于 初始化 UART 外设 的核心函数。主要配置使用哪个UART、波特率、数据位、校验位、停止位、CTS/RTS硬件流控位和收发模式。

        函数的参数 UART_HandleTypeDef *huart 是指向一个 UART 句柄结构体的指针,该结构体包含了 UART 的配置信息和运行状态。

        函数的返回值(HAL_StatusTypeDef):

返回值含义
HAL_OK初始化成功
HAL_ERROR初始化失败(如参数错误、硬件故障)
HAL_BUSYUART 正在被使用
HAL_TIMEOUT操作超时

4.2硬件初始化回调函数 -- HAL_UART_MspInit

__weak void HAL_UART_MspInit(UART_HandleTypeDef *huart);

        这个函数是 HAL 库中 UART 的底层硬件初始化函数,必须由用户根据实际硬件平台实现,用于配置 GPIO、时钟、中断等。这是一个公共的函数,通过HAL_UART_Init函数进行调用。同时也是虚函数,意思是要我们自己写函数的内容。

        函数的参数 UART_HandleTypeDef *huart 是指向一个 UART 句柄结构体的指针,该结构体包含了 UART 的配置信息和运行状态。

4.3发送数据函数 -- HAL_UART_Transmit 

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)

        该函数用于 通过 UART 以阻塞方式发送一组数据(一个字节数组),直到全部发送完成或发生错误/超时。使用简单,适合 不使用中断或 DMA 的场景。

参数说明:

参数名类型说明
huartUART_HandleTypeDef*UART 句柄指针,指向已配置好的 UART 实例(如 &huart1
pDatauint8_t*指向要发送数据的缓冲区(字节数组)
Sizeuint16_t要发送的数据长度(字节数)
Timeoutuint32_t等待发送完成的最大时间(单位:ms),设为 HAL_MAX_DELAY 表示无限等待

4.4接收数据的函数 -- HAL_UART_Receive

HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);

        该函数用于 通过 UART 以阻塞方式接收指定数量的字节数据。会一直等待直到:接收到指定数量的数据(Size 字节)或发生错误 / 超时。

参数说明:

huartUART_HandleTypeDef*UART 句柄指针(如 &huart1
pDatauint8_t*指向接收缓冲区的指针(用于存储接收到的数据)
Sizeuint16_t要接收的数据长度(字节数)
Timeoutuint32_t接收等待最大时间(单位:ms),设为 HAL_MAX_DELAY 表示无限等待

4.5使能中断源 -- __HAL_UART_ENABLE_IT

__HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)

        这是一个宏函数。该宏用于直接使能 UART 的某个中断源。

参数说明:

__HANDLE__:就是 UART的句柄指针 UART_HandleTypeDef* 。

__INTERRUPT__:中断源 

宏定义中断名称触发条件是否常用
UART_IT_CTSCTS 变化中断当 CTS 引脚电平变化时触发(硬件流控相关)❌ 较少使用
UART_IT_LBDLIN 断点检测中断检测到 LIN 总线断点标志(用于 LIN 协议)❌ 特定场景
UART_IT_TXE发送缓冲区空中断当发送寄存器为空,可以写入新数据时触发✅ 常用
UART_IT_TC发送完成中断整个数据帧传输完成后触发✅ 常用
UART_IT_RXNE接收缓冲区非空中断当收到一个字节数据时触发✅ 最常用
UART_IT_IDLE空闲总线检测中断当 UART 接收线上检测到空闲时触发(常用于接收不定长数据帧)✅ 高级应用
UART_IT_PE校验错误中断接收到的数据发生奇偶校验错误时触发⚠️ 错误检测
UART_IT_ERR错误中断(综合)包括帧错误、噪声错误、溢出错误等⚠️ 错误处理

4.6获取UART的状态标志 -- __HAL_UART_GET_FLAG

__HAL_UART_GET_FLAG(__HANDLE__, __FLAG__)

        用于读取 UART 状态标志位的宏,常用于轮询或中断中判断事件是否发生,是实现串口通信控制和错误检测的关键工具。

参数说明:

__HANDLE__:就是 UART的句柄指针 UART_HandleTypeDef* 。

__FLAG__:

宏定义对应标志描述
UART_FLAG_CTSCTS 标志位CTS 引脚状态变化(硬件流控)
UART_FLAG_LBDLIN 断点标志LIN 总线断点检测
UART_FLAG_TXE发送缓冲区空标志表示可以写入新数据
UART_FLAG_TC发送完成标志整帧数据发送完成
UART_FLAG_RXNE接收缓冲区非空标志表示已接收到一个字节
UART_FLAG_IDLE空闲总线标志UART 总线空闲检测(常用于不定长接收)
UART_FLAG_ORE溢出错误标志接收缓冲区溢出
UART_FLAG_NE噪声错误标志接收到噪声干扰
UART_FLAG_FE帧错误标志数据格式错误
UART_FLAG_PE校验错误标志奇偶校验失败

4.7清除指定的 UART 状态标志位 -- __HAL_UART_CLEAR_FLAG

__HAL_UART_CLEAR_FLAG(__HANDLE__, __FLAG__)

        该宏用于 清除指定的 UART 状态标志位。在某些中断或轮询操作中,需要手动清除标志以避免重复触发或状态错误。

参数说明:

__HANDLE__:就是 UART的句柄指针 UART_HandleTypeDef* 。

__FLAG__:

宏定义对应标志是否需要手动清除说明
UART_FLAG_CTSCTS 标志✅ 是清除 CTS 中断标志
UART_FLAG_LBDLIN 断点标志✅ 是清除 LIN 检测标志
UART_FLAG_TC发送完成标志✅ 是清除发送完成标志
UART_FLAG_IDLE空闲总线标志✅ 是常用于接收不定长数据帧后需清除
UART_FLAG_RXNE接收缓冲区非空标志❌ 否读取 DR 寄存器自动清除
UART_FLAG_TXE发送缓冲区空标志❌ 否写入 DR 自动清除
UART_FLAG_ORE溢出错误标志✅ 是清除溢出标志
UART_FLAG_NE噪声错误标志✅ 是清除噪声错误标志
UART_FLAG_FE帧错误标志✅ 是清除帧错误标志
UART_FLAG_PE校验错误标志✅ 是清除奇偶校验错误标志

5.发送接收数据实验 并 打开串口printf功能

5.1硬件连接

        一般地,STM32处理器的PA9和PA10就是对应的USART的TX和RX引脚。而且带有type-c USB线直接连接到电脑上进行调试的功能。如果读者使用的是最小系统板,就用USB转TTL再进行连线到核心板就好,功能是一样的。

5.2代码编写

5.2.1配置UART

        使用上面介绍的HAL_UART_Init配置UART1。

UART_HandleTypeDef g_uart1_handle;						//UART1句柄
/*** @brief       串口1初始化函数* @param       baudrate: 波特率, 根据自己需要设置波特率值* @retval      无*/
void uart1_init(unsigned int baudrate)
{g_uart1_handle.Instance = USART1;						//配置的是USART1g_uart1_handle.Init.BaudRate = baudrate;				//波特率g_uart1_handle.Init.WordLength = UART_WORDLENGTH_8B;	//数据位为8位g_uart1_handle.Init.StopBits = UART_STOPBITS_1;			//停止位为1位g_uart1_handle.Init.Parity = UART_PARITY_NONE;			//无校验位g_uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;	//不使用CTS/RTS硬件流控位g_uart1_handle.Init.Mode = UART_MODE_TX_RX;				//UART发送和接收HAL_UART_Init(&g_uart1_handle);							//调用初始化函数进行初始化
}

        再使用HAL_UART_MspInit进行底层硬件初始化,进行打开时钟,配置GPIO和打开中断。

        对于发送数据引脚PA9,要求是推挽复用输出模式,上拉模式,速度中速高速都行。

        对于 接收数据引脚PA10,要求是复用输入模式,上拉模式。

        要使用HAL_NVIC_EnableIRQ函数打开USART1的中断,并设置中断优先级,然后再使用宏函数打开具体的UART中断。这里打开的是空闲中断和接收中断。

*** @brief       UART底层初始化函数* @param       huart: UART句柄类型指针* @note        此函数会被HAL_UART_Init()调用*              完成时钟使能,引脚配置,中断配置* @retval      无*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{if(huart->Instance == USART1){/*	1.打开时钟	*/__HAL_RCC_USART1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/*	2.配置GPIO	*/GPIO_InitTypeDef gpio_handle = {0};gpio_handle.Pin = UART1_TX_PIN;gpio_handle.Mode = GPIO_MODE_AF_PP;gpio_handle.Pull = GPIO_PULLUP;gpio_handle.Speed = GPIO_SPEED_FREQ_MEDIUM;HAL_GPIO_Init(UART1_TX_PORT, &gpio_handle);gpio_handle.Pin = UART1_RX_PIN;gpio_handle.Mode = GPIO_MODE_AF_INPUT;gpio_handle.Pull = GPIO_PULLUP;HAL_GPIO_Init(UART1_RX_PORT, &gpio_handle);/*	3.打开中断	*/HAL_NVIC_SetPriority(USART1_IRQn, 2, 2);HAL_NVIC_EnableIRQ(USART1_IRQn);__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);		//打开接收中断__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);		//打开空闲中断}
}

5.2.2中断函数

        打开了接收中断之后,当接收到数据时,就跳到中断函数进行接收数据。当接收完成后,即进入空闲中断。在空闲中断中可进行其他操作。

        这里的逻辑比较简单,当触发了接收缓冲区非空中断(接收到1字节数据),就进行接收数据的操作。数据一字节一字节的拷贝进g_uart1_rx_buf数组中,用于存储接收的数据。接收中断的标志会自动的清除。

        当触发空闲总线检测中断(数据接收完成),就进行打印操作,并且清空用于存储接收数据的数组。然后要手动的清除空闲中断标志,否则程序会一直在这里。

/*** @brief       串口1中断服务函数* @note        在此使用接收中断,实现不定长数据收发* @param       无* @retval      无*/
void USART1_IRQHandler(void)
{unsigned char recv_data;if(__HAL_UART_GET_FLAG(&g_uart1_handle, UART_FLAG_RXNE) != RESET){if(g_uart1_rx_len >= sizeof(g_uart1_rx_buf))g_uart1_rx_len = 0;HAL_UART_Receive(&g_uart1_handle, &recv_data, 1, 1000);g_uart1_rx_buf[g_uart1_rx_len++] = recv_data;}if(__HAL_UART_GET_FLAG(&g_uart1_handle, UART_FLAG_IDLE) != RESET){printf("recv:%s\r\n", g_uart1_rx_buf);uart1_clear_rx_buf();__HAL_UART_CLEAR_IDLEFLAG(&g_uart1_handle);}
}

5.2.3重定义fputc -- 支持printf函数

        fputc() 是 printf() 的底层输出函数通过重写该函数,可以将 printf() 输出重定向到串口,便于调试和日志输出。

/*** @brief       重定义fputc函数* @note        printf函数最终会通过调用fputc输出字符串到串口*/
int fputc(int ch, FILE *f)
{while ((USART1->SR & 0X40) == 0);                 /* 等待上一个字符发送完成 */USART1->DR = (uint8_t)ch;                         /* 将要发送的字符 ch 写入到DR寄存器 */return ch;
}

        USART1->SR 是 UART 的状态寄存器(Status Register);0x40 对应的是 TXE(Transmit Data Register Empty)标志位。当 TXE=1 表示 DR 寄存器为空,即可以写入新数据。将字符 ch 写入数据寄存器(Data Register),开始通过USART1向外发送。

6.整体代码 && 实验现象

6.1uart1.h

        主要进行了UART1的引脚宏定义和函数声明。

#ifndef __UART1_H__
#define __UART1_H__#include "stdio.h"
#include "sys.h"/**********************宏定义**************************************/
/*		引脚定义	*/
#define		UART1_TX_PORT			GPIOA
#define		UART1_TX_PIN			GPIO_PIN_9#define		UART1_RX_PORT			GPIOA
#define		UART1_RX_PIN			GPIO_PIN_10/* UART收发缓冲大小 */
#define UART1_RX_BUF_SIZE            128
#define UART1_TX_BUF_SIZE            64/* 错误代码 */
#define UART1_EOK                     0      /* 没有错误 */
#define UART1_ERROR                   1      /* 通用错误 */
#define UART1_ETIMEOUT                2      /* 超时错误 */
#define UART1_EINVAL                  3      /* 参数错误 *//**********************函数声明**************************************/
void uart1_init(unsigned int baudrate);            /* 串口初始化函数 */#endif

6.2uart1.c

        操作USART1的函数。

#include "sys.h"
#include "uart1.h"
#include "string.h"/*		全局变量		*/
unsigned char g_uart1_rx_buf[UART1_RX_BUF_SIZE];		//保存接收的数据
unsigned short g_uart1_rx_len = 0;						//接收数据的长度
UART_HandleTypeDef g_uart1_handle;						//UART1句柄/*** @brief       重定义fputc函数* @note        printf函数最终会通过调用fputc输出字符串到串口*/
int fputc(int ch, FILE *f)
{while ((USART1->SR & 0X40) == 0);                                       /* 等待上一个字符发送完成 */USART1->DR = (uint8_t)ch;                                               /* 将要发送的字符 ch 写入到DR寄存器 */return ch;
}/*** @brief       串口1初始化函数* @param       baudrate: 波特率, 根据自己需要设置波特率值* @retval      无*/
void uart1_init(unsigned int baudrate)
{g_uart1_handle.Instance = USART1;						//配置的是USART1g_uart1_handle.Init.BaudRate = baudrate;				//波特率g_uart1_handle.Init.WordLength = UART_WORDLENGTH_8B;	//数据位为8位g_uart1_handle.Init.StopBits = UART_STOPBITS_1;			//停止位为1位g_uart1_handle.Init.Parity = UART_PARITY_NONE;			//无校验位g_uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;	//不使用CTS/RTS硬件流控位g_uart1_handle.Init.Mode = UART_MODE_TX_RX;				//UART发送和接收HAL_UART_Init(&g_uart1_handle);							//调用初始化函数进行初始化
}/*** @brief       UART底层初始化函数* @param       huart: UART句柄类型指针* @note        此函数会被HAL_UART_Init()调用*              完成时钟使能,引脚配置,中断配置* @retval      无*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{if(huart->Instance == USART1){/*	1.打开时钟	*/__HAL_RCC_USART1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/*	2.配置GPIO	*/GPIO_InitTypeDef gpio_handle = {0};gpio_handle.Pin = UART1_TX_PIN;gpio_handle.Mode = GPIO_MODE_AF_PP;gpio_handle.Pull = GPIO_PULLUP;gpio_handle.Speed = GPIO_SPEED_FREQ_MEDIUM;HAL_GPIO_Init(UART1_TX_PORT, &gpio_handle);gpio_handle.Pin = UART1_RX_PIN;gpio_handle.Mode = GPIO_MODE_AF_INPUT;gpio_handle.Pull = GPIO_PULLUP;HAL_GPIO_Init(UART1_RX_PORT, &gpio_handle);/*	3.打开中断	*/HAL_NVIC_SetPriority(USART1_IRQn, 2, 2);HAL_NVIC_EnableIRQ(USART1_IRQn);__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);		//打开接收中断__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);		//打开空闲中断}
}/*** @brief       UART1接收缓冲区清除* @param       无* @retval      无*/
void uart1_clear_rx_buf(void)
{memset(g_uart1_rx_buf, 0, sizeof(g_uart1_rx_buf));g_uart1_rx_len = 0;
}/*** @brief       串口1中断服务函数* @note        在此使用接收中断,实现不定长数据收发* @param       无* @retval      无*/
void USART1_IRQHandler(void)
{unsigned char recv_data;if(__HAL_UART_GET_FLAG(&g_uart1_handle, UART_FLAG_RXNE) != RESET){if(g_uart1_rx_len >= sizeof(g_uart1_rx_buf))g_uart1_rx_len = 0;HAL_UART_Receive(&g_uart1_handle, &recv_data, 1, 1000);g_uart1_rx_buf[g_uart1_rx_len++] = recv_data;}if(__HAL_UART_GET_FLAG(&g_uart1_handle, UART_FLAG_IDLE) != RESET){printf("recv:%s\r\n", g_uart1_rx_buf);uart1_clear_rx_buf();__HAL_UART_CLEAR_IDLEFLAG(&g_uart1_handle);}
}

6.3main.c

        在主函数中调用uart1_init函数,传递的参数115200是波特率。

#include "main.h"int main(void)
{HAL_Init();                         /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9); 	/* 设置时钟, 72Mhz */uart1_init(115200);while(1){delay_ms(10);}
}

6.4实验现象

        首先在KEIL5中魔术棒中勾选 Use MicroLIB选项。

        写好程序之后下载程序到开发板,然后打开电脑的串口调试助手,并且按照上面配置的波特率、停止位、数据位、校验位打开串口。

        之后发送数据,对应的接收区会有 recv:[数据]返回。就正常了。

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

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

相关文章

【PyTorch项目实战】CycleGAN:无需成对训练样本,支持跨领域图像风格迁移

文章目录 一、风格迁移模型🎨1、发展时间线2、分类与优缺点3、选择建议4、HuggingFace Demo(instruct-pix2pix) —— 在线测试 二、论文简读(1)FastStyleTransfer(快速风格迁移,Johnson et al.,…

C#Halcon从零开发_Day14_AOI缺陷检测策略1_Bolb分析+特征分析_饼干破损检测

一、引言 *缺陷检测策略1:Blob分析特征分析 *Blob分析:阈值分割、开闭运算 (1) 图像预处理 目的:增强目标与背景的对比度,抑制噪声。 常用算子: threshold():通过阈值分割将图像转换为二值图像。 mean_…

18年磨一剑!开利科技启动数字化增量投资新时代

当传统营销陷入流量困局,当数字化转型变成"面子工程",广州开利网络用18年技术积淀给出新答案——"蚓链数字化生态营销系统",让企业不再为数字化而数字化,而是通过数字化实现利润增长! 核心亮点&am…

65-Oracle Undo机制

前一期看了Oracle在redo上保持事务前滚的一致性,同样Oracle在Undo的管理机制也是现代事务型数据库的工程典范。核心在于通过多版本并发控制(MVCC)技术,在保障数据一致性与提升系统性能之间实现精妙平衡。Undo机制构建了原子事务的…

【ESP32摄像头开发实例】-实现遥控视频小车

ESP32-CAM实现遥控视频小车 文章目录 ESP32-CAM实现遥控视频小车1、硬件准备2、代码实现3、代码解析4、程序下载到开发板在本文中,将详细介绍如何使用ESP32-CAM制作一辆Wi-Fi远程控制的小车。 1、硬件准备 在开始这个项目之前,我们将强调用于构建机器人的最重要的特性和组件…

图片加载优化(缩略图)

缩略图 系统目前的问题:首页直接加载原图,原图文件通常比缩略图大数倍甚至数十倍,不仅导致加载时间长,还会造成大量流量浪费。 解决方案:上传图片时,同时生成一份较小尺寸的缩略图。用户浏览图片列表时加…

GO语言---数组

文章目录 数组的基本特性数组的声明和初始化数组元素的访问和操作数组遍历多维数组数组的长度和容量数组作为函数参数数组与切片的区别注意 数组是Go语言中最基本的数据结构之一,它是一组相同类型元素的固定长度序列。 数组的基本特性 1、固定长度:数组…

7.5.3_1处理冲突的方法-拉链法

知识总览: 拉链法: 开始散列表中没有存储任何数据元素即散列地址上的元素是空的,散列地址可以视为链表的头指针,即没有插入任何元素前链表的头指针是空的。一个散列地址对应一个链表,散列地址上实际没有存数据元素&am…

鸿蒙运动项目开发:项目运行环境切换器

##鸿蒙核心技术##运动开发# 在开发鸿蒙运动项目时,管理不同运行环境(如开发环境、测试环境、生产环境)是一个常见的需求。通过合理地切换运行环境,开发者可以方便地进行调试、测试和部署。本文将介绍如何实现一个项目运行环境切换…

Linux内核中安全创建套接字:为何inet_create未导出及正确替代方案

引言 在Linux内核开发中,当驱动程序需要创建网络套接字时,开发者常会遇到一个关键问题:核心函数inet_create(负责初始化IPv4套接字)并未导出到内核符号表。本文深入剖析这一设计决策背后的逻辑,并提供驱动程序安全创建套接字的实践方案。 一、inet_create未导出的深层原…

63、不同路径II

题目 解答: 初始化和特殊情况比较麻烦的dp obstacleGrid(0,0)1的,直接return 0即可。入口都被堵住了还怎么走。 mn1情况,直接判断 第一行初始化:dp[1][0]->dp[i][0] 碰到有障碍物的,从当前格子开始到末尾全部置…

wx小程序登录设置角色

背景。pc端登录后在访问业务链接时可以根据固定获取用户的方法LoginUser user LoginHelper.getLoginUser(); 获取到用户信息。但wx端登录后无法获取。原因处在登陆时对用户信息的设置方面pc端和小程序端登录没有使用相同的登录方法。排除得知wx端小程序登录时没有设置角色。所…

MySQL5.7 慢查询SQL语句集合

文章目录 1. 按平均执行时间排序的慢查询2. 按总执行时长排序的慢查询3. MySQL 5.7 慢查询配置检查4. 扫描行数分析(找出全表扫描)5. 高频执行的慢查询6. 当前正在执行的查询7. 慢查询统计汇总8. 表结构和索引分析8.1 表索引详情查询8.2 表大小统计 1. 按…

MySQL学习(1)——基础库操作

欢迎来到博主的专栏:MySQL学习 博主ID:代码小豪 文章目录 数据库原理基础库操作增删数据库数据库编码与校验规则验证不同的校验规则对于库中数据的影响 备份与恢复数据库 数据库原理 mysql版本:mysql8.0 操作系统:ubuntu22.4 为了减少由于环境配置以及权限限制带来的使用问题&…

C++法则12:右值引用的核心目的:支持移动语义(Move Semantics)

C法则12:右值引用的核心目的:支持移动语义(Move Semantics) 右值引用(Rvalue Reference)是C11引入的最重要特性之一,其主要设计目的就是支持移动语义(Move Semantics)。 …

【LLM学习笔记4】使用LangChain开发应用程序(上)

目录 前言一、模型、提示和解析器(model、prompt、parsers)二、储存三、模型链四、基于文档的问答1.使用向量存储查询2. 结合表征模型和向量存储使用检索问答链回答问题 前言 在前面两部分,我们分别学习了大语言模型的基础使用准则&#xff…

Negative Contrastive Estimation Negative Sampling

1. 基本概念与问题背景 1.1 大规模分类问题 在自然语言处理中,给定上下文 c c c预测单词 w w w的条件概率为: P ( w ∣ c ) exp ⁡ ( s θ ( w , c ) ) ∑ w ′ ∈ V exp ⁡ ( s θ ( w ′ , c ) ) P(w|c) \frac{\exp(s_\theta(w,c))}{\sum_{w\in V…

Flink SQL Connector Kafka 核心参数全解析与实战指南

Flink SQL Connector Kafka 是连接Flink SQL与Kafka的核心组件,通过将Kafka主题抽象为表结构,允许用户使用标准SQL语句完成数据读写操作。本文基于Apache Flink官方文档(2.0版本),系统梳理从表定义、参数配置到实战调优…

vscode内嵌浏览器实时预览vue项目

安装插件 web Preview 启动vue项目 打开预览 ctrl shift p 之后输入并选择 Open Web Preview 即可看到预览窗口,但此时明明我的页面是有内容的,但是窗口却空白的。 因为默认访问端口是3000,我们将其修改为vue项目默认的5173端口即可。 点…

计算机网络:(四)物理层的基本概念,数据通信的基础知识,物理层下面的传输媒体

计算机网络:(四)物理层的基本概念,数据通信的基础知识,物理层下面的传输媒体 前言一、物理层的基本概念1. 什么是物理层2. 物理层的核心使命3. 物理层的四大特性 二、数据通信的基础知识1. 数据通信系统的基本模型1.1 …