当前位置:首页 > 公众号精选 > 嵌入式云IOT技术圈
[导读]本程序编写基于秉火霸道STM32F103ZET6运行环境。跑这个实验之前吃了一些亏,让我一一道来! 1、软件写好了,没发现插入USB线连接到电脑后USB居然没有枚举 解决方法: 野火的这款开发板上做了一个USB上电使能IO,也就是说,当PD3为低电平时,USB才能正常工作,


本程序编写基于秉火霸道STM32F103ZET6运行环境。跑这个实验之前吃了一些亏,让我一一道来!

1、软件写好了,没发现插入USB线连接到电脑后USB居然没有枚举

解决方法:

野火的这款开发板上做了一个USB上电使能IO,也就是说,当PD3为低电平时,USB才能正常工作,如果不去使能这个管脚的话,USB自然就不工作了。

2、HAL库读写SD卡API版本问题

解决方法:

我用的是1.8.0的HAL库,这个库和老版本的HAL库在API上有重大的变更,接口的参数也不一样,含义也有区别。

老版本HAL库读写SD卡的接口

我们来看下HAL库旧版本的读SD卡接口

/**
* @brief Reads block(s) from a specified address in a card. The Data transfer
* is managed by polling mode.
* @param hsd: SD handle
* @param pReadBuffer: pointer to the buffer that will contain the received data
* @param ReadAddr: Address from where data is to be read
* @param BlockSize: SD card Data block size (in bytes)
* This parameter should be 512
* @param NumberOfBlocks: Number of SD blocks to read
* @retval SD Card error state
*/
HAL_SD_ErrorTypedef HAL_SD_ReadBlocks(SD_HandleTypeDef *hsd, uint32_t *pReadBuffer, uint64_t ReadAddr, uint32_t BlockSize, uint32_t NumberOfBlocks)

我们看到,这里的BlockSize是以字节为单位进行读的,最小应当是512,因为SD卡一个块的大小是512,所以,NumberOfBlocks表示块数,读一块,那么就是BlockSize512,读N块,那么就是BlockSize512*N。

再来看看老版本HAL库的写SD卡接口

/**
* @brief Allows to write block(s) to a specified address in a card. The Data
* transfer is managed by polling mode.
* @param hsd: SD handle
* @param pWriteBuffer: pointer to the buffer that will contain the data to transmit
* @param WriteAddr: Address from where data is to be written
* @param BlockSize: SD card Data block size (in bytes)
* This parameter should be 512.
* @param NumberOfBlocks: Number of SD blocks to write
* @retval SD Card error state
*/
HAL_SD_ErrorTypedef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint32_t *pWriteBuffer, uint64_t WriteAddr, uint32_t BlockSize, uint32_t NumberOfBlocks)

写也是一样的,这里的BlockSize是以字节为单位进行读的,最小应当是512,因为SD卡一个块的大小是512,所以,NumberOfBlocks表示块数,写一块,那么就是BlockSize512,写N块,那么就是BlockSize512*N。

新版本HAL库读写SD卡的接口

然而在最新的HAL库上,是不用乘以512的,我们来看一下1.8.0版本HAL库关于这两个函数的描述:

/**
* @brief Reads block(s) from a specified address in a card. The Data transfer
* is managed by polling mode.
* @note This API should be followed by a check on the card state through
* HAL_SD_GetCardState().
* @param hsd: Pointer to SD handle
* @param pData: pointer to the buffer that will contain the received data
* @param BlockAdd: Block Address from where data is to be read
* @param NumberOfBlocks: Number of SD blocks to read
* @param Timeout: Specify timeout value
* @retval HAL status
*/
HAL_StatusTypeDef HAL_SD_ReadBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)

我们看到,这里传入的BlockAdd,也就是块的起始地址,NumberOfBlocks表示的是多少块,所以本来就是以块为单位进行读的,所以也就不用去乘512。

/**
* @brief Allows to write block(s) to a specified address in a card. The Data
* transfer is managed by polling mode.
* @note This API should be followed by a check on the card state through
* HAL_SD_GetCardState().
* @param hsd: Pointer to SD handle
* @param pData: pointer to the buffer that will contain the data to transmit
* @param BlockAdd: Block Address where data will be written
* @param NumberOfBlocks: Number of SD blocks to write
* @param Timeout: Specify timeout value
* @retval HAL status
*/
HAL_StatusTypeDef HAL_SD_WriteBlocks(SD_HandleTypeDef *hsd, uint8_t *pData, uint32_t BlockAdd, uint32_t NumberOfBlocks, uint32_t Timeout)

