STM32——SDIO的学习(驱动SD卡)(实战篇)

news2024/11/26 2:22:43

目录

一、SDIO寄存器

1.1 SDIO电源控制寄存器(SDIO_POWER)

1.2 SDIO时钟控制寄存器(SDIO_CLKCR)

1.3 SDIO参数寄存器(SDIO_ARG)

1.4 SDIO命令寄存器(SDIO_CMD)

1.5 SDIO命令响应寄存器(SDIO_RESPCMD)

1.6 SDIO响应 1..4 寄存器(SDIO_RESPx)

1.7 SDIO数据定时器寄存器(SDIO_DTIMER)

1.8 SDIO数据长度寄存器(SDIO_DLEN)

1.9 SDIO数据控制寄存器(SDIO_DCTRL)

1.10 SDIO数据计数器寄存器(SDIO_DCOUNT)

1.11 SDIO状态寄存器(SDIO_STA)

1.12 SDIO清除中断寄存器(SDIO_ICR)

1.13 SDIO中断屏蔽寄存器(SDIO_MASK)

1.14 SDIO FIFO计数器寄存器(SDIO_FIFOCNT)

1.15 SDIO数据FIFO寄存器(SDIO_FIFO)

1.16 SDIO寄存器映像

二、开发思路

三、具体实例


书接上回:

STM32——SDIO的学习(驱动SD卡)(理论篇)_宇努力学习的博客-CSDN博客

在写程序前在最后了解一下SDIO的寄存器

一、SDIO寄存器

设备通过可以在AHB上操作的32位控制寄存器与系统通信。
必须以字(32位)的方式操作这些外设寄存器。


1.1 SDIO电源控制寄存器(SDIO_POWER)

地址偏移: 0x00
复位值: 0x0000 0000

注意: 写数据后的7个HCLK时钟周期内,不能写入这个寄存器.

1.2 SDIO时钟控制寄存器(SDIO_CLKCR)


地址偏移: 0x04
复位值: 0x0000 0000
SDIO_CLKCR寄存器控制SDIO_CK输出时钟

注意:

1 当SD/SDIO卡或多媒体卡在识别模式, SDIO_CK的频率必须低于400kHz。
2 当所有卡都被赋予了相应的地址后,时钟频率可以改变到卡总线允许的最大频率。
3 写数据后的7个HCLK时钟周期内不能写入这个寄存器。对于SD I/O卡,在读等待期间可以停
止SDIO_CK,此时SDIO_CLKCR寄存器不控制SDIO_CK
 

1.3 SDIO参数寄存器(SDIO_ARG)


地址偏移: 0x08
复位值: 0x0000 0000
SDIO_ARG寄存器包含32位命令参数,它将作为命令的一部分发送到卡中。

1.4 SDIO命令寄存器(SDIO_CMD)


地址偏移: 0x0C
复位值: 0x0000 0000
SDIO_CMD寄存器包含命令索引和命令类型位。命令索引是作为命令的一部分发送到卡中。命
令类型位控制命令通道状态机(CPSM)。
 

注意:

1 写数据后的7个HCLK时钟周期内不能写入这个寄存器。
2 多媒体卡可以发送2种响应: 48位长的短响应,或136位长的长响应。 SD卡和SD I/O卡只能发
送短响应,参数可以根据响应的类型而变化,软件将根据发送的命令区分响应的类型。 CE-ATA
设备只发送短响应
 

1.5 SDIO命令响应寄存器(SDIO_RESPCMD)


地址偏移: 0x10
复位值: 0x0000 0000
SDIO_RESPCMD寄存器包含最后收到的命令响应中的命令索引。如果传输的命令响应不包含
命令索引(长响应或OCR响应),尽管它应该包含111111b(响应中的保留域值),但RESPCMD域
的内容未知。

 

1.6 SDIO响应 1..4 寄存器(SDIO_RESPx)


地址偏移: 0x14 + 4*(x-1),其中 x = 1..4
复位值: 0x0000 0000
SDIO_RESP1/2/3/4寄存器包含卡的状态,即收到响应的部分信息。
 

1.7 SDIO数据定时器寄存器(SDIO_DTIMER)


