BEKEN: как читать и записывать память FLASH |
![]() |
Добавил(а) microsin |
Физическая память FLASH микроконтроллеров BK72xx устроена довольно непривычным образом. Каждый блок памяти в 32 байта снабжен двухбайтовой контрольной суммой CRC16, вычисленной по полиному 0x8005 с начальным значением 0xFFFF. [ блок 32 байта ][CRC16] Код из репозитория RT-Thread для микроконтроллера BK7231N: /*
* Контрольная сумма CRC16 для 32-байтных блоков микроконтроллеров BEKEN.
* Старший байт (MSB) идет первым, полином 8005, начальное значение FFFF.
*/
unsigned short CRC16_CCITT_FALSE(unsigned char *puchMsg, unsigned int usDataLen) { unsigned short wCRCin = 0xFFFF; unsigned short wCPoly = 0x8005; unsigned char wChar = 0; int i; while (usDataLen--) { wChar = *(puchMsg++); wCRCin ^= (wChar << 8); for(i = 0;i < 8;i++) { if(wCRCin & 0x8000) wCRCin = (wCRCin << 1) ^ wCPoly; else wCRCin = wCRCin << 1; } } return (wCRCin); } Чтение FLASH. Память программ и данных находится в памяти FLASH, и она доступна для ядра процессора в логическом виде, без контрольных сумм CRC16. Аппаратура доступа к памяти на лету преобразует логические адреса в физические: [ блок 32 байта ] В коде программы нельзя напрямую читать и записывать адресное пространство flash, иначе (случайным образом) может происходить непредсказуемое поведение, обычно заканчивающееся зависанием с перезагрузкой по сторожевому таймеру. Для чтения памяти необходимо использовать специальный драйвер в модуле flash.c, который находится в каталоге ./beken378/driver/flash репозитория [1]. Ниже показан пример чтения физического содержимого памяти, вместе с контрольными суммами CRC16. Для вызова функций из модуля flash.c используется промежуточная модель драйвера на основе функций ddev_open, ddev_read, ddev_close. uint8_t xbuf[EF_ERASE_MIN_SIZE];
// Вывод физического содержимого памяти FLASH MCU BK7231N:
static int xdump (int argc, char **argv) { uint32_t address, len; UINT32 status; DD_HANDLE flash_handle; switch (argc) { case 2: sscanf(argv[1], "%X", &address); len = 64; break; case 3: sscanf(argv[1], "%X", &address); sscanf(argv[2], "%X", &len); break; default: bk_printf("Usage: %s addrhex [lenhex]\n", argv[0]); return 1; } flash_handle = ddev_open(FLASH_DEV_NAME, &status, 0); ddev_read(flash_handle, (char *)xbuf, len, address); dump_hex(address, (void*)xbuf, len); ddev_close(flash_handle); return 0; } MSH_CMD_EXPORT(xdump, dump physical FLASH memory); Пример вывода физического содержимого первых 64 байт FLASH, где находится код загрузчика: msh />xdump 0 40
00000000: AA 00 00 EA 14 F0 9F E5 14 F0 9F E5 14 F0 9F E5 ................
00000010: 14 F0 9F E5 14 F0 9F E5 14 F0 9F E5 14 F0 9F E5 ................
00000020: 16 CE B8 05 00 00 4C 05 00 00 C8 05 00 00 D8 05 ......L.........
00000030: 00 00 E8 05 00 00 5C 05 00 00 6C 05 00 00 EF BE ......\...l.....
Для практического использования данных FLASH в программе, чтобы читать и записывать данные не по физическим, а по логическим адресам, удобнее другой интерфейс, реализованный в модуле fal_flash_beken7231_crc_port.c (находится в каталоге samples/ota/port/). Пример чтения логического содержимого памяти FLASH: // Вывод логического содержимого памяти FLASH MCU BK7231N:
static int dump (int argc, char **argv) { uint32_t logaddress, len, portion; uint8_t buf[32]; switch (argc) { case 2: sscanf(argv[1], "%X", &logaddress); len = 64; break; case 3: sscanf(argv[1], "%X", &logaddress); sscanf(argv[2], "%X", &len); break; default: bk_printf("Usage: %s addrhex [lenhex]\n", argv[0]); return 1; } fal_flash_dev_t ffd = (fal_flash_dev_t)&beken_onchip_flash_crc; ffd->ops.read(FLASH_NVSETTINGS_ADDR, (uint8_t*)&nvdata, sizeof(nvdata)); while(len) { portion = len < 32 ? len : 32; ffd->ops.read(logaddress, buf, portion); dump_hex(logaddress, buf, portion); logaddress += 32; len -= portion; } return 0; } MSH_CMD_EXPORT(dump, dump logical FLASH memory); Пример вывода логического содержимого первых 64 байт FLASH, где находится код загрузчика: msh />dump 0 40
00000000: AA 00 00 EA 14 F0 9F E5 14 F0 9F E5 14 F0 9F E5 ................
00000010: 14 F0 9F E5 14 F0 9F E5 14 F0 9F E5 14 F0 9F E5 ................
00000020: B8 05 00 00 4C 05 00 00 C8 05 00 00 D8 05 00 00 ....L...........
00000030: E8 05 00 00 5C 05 00 00 6C 05 00 00 EF BE AD DE ....\...l.......
Обратите внимание, что в этом примере вывода по адресу 00000020 отсутствут байты CRC16 от предыдущего блока 32 байт. Запись FLASH. Запись в память FLASH реализуется аналогично, либо по физическим адресам через интерфейс flash.c, либо по логическим адресам через интерфейс fal_flash_beken7231_crc_port.c. Ниже приведен код записи физическим адресам. Он дан только для примера, это ошибочный код, потому что здесь блоки по 32 байта перед записью не снабжаются контрольной суммой CRC16. // Запись физического содержимого FLASH:
void Save (void) { UINT32 status; DD_HANDLE flash_hdl; GLOBAL_INT_DECLARATION(); uint32_t address, len; nvdata.CRC32 = ef_calc_crc32(0, &nvdata, sizeof(TNVsettings)-sizeof(uint32_t)); flash_hdl = ddev_open(FLASH_DEV_NAME, &status, 0); if (DD_HANDLE_UNVALID == flash_hdl) { os_printf("%s open failed\r\n", __FUNCTION__); return; } // Снятие защиты на запись: bk_flash_enable_security(FLASH_PROTECT_NONE); // Перед записью записываемый сектор необходимо стереть: address = FLASH_NVSETTINGS_ADDR; // физический адрес FLASH len = EF_ERASE_MIN_SIZE; GLOBAL_INT_DISABLE(); // запрет прерываний ef_port_erase(address, len); nvdata.seed = rt_tick_get(); nvdata.CRC32 = ef_calc_crc32(0, &nvdata, sizeof(TNVsettings)-sizeof(uint32_t)); // Запись по данных физическому адресу FLASH_NVSETTINGS_ADDR: ddev_write(flash_hdl, (char*)&nvdata, sizeof(nvdata), FLASH_NVSETTINGS_ADDR); GLOBAL_INT_RESTORE(); // восстановление прерываний // Возобновление защиты на запись: bk_flash_enable_security(FLASH_UNPROTECT_LAST_BLOCK); ddev_close(flash_hdl); } Для работы с памятью по физическим адресам на чтение и запись удобно использовать интерфейс драйвера fal_flash_beken7231_crc_port.c. Он сам заботится о добавлении контрольных сумм CRC16 в записываемые данные и об остановке и возобновлении прерываний. Ниже показан практический пример записи в FLASH по логическим адресам с помощью интерфейса erase и write драйвера. Имейте в виду, что перед использованием интерфейса этого драйвера необходимо вызывать функцию fal_init (обычно при старте приложения). Как видно, код получился намного проще: // Запись логического содержимого FLASH:
void Save (void) { fal_flash_dev_t ffd = (fal_flash_dev_t)&beken_onchip_flash_crc; bk_flash_enable_security(FLASH_PROTECT_NONE); ffd->ops.erase(FLASH_NVSETTINGS_ADDR, sizeof(nvdata)); nvdata.seed = rt_tick_get(); nvdata.CRC32 = ef_calc_crc32(0, &nvdata, sizeof(TNVsettings)-sizeof(uint32_t)); ffd->ops.write(FLASH_NVSETTINGS_ADDR, (uint8_t*)&nvdata, sizeof(nvdata)); bk_flash_enable_security(FLASH_UNPROTECT_LAST_BLOCK); } [Ссылки] 1. bekencorp / bdk_rtt site:github.com. |