imuncle / imuncle.github.io

大叔的个人小站
https://imuncle.github.io/
78 stars 17 forks source link

嵌入式视觉OpenOV研发(二) 读卡器功能 #82

Open imuncle opened 4 years ago

imuncle commented 4 years ago

先把读卡器功能实现,相对于真正核心的嵌入式视觉代码来说,这种如同外设一般的存在就应该尽早被攻克掉。

SDIO协议及SD卡简介

这里推荐看正点原子的教程,讲的很详细。这里真心给正点原子点个赞!暑假的时候突然发现正点原子入驻B站了,可以直接在线观看高清的视频教程了!去他妈的限速百度云,保存到网盘在线看也没法高清,还时不时卡顿,入驻B站这一操作简直感动到哭!

SD卡驱动

不得不说,STM32CubeMX帮我们省去了很多很多外设的操作步骤。按照正点原子的教程,我们需要首先把SD卡的时钟降下来,发送指令初始化SD卡,初始化成功之后再把时钟升上去,每次发送命令操作寄存器,处理SD卡长短响应之类的,完全不用我们去管,只管调用函数就行。

STM32CubeMX里面的配置也非常简单,直接使能,一步到位: image

之后读写SD卡什么的直接调用函数就行了。

USB接口模拟大容量存储设备

USB接口真的有好多用处啊,基本上是可以模拟出所有USB接口的设备了,之前有写过一篇USB实现虚拟串口CDC的文章,这次实现模拟大容量存储设备,前几天还模拟了一下HID人体学输入设备,后面把HID的文章补上。说一句,HID比CDC好用多了!

限于篇幅,这里直接贴上cubemx的配置过程,在后面HID的文章中再稍微讲讲USB是怎么让电脑识别为大容量存储设备的。

image

首先使能USB_OTG_FS,这里OTG是On-The-Go的缩写,实现了在没有Host的情况下,设备间的数据传送。FS代表全速(Full Speed),另外还要一个选项是HS(High Speed),速度比全速更快,但是需要外部高速芯片的支持,比如加一个高速PHY,这里选FS就行。

image

然后配置为Mass Storage Class,即MSC,大容量存储设备。上图中我其实还使能了FATS,但那是方便我通过单片机操作文件,这里模拟U盘用不到。

Device Descriptor中可以设置自己的设备名称,但影响不大,因为Windows文件管理器里面分配的盘符和名称跟这个没啥关系。

最后要把堆栈空间给大一点: image

完成这些之后直接生成代码,编译,烧写,然后把开发板USB插电脑上,就可以直接识别为U盘了,不过因为还没跟SD卡对接起来,所以电脑会提示是否格式化,而且打不开。

USB与SD卡对接

这一步也是非常easy!打开usbd_storage_if.c,修改以下几个函数就行:

/**
  * @brief  .
  * @param  lun: .
  * @param  block_num: .
  * @param  block_size: .
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
  /* USER CODE BEGIN 3 */
  HAL_SD_CardInfoTypeDef SDCardInfo;
    HAL_SD_GetCardInfo(&hsd, &SDCardInfo);
    *block_num = SDCardInfo.BlockNbr;
  *block_size = SDCardInfo.BlockSize;
  return (USBD_OK);
  /* USER CODE END 3 */
}
/**
  * @brief  .
  * @param  lun: .
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_IsReady_FS(uint8_t lun)
{
  /* USER CODE BEGIN 4 */
  if(HAL_SD_GetState(&hsd) == HAL_SD_STATE_READY)
    {
        return (USBD_OK);
    }

    else
    {
        return (USBD_FAIL);
    }
  /* USER CODE END 4 */
}
/**
  * @brief  .
  * @param  lun: .
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
  /* USER CODE BEGIN 6 */

  if(HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_TRANSFER)
    {
        if(HAL_SD_ReadBlocks(&hsd, buf, blk_addr, blk_len, SDMMC_DATATIMEOUT) == HAL_OK)
        {
            return (USBD_OK);
        }
        else
        {
            return (USBD_FAIL);
        }
    }
    else
        return (USBD_BUSY);
  /* USER CODE END 6 */
}
/**
  * @brief  .
  * @param  lun: .
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
  /* USER CODE BEGIN 7 */
  if(HAL_SD_GetCardState(&hsd) == HAL_SD_CARD_TRANSFER)
    {
        if(HAL_SD_WriteBlocks(&hsd, buf, blk_addr, blk_len, SDMMC_DATATIMEOUT) == HAL_OK)
        {
            return (USBD_OK);
        }
        else
        {
            return (USBD_FAIL);
        }
    }
    else
        return (USBD_BUSY);
  /* USER CODE END 7 */
}

这里要注意,读写SD卡的函数不要用DMA方式或者IT方式,一定要用最普通的阻塞方式,不然U盘内容会加载失败。

效果

最后,我的“读卡器”成功了! image

不过我试了一下U盘的写速度,很稳定的355KB/s,速度慢到出乎我的意料,可能是没有外部高速芯片支持的缘故吧,而且耗电量贼大。

QQ截图20191015104927

我的电路板上用的5V转3.3V的芯片是LP5907,最大电流为500mA,但我频繁读写SD卡后这个芯片就发烫了,而且F4芯片也在发烫,差TF卡资料发现原来TF卡读写真的很费电: image

居然可以达到500mA!我以为我这系统上耗电大户是摄像头和LCD,没想到竟然是SD卡,失策失策!

参考

LUCKandII commented 4 years ago

👍