从0开始的STM32之旅8 串口通信(II)

目录

在开始理解底层原理之前,我们先尝试一下

怎么做

进一步理解

HAL_UART_Transmit

HAL_UART_Receive


在开始理解底层原理之前,我们先尝试一下

现在我们综合一下,要求完成如下的事情:

在主程序中存在一个flag变量描述当前有没有嗯下按钮,当摁下时,打印“finishing flip!”字样,没有的话要求每间隔一秒输出输出Loop is working...,当然,摁扭嗯下的时候,要求打印日志You pressed a key, with led toggled once!,以及反转小灯状态一次!

你的程序效果在串口软件如下:

怎么做

首先打开STM32CubeMX,配置好时钟之后,我们按照从0开始的STM32库——中断部分所谈到的那样,首先配置好按钮引脚中断的优先级(低于Systick的Time Base优先级),配置好LED的GPIO。下一步,就是开启我们的Connections->USART1(看官按照自己的做就行),选择好异步即可

在生成的主程序中,写下这些逻辑:

static const char* buffer = "You pressed a key, with led toggled once!\n"; // 定义按键触发时发送的消息
char flip = 0; // 用于标记LED状态反转
​
// 外部中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t Pin){HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin); // 切换LED状态HAL_UART_Transmit(&huart1, (uint8_t*)buffer, strlen(buffer), -1); // 通过UART发送按键触发消息flip = 1; // 设置flip标志,表示完成反转
}
​
/*** @brief  应用程序入口点。* @retval int*/
int main(void)
{/* MCU配置部分--------------------------------------------------------*/
​/* 重置所有外设,初始化Flash接口和Systick定时器。 */HAL_Init();/* 配置系统时钟 */SystemClock_Config();/* 初始化所有已配置的外设 */MX_GPIO_Init();MX_USART1_UART_Init();
​char* plain_buffer = "Loop is working...\n"; // 循环中定期发送的消息char* plain_buffer2 = "finishing flip!\n"; // 完成LED反转后发送的消息
​while (1){// 发送周期性消息HAL_UART_Transmit(&huart1, (uint8_t*)plain_buffer, strlen(plain_buffer), -1); HAL_Delay(1000); // 延时1秒
​if(flip){flip = 0; // 重置flip标志// 发送LED反转完成的消息HAL_UART_Transmit(&huart1, (uint8_t*)plain_buffer2, strlen(plain_buffer2), -1); }}
}

程序很简单!

进一步理解

在HAL库,我们的串口通信是依靠一个叫做UART_HandleTypeDef的结构体进行的,这个方法内部配置了我们的通信约定,可以查看HAL为我们生成的代码:

static void MX_USART1_UART_Init(void)
{
​/* USER CODE BEGIN USART1_Init 0 */
​/* USER CODE END USART1_Init 0 */
​/* USER CODE BEGIN USART1_Init 1 */
​/* USER CODE END USART1_Init 1 */huart1.Instance = USART1;huart1.Init.BaudRate = 115200;huart1.Init.WordLength = UART_WORDLENGTH_8B;huart1.Init.StopBits = UART_STOPBITS_1;huart1.Init.Parity = UART_PARITY_NONE;huart1.Init.Mode = UART_MODE_TX_RX;huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart1.Init.OverSampling = UART_OVERSAMPLING_16;if (HAL_UART_Init(&huart1) != HAL_OK){Error_Handler();}/* USER CODE BEGIN USART1_Init 2 */
​/* USER CODE END USART1_Init 2 */
}

我们可以看到生成的办法是:

首先初始化实际的USART实例,实际上我们使用的是USART1,那根据HAL库,就是我们的USART1这个定义的句柄。

下一步就是设置波特率。值得一提的是:这个设置不一定要遵循115200,实际上只要是双方约定的速率即可

下一个就是数据的字长,比如说我们这里就设置为8bit

...

设置好了之后。我们采用初始化的办法进行初始化

配置项可能的值说明
huart1.InstanceUSART1, USART2, USART3, UART4, UART5, ...UART 外设实例选择
huart1.Init.BaudRate9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600, 1000000波特率,决定每秒传输的数据位数
huart1.Init.WordLengthUART_WORDLENGTH_8B, UART_WORDLENGTH_9B数据字长,8位或9位数据
huart1.Init.StopBitsUART_STOPBITS_1, UART_STOPBITS_2, UART_STOPBITS_1_5停止位,决定每个数据包的结束标志位
huart1.Init.ParityUART_PARITY_NONE, UART_PARITY_EVEN, UART_PARITY_ODD校验位,决定错误检测方式
huart1.Init.ModeUART_MODE_TX_RX, UART_MODE_TX, UART_MODE_RX数据传输模式,支持发送、接收或两者都支持
huart1.Init.HwFlowCtlUART_HWCONTROL_NONE, UART_HWCONTROL_RTS, UART_HWCONTROL_CTS, UART_HWCONTROL_RTS_CTS硬件流控制,决定是否启用流控制
huart1.Init.OverSamplingUART_OVERSAMPLING_8, UART_OVERSAMPLING_16过采样,决定波特率的采样精度

现在,我们调用HAL_UART_Init传递实际的参数来完成最后的初始化,HAL_UART_Init 函数负责初始化 STM32 的 UART 外设,配置通信参数,并确保外设能够正常工作。它包括硬件流控制、波特率、数据位、停止位等多个重要参数的配置,同时还考虑了回调函数的注册和低级硬件的初始化。

/*** @brief  Initializes the UART mode according to the specified parameters in*         the UART_InitTypeDef and creates the associated handle.* @param  huart  Pointer to a UART_HandleTypeDef structure that contains*                the configuration information for the specified UART module.* @retval HAL status*/
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)
{/* Check the UART handle allocation */if (huart == NULL){return HAL_ERROR;  // 如果传入的 UART 句柄为空,返回错误状态}
​/* Check the parameters */if (huart->Init.HwFlowCtl != UART_HWCONTROL_NONE){// 硬件流控制只适用于 USART1, USART2 和 USART3,因此需要检查是否是这些实例之一assert_param(IS_UART_HWFLOW_INSTANCE(huart->Instance));  // 通过宏检查 UART 是否为支持硬件流控制的实例assert_param(IS_UART_HARDWARE_FLOW_CONTROL(huart->Init.HwFlowCtl));  // 检查硬件流控制的参数是否合法}else{assert_param(IS_UART_INSTANCE(huart->Instance));  // 如果没有硬件流控制,则检查是否为有效的 UART 实例}
​// 校验 UART 字长是否合法assert_param(IS_UART_WORD_LENGTH(huart->Init.WordLength));  
​
#if defined(USART_CR1_OVER8)// 如果 MCU 支持 8 倍过采样,则需要验证过采样设置是否合法assert_param(IS_UART_OVERSAMPLING(huart->Init.OverSampling));  
#endif /* USART_CR1_OVER8 */
​// 检查 UART 的当前状态是否为重置状态if (huart->gState == HAL_UART_STATE_RESET){/* Allocate lock resource and initialize it */huart->Lock = HAL_UNLOCKED;  // 解锁 UART 资源,准备初始化
​#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)// 如果启用了回调函数注册机制,则调用默认回调函数初始化UART_InitCallbacksToDefault(huart);
​// 检查是否设置了用户的初始化回调函数,如果没有则使用默认回调if (huart->MspInitCallback == NULL){huart->MspInitCallback = HAL_UART_MspInit;  // 使用默认的硬件初始化回调}
​/* Init the low level hardware */// 调用用户的回调函数进行硬件初始化huart->MspInitCallback(huart); #else/* Init the low level hardware : GPIO, CLOCK */// 如果没有启用回调函数,则调用默认的 HAL_UART_MspInit 进行硬件初始化HAL_UART_MspInit(huart);  #endif /* (USE_HAL_UART_REGISTER_CALLBACKS) */}
​// 更新 UART 状态为忙碌状态,表示正在初始化huart->gState = HAL_UART_STATE_BUSY;
​/* Disable the peripheral */// 禁用 UART 外设,防止在初始化过程中出现干扰__HAL_UART_DISABLE(huart);  
​/* Set the UART Communication parameters */// 根据配置结构体中的参数,设置 UART 的通信参数UART_SetConfig(huart);  // 设置波特率、数据位、停止位、校验等配置
​/* In asynchronous mode, the following bits must be kept cleared:- LINEN and CLKEN bits in the USART_CR2 register,- SCEN, HDSEL and IREN bits in the USART_CR3 register. */// 在异步模式下,清除 USART_CR2 和 USART_CR3 寄存器中某些特定位,以确保 UART 正常工作CLEAR_BIT(huart->Instance->CR2, (USART_CR2_LINEN | USART_CR2_CLKEN));  // 禁用 LIN 和时钟CLEAR_BIT(huart->Instance->CR3, (USART_CR3_SCEN | USART_CR3_HDSEL | USART_CR3_IREN));  // 禁用智能卡、半双工和红外功能
​/* Enable the peripheral */// 启用 UART 外设,使能 UART 的发送和接收功能__HAL_UART_ENABLE(huart);  
​/* Initialize the UART state */// 初始化 UART 状态,表示 UART 初始化完成huart->ErrorCode = HAL_UART_ERROR_NONE;  // 错误代码清零huart->gState = HAL_UART_STATE_READY;  // 更新为就绪状态huart->RxState = HAL_UART_STATE_READY;  // 接收状态更新为就绪
​return HAL_OK;  // 返回成功状态,表示初始化完成
}

HAL_UART_Init首先进行的就是参数检查,他检查的是:

  1. huart 检查: 首先,检查传入的 huart 指针是否为空。若为空,则返回 HAL_ERROR,表示初始化失败。

  2. 硬件流控制检查: 如果选择了硬件流控制(HwFlowCtl != UART_HWCONTROL_NONE),则需要验证 UART 是否为支持硬件流控制的外设(如 USART1、USART2 和 USART3)。通过 assert_param(IS_UART_HWFLOW_INSTANCE(huart->Instance)) 宏检查实例是否合法。同时,还要检查硬件流控制参数是否合法(IS_UART_HARDWARE_FLOW_CONTROL)。

  3. 其他配置项检查: 校验数据字长(WordLength)、过采样(OverSampling)和 UART 实例是否有效。通过多个宏进行这些检查,确保配置项在硬件上是支持的。

下一步:状态初始化!,我们需要对UART做状态机的初始化:

  • gState 检查: 如果 huart->gState 的状态是 HAL_UART_STATE_RESET,说明 UART 外设尚未初始化,需要进行初始化过程。否则,继续执行后续步骤。

  • 锁定机制: 初始化时,设置 huart->Lock = HAL_UNLOCKED,表示 UART 外设当前没有被锁定,可以进行配置。

  • 回调函数注册(可选): 如果启用了回调函数注册(USE_HAL_UART_REGISTER_CALLBACKS == 1),会将回调函数设置为默认回调,并检查是否用户已经注册了初始化回调函数。如果没有,则使用默认的 HAL_UART_MspInit 作为初始化回调函数。

    如果没有启用回调,则直接调用 HAL_UART_MspInit 进行低级硬件初始化(如 GPIO 配置和时钟设置)。

现在我们来配置UART:

  • 禁用 UART 外设: 在进行配置之前,首先禁用 UART 外设,防止在配置过程中发生数据传输或接收。

  • 配置通信参数: 调用 UART_SetConfig 函数,设置 UART 的波特率、数据位长度、停止位数、校验方式等。这个函数会根据 huart->Init 中的配置项来设置相关寄存器。

  • 清除无关功能: 在异步模式下,清除一些不必要的功能(如 LIN、时钟、半双工、红外等),确保 UART 以标准模式运行。

关键的一步在于:启用 UART 外设后就要更新状态到可用,然后结束函数调用

  • 启用 UART: 配置完成后,通过 __HAL_UART_ENABLE(huart) 启用 UART 外设,开始实际的通信工作。

  • 更新状态: 最后,重置 ErrorCodeHAL_UART_ERROR_NONE,表示没有发生错误,并将 gStateRxState 更新为 HAL_UART_STATE_READY,表示 UART 已准备好进行数据收发。

  • 成功返回: 如果所有步骤都成功完成,返回 HAL_OK,表示初始化成功。

HAL_UART_Transmit

我们刚刚使用到的是这个函数:HAL_UART_Transmit 函数,用于通过 UART 外设发送数据,采用阻塞模式(即函数会一直等待直到数据发送完成或超时)。它接受一个数据缓冲区指针、数据大小和超时设置作为输入参数。传输过程中,函数会不断检查 TXE(发送数据寄存器空标志)和 TC(传输完成标志)来控制数据的发送。

/*** @brief  Sends an amount of data in blocking mode.* @note   When UART parity is not enabled (PCE = 0), and Word Length is configured to 9 bits (M1-M0 = 01),*         the sent data is handled as a set of u16. In this case, Size must indicate the number*         of u16 provided through pData.* @param  huart Pointer to a UART_HandleTypeDef structure that contains*               the configuration information for the specified UART module.* @param  pData Pointer to data buffer (u8 or u16 data elements).* @param  Size  Amount of data elements (u8 or u16) to be sent* @param  Timeout Timeout duration* @retval HAL status*/
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{uint8_t  *pdata8bits;  // 指向 8 位数据的指针uint16_t *pdata16bits; // 指向 16 位数据的指针uint32_t tickstart = 0U; // 用于超时管理的时间戳
​/* 检查是否有传输过程正在进行 */if (huart->gState == HAL_UART_STATE_READY){// 如果传入的数据为空或者数据大小为0,则返回错误if ((pData == NULL) || (Size == 0U)){return  HAL_ERROR;}
​/* 处理锁定 */__HAL_LOCK(huart);  // 锁定 UART 句柄,防止多线程环境下的竞争
​huart->ErrorCode = HAL_UART_ERROR_NONE;  // 初始化错误代码huart->gState = HAL_UART_STATE_BUSY_TX;  // 将 UART 状态设置为忙碌,表示正在进行传输
​/* 初始化 tickstart,用于超时管理 */tickstart = HAL_GetTick();  // 获取当前的时间戳,用于计算超时
​huart->TxXferSize = Size;  // 设置要发送的数据总量huart->TxXferCount = Size; // 设置剩余要发送的数据量
​/* 如果是9位数据并且没有启用校验,则需要将数据处理为 uint16_t 指针 */if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE)){pdata8bits  = NULL;  // 对于 9 位数据,8 位数据指针为空pdata16bits = (uint16_t *) pData;  // 数据指针被转换为 16 位指针}else{pdata8bits  = pData;  // 否则,8 位数据指针指向传入的数据pdata16bits = NULL;   // 16 位数据指针为空}
​/* 解锁处理 */__HAL_UNLOCK(huart);  // 解锁 UART 句柄,允许其他操作
​/* 开始数据传输 */while (huart->TxXferCount > 0U){// 等待 TXE 标志(发送数据寄存器为空),直到超时或成功if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK){return HAL_TIMEOUT;  // 如果超时,则返回超时错误}
​// 如果是 9 位数据,则将数据写入数据寄存器时需要将其作为 16 位处理if (pdata8bits == NULL){huart->Instance->DR = (uint16_t)(*pdata16bits & 0x01FFU);  // 将 16 位数据的低 9 位写入数据寄存器pdata16bits++;  // 移动到下一个数据}else{huart->Instance->DR = (uint8_t)(*pdata8bits & 0xFFU);  // 将 8 位数据写入数据寄存器pdata8bits++;  // 移动到下一个字节}
​huart->TxXferCount--;  // 递减待发送数据计数}
​// 等待直到 TC(传输完成)标志被置位,表示所有数据已成功发送if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK){return HAL_TIMEOUT;  // 如果超时,则返回超时错误}
​/* 传输结束,恢复 UART 状态为就绪 */huart->gState = HAL_UART_STATE_READY;
​return HAL_OK;  // 返回成功状态,表示数据已成功发送}else{return HAL_BUSY;  // 如果 UART 正在忙碌,则返回忙碌状态}
}

也就是做下面这个顺序的检查:

  1. 状态检查: 函数首先检查 UART 是否处于就绪状态(HAL_UART_STATE_READY)。如果 UART 正忙,则返回 HAL_BUSY,否则开始初始化传输。

  2. 参数检查: 如果数据缓冲区为空或者传输大小为零,则返回错误。

  3. 锁定 UART 资源: 使用 __HAL_LOCK(huart) 锁定 UART 资源,以避免在多任务环境下同时访问同一个 UART 外设。

  4. 设置初始状态: 设置错误码为 HAL_UART_ERROR_NONE,并将 UART 的状态设置为 HAL_UART_STATE_BUSY_TX,表示当前正在进行数据传输。

  5. 数据指针设置: 如果配置了 9 位数据传输并且未启用校验,则数据以 uint16_t 形式进行处理(每个数据元素是 16 位),否则按 uint8_t 数据处理。

  6. 开始传输: 在数据传输过程中,函数进入一个循环,依次检查 TXE 标志是否已置位,若已置位则将数据写入 DR 寄存器。数据可以是 8 位或 16 位,具体取决于配置。

  7. 超时管理: 如果在传输过程中没有及时收到 TXETC 标志,且超时,函数会返回 HAL_TIMEOUT

  8. 完成传输: 在所有数据发送完成后,检查 TC 标志,确认数据传输完全结束,并恢复 UART 状态为就绪(HAL_UART_STATE_READY)。

  9. 返回状态: 如果一切顺利,函数返回 HAL_OK,否则返回超时或错误状态。

HAL_UART_Receive

笔者在这里没有设计实验,这是因为这个函数笔者没咋用过,后面我们会进一步介绍更加高级的(基于中断和DMA的通信!):HAL_UART_Receive 函数是阻塞的接受!用于通过 UART 接收指定数量的数据,采用阻塞模式(即直到数据接收完成或超时)。函数等待接收缓冲区的数据,并将其存储到提供的缓冲区中。函数会根据配置的字长和是否启用了校验来决定如何处理接收到的数据。

/*** @brief  以阻塞模式接收一定数量的数据。* @note   当 UART 未启用校验(PCE = 0),且字长配置为 9 位(M1-M0 = 01)时,*         接收到的数据将作为一组 u16 来处理。在这种情况下,Size 参数应表示*         pData 中的 u16 数据数量。* @param  huart 指向 UART_HandleTypeDef 结构体的指针,该结构体包含*               配置的 UART 模块的信息。* @param  pData 指向数据缓冲区的指针(可以是 u8 或 u16 数据元素)。* @param  Size  要接收的数据元素数量(u8 或 u16)。* @param  Timeout 超时时间,单位为毫秒。* @retval HAL 状态*/
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{uint8_t  *pdata8bits;  // 用于处理 8 位数据的指针uint16_t *pdata16bits; // 用于处理 16 位数据的指针uint32_t tickstart = 0U; // 用于超时管理的时间戳
​/* 检查是否已有接收过程正在进行 */if (huart->RxState == HAL_UART_STATE_READY){// 检查参数是否有效:pData 为空或 Size 为 0if ((pData == NULL) || (Size == 0U)){return HAL_ERROR;}
​/* 锁定 UART,防止并发访问 */__HAL_LOCK(huart);
​huart->ErrorCode = HAL_UART_ERROR_NONE;  // 重置错误码huart->RxState = HAL_UART_STATE_BUSY_RX;  // 将 UART 状态设置为正在接收数据huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;  // 设置为标准接收类型
​/* 开始超时管理,记录当前时间戳 */tickstart = HAL_GetTick();
​huart->RxXferSize = Size;  // 设置要接收的总数据量huart->RxXferCount = Size; // 设置剩余要接收的数据量
​/* 如果启用了 9 位数据传输且没有校验,处理为 16 位数据 */if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE)){pdata8bits  = NULL;  // 不使用 8 位指针pdata16bits = (uint16_t *) pData;  // 使用 16 位指针处理 9 位数据}else{pdata8bits  = pData;  // 使用 8 位指针处理 8 位数据pdata16bits = NULL;   // 不使用 16 位指针}
​/* 解锁 UART,允许其他操作 */__HAL_UNLOCK(huart);
​/* 循环等待接收数据,直到所有数据接收完成 */while (huart->RxXferCount > 0U){// 等待 RXNE 标志位被置位(数据可用)if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, tickstart, Timeout) != HAL_OK){return HAL_TIMEOUT;  // 超时,返回超时错误}
​// 如果是 9 位数据,处理为 16 位数据if (pdata8bits == NULL){*pdata16bits = (uint16_t)(huart->Instance->DR & 0x01FF);  // 从数据寄存器读取 9 位数据pdata16bits++;  // 移动到下一个数据位置}else{// 根据字长和是否启用校验来选择数据的处理方式if ((huart->Init.WordLength == UART_WORDLENGTH_9B) || ((huart->Init.WordLength == UART_WORDLENGTH_8B) && (huart->Init.Parity == UART_PARITY_NONE))){*pdata8bits = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);  // 从数据寄存器读取 8 位数据}else{*pdata8bits = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);  // 从数据寄存器读取 7 位数据(排除校验位)}pdata8bits++;  // 移动到下一个字节}
​huart->RxXferCount--;  // 减少剩余接收的数据量}
​/* 接收完成后,将 UART 状态恢复为 READY */huart->RxState = HAL_UART_STATE_READY;
​return HAL_OK;  // 返回成功}else{return HAL_BUSY;  // 如果 UART 正在忙碌,则返回 HAL_BUSY}
}
​
  1. 状态检查: 函数首先检查 UART 的接收状态(huart->RxState),确保没有正在进行的接收过程。如果 UART 正在接收数据,则返回 HAL_BUSY,否则继续执行。

  2. 参数检查: 如果数据缓冲区为空或者传输大小为零,函数返回 HAL_ERROR

  3. 锁定 UART 资源: 使用 __HAL_LOCK(huart) 锁定 UART,防止其他任务同时访问该资源。

  4. 初始化接收状态: 清除错误代码,并将 UART 的接收状态设置为 HAL_UART_STATE_BUSY_RX,表示正在进行数据接收。同时初始化超时管理的时间戳 tickstart

  5. 数据指针设置: 如果配置为 9 位数据传输且没有启用校验,数据指针会被处理为 uint16_t 类型,否则按 uint8_t 类型处理数据。

  6. 开始接收数据: 在接收过程中,函数进入一个循环,检查 RXNE 标志是否被置位(表示数据已准备好),然后读取数据并存储到缓冲区。如果是 9 位数据,则从数据寄存器读取 9 位数据;否则,根据数据位长(8 位或 7 位)读取相应的数据。

  7. 超时管理: 如果接收过程中的 RXNE 标志未在指定超时时间内被置位,函数会返回 HAL_TIMEOUT

  8. 完成接收: 在接收到所有数据后,UART 状态被恢复为 HAL_UART_STATE_READY,表示接收过程已完成。

  9. 返回状态: 如果接收过程成功完成,函数返回 HAL_OK,否则返回超时或错误状态。

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

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

相关文章

springboot的增删改查商城小实践(b to c)

首先准备一张表,根据业务去设计表 订单编号是参与业务的,他那订单编号里面是有特殊意义的,比如说像什么一些年月日什么的,一些用户的ID都在那编号里面呢?不能拿这种东西当主件啊 根据数据量去决定数据类型 价格需要注意…

AndroidStudio-视图基础

一、设置视图的宽高 1.在XML文件中设置视图宽高 视图宽度通过属性android:layout_width表达,视图高度通过属性android:layout_height表达,宽高的取值主要有下列三种: (1)wrap_content:表示与内容自适应。对于文本视图来说&…

电子科大、同济大学与新加坡国立大学联合发布Math-LLaVA:增强多模态大语言模型的数学推理能力

一、结论写在前面 下面介绍的论文来自:电子科技大学、新加坡科技设计大学、同济大学、新加坡国立大学。 论文标题:Math-LLaVA: Bootstrapping Mathematical Reasoning for Multimodal Large Language Models 论文链接:https://arxiv.org/p…

高校体育场管理系统+ssm

摘 要 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,高校体育场管理系统被用户普遍使用,为方便用户…

杂谈:业务说的场景金融是什么?

引言:市场格局的转变 在供应短缺的年代,是典型的卖方市场。为了保证稳定供货,买方会提前一段时间下单,也几乎没什么议价能力。卖方只需等着接单就行。 现在很多领域的供应商数量越来越多,而且随着互联网的普及&#…

知从科技受邀出席ARM日产技术日

10月29日,上海知从科技有限公司受 ARM 之邀,参与了由其主办的日产技术日活动。此次活动在日本神奈川县厚木市的日产技术中心盛大举行,这一活动汇聚了行业内的前沿技术与精英人才,成为科技创新技术交流的重要平台。 知从科技积极参…

驱动前的准备

驱动前的准备 目录 驱动前的准备 移植SDK 补充:怎么使用我的虚拟机/怎么把自己的虚拟机拷贝到其他磁盘 移植SDK -- 将压缩包复制到虚拟机里面 -- 删除备份文件 -- 解压这个压缩包(会占用大量的空间->有空间再去做!) 补充:怎么使用我的…

【网页设计】CSS 定位

目标 能够说出为什么要用定位能够说出定位的4种分类能够说出4种定位各自的特点能够说出为什么常用子绝父相布局能够写出淘宝轮播图布局能够说出显示隐藏的2种方式以及区别 1. 定位 1.1 为什么需要定位 提问: 以下情况使用标准流或者浮动能实现吗?1. …

Spring Boot框架:计算机课程管理的工程认证之桥

3系统分析 3.1可行性分析 通过对本基于工程教育认证的计算机课程管理平台实行的目的初步调查和分析,提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本基于工程教育认证的计算机课程管理平…

yolov10断点续训

1. 前言 我们在使用yolov10进行训练的时候往往会因为各种各样的原因中断训练,如: 开了太多程序导致崩溃突然断电其他原因 这时候如果下次能继续上次训练的结果继续训练会节省很多时间 2.复现与解决办法 2.1 正常启动训练 我们先正常启动一个训练 …

【笔记】LLC电路工作频点选择 2-1 输出稳定性的限制

LLC工作模式的分析参考了:现代电力电子学,电力出版社,李永东 1.LLC电路可以选择VCS也可以选择ZVS 1.1选择ZCS时,开关管与谐振电感串联后,与谐振电容并联: 1.2选择ZVS时,开关管仅仅安装在谐振电…

手把手教你写Unity3D飞机大战(4)人机飞机的移动

写在最前面的话 上一篇博客,我们控制了玩家的移动,但这还不够,我们需要让敌方也动起来。 一、大致概要 人机的移动,我们采用随机数来控制,分别包括(前进,转弯,爬升,俯冲&…

Spring Boot助力计算机课程管理:符合工程认证

摘要 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了基于工程教育认证的计算机课程管理平台的开发全过程。通过分析基于工程教育认证的计算机课程管理平台管理的不足,创建了一个计算机管理基于工程教育认…

Vue2基础

1.环境准备 安装脚手架:全局安装 作用:在任何一个目录下都能通过vue命令创建项目 注意:低版本的node执行这行命令可能会报错,需要升级版本 npm install -g vue/cli 创建项目 在指定的目录下,执行命令 作用&…

RAG如何提升视觉问答?剑桥大学博士论文《使用检索方法增强多模态问答系统》

开发能够处理复杂任务的人工智能系统的需求推动了深度学习的快速发展,尤其是自 2016 年以来,神经网络模型已成为主流方法。这些模型的应用范围广泛,从推荐系统到语音识别,彻底变革了多个领域。然而,仍然存在一些挑战&a…

C++初阶学习第九弹-----vector的模拟实现

C初阶学习第六弹------标准库中的string类_c# string[]-CSDN博客 C初阶学习第七弹——string的模拟实现-CSDN博客 C初阶学习第八弹--深入解析vector的使用-CSDN博客 一.vector的成员变量 目录 一.vector的成员变量 二.vector的模拟实现 2.1vector的构造与析构 2.2迭代器…

提升网站流量的搜索引擎优化实用指南

内容概要 搜索引擎优化(SEO)是提升网站可见性与流量的重要过程。在当今数字时代,理解这一领域的基本概念至关重要。SEO不仅仅是关于提高关键词排名,更是关于如何创造更好的用户体验和吸引目标受众。以下是一些关键要素&#xff0…

求教0基础入门大模型的学习路线?java出身,数学良好,希望入局大模型算法,有无必要从cnn学起?

目录 前言: Prompt工程: 2.AI编程 3.API调用 4.大模型应用开发 1)RAG 2)Agent 5.深水区:模型训练和微调 1)Fine-tuning 2)多模态 6.产品和交付 前言 本人本科学历java开发出身,数学基础良好,希望入局大模…

ubuntu 安装 mongodb 笔记记录

https://www.mongodb.com/try/download/community 以上是下载地址 查看系统 (base) duyichengduyicheng-computer:~$ cat /proc/version Linux version 6.8.0-48-generic (builddlcy02-amd64-010) (x86_64-linux-gnu-gcc-13 (Ubuntu 13.2.0-23ubuntu4) 13.2.0, GNU ld (GNU …

隐藏式水印了解一下?你以为加水印很麻烦?

隐藏式水印了解一下?你以为加水印很麻烦? 想在网页上添加水印?想要隐形又清晰的水印效果?watermark-js-plus或许就是你正在找的工具!本文将详细介绍这款前端水印库的特点和使用方法,帮你轻松搞定网页水印问…