地址偏移: 0x24
复位值: 0x0000 0000
SDIO_DTIMER寄存器包含以卡总线时钟周期为单位的数据超时时间。
一个计数器从SDIO_DTIMER寄存器加载数值,并在数据通道状态机(DPSM)进入Wait_R或繁忙
状态时进行递减计数,当DPSM处在这些状态时,如果计数器减为0,则设置超时标志。
 

注意 在写入数据控制寄存器进行数据传输之前,必须先写入数据定时器寄存器和数据长度寄存器。

1.8 SDIO数据长度寄存器(SDIO_DLEN)


地址偏移: 0x28
复位值: 0x0000 0000
SDIO_DLEN寄存器包含需要传输的数据字节长度。当数据传输开始时,这个数值被加载到数据
计数器中。
 

 

注意对于块数据传输,数据长度寄存器中的数值必须是数据块长度(见SDIO_DCTRL)的倍数。在写
入数据控制寄存器进行数据传输之前,必须先写入数据定时器寄存器和数据长度寄存器。

1.9 SDIO数据控制寄存器(SDIO_DCTRL)


地址偏移: 0x2C
复位值: 0x0000 0000
SDIO_DCTRL寄存器控制数据通道状态机(DPSM)。

 

注意 写数据后的7个HCLK时钟周期内不能写入这个寄存器。
 

1.10 SDIO数据计数器寄存器(SDIO_DCOUNT)


地址偏移: 0x30
复位值: 0x0000 0000
当DPSM从空闲状态进入Wait_R或Wait_S状态时, SDIO_DCOUNT寄存器从数据长度寄存器加
载数值(见SDIO_DLEN),在数据传输过程中,该计数器的数值递减直到减为0,然后DPSM进入
空闲状态并设置数据状态结束标志DATAEND。

 

注意 只能在数据传输结束时读这个寄存器

1.11 SDIO状态寄存器(SDIO_STA)


地址偏移: 0x34
复位值: 0x0000 0000
SDIO_STA是一个只读寄存器,它包含两类标志:
● 静态标志(位[23:22、 10:0]):写入SDIO中断清除寄存器(见SDIO_ICR),可以清除这些位。
● 动态标志(位[21:11]):这些位的状态变化根据它们对应的那部分逻辑而变化(例如: FIFO满
和空标志变高或变低随FIFO的数据写入变化)。

 

1.12 SDIO清除中断寄存器(SDIO_ICR)


地址偏移: 0x38
复位值: 0x0000 0000
SDIO_ICR是一个只写寄存器,在对应寄存器位写’1’将清除SDIO_STA状态寄存器中的对应位。
 

 

 

1.13 SDIO中断屏蔽寄存器(SDIO_MASK)


地址偏移: 0x3C
复位值: 0x0000 0000
在对应位置’1’, SDIO_MASK中断屏蔽寄存器决定哪一个状态位产生中断。

 

 

 

1.14 SDIO FIFO计数器寄存器(SDIO_FIFOCNT)


地址偏移: 0x48
复位值: 0x0000 0000
SDIO_FIFOCNT寄存器包含还未写入FIFO或还未从FIFO读出的数据字数目。当在数据控制寄
存器(SDIO_DCTRL)中设置了数据传输使能位DTEN,并且DPSM处于空闲状态时, FIFO计数
器从数据长度寄存器(见SDIO_DLEN)加载数值。如果数据长度未与字对齐(4的倍数),则最后剩
下的1~3个字节被当成一个字处理。
 

1.15 SDIO数据FIFO寄存器(SDIO_FIFO)


地址偏移: 0x80
复位值: 0x0000 0000
接收和发送FIFO是32位的宽度读或写一组寄存器,它在连续的32个地址上包含32个寄存器,
CPU可以使用FIFO读写多个操作数。

1.16 SDIO寄存器映像


下表是SDIO寄存器的总结。
 

 

 

二、开发思路

        略掉。我们按照ST官方文档来学习。从程序中学习他们的开发流程。我觉得这时最快的方式。在外面都学会了以后开创直接的东西的时候才需要按照基础知识到程序的流程。

