为了更好的掌握安富莱V7教程的知识点,而且个人感觉使用Cube增加新外设更直观方便,每个实例我都会重新用STM32CubeMX重新做一遍。在这个过程中,之前看教程忽略的细节和知识点会以各种报错和硬件错误的方式重新浮现出来。在这里记录一下开发过程并附上源码,有需要的伙伴自取。
https://gitee.com/yangkangjia2718/armbbs-sd-usb-device-msc.git
1 使用STM32CubeMX 新建工程
1.1 初始化 SDMMC1
我使用的SD卡类型是MicroSD HC,容量16GB,速度U1,最大读写速度10M/s,这种SD卡支持的最大时钟频率一般在25MHz,这里要注意。
模式我们选择四线,时钟分频选择8分频。我配置的CPU速度是480M,给到SDMMC MUX的速度是240M,所以需要10分频来降低SD卡的时钟。
打开 SDMMC1 global interrupt ,SDMMC自带IDMA,在FileX和USBX中要用到DMA的传输完成中断。
还有一个SD卡的检测引脚我没有用,这也是我疑惑的一个地方。
我查到的资料是插卡后,这个引脚会被拉低,但是实际使用的时候是高电平。
然后ST提供的官方例程也比较奇怪,上电开机的时候检测SD卡是否存在用的是低电平,而在传输过程中用的是高电平。已经彻底懵了。
[C] 纯文本查看 复制代码 /* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SDMMC1_SD_Init();
/* USER CODE BEGIN 2 */
/* Check if SD card is present */
if(HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_5) == GPIO_PIN_RESET)
{
Error_Handler();
}
[C] 纯文本查看 复制代码 /* Check if the SD card is present */
if (HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_5) == GPIO_PIN_SET)
{
/* Check id SD card is ready */
if(check_sd_status() != HAL_OK)
{
Error_Handler();
}
/* Start the Dma write */
status = HAL_SD_ReadBlocks_DMA(&hsd1, data_pointer, lba, number_blocks);
if(status != HAL_OK)
{
Error_Handler();
}
/* Wait on readflag until SD card is ready to use for new operation */
if (tx_event_flags_get(&EventFlag, SD_READ_FLAG, TX_OR_CLEAR,
&ReadFlags, TX_WAIT_FOREVER) != TX_SUCCESS)
{
Error_Handler();
}
}
https://github.com/STMicroelectr ... /USBX/Ux_Device_MSC
配置MPU,打开Cache。在FileX中默认使用DMA传输数据,为了保持数据一致性有Cache相关的操作,如果不开启Cache,会进硬件错误中断。MPU配置策略如下:
注意:SDMMC1 能访问的RAM只有 AXI RAM(0x24000000, 512KB)
1.2 初始化 USB OTG FS
模式选择 Device Only,时钟直接选择 RC48,配置为48M,打开USB的全局中断。参数保持默认即可,不需要调整。
2 X-CUBE-AZRTOS-H7
这里多说一句,如果是第一次使用CubeMX添加ThreadX,需要在Software Packs -> Manage Software Packs中下载AZRTOS的软件支持包。
2.1 调整系统滴答计数器的时钟源
SysTick 要给操作系统使用,将裸机的时钟源改成TIMER1即可。
2.2 添加 ThreadX Core FileX USBX
2.3 AZRTOS Configuration
如下图,将新添加的四个组件全部打开,在AZRTOS Application中调整内存并根据需要创建任务和信号量等。
在ThreadX中调整每秒产生的时钟个数,RTOS的调度算法基于这个去管理任务。
FileX 和 USBX 中使用默认配置即可。
3 Project Manager
3.1 Project
增加堆栈的大小,我这里设置的偏大。
3.2 Adavanced Settings
我们不在main函数中初始化USB。把这个勾选上。
点击 GENERATE CODE。至此,恭喜你完成了工程的搭建!!!
现在你可以尝试使用FileX创建文件了!
[C] 纯文本查看 复制代码 /**
* @brief Main thread entry.
* @param thread_input: ULONG user argument used by the thread entry
* @retval none
*/
void fx_app_thread_entry(ULONG thread_input)
{
UINT sd_status = FX_SUCCESS;
/* USER CODE BEGIN fx_app_thread_entry 0 */
/* USER CODE END fx_app_thread_entry 0 */
/* Open the SD disk driver */
sd_status = fx_media_open(&sdio_disk, FX_SD_VOLUME_NAME, fx_stm32_sd_driver, (VOID *)FX_NULL, (VOID *) fx_sd_media_memory, sizeof(fx_sd_media_memory));
/* Check the media open sd_status */
if (sd_status != FX_SUCCESS)
{
/* USER CODE BEGIN SD open error */
while(1);
/* USER CODE END SD open error */
}
/* USER CODE BEGIN fx_app_thread_entry 1 */
/* 创建文件 */
sd_status = fx_file_create(&sdio_disk, "myFile008.txt");
/* 卸载SD卡 */
sd_status = fx_media_close(&sdio_disk);
/* USER CODE END fx_app_thread_entry 1 */
}
4 USBX MSC 用户代码
https://github.com/STMicroelectronics/x-cube-azrtos-h7/tree/main/Projects/STM32H735G-DK/Applications/USBX/Ux_Device_MSC 参考官方例程编辑用户代码。
4.1 USB硬件初始化以及USBX注册H7 USB控制接口 app_usbx_device.c
在Cube配置中取消的USB初始化放在了USBX的任务中,除此之外我们还设置了端点FIFO,以及注册了USBX的硬件控制接口。
[C] 纯文本查看 复制代码 /**
* @brief Function implementing app_ux_device_thread_entry.
* @param thread_input: User thread input parameter.
* @retval none
*/
static VOID app_ux_device_thread_entry(ULONG thread_input)
{
/* USER CODE BEGIN app_ux_device_thread_entry */
TX_PARAMETER_NOT_USED(thread_input);
USBX_APP_Device_Init();
/* USER CODE END app_ux_device_thread_entry */
}
/* USER CODE BEGIN 1 */
VOID USBX_APP_Device_Init(VOID)
{
/* USER CODE BEGIN USB_Device_Init_PreTreatment_0 */
/* USER CODE END USB_Device_Init_PreTreatment_0 */
/* USB_OTG_HS init function */
MX_USB_OTG_FS_PCD_Init();
/* USER CODE BEGIN USB_Device_Init_PreTreatment_1 */
/* Set Rx FIFO */
HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_FS, 0x200);
/* Set Tx FIFO 0 */
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 0, 0x40);
/* Set Tx FIFO 1 */
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x100);
/* USER CODE END USB_Device_Init_PreTreatment_1 */
/* Initialize and link controller HAL driver */
ux_dcd_stm32_initialize((ULONG)USB_OTG_HS, (ULONG)&hpcd_USB_OTG_FS);
/* Start USB device */
HAL_PCD_Start(&hpcd_USB_OTG_FS);
/* USER CODE BEGIN USB_Device_Init_PostTreatment */
/* USER CODE END USB_Device_Init_PostTreatment */
}
4.2 USB MSC 需要用户编写的接口
- USBD_STORAGE_Read
- USBD_STORAGE_Write
- USBD_STORAGE_GetMediaLastLba
- USBD_STORAGE_GetMediaBlocklength
其中③和④中的信息需要从SD卡中获取,在main函数初始化SDMMC后就可以通过以下函数获取了
[C] 纯文本查看 复制代码 MX_SDMMC1_SD_Init();
/* USER CODE BEGIN 2 */
/* Get SD card info */
status = HAL_SD_GetCardInfo(&hsd1, &USBD_SD_CardInfo);
if (status != HAL_OK)
{
Error_Handler();
}
以上函数的具体实现请参考官方示例代码或者我提供的工程源码,这里不再赘述。
注意:
- 在IDMA发送和接收完成的回调函数中要设置事件标志组。fx_stm32_sd_driver_glue.c
- 因为开了Cache所以要解决数据一致性问题,在MSC读写接口中要操作Cache保证数据的一致性
- FileX和USBX都开启,会有资源访问冲突的问题,我这边的临时解决方案是挂起FileX相关任务。后边可能会使用互斥信号量来解决。
以上就是所有内容了,在写这个贴子的过程,基本上重新梳理了SDMMC RAM Cache USBX等相关的知识点,收获还是蛮大的。
看硬汉教程,网上查询资料,然后用Cube重新实现,最后梳理知识点发帖,基本上完成了吸收 实践 理解 输出的闭环,感觉比单纯看教程实现功能收获更大一些。
请指正。
|