Embedded HandbookПриложения
Все разделы › VII. Документация и её язык
STM32F407, STM32F405, STM32F429 · уровень средний · 16 мин чтения
Открыть в интерактивном справочнике

39б. Errata на практике: как читать список ошибок кристалла и обходить их

В разделе 39 сказано главное: перечень ошибок (errata sheet) читают до разработки, а не после третьего часа отладки. Раздел 39а показал, где лежат официальные документы. Здесь — сам перечень в руках: как узнать ревизию своего кристалла, как читать сводную таблицу и отдельный пункт, какие обходы пишутся в код и как их держать, чтобы через год было понятно, откуда взялась странная строчка.

Опоры: перечни ES0182 Rev 13 (F405/407/415/417) и ES0206 Rev 18 (F427/429/437/439), справочные руководства RM0090 и RM0033 — все из паков Keil; заголовки и HAL F4 из пака. Сводные таблицы разобраны своей моделью на ПК, обходы собраны armclang для F407.

Как это называется в документации

Оригинал Перевод
errata sheet перечень ошибок кристалла
device limitation ограничение (ошибка) устройства
silicon revision ревизия кристалла
revision code marked on device код ревизии в маркировке корпуса
REV_ID, DEV_ID код ревизии и код устройства в DBGMCU_IDCODE
workaround обходной путь
partial workaround частичный обход: реже или не во всех режимах
documentation limitation ошибка в документации, а не в кристалле

39б.1 Где лежит перечень

На этой машине перечни ошибок лежат в паках Keil, в папке Documentation, под номерами вида DM000xxxxx.pdf — по имени файла не догадаться. Модель открыла первую страницу каждого PDF в паках STM32 и нашла 19 перечней: один в паке F2, 14 в паке F3 и 4 в паке F4. Для F407 — DM00037591.pdf, это ES0182 Rev 13 на 45 страниц; для F429 — DM00068628.pdf, ES0206 Rev 18 на 54 страницы.

В паке F1 перечня нет: там шестнадцать PDF — описания, справочные руководства и руководство по ядру. HAL F1 из архива STM32Cube на него только ссылается: в комментарии к HAL_GetREVID() сказано, что у F103x8/xB и ряда других F1 регистры DBGMCU_IDCODE и DBGMCU_CR доступны лишь под отладчиком, «Refer to errata sheet». Сам перечень F1 скачивают со страницы продукта (раздел 39а).

Перечень состоит из четырёх частей: опознание кристалла, сводная таблица, пункты по одному и история редакций.

Схема перечня ошибок ES0182: опознание кристалла по маркировке и REV_ID, сводная таблица на 68 пунктов с отметками A, N, P или прочерк для каждой ревизии, пункт с описанием и обходом, история редакций; ревизия читается из DBGMCU_IDCODE по адресу 0xE004 2000, обход идёт в код с номером пункта в комментарии — почти всегда без условия, по ревизии лишь изредка


39б.2 Какая у меня ревизия

Первая страница перечня говорит, как узнать ревизию: по коду под номером заказа на корпусе или по полю REV_ID — битам 31:16 регистра DBGMCU_IDCODE по адресу 0xE004 2000. Таблица кодов — в справочном руководстве, у F4 это RM0090, раздел 38.6.1:

REV_ID F405/407 F42x/F43x
0x1000 A A
0x1001 Z —
0x1003 1 Y
0x1007 2 1
0x100F Y и 4 —
0x101F 5 и 6 —
0x2001 — 3
0x2003 — 5 и B

Код устройства DEV_ID (биты 11:0) — 0x413 у F405/407 и 0x419 у F42x. Документы расходятся в мелочи: у кода 0x2003 RM0090 пишет «5 и B», а таблица 2 самого ES0206 — «4, 5 и B». Для выбора обходов верить перечню: он новее и написан ровно про ошибки.