三、具体实例

使用之前先来看看怎么取消初始化。 

/**
  * @brief  DeInitializes the SDIO interface.
  * @param  None
  * @retval None
  */
void SD_LowLevel_DeInit(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure;
  
  /*!< Disable SDIO Clock */
  SDIO_ClockCmd(DISABLE);
  
  /*!< Set Power State to OFF */
  SDIO_SetPowerState(SDIO_PowerState_OFF);

  /*!< DeInitializes the SDIO peripheral */
  SDIO_DeInit();
  
  /*!< Disable the SDIO AHB Clock */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_SDIO, DISABLE);

  /*!< Configure PC.08, PC.09, PC.10, PC.11, PC.12 pin: D0, D1, D2, D3, CLK pin */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

  /*!< Configure PD.02 CMD line */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
  GPIO_Init(GPIOD, &GPIO_InitStructure);
}

 先关闭时钟和电源。

然后对SDIO外设的相关寄存器进行设置。

/**
  * @brief  Deinitializes the SDIO peripheral registers to their default reset values.
  * @param  None
  * @retval None
  */
void SDIO_DeInit(void)
{
  SDIO->POWER = 0x00000000;
  SDIO->CLKCR = 0x00000000;
  SDIO->ARG = 0x00000000;
  SDIO->CMD = 0x00000000;
  SDIO->DTIMER = 0x00000000;
  SDIO->DLEN = 0x00000000;
  SDIO->DCTRL = 0x00000000;
  SDIO->ICR = 0x00C007FF;
  SDIO->MASK = 0x00000000;
}

其实就是按照流程给对应寄存器写对应的值。

然后关闭AHB总线

这时得注意有没有其它设备使用这条总线。

然后配置使用的引脚模式为浮空输入。

上面的五个引脚分别是四根数据线和一根时钟线。

下面那根是控制线。

看完取消使能再看使能

/**
  * @brief  Initializes the SD Card and put it into StandBy State (Ready for 
  *         data transfer).
  * @param  None
  * @retval None
  */
void SD_LowLevel_Init(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure;

  /*!< GPIOC and GPIOD Periph clock enable */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE);

  /*!< Configure PC.08, PC.09, PC.10, PC.11, PC.12 pin: D0, D1, D2, D3, CLK pin */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

  /*!< Configure PD.02 CMD line */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
  GPIO_Init(GPIOD, &GPIO_InitStructure);
  
  /*!< Enable the SDIO AHB Clock */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_SDIO, ENABLE);

  /*!< Enable the DMA2 Clock */
  RCC_AHBPeriphClockCmd(SD_SDIO_DMA_CLK, ENABLE);
}

和取消相反,先使能引脚对应的时钟总线。

将四个数据引脚 和时钟引脚初始化为复用推挽输出。

使能控制引脚和SDIO与DMA的总线。

/**
  * @brief  Allows to erase memory area specified for the given card.
  * @param  startaddr: the start address.
  * @param  endaddr: the end address.
  * @retval SD_Error: SD Card Error code.
  */
