diff --git a/_posts/2024-02-18-PCB-manufacturing-checklist.md b/_posts/2024-02-18-PCB-manufacturing-checklist.md index 3247399..c9551b6 100644 --- a/_posts/2024-02-18-PCB-manufacturing-checklist.md +++ b/_posts/2024-02-18-PCB-manufacturing-checklist.md @@ -20,6 +20,8 @@ A list of things to check before putting a board into production. Update this li ![Remove order number options](/assets/img/2024-02-18-PCB-manufacturing-ch/order-number.png) - [ ] Make sure expensive items/option parts not meant for assembly are not placed down/autodetected by JLCPCB's parts catalog (you should use the variants system to ensure this cannot happen). - [ ] Check the orientation of each IC and diode in the PCBA list. For each item, make a note that it has been checked/corrected. +- [ ] Check if impedance control is needed. Select `JLC04161H-7628` for standard impedance control stackup.\ + ![Impedance stackup](/assets/img/2024-02-18-PCB-manufacturing-ch/stackup.png) ### Checkout diff --git a/_posts/2024-06-23-Debugging-STM32-USB-Mass-Storage-Class-Device.md b/_posts/2024-06-23-Debugging-STM32-USB-Mass-Storage-Class-Device.md index 7358842..1eb7940 100644 --- a/_posts/2024-06-23-Debugging-STM32-USB-Mass-Storage-Class-Device.md +++ b/_posts/2024-06-23-Debugging-STM32-USB-Mass-Storage-Class-Device.md @@ -47,6 +47,8 @@ Commenting out the `HAL_Delay` in the `SDMMC_PowerState_ON` function and the one Try changing the clock division to something larger like `4` or greater. Once it is stable try decreasing the divider to maximize speed. When I used lower values, the bus was unstable and I could not retrieve files or perform format operations. +> **EDIT: It's now working with 4-bit bus width and 0 clock div, not sure what I did to make it work but it works. I think it might be that I increased my `SYSCLK` since when I started the project it was at the default `16 MHz` since it was using the HSI instead of the HSE.** + ```c hmmc1.Init.ClockDiv = 4; ``` @@ -61,7 +63,11 @@ The format fails with: I guess my board doesn't work in 4-bit mode so I just compromised with 1-bit mode and it worked again. -## 4. DMA not working on STM32L476RET6 STM32L476 +> **EDIT: It's now working with 4-bit bus width and 0 clock div, not sure what I did to make it work but it works. I think it might be that I increased my `SYSCLK` since when I started the project it was at the default `16 MHz` since it was using the HSI instead of the HSE.** + +## 4. DMA/Multiple block DMA not working on STM32L476RET6 STM32L476 + +Enable "SDMMC hardware flow control" under the SDMMC peripheral. Make sure to switch the DMA direction each for RX and TX @@ -69,25 +75,73 @@ Since we are doing bi-directional transfer just use the SDMMC1 DMA request type. ![dma config for sdmmc1](/assets/img/2024-06-23-Debugging-STM32-USB-/dma_settings.png) -Also make sure the preemption priority of the USB is higher than the DMA and SDMMC peripherals and to enable the SDMMC global interrupt. +The order of preemption priority is crucial, otherwise the transfer will hang. For example `HAL_MMC_GetCardState()`, will be stuck in the `HAL_MMC_CARD_SENDING` state since the interrupt priority is wrong. In my case I am using: | NVIC interrupt table | Preemption Priority | Sub Priority | | -------------------------------------------------------- | ------------------- | ------------ | -| SDMMC1 global interrupt | 0 | 0 | -| DMA2 channel4 global interrupt (Use your DMA controller) | 1 | 0 | -| USB OTG FS global interrupt | 3 | 0 | +| DMA2 channel4 global interrupt (Use your DMA controller) | 0 | 0 | +| SDMMC1 global interrupt | 1 | 0 | +| USB OTG FS global interrupt | 2 | 0 | + +You may use any priority value but the priorities should in the same order. ![nvic priority table](/assets/img/2024-06-23-Debugging-STM32-USB-/nvic_settings.png) -## Performance with DMA +## Performance with DMA but 512 packet size Kind of terrible but I am using 1-bit bus width since 4-bit isn't working for me. +I am using the Toshiba `THGBMJG6C1LBAIL` 8G eMMC module since I thought it was cheap on JLCPCB (but see conclusion for my thoughts on eMMC). + ![crystaldiskmark performance](/assets/img/2024-06-23-Debugging-STM32-USB-/crystaldiskmark.png) -## Sample code for eMMC and USB mass storage class device +> **EDIT: It's now working with 4-bit bus width and 0 clock div, not sure what I did to make it work but it works. I think it might be that I increased my `SYSCLK` since when I started the project it was at the default `16 MHz` since it was using the HSI instead of the HSE.** +> +> This caused a sizeable performance increase! However it's still quite terrible, since the eMMC module I used claims read/write speeds of 45/35 MB/s at 52 MHz in SDR mode at 3.3V. Obviously having a HAL puts overhead and this figure is probably dependant on the block size. +> +> ![crystaldiskmark performance 4bit](/assets/img/2024-06-23-Debugging-STM32-USB-/crystaldiskmark_fast.png) + +## 5. MMC performance is very bad, slow write speeds + +One last thing to change is the packet size. Larger packet sizes will perform better (at the cost of more memory). + +Change the packet size under the `USB_DEVICE` middleware parameters: + +![configure media packet size](/assets/img/2024-06-23-Debugging-STM32-USB-/packet_size_cubemx.png) + +Alternatively, in `usbd_conf.h`, change: + +```c +#define MSC_MEDIA_PACKET 512U +``` + +to some **multiple of 512** + +For example: + +```c +#define MSC_MEDIA_PACKET 32678U // MUST BE A MULTIPLE OF 512 +``` + +You may need to modify your minimum stack/heap size to accomodate for the larger packet size. + +The maximum is `32678` bytes + +The read/write speeds improve on the previous tests, however it is still no where near the limit of the eMMC module. Currently I have not tried anything else to improve the speed, if there is any way then let me know. + +![crystaldiskmark benchmark with largest media packet size](/assets/img/2024-06-23-Debugging-STM32-USB-/crystaldiskmark_fast_large_buf.png) + +## Conclusion + +Overall, eMMC is mechanically robust, however from a cost perspective I wouldn't choose this in a future project unless necesary. While it appears to be cheaper than SD cards and holders, the tight tolerances require JLCPCB's 4-wire kelvin testing for the smaller vias (Unless you use 6-layer which has the pad in via for free), and requires X-ray inspection which is another fee. I wouldn't attempt soldering them manually since I've never dealt with BGA so this is a fee I had to accept. + +In my next project I will try out those embedded SD card modules, they come in DFN-8 packages and should be much cheaper than eMMC while offerring high capacity than NOR flash, but they are less widely known and I would not use them in a project which is going into production, only a hobby project. + +I will update this post with more cases as I run into them. Comment on your experiences debugging USB on STM32 and I'll quote them in the article. + +## Sample code for eMMC and USB mass storage class device (`#define BLOCKING` for simple, non-DMA implementation) ### `main.c` @@ -105,14 +159,16 @@ static void MX_SDMMC1_MMC_Init(void) hmmc1.Instance = SDMMC1; hmmc1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING; hmmc1.Init.ClockBypass = SDMMC_CLOCK_BYPASS_DISABLE; - hmmc1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE; - hmmc1.Init.BusWide = SDMMC_BUS_WIDE_1B; - hmmc1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE; - hmmc1.Init.ClockDiv = 2; // MODIFY THIS AS APPROPRIATE + hmmc1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_ENABLE; + hmmc1.Init.BusWide = SDMMC_BUS_WIDE_4B; // MODIFY THIS AS APPRORIATE - CHANGE TO 1 IF STUFF ISN'T WORKING + hmmc1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_ENABLE; // ENABLE HARDWARE FLOW CONTROL + hmmc1.Init.ClockDiv = 0; // MODIFY THIS AS APPROPRIATE - USE A HIGHER VALUE IF STUFF ISN'T WORKING if (HAL_MMC_Init(&hmmc1) != HAL_OK) { Error_Handler(); } + + // ⚠ NOTE: DO NOT FORGET TO MODIFY THIS AS WELL!!! if (HAL_MMC_ConfigWideBusOperation(&hmmc1, SDMMC_BUS_WIDE_1B) != HAL_OK) { Error_Handler(); @@ -124,85 +180,17 @@ static void MX_SDMMC1_MMC_Init(void) ### `usbd_storage_if.c` -```c -int8_t STORAGE_Init_FS(uint8_t lun) -{ - /* USER CODE BEGIN 2 */ - // ALREADY INITIALIZED IN `MX_SDMMC1_MMC_Init` FUNCTION. - return USBD_OK; - /* USER CODE END 2 */ -} - -int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size) -{ - /* USER CODE BEGIN 3 */ - HAL_MMC_CardInfoTypeDef card_info; - HAL_StatusTypeDef status = HAL_MMC_GetCardInfo(&hmmc1, &card_info); - *block_num = card_info.LogBlockNbr - 1; - *block_size = card_info.LogBlockSize; - return status; - /* USER CODE END 3 */ -} - -int8_t STORAGE_IsReady_FS(uint8_t lun) -{ - /* USER CODE BEGIN 4 */ - // eMMC IS ALWAYS CONNECTED TO THE BOARD AND IS ALWAYS READY SINCE IT IS - // INITIALIZED AT THE START. - return USBD_OK; - /* USER CODE END 4 */ -} - -int8_t STORAGE_IsWriteProtected_FS(uint8_t lun) -{ - /* USER CODE BEGIN 5 */ - // ASSUME eMMC IS NEVER WRITE PROTECTED ON THIS PARTICULAR BOARD - // WRITE PROTECT FEATURE IS NOT USED. - return USBD_OK; - /* USER CODE END 5 */ -} - -int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) -{ - /* USER CODE BEGIN 6 */ - // TODO: USE DMA TRANSFER LATER - int8_t status = HAL_MMC_ReadBlocks(&hmmc1, buf, blk_addr, (uint32_t)blk_len, TIMEOUT); - while (HAL_MMC_GetCardState(&hmmc1) != HAL_MMC_CARD_TRANSFER) - ; - return status; - - /* USER CODE END 6 */ -} - -int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) -{ - /* USER CODE BEGIN 7 */ - // TODO: USE DMA TRANSFER LATER - int8_t status = HAL_MMC_WriteBlocks(&hmmc1, buf, blk_addr, (uint32_t)blk_len, TIMEOUT); - while (HAL_MMC_GetCardState(&hmmc1) != HAL_MMC_CARD_TRANSFER) - ; - return status; - /* USER CODE END 7 */ -} - -``` - -## Sample code DMA version - -### `usbd_storage_if.c` - -```c -volatile uint8_t mmc_rx_done = 1; -volatile uint8_t mmc_tx_done = 1; +````c +volatile uint8_t mmc_transaction_blks_left = 0; void HAL_MMC_RxCpltCallback(MMC_HandleTypeDef *hmmc) { - mmc_rx_done = 1; + mmc_transaction_blks_left = 0; } void HAL_MMC_TxCpltCallback(MMC_HandleTypeDef *hmmc) { - mmc_tx_done = 1; + mmc_transaction_blks_left = 0; } // CHANGE DMA DIRECTION @@ -215,15 +203,14 @@ HAL_StatusTypeDef MMC_DMA_direction(uint32_t direction) return HAL_DMA_Init(&hdma_sdmmc1); } -// +// MORE AUTOGENERATED CODE... // [...] -// /** - * @brief Initializes over USB FS IP - * @param lun: - * @retval USBD_OK if all operations are OK else USBD_FAIL - */ + * @brief Initializes over USB FS IP + * @param lun: + * @retval USBD_OK if all operations are OK else USBD_FAIL + */ int8_t STORAGE_Init_FS(uint8_t lun) { /* USER CODE BEGIN 2 */ @@ -233,12 +220,12 @@ int8_t STORAGE_Init_FS(uint8_t lun) } /** - * @brief . - * @param lun: . - * @param block_num: . - * @param block_size: . - * @retval USBD_OK if all operations are OK else USBD_FAIL - */ + * @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 */ @@ -251,10 +238,10 @@ int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_ } /** - * @brief . - * @param lun: . - * @retval USBD_OK if all operations are OK else USBD_FAIL - */ + * @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 */ @@ -265,10 +252,10 @@ int8_t STORAGE_IsReady_FS(uint8_t lun) } /** - * @brief . - * @param lun: . - * @retval USBD_OK if all operations are OK else USBD_FAIL - */ + * @brief . + * @param lun: . + * @retval USBD_OK if all operations are OK else USBD_FAIL + */ int8_t STORAGE_IsWriteProtected_FS(uint8_t lun) { /* USER CODE BEGIN 5 */ @@ -279,20 +266,35 @@ int8_t STORAGE_IsWriteProtected_FS(uint8_t lun) } /** - * @brief . - * @param lun: . - * @retval USBD_OK if all operations are OK else USBD_FAIL - */ + * @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 */ - mmc_rx_done = 0; + hmmc1.ErrorCode = HAL_MMC_ERROR_NONE; + while (HAL_MMC_GetCardState(&hmmc1) != HAL_MMC_CARD_TRANSFER) + ; // FIXME: IMPLEMENT TIMEOUT FAILSAFE +#ifdef BLOCKING + if (HAL_MMC_ReadBlocks(&hmmc1, buf, blk_addr, blk_len, TIMEOUT) != HAL_OK) + return USBD_FAIL; +#else + mmc_transaction_blks_left = 1; if (MMC_DMA_direction(DMA_PERIPH_TO_MEMORY) != HAL_OK) + { + mmc_transaction_blks_left = 0; return USBD_FAIL; + } if (HAL_MMC_ReadBlocks_DMA(&hmmc1, buf, blk_addr, blk_len) != HAL_OK) + { + mmc_transaction_blks_left = 0; return USBD_FAIL; - while (!mmc_rx_done) + } + while (mmc_transaction_blks_left) ; +#endif + hmmc1.ErrorCode = HAL_MMC_ERROR_NONE; while (HAL_MMC_GetCardState(&hmmc1) != HAL_MMC_CARD_TRANSFER) ; // FIXME: IMPLEMENT TIMEOUT FAILSAFE return USBD_OK; @@ -300,20 +302,35 @@ int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t bl } /** - * @brief . - * @param lun: . - * @retval USBD_OK if all operations are OK else USBD_FAIL - */ + * @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 */ - mmc_tx_done = 0; + hmmc1.ErrorCode = HAL_MMC_ERROR_NONE; + while (HAL_MMC_GetCardState(&hmmc1) != HAL_MMC_CARD_TRANSFER) + ; // FIXME: IMPLEMENT TIMEOUT FAILSAFE +#ifdef BLOCKING + if (HAL_MMC_WriteBlocks(&hmmc1, buf, blk_addr, blk_len, TIMEOUT) != HAL_OK) + return USBD_FAIL; +#else + mmc_transaction_blks_left = 1; if (MMC_DMA_direction(DMA_MEMORY_TO_PERIPH) != HAL_OK) + { + mmc_transaction_blks_left = 0; return USBD_FAIL; + } if (HAL_MMC_WriteBlocks_DMA(&hmmc1, buf, blk_addr, blk_len) != HAL_OK) + { + mmc_transaction_blks_left = 0; return USBD_FAIL; - while (!mmc_tx_done) + } + while (mmc_transaction_blks_left) ; +#endif + hmmc1.ErrorCode = HAL_MMC_ERROR_NONE; while (HAL_MMC_GetCardState(&hmmc1) != HAL_MMC_CARD_TRANSFER) ; // FIXME: IMPLEMENT TIMEOUT FAILSAFE return USBD_OK; @@ -321,18 +338,14 @@ int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t b } /** - * @brief . - * @param None - * @retval . - */ + * @brief . + * @param None + * @retval . + */ int8_t STORAGE_GetMaxLun_FS(void) { /* USER CODE BEGIN 8 */ return STORAGE_LUN_NBR - 1; /* USER CODE END 8 */ -} -``` - -## Conclusion - -I will update this post with more cases as I run into them. Comment on your experiences debugging USB on STM32 and I'll quote them in the article. +}``` +```` diff --git a/assets/img/2024-02-18-PCB-manufacturing-ch/stackup.png b/assets/img/2024-02-18-PCB-manufacturing-ch/stackup.png new file mode 100644 index 0000000..0c69997 Binary files /dev/null and b/assets/img/2024-02-18-PCB-manufacturing-ch/stackup.png differ diff --git a/assets/img/2024-06-23-Debugging-STM32-USB-/crystaldiskmark_fast.png b/assets/img/2024-06-23-Debugging-STM32-USB-/crystaldiskmark_fast.png new file mode 100644 index 0000000..51bf8bb Binary files /dev/null and b/assets/img/2024-06-23-Debugging-STM32-USB-/crystaldiskmark_fast.png differ diff --git a/assets/img/2024-06-23-Debugging-STM32-USB-/crystaldiskmark_fast_large_buf.png b/assets/img/2024-06-23-Debugging-STM32-USB-/crystaldiskmark_fast_large_buf.png new file mode 100644 index 0000000..19d21ca Binary files /dev/null and b/assets/img/2024-06-23-Debugging-STM32-USB-/crystaldiskmark_fast_large_buf.png differ diff --git a/assets/img/2024-06-23-Debugging-STM32-USB-/nvic_settings.png b/assets/img/2024-06-23-Debugging-STM32-USB-/nvic_settings.png index f964d7f..193cb0f 100644 Binary files a/assets/img/2024-06-23-Debugging-STM32-USB-/nvic_settings.png and b/assets/img/2024-06-23-Debugging-STM32-USB-/nvic_settings.png differ diff --git a/assets/img/2024-06-23-Debugging-STM32-USB-/packet_size_cubemx.png b/assets/img/2024-06-23-Debugging-STM32-USB-/packet_size_cubemx.png new file mode 100644 index 0000000..f88c7fa Binary files /dev/null and b/assets/img/2024-06-23-Debugging-STM32-USB-/packet_size_cubemx.png differ