Compare commits

...

5 Commits

10 changed files with 180 additions and 127 deletions

View File

@ -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

View File

@ -9,6 +9,10 @@ tags: [electronics, emmc, embedded] # systems | embedded | rf | microwave | elec
Summary of my thought process when troubleshooting a board containing eMMC and USB mass storage class device on the STM32L476.
## If you are using the STM32L476, check out my repository which has a working example: https://github.com/peter-tanner/eMMC-USB-mass-storage-device-STM32L476
## Prerequisites
Download Wireshark and the USB pcap plugin.
TLDR: Minimum configuration you should try for troubleshooting is:
@ -47,6 +51,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 +67,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 +79,77 @@ 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.
This benchmark is with the largest media packet size (32768), 4 bit wide bus, 0 clock div and DMA.
![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)
**Adapted from https://github.com/peter-tanner/eMMC-USB-mass-storage-device-STM32L476**
### `main.c`
@ -105,14 +167,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 +188,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 +211,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 +228,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 +246,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 +260,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 +274,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 +310,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 +346,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.
}```
````

View File

@ -0,0 +1,30 @@
---
title: Adding the canonical tag to Gitea to prevent duplicate spam
author: peter
date: 2024-06-29 02:55:28 +0800
categories: [SelfHosting] # Blogging | Electronics | Programming | Mechanical
tags: [tip] # systems | embedded | rf | microwave | electronics | solidworks | automation
# image: assets/img/2024-06-29-Adding-the-canonical/preview.png
---
Gitea doesn't include the canonical tag in their pages, leading to a lot of duplicate pages being indexed by Google. Each page being indexed has a different sorting option, or other superflous parameter.
> Duplicate without user-selected canonical
>
> These pages aren't indexed or served on Google
Not sure if having a lot of these errors affects SEO but I've noticed a lot of my real pages are being discovered but not indexed by Google.
![Search console page getting spammed with duplicate urls](/assets/img/2024-06-29-Adding-the-canonical/search%20console.png)
To fix this,
1. Create a `custom/templates/base/` directory under your Gitea installation. In my case it is in `/var/lib/gitea/custom/templates/base/`.
2. CD to the base directory and run `wget https://raw.githubusercontent.com/go-gitea/gitea/main/templates/base/head.tmpl`. Make sure your Gitea is updated to the latest version, or choose the right version instead of the `main` branch.
3. Modify `head.tmpl`: Add this line somewhere in the `<head>` section:
```html
<link rel="canonical" href="{{AppUrl}}{{if $.Link}}{{slice $.Link 1}}{{end}}" />
```
4. Restart the gitea service

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View File

@ -25,8 +25,8 @@ generate_front_matter() {
echo "title: $1"
echo "author: peter"
echo "date: $current_date"
echo "categories: [Blogging] # Blogging | Electronics | Programming | Mechanical"
echo "tags: [getting started] # systems | embedded | rf | microwave | electronics | solidworks | automation"
echo "categories: [Blogging] # Blogging | Electronics | Programming | Mechanical | SelfHosting"
echo "tags: [getting started] # systems | embedded | rf | microwave | electronics | solidworks | automation | tip"
echo "# image: assets/img/${filename:0:31}/preview.png"
echo "---"
}