SD_Error SD_Erase(uint32_t startaddr, uint32_t endaddr)
{
  SD_Error errorstatus = SD_OK;
  uint32_t delay = 0;
  __IO uint32_t maxdelay = 0;
  uint8_t cardstate = 0;

  /*!< Check if the card coomnd class supports erase command */
  if (((CSD_Tab[1] >> 20) & SD_CCCC_ERASE) == 0)
  {
    errorstatus = SD_REQUEST_NOT_APPLICABLE;
    return(errorstatus);
  }

  maxdelay = 120000 / ((SDIO->CLKCR & 0xFF) + 2);

  if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED)
  {
    errorstatus = SD_LOCK_UNLOCK_FAILED;
    return(errorstatus);
  }

  if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
  {
    startaddr /= 512;
    endaddr /= 512;
  }
  
  /*!< According to sd-card spec 1.0 ERASE_GROUP_START (CMD32) and erase_group_end(CMD33) */
  if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) || (SDIO_HIGH_CAPACITY_SD_CARD == CardType))
  {
    /*!< Send CMD32 SD_ERASE_GRP_START with argument as addr  */
    SDIO_CmdInitStructure.SDIO_Argument = startaddr;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_START;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);

    errorstatus = CmdResp1Error(SD_CMD_SD_ERASE_GRP_START);
    if (errorstatus != SD_OK)
    {
      return(errorstatus);
    }

    /*!< Send CMD33 SD_ERASE_GRP_END with argument as addr  */
    SDIO_CmdInitStructure.SDIO_Argument = endaddr;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_END;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);

    errorstatus = CmdResp1Error(SD_CMD_SD_ERASE_GRP_END);
    if (errorstatus != SD_OK)
    {
      return(errorstatus);
    }
  }

  /*!< Send CMD38 ERASE */
  SDIO_CmdInitStructure.SDIO_Argument = 0;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ERASE;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);

  errorstatus = CmdResp1Error(SD_CMD_ERASE);

  if (errorstatus != SD_OK)
  {
    return(errorstatus);
  }

  for (delay = 0; delay < maxdelay; delay++)
  {}

  /*!< Wait till the card is in programming state */
  errorstatus = IsCardProgramming(&cardstate);
  delay = SD_DATATIMEOUT;
  while ((delay > 0) && (errorstatus == SD_OK) && ((SD_CARD_PROGRAMMING == cardstate) || (SD_CARD_RECEIVING == cardstate)))
  {
    errorstatus = IsCardProgramming(&cardstate);
    delay--;
  }

  return(errorstatus);
}

上面这个函数是擦除SD卡某个区域的。

下面的函数是初始化整个SD卡,并让其进入待机状态。

/**
  * @brief  Initializes the SD Card and put it into StandBy State (Ready for data 
  *         transfer).
  * @param  None
  * @retval SD_Error: SD Card Error code.
  */
SD_Error SD_Init(void)
{
  __IO SD_Error errorstatus = SD_OK;
  
  NVIC_Configuration();
  
  /* SDIO Peripheral Low Level Init */
  SD_LowLevel_Init();

  SDIO_DeInit();

  errorstatus = SD_PowerON();

  if (errorstatus != SD_OK)
  {
    /*!< CMD Response TimeOut (wait for CMDSENT flag) */
    return(errorstatus);
  }

  errorstatus = SD_InitializeCards();

  if (errorstatus != SD_OK)
  {
    /*!< CMD Response TimeOut (wait for CMDSENT flag) */
    return(errorstatus);
  }

  /*!< Configure the SDIO peripheral */
  /*!< SDIO_CK = SDIOCLK / (SDIO_TRANSFER_CLK_DIV + 2) */
  SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV;
  SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
  SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
  SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
  SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;
  SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
  SDIO_Init(&SDIO_InitStructure);

  /*----------------- Read CSD/CID MSD registers ------------------*/
  errorstatus = SD_GetCardInfo(&SDCardInfo);

  if (errorstatus == SD_OK)
  {
    /*----------------- Select Card --------------------------------*/
    errorstatus = SD_SelectDeselect((uint32_t) (SDCardInfo.RCA << 16));
  }

  if (errorstatus == SD_OK)
  {
    errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b);
  }  

  return(errorstatus);
}

 对单个数据块做读写测试。之前学习系统移植的时候,我们了解到SD卡是以512个字节为一个块的。所以这个Blocksize就是一个宏定义的512

/*
 * 函数名:SD_SingleBlockTest
 * 描述  :	单个数据块读写测试
 * 输入  :无
 * 输出  :无
 */