写SD卡的接口也是一样的,这里传入的BlockAdd,也就是块的起始地址,NumberOfBlocks表示的是多少块,所以本来就是以块为单位进行写的,所以也就不用去乘512。

所以,在实现USB大容量存储设备接口的时候,我应该这么来实现:

/**
* @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_OK != HAL_SD_ReadBlocks(&hsd,(uint8_t *)buf, blk_addr , blk_len, 1000))
return USBD_FAIL ;
return (USBD_OK);
/* 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_OK != HAL_SD_WriteBlocks(&hsd, (uint8_t *)buf, blk_addr , blk_len, 1000))
return USBD_FAIL ;
return (USBD_OK);
/* USER CODE END 7 */
}

好,这两个问题解决了,一下来看看这么做的。

一、USB使能管脚配置

PD3管脚,默认输出电平为低电平,也就是D+管脚为使能状态,USB供上了电。

PB1是我拿来做调试的灯。

二、RCC时钟

这里要切记一点,因为这里我们用到了USB,USB的时钟要配置为48MHz,具体看手册。而SDIO是时钟是HCLK的二分频。

三、调试接口

这里选择串行调试。

四、SDIO配置

这里,我们配置模式为4位宽总线的SD卡模式,时钟分频因子之前在步骤二中我们已经知道了,SDIO的时钟频率是HCLK的二分频,所以SDIOCLK clock divide factor这个选项我们设置为2。


开启SDIO全局中断。

五、USB配置


这里将USB设备配置为大容量存储,其余默认即可。

六、生成并添加代码逻辑


这里我们把栈的大小稍微调大一点,以便我们后期在代码里进行测试。

在usbd_storage_if.c中实现如下接口:

STORAGE_GetCapacity_FS      获取U盘容量信息
STORAGE_IsReady_FS 获取U盘状态
STORAGE_Read_FS 读U盘
STORAGE_Write_FS 写U盘

通过接口获取有多少块以及块的大小。

/**
* @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 */
*block_num = hsd.SdCard.BlockNbr ;
*block_size = hsd.SdCard.BlockSize ;
return (USBD_OK);
/* USER CODE END 3 */
}

判断SD卡的状态是否已经准备好了,状态的描述如下:

typedef enum
{
HAL_SD_STATE_RESET = ((uint32_t)0x00000000U), /*!< SD not yet initialized or disabled */
HAL_SD_STATE_READY = ((uint32_t)0x00000001U), /*!< SD initialized and ready for use */
HAL_SD_STATE_TIMEOUT = ((uint32_t)0x00000002U), /*!< SD Timeout state */
HAL_SD_STATE_BUSY = ((uint32_t)0x00000003U), /*!< SD process ongoing */
HAL_SD_STATE_PROGRAMMING = ((uint32_t)0x00000004U), /*!< SD Programming State */
HAL_SD_STATE_RECEIVING = ((uint32_t)0x00000005U), /*!< SD Receiving State */
HAL_SD_STATE_TRANSFER = ((uint32_t)0x00000006U), /*!< SD Transfert State */
HAL_SD_STATE_ERROR = ((uint32_t)0x0000000FU) /*!< SD is in error state */
}HAL_SD_StateTypeDef;

/**
* @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 */
uint8_t state = 0;
state = HAL_SD_GetState(&hsd) ;
if(HAL_SD_STATE_READY != state)
return USBD_FAIL ;
return (USBD_OK);
/* USER CODE END 4 */
}

实现USB读写SD卡

/**
* @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_OK != HAL_SD_ReadBlocks(&hsd,(uint8_t *)buf, blk_addr , blk_len, 1000))
return USBD_FAIL ;
return (USBD_OK);
/* 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_OK != HAL_SD_WriteBlocks(&hsd, (uint8_t *)buf, blk_addr , blk_len, 1000))
return USBD_FAIL ;
return (USBD_OK);
/* USER CODE END 7 */
}

在主函数的while循环里添加调试闪烁灯,标记当前系统是处于一个正常的运行状态。

/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */


/* MCU Configuration--------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */
SystemClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SDIO_SD_Init();
MX_USART2_UART_Init();
MX_USB_DEVICE_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
//LED调试灯以500ms的频率进行翻转
HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
HAL_Delay(1000);
}

/* USER CODE END 3 */
}

