【STM32 HAL库】全双工I2S+双缓冲DMA的使用

news2024/9/22 21:14:05

1、配置I2S

我们的有效数据是32位的,使用飞利浦格式。

在这里插入图片描述

2、配置DMA

在这里插入图片描述

**这里需要注意:**i2s的DR寄存器是16位的,如果需要发送32位的数据,是需要写两次DR寄存器的,所以DMA的外设数据宽度设置16位,而不是32位。

3、完善I2S文件

i2s.c和i2s.h文件都是MX自动生成的,并且生成MX_I2S3_Init函数进行了初始化,MX_I2S3_Init函数里面其实依次调用了HAL_I2S_Init库函数(和MCU不相关的初始化)和HAL_I2S_MspInit库函数(是个weak函数,和MCU相关的初始化)。所以,我们自己要写的代码也加到这个文件中。

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    i2s.c
  * @brief   This file provides code for the configuration
  *          of the I2S instances.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "i2s.h"

/* USER CODE BEGIN 0 */
#include "stdio.h"

#define  TX_DATA_16                 (0x1234)
#define  TX_DATA_32                 (0x12345678)
#define  HALF_TX_BUFFER_SIZE        (256)
#define  TX_BUFFER_SIZE             (HALF_TX_BUFFER_SIZE*2)
uint32_t tx_buffer[TX_BUFFER_SIZE];

/* USER CODE END 0 */

I2S_HandleTypeDef hi2s3;
DMA_HandleTypeDef hdma_spi3_tx;