void SD_SingleBlockTest(void)
{  
  /* Fill the buffer to send */
  Fill_Buffer(Buffer_Block_Tx, BLOCK_SIZE, 0x320F);

  if (Status == SD_OK)
  {
    /* Write block of 512 bytes on address 0 */
    Status = SD_WriteBlock(Buffer_Block_Tx, 0x00, BLOCK_SIZE);
		
    /* Check if the Transfer is finished */
    Status = SD_WaitWriteOperation();	  
    while(SD_GetStatus() != SD_TRANSFER_OK); 
  }

  if (Status == SD_OK)
  {
    /* Read block of 512 bytes from address 0 */
    Status = SD_ReadBlock(Buffer_Block_Rx, 0x00, BLOCK_SIZE);//读取数据
    /* Check if the Transfer is finished */
    Status = SD_WaitReadOperation();
    while(SD_GetStatus() != SD_TRANSFER_OK);
  }

  /* Check the correctness of written data */
  if (Status == SD_OK)
  {
    TransferStatus1 = Buffercmp(Buffer_Block_Tx, Buffer_Block_Rx, BLOCK_SIZE);	//比较
  }
  
  if(TransferStatus1 == PASSED)
    printf("》单块读写测试成功!\n" );
 
  else  
  	printf("》单块读写测试失败!\n " );  
}

 这里是自己实现的三个小函数。方便在操作时使用对应功能。

/*
 * 函数名:Buffercmp
 * 描述  :比较两个缓冲区中的数据是否相等
 * 输入  :-pBuffer1, -pBuffer2 : 要比较的缓冲区的指针
 *         -BufferLength 缓冲区长度
 * 输出  :-PASSED 相等
 *         -FAILED 不等
 */
TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint32_t BufferLength)
{
  while (BufferLength--)
  {
    if (*pBuffer1 != *pBuffer2)
    {
      return FAILED;
    }

    pBuffer1++;
    pBuffer2++;
  }

  return PASSED;
}


/*
 * 函数名:Fill_Buffer
 * 描述  :在缓冲区中填写数据
 * 输入  :-pBuffer 要填充的缓冲区
 *         -BufferLength 要填充的大小
 *         -Offset 填在缓冲区的第一个值
 * 输出  :无 
 */
void Fill_Buffer(uint8_t *pBuffer, uint32_t BufferLength, uint32_t Offset)
{
  uint16_t index = 0;

  /* Put in global buffer same values */
  for (index = 0; index < BufferLength; index++ )
  {
    pBuffer[index] = index + Offset;
  }
}

/*
 * 函数名:eBuffercmp
 * 描述  :检查缓冲区的数据是否为0
 * 输入  :-pBuffer 要比较的缓冲区
 *         -BufferLength 缓冲区长度        
 * 输出  :PASSED 缓冲区的数据全为0
 *         FAILED 缓冲区的数据至少有一个不为0 
 */
TestStatus eBuffercmp(uint8_t* pBuffer, uint32_t BufferLength)
{
  while (BufferLength--)
  {
    /* In some SD Cards the erased state is 0xFF, in others it's 0x00 */
    if ((*pBuffer != 0xFF) && (*pBuffer != 0x00))//擦除后是0xff或0x00
    {
      return FAILED;
    }

    pBuffer++;
  }

  return PASSED;
}

对多块读写如下:

/*
 * 函数名:SD_MultiBlockTest
 * 描述  :	多数据块读写测试
 * 输入  :无
 * 输出  :无
 */
void SD_MultiBlockTest(void)
{
  /* Fill the buffer to send */
  Fill_Buffer(Buffer_MultiBlock_Tx, MULTI_BUFFER_SIZE, 0x0);

  if (Status == SD_OK)
  {
    /* Write multiple block of many bytes on address 0 */
    Status = SD_WriteMultiBlocks(Buffer_MultiBlock_Tx, 0x00, BLOCK_SIZE, NUMBER_OF_BLOCKS);
    /* Check if the Transfer is finished */
    Status = SD_WaitWriteOperation();
    while(SD_GetStatus() != SD_TRANSFER_OK);
  }

  if (Status == SD_OK)
  {
    /* Read block of many bytes from address 0 */
    Status = SD_ReadMultiBlocks(Buffer_MultiBlock_Rx, 0x00, BLOCK_SIZE, NUMBER_OF_BLOCKS);
    /* Check if the Transfer is finished */
    Status = SD_WaitReadOperation();
    while(SD_GetStatus() != SD_TRANSFER_OK);
  }

  /* Check the correctness of written data */
  if (Status == SD_OK)
  {
    TransferStatus2 = Buffercmp(Buffer_MultiBlock_Tx, Buffer_MultiBlock_Rx, MULTI_BUFFER_SIZE);
  }
  
  if(TransferStatus2 == PASSED)	  
  	printf("》多块读写测试成功!\n " );

  else 
  	printf("》多块读写测试失败!\n  " );  

}