Подвох, который перечень описывает сам, — пункт 2.1.2 ES0182. У ревизии A код устройства такой же, как у F2, а REV_ID читается 0x2000 вместо 0x1000. По RM0033 слово 0x2000 0411 — это F205/207 ревизии B. Обход из перечня: отличать по ядру, у F2 — Cortex-M3, у F4 — Cortex-M4 (номер ядра в SCB->CPUID — 0xC23 и 0xC24, раздел 2а):

    if (dev == DEV_F2 && rev == 0x2000u && part == PART_M4)
        return REV_A;                                   /* ES0182 2.1.2 */
    if (dev != DEV_F405_407)
        return REV_UNKNOWN;

Модель повторяет эту функцию: 0x1007 6413 — F405/407 ревизии 2; 0x2000 0411 с ядром 0xC24 — ревизия A, а с ядром 0xC23 — F2 ревизии B; 0x1008 6413 — кода нет в таблице, и функция честно отвечает «неизвестно».


39б.3 Сводная таблица: что исправлено

Сводка — это таблица «пункт × ревизия». Отметки в ES0182: A — обход есть, N — обхода нет, P — обход частичный, прочерк — исправлено. В новых перечнях, как ES0206, добавилась D — ошибка документации, а прочерк означает «у этой ревизии ошибки нет».

Модель разобрала таблицу 4 ES0182 целиком — 68 пунктов в 15 группах (система — 17, USART — 9, I²C и SDIO — по 6, остальные меньше) и ещё две ошибки самого ядра Cortex-M4 в разделе 1:

Ревизия A — обход есть N — обхода нет P — частичный нет ошибки
A 52 14 1 1
1, 2, 4, 5, 6, Y, Z 51 14 1 2

Главный вывод: за девять с лишним лет редакций (первая — сентябрь 2011, тринадцатая — январь 2021) у поздних ревизий исправлены два пункта из 68. По ревизиям различаются всего три строки: 2.1.1 (предвыборка флеша у A), 2.1.2 (код устройства у A) и 2.1.7 (вывод PDR_ON у Z и позже — обратная ситуация: ошибка появилась). Почти всё остальное — поведение кристалла навсегда.

У F42x картина живее: в таблице 3 ES0206 — 85 строк, у ревизии A отмечено 84, у последней (4, 5 и B) — 74; одиннадцать пунктов ревизии A в последней исправлены, один (2.3.10, пропуск пакетной записи в PSRAM) появился с ревизии 1. Сам перечень тоже не без ошибок: номер 2.10.2 записан в сводке дважды.


39б.4 Как устроен пункт

Каждый пункт — заголовок и два абзаца: Description (что и при каких условиях) и Workaround (что делать). Условия читают внимательно: они сужают беду. Пункт 2.5.3 ES0182 — нарушение времени повторного START у I²C — бывает только у ведущего, в стандартном режиме от 88 до 100 кГц; обход — опуститься до 88 кГц или перейти в быстрый режим. Пункт 2.5.5 — фронты I²C при шине 5 В: при VDD 3,3 В напряжение шины не выше (VDD + 0,3) / 0,7 = 5,14 В.

Пример целиком — пункт 2.1.13 «Delay after an RCC peripheral clock enabling». Описание: после записи бита включения такта блок оживает не сразу — через 2 такта AHB у блоков на AHB и через 1 + (делитель AHB/APB) тактов у блоков на APB. Обходов три: команда DSB, n команд NOP или холостое чтение того же регистра. HAL F4 выбрал третий и написал его в каждый макрос включения такта — с тем же заголовком в комментарии:

#define __HAL_RCC_GPIOA_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
                                        UNUSED(tmpreg); \
                                          } while(0U)

В заголовках HAL F4 из пака эта строка встречается 243 раза. Так выглядит обход, записанный по правилам: код рядом с причиной, причина — заголовком пункта.


39б.5 Шесть обходов в коде

Файл errata39c.c собирает обходы из ES0182 для F407 — каждый с номером пункта и редакцией перечня. Все они без условий: по сводке эти ошибки есть у всех ревизий.

2.1.13 — пауза после включения такта. Своё включение без HAL — запись и холостое чтение:

void gpioa_clock_on(void)
{
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    (void)RCC->AHB1ENR;                                 /* ES0182 2.1.13 */
}

Компилятор чтение не выбросил — регистр объявлен volatile. Дизассемблер armclang: запись, затем то самое чтение:

gpioa_clock_on:  MOV   r0,#0x3830
                 MOVT  r0,#0x4002
                 LDR   r1,[r0,#0]
                 ORR   r1,r1,#1
                 STR   r1,[r0,#0]
                 LDR   r0,[r0,#0]
                 BX    lr

2.1.17 — снятие защиты резервной области. После записи бита DBP — холостое чтение PWR_CR. HAL делает то же в HAL_PWR_EnableBkUpAccess() (переменная dummyread).

2.14.1 — смена последовательности АЦП. Если преобразование запущено программой, запись в ADC_SQRx сбрасывает текущее преобразование, и само оно не перезапустится. Обход — снова взвести SWSTART:

    ADC1->SQR3 = (ADC1->SQR3 & ~ADC_SQR3_SQ1) | (ch & 0x1Fu);
    ADC1->CR2 |= ADC_CR2_SWSTART;                       /* ES0182 2.14.1 */

При запуске от таймера ошибки нет: новая последовательность начнётся со следующего запуска.

2.15.1 и 2.15.2 — остановка ЦАП с DMA. Флаг опустошения DMAUDR поток DMA не останавливает, а снятие DMAEN не снимает висящий запрос — после перезапуска ЦАП придёт лишний. Порядок из перечня:

    if (DAC->SR & DAC_SR_DMAUDR1)                       /* шаг 1: было ли опустошение */
        DAC->SR = DAC_SR_DMAUDR1;
    DAC->CR &= ~DAC_CR_DMAEN1;                          /* шаг 2 */
    s->CR &= ~DMA_SxCR_EN;                              /* шаг 3 */

DMAUDR1 сбрасывается записью единицы (RM0090, DAC_SR).

2.5.6 и 2.5.2 — ложная ошибка шины I²C. Ведущий может получить BERR по ошибке; передача при этом не страдает, и перечень велит только сбросить флаг. BERR сбрасывается записью нуля. Читать SR2, чтобы узнать «ведущий ли я», в обработчике нельзя: чтение SR1, а за ним SR2 сбрасывает ADDR. Поэтому режим — флаг своего драйвера:

void I2C1_ER_IRQHandler(void)
{
    if ((I2C1->SR1 & I2C_SR1_BERR) && i2c1_master)
        I2C1->SR1 = ~I2C_SR1_BERR;                      /* ES0182 2.5.6: BERR сбрасывается записью 0 */
}

Если же чужой ведущий поставил STOP посреди байта и START больше не выходит (2.5.2), обход — программный сброс блока битом SWRST.

1.2 — VDIV и VSQRT в коротком прерывании. Ошибка ядра 776924: деление или корень FPU длится 14 тактов, и если за это время пришло и кончилось прерывание из одной-двух команд без команд FPU, результат не запишется. Обход — выключить ленивое сохранение FPU, бит LSPEN (бит 30 FPCCR по адресу 0xE000EF34), или не писать обработчиков короче трёх команд, не считая команды возврата.

Ещё один обход — только для отладки: 2.1.3, три NOP после WFI, если сон отлаживают с выключенной предвыборкой. Дизассемблер sleep_debug_safe — ровно WFI, три NOP и возврат.

Не всё лечится кодом. Пункт 2.1.10 (DMA2 портит данные, если одновременно обслуживает DCMI, FSMC с внешним FIFO или вывод GPIO и блок на APB) решается раскладкой потоков на этапе проекта, а четырнадцать пунктов с отметкой N обхода не имеют вовсе — их учитывают, выбирая, какими блоками пользоваться.


39б.6 Условие по ревизии и как держать обходы

Проверка REV_ID нужна редко — только для строк, где сводка у ревизий разная. У F407 пример один, и он сталкивается с другим пунктом. У ревизии A нет очереди предвыборки флеша (2.1.1, обхода нет), а 2.1.15 для точного АЦП советует предвыборку выключить, кэши команд и данных оставить:

void flash_accel_setup(enum f40x_rev rev, int precise_adc)
{
    uint32_t acr = (FLASH->ACR & FLASH_ACR_LATENCY) | FLASH_ACR_ICEN | FLASH_ACR_DCEN;
    if (rev != REV_A && !precise_adc)
        acr |= FLASH_ACR_PRFTEN;
    FLASH->ACR = acr;
}

Правила, которые делают обходы понятными через год:


39б.7 Грабли


39б.8 Итоги

Полные исходники

errata39c.c — обходы ошибок кристалла STM32F405/407.

/* errata39c.c — раздел 39б: обходы ошибок кристалла STM32F405/407 в коде.
 *
 * Источник — перечень ошибок ES0182 Rev 13 (январь 2021), файл DM00037591.pdf
 * в паке Keil STM32F4xx_DFP 2.17.1. Номер пункта у каждого обхода — по этой
 * редакции; в других перечнях тот же пункт стоит под другим номером.
 *
 * Сборка (проверка раздела):
 *   armclang --target=arm-arm-none-eabi -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard
 *            -DSTM32F407xx -include stm32f4xx.h -O2 -Wall -Wextra -c errata39c.c
 */
#include <stdint.h>

/* Адреса и поля — из заголовков пака; разойдутся с документом — сборка остановится. */
_Static_assert(DBGMCU_BASE == 0xE0042000UL, "DBGMCU_IDCODE по адресу 0xE004 2000 (RM0090, 38.6.1)");
_Static_assert(DBGMCU_IDCODE_REV_ID_Pos == 16U, "REV_ID — биты 31:16");
_Static_assert(DBGMCU_IDCODE_DEV_ID_Msk == 0xFFFUL, "DEV_ID — биты 11:0");
_Static_assert(FPU_BASE + 4UL == 0xE000EF34UL, "FPCCR по адресу 0xE000EF34 (ES0182, 1.2)");
_Static_assert(FPU_FPCCR_LSPEN_Pos == 30U, "LSPEN — бит 30 FPCCR (ES0182, 1.2)");

enum f40x_rev { REV_UNKNOWN = 0, REV_A, REV_Z, REV_1, REV_2, REV_Y_4, REV_5_6 };

#define DEV_F405_407 0x413u   /* RM0090, 38.6.1 */
#define DEV_F2       0x411u   /* RM0033: код F205/207 */
#define PART_M4      0xC24u   /* номер ядра в SCB->CPUID */

/* Ревизия кристалла по DBGMCU_IDCODE.
 * ES0182 2.1.2 «MCU device ID is incorrect» (только ревизия A): код устройства — как у F2,
 * REV_ID — 0x2000 вместо 0x1000. Обход из перечня: отличать F4 от F2 по ядру. */
enum f40x_rev f40x_revision(void)
{
    uint32_t id   = DBGMCU->IDCODE;
    uint32_t dev  = id & DBGMCU_IDCODE_DEV_ID_Msk;
    uint32_t rev  = id >> DBGMCU_IDCODE_REV_ID_Pos;
    uint32_t part = (SCB->CPUID & SCB_CPUID_PARTNO_Msk) >> SCB_CPUID_PARTNO_Pos;

    if (dev == DEV_F2 && rev == 0x2000u && part == PART_M4)
        return REV_A;                                   /* ES0182 2.1.2 */
    if (dev != DEV_F405_407)
        return REV_UNKNOWN;
    switch (rev) {                                      /* RM0090, 38.6.1 */
    case 0x1000u: return REV_A;
    case 0x1001u: return REV_Z;
    case 0x1003u: return REV_1;
    case 0x1007u: return REV_2;
    case 0x100Fu: return REV_Y_4;
    case 0x101Fu: return REV_5_6;
    default:      return REV_UNKNOWN;                   /* новая ревизия: сверить с перечнем */
    }
}

/* Флеш: ES0182 2.1.1 «ART Accelerator prefetch queue instruction is not supported» —
 * у ревизии A очереди предвыборки нет (обхода нет, исправлено в Z и 1).
 * ES0182 2.1.15 «Internal noise impacting the ADC accuracy» — для точного АЦП
 * предвыборку выключить, кэши команд и данных оставить. */
void flash_accel_setup(enum f40x_rev rev, int precise_adc)
{
    uint32_t acr = (FLASH->ACR & FLASH_ACR_LATENCY) | FLASH_ACR_ICEN | FLASH_ACR_DCEN;
    if (rev != REV_A && !precise_adc)
        acr |= FLASH_ACR_PRFTEN;
    FLASH->ACR = acr;
}

/* ES0182 2.1.13 «Delay after an RCC peripheral clock enabling»: после включения такта
 * нужна пауза — обход 3 из перечня: холостое чтение того же регистра. */
void gpioa_clock_on(void)
{
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    (void)RCC->AHB1ENR;                                 /* ES0182 2.1.13 */
}

/* ES0182 2.1.17 «Possible delay in backup domain protection disabling/enabling after
 * programming the DBP bit»: холостое чтение PWR_CR сразу после записи DBP. */
void backup_domain_unlock(void)
{
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
    (void)RCC->APB1ENR;                                 /* ES0182 2.1.13 */
    PWR->CR |= PWR_CR_DBP;
    (void)PWR->CR;                                      /* ES0182 2.1.17 */
}

/* ES0182 2.14.1 «ADC sequencer modification during conversion»: при запуске программой
 * смена SQRx сбрасывает текущее преобразование, и само оно не перезапускается —
 * взвести SWSTART ещё раз. */
void adc1_set_first_channel(uint32_t ch)
{
    ADC1->SQR3 = (ADC1->SQR3 & ~ADC_SQR3_SQ1) | (ch & 0x1Fu);
    ADC1->CR2 |= ADC_CR2_SWSTART;                       /* ES0182 2.14.1 */
}

/* ES0182 2.15.1 и 2.15.2: DMAUDR поток DMA не останавливает, а DMAEN=0 не снимает
 * запрос — порядок остановки DAC с DMA из перечня. */
void dac1_dma_stop(DMA_Stream_TypeDef *s)
{
    if (DAC->SR & DAC_SR_DMAUDR1)                       /* шаг 1: было ли опустошение */
        DAC->SR = DAC_SR_DMAUDR1;
    DAC->CR &= ~DAC_CR_DMAEN1;                          /* шаг 2 */
    s->CR &= ~DMA_SxCR_EN;                              /* шаг 3 */
    while (s->CR & DMA_SxCR_EN) { }
    /* шаги 4–5: заново настроить DAC, DMA, запуск — в вызывающем коде */
}

/* ES0182 2.5.6 «Spurious Bus Error detection in Master mode»: ведущему — только
 * сбросить BERR, передача идёт дальше.
 * ES0182 2.5.2 «Start cannot be generated after a misplaced Stop»: обход — SWRST.
 * SR2 здесь не читаем: чтение SR1, а за ним SR2 сбрасывает ADDR (RM0090, I2C_SR1),
 * поэтому «ведущий ли мы» — флаг своего драйвера. */
volatile uint8_t i2c1_master;                           /* 1 от START до STOP */

void I2C1_ER_IRQHandler(void)
{
    if ((I2C1->SR1 & I2C_SR1_BERR) && i2c1_master)
        I2C1->SR1 = ~I2C_SR1_BERR;                      /* ES0182 2.5.6: BERR сбрасывается записью 0 */
}

void i2c1_soft_reset(void)                              /* ES0182 2.5.2 */
{
    I2C1->CR1 |= I2C_CR1_SWRST;
    I2C1->CR1 &= ~I2C_CR1_SWRST;
    /* дальше: заново записать CR2, CCR, TRISE и включить PE */
}

/* ES0182 1.2 «VDIV or VSQRT instructions might not complete correctly when very short
 * ISRs are used» (ошибка ядра 776924): обход 1 — выключить ленивое сохранение FPU. */
void fpu_lazy_off(void)
{
    FPU->FPCCR &= ~FPU_FPCCR_LSPEN_Msk;                 /* ES0182 1.2 */
}

/* ES0182 2.1.3 «Debugging Sleep/Stop mode with WFE/WFI entry»: три NOP после WFI. */
void sleep_debug_safe(void)
{
    __WFI();
    __NOP(); __NOP(); __NOP();                          /* ES0182 2.1.3 */
}

Дальше: раздел 39в «Даташит чужой микросхемы → драйвер: HTS221 от первой страницы до проверки без платы» — как из даташита датчика вырастает драйвер и как проверить его без платы.

Открыть в интерактивном справочнике