/* I2S3 init function */
void MX_I2S3_Init(void)
{

  /* USER CODE BEGIN I2S3_Init 0 */
	//这里有一点需要注�???,i2s的DR�???16位的,�???以如果想发�??32位的数据,得写两次,
	//如果想发�???0x12345678,就得先发�???0x1234,再发�???0x5678(标准飞利浦格式是高位在前)
	//但是32位数组是小端�???,�???以就�???要重组一�???
    for(int i=0;i<TX_BUFFER_SIZE;i++)
    {
    	*(tx_buffer+i)= (TX_DATA_32<<16)|(TX_DATA_32>>16);
    }
  /* USER CODE END I2S3_Init 0 */

  /* USER CODE BEGIN I2S3_Init 1 */

  /* USER CODE END I2S3_Init 1 */
  hi2s3.Instance = SPI3;
  hi2s3.Init.Mode = I2S_MODE_MASTER_TX;
  hi2s3.Init.Standard = I2S_STANDARD_PHILIPS;
  hi2s3.Init.DataFormat = I2S_DATAFORMAT_32B;
  hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
  hi2s3.Init.AudioFreq = I2S_AUDIOFREQ_48K;
  hi2s3.Init.CPOL = I2S_CPOL_HIGH;
  hi2s3.Init.ClockSource = I2S_CLOCK_PLL;
  hi2s3.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE;
  if (HAL_I2S_Init(&hi2s3) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN I2S3_Init 2 */

  /* USER CODE END I2S3_Init 2 */

}

void HAL_I2S_MspInit(I2S_HandleTypeDef* i2sHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
  if(i2sHandle->Instance==SPI3)
  {
  /* USER CODE BEGIN SPI3_MspInit 0 */

  /* USER CODE END SPI3_MspInit 0 */

  /** Initializes the peripherals clock
  */
    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
    PeriphClkInitStruct.PLLI2S.PLLI2SN = 192;
    PeriphClkInitStruct.PLLI2S.PLLI2SR = 2;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
    {
      Error_Handler();
    }

    /* I2S3 clock enable */
    __HAL_RCC_SPI3_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    /**I2S3 GPIO Configuration
    PA4     ------> I2S3_WS
    PC7     ------> I2S3_MCK
    PC10     ------> I2S3_CK
    PC12     ------> I2S3_SD
    */
    GPIO_InitStruct.Pin = WCK_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
    HAL_GPIO_Init(WCK_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_7|BCK_Pin|DI_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    /* I2S3 DMA Init */
    /* SPI3_TX Init */
    hdma_spi3_tx.Instance = DMA1_Stream5;
    hdma_spi3_tx.Init.Channel = DMA_CHANNEL_0;
    hdma_spi3_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_spi3_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_spi3_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_spi3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_spi3_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_spi3_tx.Init.Mode = DMA_CIRCULAR;
    hdma_spi3_tx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_spi3_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_spi3_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(i2sHandle,hdmatx,hdma_spi3_tx);

  /* USER CODE BEGIN SPI3_MspInit 1 */

  /* USER CODE END SPI3_MspInit 1 */
  }
}

void HAL_I2S_MspDeInit(I2S_HandleTypeDef* i2sHandle)
{

  if(i2sHandle->Instance==SPI3)
  {
  /* USER CODE BEGIN SPI3_MspDeInit 0 */

  /* USER CODE END SPI3_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_SPI3_CLK_DISABLE();

    /**I2S3 GPIO Configuration
    PA4     ------> I2S3_WS
    PC7     ------> I2S3_MCK
    PC10     ------> I2S3_CK
    PC12     ------> I2S3_SD
    */
    HAL_GPIO_DeInit(WCK_GPIO_Port, WCK_Pin);

    HAL_GPIO_DeInit(GPIOC, GPIO_PIN_7|BCK_Pin|DI_Pin);

    /* I2S3 DMA DeInit */
    HAL_DMA_DeInit(i2sHandle->hdmatx);
  /* USER CODE BEGIN SPI3_MspDeInit 1 */

  /* USER CODE END SPI3_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */

int I2S_DMA_Start_Transmit()
{
	return HAL_I2S_Transmit_DMA(&hi2s3, (uint16_t *)tx_buffer, TX_BUFFER_SIZE);
}

int I2S_DMA_Stop()
{
	return HAL_I2S_DMAStop(&hi2s3);
}


/**
  * @brief  Tx Transfer Half completed callbacks
  * @param  hi2s pointer to a I2S_HandleTypeDef structure that contains
  *         the configuration information for I2S module
  * @retval None
  */
void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
	printf("%s\r\n",__func__);
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hi2s);
  for(int i=0;i<HALF_TX_BUFFER_SIZE;i++)
  {
	  *(tx_buffer+i)+=1;
  }

  /* NOTE : This function Should not be modified, when the callback is needed,
            the HAL_I2S_TxHalfCpltCallback could be implemented in the user file
   */
}

/**
  * @brief  Tx Transfer completed callbacks
  * @param  hi2s pointer to a I2S_HandleTypeDef structure that contains
  *         the configuration information for I2S module
  * @retval None
  */
void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)
{
	printf("%s\r\n",__func__);

  /* Prevent unused argument(s) compilation warning */
  UNUSED(hi2s);
  for(int i=0;i<HALF_TX_BUFFER_SIZE;i++)
  {
  	  *(tx_buffer+HALF_TX_BUFFER_SIZE+i)-=1;
  }
  /* NOTE : This function Should not be modified, when the callback is needed,
            the HAL_I2S_TxCpltCallback could be implemented in the user file
   */
}

/**
  * @brief  I2S error callbacks
  * @param  hi2s pointer to a I2S_HandleTypeDef structure that contains
  *         the configuration information for I2S module
  * @retval None
  */
void HAL_I2S_ErrorCallback(I2S_HandleTypeDef *hi2s)
{
  /* Prevent unused argument(s) compilation warning */
  printf("HAL_I2S_Error\r\n");

  /* NOTE : This function Should not be modified, when the callback is needed,
            the HAL_I2S_ErrorCallback could be implemented in the user file
   */
}

/* USER CODE END 1 */

  1. 其实这里使用了DMA双buffer的思路,但是我没有使用双buffer,而是一个buffer的前后部分。当TxHalfCplt的时候,我们去更新buffer前半部分数据,当TxCplt的时候,我们去更新buffer的后半部分数据。HAL库没有很好封装DMA双buffer的配置函数。
  2. 关于DMA的buffer填充问题,I2S的DR寄存器是15位的,所以配置DMA的数据宽度也是16位的,如果I2S是32位的数据格式,那么需要写两次DR寄存器才能组一帧I2S数据,例如I2S想发送0x12345678,那么就得先发送0x1234,再发送0x5678(I2S飞利浦格式就是这样,高位在前),所以填充buffer的时候,也得按该顺序填充。
  3. 关于全双工DMA的封装,HAL好像也没有很好的支持,等下次再介绍。。。

在这里插入图片描述

4、I2S实现DMA双buffer发送

我们如果看过HAL库接口的话,就应该知道。在dma_ex文件中封装了DMA双buffer的接口,但是在i2s文件或者i2s_ex中没有封装双buffer的接口。所以,我们打算仿照HAL_I2S_Transmit_DMA库函数实现一个函数。

/* USER CODE BEGIN 1 */
static void I2S_DMAM0TxHalfCplt(DMA_HandleTypeDef *hdma)
{
  I2S_HandleTypeDef *hi2s = (I2S_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent; /* Derogation MISRAC2012-Rule-11.5 */

  /* Call user Tx half complete callback */
  printf("%s\r\n",__func__);
}

static void I2S_DMAM0TxCplt(DMA_HandleTypeDef *hdma)
{
	I2S_HandleTypeDef *hi2s = (I2S_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent; /* Derogation MISRAC2012-Rule-11.5 */

	  /* Call user Tx complete callback */
	printf("%s\r\n",__func__);
}

static void I2S_DMAM1TxHalfCplt(DMA_HandleTypeDef *hdma)
{
  I2S_HandleTypeDef *hi2s = (I2S_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent; /* Derogation MISRAC2012-Rule-11.5 */

  /* Call user Tx half complete callback */
  printf("%s\r\n",__func__);
}

static void I2S_DMAM1TxCplt(DMA_HandleTypeDef *hdma)
{
	I2S_HandleTypeDef *hi2s = (I2S_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent; /* Derogation MISRAC2012-Rule-11.5 */

	  /* Call user Tx complete callback */
	printf("%s\r\n",__func__);
}

static void I2S_DMA_Error(DMA_HandleTypeDef *hdma)
{
	I2S_HandleTypeDef *hi2s = (I2S_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent; /* Derogation MISRAC2012-Rule-11.5 */
	printf("%s\r\n",__func__);
}

static HAL_StatusTypeDef HAL_I2S_Transmit_DMA_DBuffer(I2S_HandleTypeDef *hi2s, uint16_t *pData0,uint16_t *pData1, uint16_t Size)
{
  uint32_t tmpreg_cfgr;

  if ((pData0 == NULL) || (Size == 0U))
  {
    return  HAL_ERROR;
  }

  /* Process Locked */
  __HAL_LOCK(hi2s);

  if (hi2s->State != HAL_I2S_STATE_READY)
  {
    __HAL_UNLOCK(hi2s);
    return HAL_BUSY;
  }

  /* Set state and reset error code */
  hi2s->State = HAL_I2S_STATE_BUSY_TX;
  hi2s->ErrorCode = HAL_I2S_ERROR_NONE;
  hi2s->pTxBuffPtr = pData0;

  tmpreg_cfgr = hi2s->Instance->I2SCFGR & (SPI_I2SCFGR_DATLEN | SPI_I2SCFGR_CHLEN);

  if ((tmpreg_cfgr == I2S_DATAFORMAT_24B) || (tmpreg_cfgr == I2S_DATAFORMAT_32B))
  {
    hi2s->TxXferSize = (Size << 1U);
    hi2s->TxXferCount = (Size << 1U);
  }
  else
  {
    hi2s->TxXferSize = Size;
    hi2s->TxXferCount = Size;
  }

  /* Set the I2S Tx DMA Half transfer complete callback */
  hi2s->hdmatx->XferHalfCpltCallback = I2S_DMAM0TxHalfCplt;

  /* Set the I2S Tx DMA transfer complete callback */
  hi2s->hdmatx->XferCpltCallback = I2S_DMAM0TxCplt;

  hi2s->hdmatx->XferM1HalfCpltCallback=I2S_DMAM1TxHalfCplt;//callback
  hi2s->hdmatx->XferM1CpltCallback=I2S_DMAM1TxCplt;//callback

  /* Set the DMA error callback */
  hi2s->hdmatx->XferErrorCallback = I2S_DMA_Error;

  /* Enable the Tx DMA Stream/Channel */
  if (HAL_OK != HAL_DMAEx_MultiBufferStart_IT(hi2s->hdmatx,
		  (uint32_t)hi2s->pTxBuffPtr,
		  (uint32_t)&hi2s->Instance->DR,
		  (uint32_t)pData1,
		  hi2s->TxXferSize))
  {
    /* Update SPI error code */
    SET_BIT(hi2s->ErrorCode, HAL_I2S_ERROR_DMA);
    hi2s->State = HAL_I2S_STATE_READY;

    __HAL_UNLOCK(hi2s);
    return HAL_ERROR;
  }

  /* Check if the I2S is already enabled */
  if (HAL_IS_BIT_CLR(hi2s->Instance->I2SCFGR, SPI_I2SCFGR_I2SE))
  {
    /* Enable I2S peripheral */
    __HAL_I2S_ENABLE(hi2s);
  }

  /* Check if the I2S Tx request is already enabled */
  if (HAL_IS_BIT_CLR(hi2s->Instance->CR2, SPI_CR2_TXDMAEN))
  {
    /* Enable Tx DMA Request */
    SET_BIT(hi2s->Instance->CR2, SPI_CR2_TXDMAEN);
  }

  __HAL_UNLOCK(hi2s);
  return HAL_OK;
}

注意几点:

  1. 这些函数还是写在i2s.c文件中。
  2. 相比原函数,在函数参数上多了一个buffer地址。
  3. 原函数中的回调函数都是本地函数,不供其他文件调用,所以全部重新定义在我们的本文件中。并且多了m1 buffer的回调函数。
  4. DMA_Start函数更换为双buffer函数接口。其他的地方都没有改变。
    在这里插入图片描述
    在这里插入图片描述

5、全双工I2S实现

在这里插入图片描述
配置全双工Master模式,其实看源码就知道,当配置全双工的时候,用到了两个i2s外设。
在这里插入图片描述
如果我们配置i2s为Master_Tx,那么i2s_ex就会自动被配置为Slave_Rx。当然,这些在HAL库中都已经封装好了,我们使用起来还是不麻烦的。
但是,HAL库封装的函数很奇怪,所以我们仿照原库函数自己修改了一下。

static void I2S_DMARxHalfCplt(DMA_HandleTypeDef *hdma)
{
	printf("%s\r\n",__func__);
}

static void I2S_DMARxCplt(DMA_HandleTypeDef *hdma)
{
	printf("%s\r\n",__func__);
}

static void I2S_DMATxHalfCplt(DMA_HandleTypeDef *hdma)
{
	printf("%s\r\n",__func__);
}

static void I2S_DMATxCplt(DMA_HandleTypeDef *hdma)
{
	printf("%s\r\n",__func__);
}

static HAL_StatusTypeDef HAL_I2SEx_TransmitReceive_DMA_Modify(I2S_HandleTypeDef *hi2s,
                                                uint16_t *pTxData,
                                                uint16_t *pRxData,
                                                uint16_t Size)
{
  uint32_t *tmp = NULL;
  uint32_t tmp1 = 0U;
  HAL_StatusTypeDef errorcode = HAL_OK;

  if (hi2s->State != HAL_I2S_STATE_READY)
  {
    errorcode = HAL_BUSY;
    goto error;
  }

  if ((pTxData == NULL) || (pRxData == NULL) || (Size == 0U))
  {
    return  HAL_ERROR;
  }

  /* Process Locked */
  __HAL_LOCK(hi2s);

  hi2s->pTxBuffPtr = pTxData;
  hi2s->pRxBuffPtr = pRxData;

  tmp1 = hi2s->Instance->I2SCFGR & (SPI_I2SCFGR_DATLEN | SPI_I2SCFGR_CHLEN);
  /* Check the Data format: When a 16-bit data frame or a 16-bit data frame extended
  is selected during the I2S configuration phase, the Size parameter means the number
  of 16-bit data length in the transaction and when a 24-bit data frame or a 32-bit data
  frame is selected the Size parameter means the number of 16-bit data length. */
  if ((tmp1 == I2S_DATAFORMAT_24B) || (tmp1 == I2S_DATAFORMAT_32B))
  {
    hi2s->TxXferSize  = (Size << 1U);
    hi2s->TxXferCount = (Size << 1U);
    hi2s->RxXferSize  = (Size << 1U);
    hi2s->RxXferCount = (Size << 1U);
  }
  else
  {
    hi2s->TxXferSize  = Size;
    hi2s->TxXferCount = Size;
    hi2s->RxXferSize  = Size;
    hi2s->RxXferCount = Size;
  }

  hi2s->ErrorCode = HAL_I2S_ERROR_NONE;
  hi2s->State     = HAL_I2S_STATE_BUSY_TX_RX;

  /* Set the I2S Rx DMA Half transfer complete callback */
  hi2s->hdmarx->XferHalfCpltCallback = I2S_DMARxHalfCplt;

  /* Set the I2S Rx DMA transfer complete callback */
  hi2s->hdmarx->XferCpltCallback  = I2S_DMARxCplt;

  /* Set the I2S Rx DMA error callback */
  hi2s->hdmarx->XferErrorCallback = NULL;

  /* Set the I2S Tx DMA Half transfer complete callback as NULL */
  hi2s->hdmatx->XferHalfCpltCallback  = I2S_DMATxHalfCplt;

  /* Set the I2S Tx DMA transfer complete callback as NULL */
  hi2s->hdmatx->XferCpltCallback  = I2S_DMATxCplt;

  /* Set the I2S Tx DMA error callback */
  hi2s->hdmatx->XferErrorCallback = NULL;

  tmp1 = hi2s->Instance->I2SCFGR & SPI_I2SCFGR_I2SCFG;
  /* Check if the I2S_MODE_MASTER_TX or I2S_MODE_SLAVE_TX Mode is selected */
  if ((tmp1 == I2S_MODE_MASTER_TX) || (tmp1 == I2S_MODE_SLAVE_TX))
  {
    /* Enable the Rx DMA Stream */
    tmp = (uint32_t *)&pRxData;
    HAL_DMA_Start_IT(hi2s->hdmarx, (uint32_t)&I2SxEXT(hi2s->Instance)->DR, *(uint32_t *)tmp, hi2s->RxXferSize);

    /* Enable Rx DMA Request */
    SET_BIT(I2SxEXT(hi2s->Instance)->CR2, SPI_CR2_RXDMAEN);

    /* Enable the Tx DMA Stream */
    tmp = (uint32_t *)&pTxData;
    HAL_DMA_Start_IT(hi2s->hdmatx, *(uint32_t *)tmp, (uint32_t)&hi2s->Instance->DR, hi2s->TxXferSize);

    /* Enable Tx DMA Request */
    SET_BIT(hi2s->Instance->CR2, SPI_CR2_TXDMAEN);

    /* Check if the I2S is already enabled */
    if ((hi2s->Instance->I2SCFGR & SPI_I2SCFGR_I2SE) != SPI_I2SCFGR_I2SE)
    {
      /* Enable I2Sext(receiver) before enabling I2Sx peripheral */
      __HAL_I2SEXT_ENABLE(hi2s);

      /* Enable I2S peripheral after the I2Sext */
      __HAL_I2S_ENABLE(hi2s);
    }
  }
  else
  {
    /* Check if Master Receiver mode is selected */
    if ((hi2s->Instance->I2SCFGR & SPI_I2SCFGR_I2SCFG) == I2S_MODE_MASTER_RX)
    {
      /* Clear the Overrun Flag by a read operation on the SPI_DR register followed by a read
      access to the SPI_SR register. */
      __HAL_I2S_CLEAR_OVRFLAG(hi2s);
    }
    /* Enable the Tx DMA Stream */
    tmp = (uint32_t *)&pTxData;
    HAL_DMA_Start_IT(hi2s->hdmatx, *(uint32_t *)tmp, (uint32_t)&I2SxEXT(hi2s->Instance)->DR, hi2s->TxXferSize);

    /* Enable Tx DMA Request */
    SET_BIT(I2SxEXT(hi2s->Instance)->CR2, SPI_CR2_TXDMAEN);

    /* Enable the Rx DMA Stream */
    tmp = (uint32_t *)&pRxData;
    HAL_DMA_Start_IT(hi2s->hdmarx, (uint32_t)&hi2s->Instance->DR, *(uint32_t *)tmp, hi2s->RxXferSize);

    /* Enable Rx DMA Request */
    SET_BIT(hi2s->Instance->CR2, SPI_CR2_RXDMAEN);

    /* Check if the I2S is already enabled */
    if ((hi2s->Instance->I2SCFGR & SPI_I2SCFGR_I2SE) != SPI_I2SCFGR_I2SE)
    {
      /* Enable I2Sext(transmitter) before enabling I2Sx peripheral */
      __HAL_I2SEXT_ENABLE(hi2s);
      /* Enable I2S peripheral before the I2Sext */
      __HAL_I2S_ENABLE(hi2s);
    }
  }

error :
  __HAL_UNLOCK(hi2s);
  return errorcode;
}

相比原函数,我们就修改了一个地方,那就是把TX的回调函数也赋值了,其实这里不理解的地方有两个:

  1. 为什么不给TX的回调函数赋值
  2. 为什么RX的回调函数命名TxRx的回调函数
    我们在实验中直接将DI接在DO上了,最后看看调试结果
    在这里插入图片描述
    最后,如果想在全双工中使用DMA双buffer,可以仿照上文中函数的修改即可,这里就不做示范了。

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

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

相关文章

pgsql的update语句在set里进行字段的运算 SET sort = sort +1

一、场景 需求&#xff1a;version 版本字段是记录数据更新的次数&#xff0c;新增时自动填充 version1 ,每更新一次数据 version就自增1。项目里单表插入和更新要手写update语句进行插入和更新。 –表中int4类型的字段 version 是1时&#xff0c;由1变成2 – version 是null…

嵌入式人工智能(10-基于树莓派4B的DS1302实时时钟RTC)

1、实时时钟&#xff08;Real Time Clock&#xff09; RTC&#xff0c;全称为实时时钟&#xff08;Real Time Clock&#xff09;&#xff0c;是一种能够提供实时时间信息的电子设备。RTC通常包括一个计时器和一个能够记录日期和时间的电池。它可以独立于主控芯片工作&#xff…

5.过滤器Filter(doFilter()+chain.doFilter())

过滤器Filter 文章目录 过滤器Filter一、过滤器简介1.定义2.作用3.拦截原理4.常用方法:5.Filter的生命周期4.web.xml中配置5.WebFilter 一、过滤器简介 1.定义 过滤器是对Web应用程序的请求和响应添加功能的Web服务组件(实现 javax.servlet.Filter 接口的 Java 类。) 调用web…

Neuralink首款产品Telepathy:意念控制设备的革新与挑战

近年来&#xff0c;科技领域不断涌现出令人惊叹的突破&#xff0c;其中尤以脑机接口&#xff08;BCI&#xff09;技术为代表。近日&#xff0c;Elon Musk的Neuralink公司发布了其首款脑机接口产品Telepathy&#xff0c;引发了广泛关注。本文将详细探讨Telepathy的功能、技术原理…

Java语言程序设计基础篇_编程练习题**15.6(两个消息交替出现)

**15.6(两个消息交替出现) 编写一个程序&#xff0c;当单击鼠标时面板上交替显示两个文本"Java is fun"和"Java is powerful" 代码展示&#xff1a;编程练习题15_6TwoInfo.java package chapter_15;import javafx.application.Application; import javafx…

JavaScript之Web APIs-DOM

目录 DOM获取元素一、Web API 基本认知1.1 变量声明1.2 作用和分类1.3 DOM树1.4 DOM对象 二、获取DOM对象2.1 通过CSS选择器来获取DOM元素2.2 通过其他方式来获取DOM元素 三、操作元素内容3.1 元素.innerTest属性3.2 元素.innerHTML属性 四、操作元素属性4.1 操作元素常用属性4…

mysql无法启动

总是报错&#xff1a; 1、Job for mysql.service failed because the control process exited with error code. See "systemctl status mysql.service" and "journalctl -xeu mysql.service" for details. 2、ERROR 2002 (HY000): Cant connect to local …

Linux可视化工具-netdata之docker安装

版本要求 docker cli安装 docker pull netdata/netdata docker run -d --namenetdata \ --pidhost \ --networkhost \ -v netdataconfig:/etc/netdata \ -v netdatalib:/var/lib/netdata \ -v netdatacache:/var/cache/netdata \ -v /:/host/root:ro,rslave \ -v /etc/passwd…

常用注意力机制 SENet CBAM ECA

在处理脑电信号时通常会用到一些注意力机制,来给不同的脑电通道不同的权重,进而体现出不同脑电通道在分类中的重要性。下面整理几种常见的通道注意力机制,方便以后查阅。 常用注意力机制 SENet CBAM ECA 注意力机制SENet(Squeeze-and-Excitation Network)SENet原理SENet P…

五. TensorRT API的基本使用-load-model

目录 前言0. 简述1. 案例运行2. 代码分析2.1 main.cpp2.2 model.hpp2.3 model.cpp2.4 其它 总结下载链接参考 前言 自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考 本次课程我们来学习课程第五章—TensorRT …

达梦数据库审计日志采集

目录 1. 审计功能简介2. dm8官方技术参考文档3. dm8审计功能配置3.1 登录审计用户3.2 开启审计开关3.3 查询审计日志3.4 审计设置3.4.1 配置语句级审计3.4.2 取消语句级审计3.5 审计日志查阅4. python获取dm8审计日志1. 审计功能简介 审计机制是 DM 数据库管理系统安全管理的重…

第十一课:综合项目实践

下面是我们搭建的一个综合实践的拓扑图&#xff1a; 我们要完成以下目标&#xff1a; 网络中有3个不同部门&#xff0c;均可自动获取地址各部门可相互访问&#xff0c;也可访问内部服务网172.16.100.1&#xff0c;PC1不允许访问互联网&#xff0c;PC1和PC3可以访问互联网内网服…

nginx配置文件说明

Nginx的配置文件说明 Nginx配置文件的主要配置块可以分为三个部分&#xff1a;全局配置块&#xff08;events和http块&#xff09;&#xff0c;events块和http块。这三个部分共同定义了Nginx服务器的整体行为和处理HTTP请求的方式。 全局配置块&#xff1a; 包含了影响Nginx服…

Vue3组件样式

在 Vue3开发中&#xff0c;我们经常需要对元素的类和样式进行动态控制。本文将详细介绍如何使用 Vue.js 的特性来实现这一目标。 class 绑定 绑定对象&#xff1a; 在 Vue.js 中&#xff0c;我们可以使用对象语法来绑定 class。例如&#xff1a; <div :class"{ act…

卸载顽固的驱动或软件

在Windows系统&#xff0c;有些软件或驱动&#xff0c;为了防止被卸载&#xff0c;特地在C:\Windows\System32\drivers目录里&#xff0c;生成xxx.sys文件。这些xxx.sys文件&#xff0c;无法直接删除&#xff0c;用杀毒软件也很难卸载。     这里介绍一种方法&#xff0c;可以…

基于GTX的64B66B编码的自定义接收模块(高速收发器二十二)

点击进入高速收发器系列文章导航界面 1、自定义PHY顶层模块 前文设计了64B66B自定义PHY的发送模块&#xff0c;本文完成自定义PHY剩余的模块的设计&#xff0c;整体设计框图如下所示。 其中phy_tx是自定义PHY的发送数据模块&#xff0c;scrambler是加扰模块&#xff0c;rx_slip…

阿里云OS Copilot:解锁操作系统运维与编程的智能助手

目录 引言 OS Copilot简介 OS Copilot的环境准备 创建实验资源 安全设置 设置安全组端口 创建阿里云AccessKey 准备系统环境 OS Copilot的实操 场景一、用OS Copilot写脚本和注释代码 场景二、使用OS Copilot进行对话问答 场景三、使用OS Copilot辅助编程学习 清理…

P15-P18-随机梯度下降-自适应学习率-超参数筛选-正则化

文章目录 随机梯度下降和自适应学习率超参数筛选交叉验证 正则化权重衰减Dropout 简介 本文主要讨论了机器学习中随机梯度下降&#xff08;SGD&#xff09;和自适应学习率算法的原理及应用。SGD通过随机选择小批量样本计算损失值&#xff0c;减少了计算量&#xff0c;加快了训练…

国内访问Docker Hub慢问题解决方法

在国内访问Docker Hub时可能会遇到一些困难&#xff0c;但幸运的是&#xff0c;有多种解决方案可以帮助你顺利下载Docker镜像。以下是一些有效的解决方案&#xff1a; 配置Docker镜像源&#xff1a;你可以通过配置Docker的daemon.json文件来使用国内镜像源&#xff0c;比如DaoC…

Spring Web MVC(一篇带你了解并入门,附带常用注解)

一&#xff0c;什么是Spring Web MVC 先看一下官网怎么说&#xff1a; 也就是Spring Web MVC一开始就是包含在Spring框架里面的&#xff0c;但通常叫做Spring MVC。 也可以总结出一个信息&#xff0c;这是一个Web框架。后面我就简称为Spring MVC了。 1.1MVC MVC也就是Mode…