以上驱动程序来自ST官方驱动库。应用层测试demo来自硬石头。

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

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

相关文章

C语言程序设计题/C语言计算机二级考前押题版

C语言程序设计题/C语言计算机二级考试押题版 与 数位 和 数 有关 求max与min 任意四个数 运算符和表达式版本 #include <stdio.h> int main( ) {int a,b,c,d;int max,min;printf("please input 4 integers:");scanf("%d%d%d%d", &a, &b, …

ESP32通过 WiFi 传输视频

概述 这是ESP32 WiFi视频流的入门教程。ESP32-CAM 是一款带有ESP32-S 芯片的小型摄像头模块,除了OV2640 相机和多个用于连接外围设备的 GPIO 外,它还具有一个microSD 卡插槽,可用于存储相机拍摄的图像。 ESP32-CAM 简介 ESP32-CAM 是一款采用ESP32-S 芯片的超小型相机模组…

HP EliteBook 840 G6电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网&#xff0c;转载需注明出处。&#xff08;下载请直接百度黑果魏叔&#xff09; 硬件配置 硬件型号驱动情况 主板HP Elitebook 840 G6 处理器Intel(R) Core(TM) i7-8565U CPU 1.80GHz (max 4.60Ghz) Kaby Lake R已驱动 内存SK Hynix 32 GB (2x16) 2…

QT 设计ROS GUI界面订阅和发布话题

QT 设计ROS GUI界面订阅和发布话题 主要参考下面的博客 ROS项目开发实战&#xff08;三&#xff09;——使用QT进行ROS的GUI界面设计&#xff08;详细教程附代码&#xff01;&#xff01;&#xff01;&#xff09; Qt ROS 相关配置请看上一篇博客 首先建立工作空间和功能包&a…

深度学习训练营之船类识别

深度学习训练营之船类识别 原文链接环境介绍前言收获前置工作设置GPU导入图片数据预处理 数据可视化配置数据集数据显示 构建模型模型训练编译训练模型 结果可视化(模型评估)损失值可视化混淆矩阵各项指标评估 原文链接 &#x1f368; 本文为&#x1f517;365天深度学习训练营 …

腾讯云轻量应用服务器的端口怎么开通?

腾讯云轻量应用服务器怎么使用&#xff1f;端口在哪开启&#xff1f;在防火墙中可以开启端口号。 腾讯云轻量应用服务器端口怎么开通&#xff1f;在轻量服务器管理控制台的防火墙中开启端口&#xff0c;如果是CVM云服务器在安全组中开通&#xff0c;阿腾云以轻量应用服务器开通…

Python+Socket实现多人聊天室,功能:好友聊天、群聊、图片、表情、文件等

一、项目简介 本项目主要基于python实现的多人聊天室&#xff0c;主要的功能如下&#xff1a; 登录注册添加好友与好友进行私聊创建群聊邀请/申请加入群聊聊天发送图片聊天发送表情聊天发送文件聊天记录保存在本地中聊天过程中发送的文件保存本地 二、环境介绍 python3.8my…

浅谈 RISC-V 软件开发生态之 IDE

软件开发者是芯片公司非常重要的资产&#xff0c;CPU做出来是不够的&#xff0c;要让更多的软件开发者用这颗芯片才是成功。国际大厂们都有一只较大的软件团队&#xff0c;在做面向开发者的软件工具和SDK等。--张先轶博士&#xff1a;为什么RISC-V需要共建软件生态? 前言 目前…

VsCode终端无法使用conda切换环境的问题

一.问题描述: windows的cmd可以正常使用conda切换环境 为了方便想使用vscode的终端,但是报错: PS C:\Users\admin\Desktop\MyProject> conda activate objection1.8.4 CommandNotFoundError: Your shell has not been properly configured to use conda activate. invocati…

智能排班系统 【管理系统功能、操作说明——下篇】