七、运行结果

将USB线插入USB DEVICE的接口,并连接到PC端的USB口,在PC端弹出可移动磁盘,实验成功。

后期我们可以再移植一个fatfs文件系统,使之支持文件操作。



我用的是4GB的内存卡,PC端显示3.68GB,为什么呢?

度娘一下:https://zhidao.baidu.com/question/74222131.html

你电脑的算法是1024MB=1GB U盘厂家的算法是1000MB=1GB

还要加上法律允许的产品误差,一般厂家会取最小值,不会多给你空间的。

https://zhidao.baidu.com/question/559657182.html

硬件厂商为了计算方便采用的是十进制,也就是满1000字节算1K,满1000K算1M,以此类推。

软件设计上由于计算机采用的是二进制所以是满1024字节即2的10次方算1K,1024K算1M;所以你的卡越大差距就会越大,这点在电脑硬盘上感觉会更明显一些。

往期精彩

嵌入式系统软件架构设计(长篇深度好文)

专为MCU项目开发提速的代码框架BabyOS

嵌入式C语言代码优化方案(深度好文,建议花时间研读并收藏)

分享一个在Keil开发环境中配置代码格式化工具Astyle(美化代码风格)

stm32cubeMX学习、USB DFU(Download Firmware Update)固件更新

若觉得本次分享的文章对您有帮助,随手点[在看]并转发分享,也是对我的支持。

免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

近日,海关发布通报一女子手推电单车内藏上万张存储卡,在经沙头角边境特别管理区旅检大厅入境时被查获。

关键字: SD卡

台湾台北, July 14, 2022 (GLOBE NEWSWIRE) -- 特种存储和内存解决方案领域的全球领先企业ATP Electronics推出新的3D三层单元(TLC)S750/S650系列SD和microS...

关键字: DVR MICROSD SD卡 行车记录仪

摘要:在电力终端出现故障以及需要现场升级时,需要记录终端运行日志,根据终端保存日志,定位终端问题。针对现场故障出现较多以及需要升级的终端较多的情况,提出了一种基于读取U盘信息进行进制转换,打开超级终端串口的机制,对于运维...

关键字: 电力终端 U盘 超级终端

目前移动存储的方案主要有、移动硬盘、硬盘盒三种常见的解决方案。U盘因为体积和颗粒限制往往存在速度慢、容量小的问题。如果需要高速且大容量的U盘,价格往往不尽如人意。那么移动硬盘和硬盘盒显然是更好的选择。那么这两者应该选择哪...

关键字: 硬盘盒 移动硬盘 U盘

“优盘”是闪存走进日常生活的最明显写照,其实早在U盘之前,闪存已经出现在许多电子产品之中。传统的存储数据方式是采用RAM的易失存储,电池没电了数据就会丢失。采用闪存的产品,克服了这一毛病,使得数据存储更为可靠。除了闪存盘...

关键字: U盘 BIOS 数码相机

快闪存储器(英语:flash memory),是一种电子式可清除程序化只读存储器的形式,允许在操作中被多次擦或写的存储器。这种科技主要用于一般性数据存储,以及在计算机与其他数字产品间交换传输数据,如储存卡与U盘。闪存是一...

关键字: 快闪存储器 储存卡 U盘

软盘(Floppy Disk)是个人计算机(PC)中最早使用的可移介质。软盘的读写是通过软盘驱动器完成的。软盘驱动器设计能接收可移动式软盘,常用的就是容量为1.44MB的3.5英寸软盘,它曾经盛极一时。之后由于U盘的出现...

关键字: 软盘 PC U盘

PX 系列工业级存储卡支持 SMART 命令,并具有高性能、高可靠性

关键字: 存储卡 ARM MICROSD SD卡

作者:杨小伟,来源:数码之家坏的手机越来越多,想利用下把字库拆下做几个U盘,因为是第一次焊接BGA这玩意,工具有限,败了些工具回来玩,网上找资料做了些功课!找了几部报废的手机拆出主板拆出emmc字库,容量有海力士4G,镁...

关键字: U盘

摘要:以铁路道口应用为例,设计了一种基于SD卡的信息数据存储警示系统。该系统可在需要警示的情况下,通过人工操作或自动触发发出相应的警示信息,包括LED显示屏显示文字警示信息、语音系统播放警示语音、警灯闪烁进行灯光警示。文...

关键字: SD卡 文字 声光 警示系统
关闭
关闭