文章目录 页面与功能展示排班日历月视图&#xff08;按职位查询&#xff09;月视图&#xff08;按员工查询&#xff09;周视图 排班任务管理创建排班计算任务设置任务的排班规则设置工作日客流量导入任务计算查看任务结果发布任务任务多算法计算 页面与功能展示 排班日历 在排…

vcruntime140.dll丢失的解决方法?教你如何修复好dll文件

Vcruntime140.dll文件是Windows操作系统中非常重要的一个动态链接库文件&#xff0c;用于支持使用Microsoft Visual C编译器创建的应用程序的运行。当Windows系统中的vcruntime140.dll文件丢失时&#xff0c;可能会导致某些应用程序无法正常启动。在尝试启动应用程序时&#xf…

Android HCE开发

我们来详细说明一下关于不同模式下的AID响应问题&#xff08;前提&#xff1a;一个手机&#xff0c;手机上有A、B两个HCE APP&#xff0c;通过读卡器向手机发送APDU选择指令&#xff09; 1、A和B的应用AID设置的都是payment模式&#xff0c; 只有手机当前选定的默认支付APP会响…

基于Faster rcnn pytorch的遥感图像检测

基于Faster rcnn pytorch的遥感图像检测 代码&#xff1a;https://github.com/jwyang/faster-rcnn.pytorch/tree/pytorch-1.0 数据集 使用RSOD遥感数据集&#xff0c;VOC的数据格式如下&#xff1a; RSOD是一个开放的目标检测数据集&#xff0c;用于遥感图像中的目标检测。…

图片类型转换,url,File,Base64,Blob

一&#xff0c;图片url转化为文件 function urlToFile(url, imageName) {return new Promise((resolve, reject) > {var blob nullvar xhr new XMLHttpRequest()xhr.open(GET, url)xhr.setRequestHeader(Accept, image/png)xhr.responseType blobxhr.onload () > {b…

JVM内存模型和结构详解

JVM内存模型和Java内存模型都是面试的热点问题&#xff0c;名字看感觉都差不多&#xff0c;实际上他们之间差别还是挺大的。 通俗点说&#xff0c;JVM内存结构是与JVM的内部存储结构相关&#xff0c;而Java内存模型是与多线程编程相关mikechen。 什么是JVM JVM是Java Virtual …

Redis缓存雪崩及解决办法

缓存雪崩 1.缓存雪崩是指在同- -时段大量的缓存key同时失效或者Redis服务宕机&#xff0c;导致大量请求到 达数据库&#xff0c;带来巨大压力。 2.解决方案: ◆给不同的Key的TTL添加随机值 ◆利用Redis集群提高服务的可用性 ◆给缓存业务添加降级限流策略 降级可做为系统的保底…

java程序1补充:从键盘输入圆的半径,求圆的周长和面积(简易与交互两版)

编写一个java程序&#xff0c;从键盘输入圆的半径&#xff0c;求圆的周长和面积&#xff0c;并输出。 要求&#xff1a; &#xff08;1&#xff09;半径仅考虑int型正整数&#xff0c;并综合利用所学较好地处理异常输入&#xff0c;包括非法整数、负整数输入时的处理。输入半…

网络编程套接字基本概念认识

目录 认识端口号 认识TCP协议 认识UDP协议 网络字节序 socket编程接口 socket 常见API sockaddr结构 认识端口号 端口号(port)是传输层协议的内容 端口号是一个2字节16位的整数; 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理; IP地址 …

数字电路仿真编译文件指定方式

目录 1 最基本的方式 2 指定多个文件 3. 使用filelist文件 4 指定整个目录中的所有指定后缀的文件 5 指定include文件的搜索路径 6 追加宏的定义 7 Verdi相关的选项 8 Vivado中的处理方式 1 最基本的方式 最基本的方式就是直接在命令行指定单个源文件&#xff0c;比如说…

VMware安装华为存储模拟器

实验需要的软件&#xff1a; VMware Workstation Pro ST00000000201812000*配套的license文件 Dorado5000 V3存储模拟器磁盘 存储模拟器磁盘和license文件资源&#xff0c;在本人主页发布的资源中去下载 安装步骤&#xff1a; 1. 点击 “文件->新建虚拟机”。 2. 在弹框…