Любое уважающее себя устройство должно следить за целостностью своей прошивки и сигнализировать если что-то не так.
Для проверки целостности данных удобно пользоваться циклическим избыточным кодом или CRC. Эта штука позволяет рассчитать число уникальное для набора данных. Таким образом имея данные и это число можно проверить верны ли данные или что-то пошло не так и у нас вместо нужных чисел полная билиберда.
В современных микроконтроллерах (вроде stm32) есть даже целый модуль для расчета CRC. Stm32f407 считает CRC32.
Существует огромное количество разновидностей расчета CRC в зависимости от разрядности результата и порождающего полинома. Но сейчас не об этом. Об алгоритмах еще напишу.
Подсчет контрольной суммы позволяют делать и современные среды разработки. Например в IAR есть приблуда, которая позволяет вычислить и впихнуть в нужное место памяти CRC прошивки.
Этим и займемся.
Создадим проект, который средствами IAR будет считать контрольную сумму прошивки, а при помощи периферии stm32 проверим верна ли прошивка.
Для настройки плюшки расчета CRC в IAR необходимо перейти в опции проекта и в меню Linker перейти во вкладку Checksum. В ней необходимо настроить пределы участка памяти для расчета и задать алгоритм CRC.
Теперь нужно изменить скрипт компановщика. Для этого переходим в Linker — Config и ставим галочку Override default и сохраняем в папку с проектом. Теперь нужно подредактировать этот файл. Добавляем в конец файла такую строку:
place at address mem:__ICFEDIT_region_ROM_end__-3 { readonly section .checksum };
Т.к. мы используем 4 байтный CRC32 нам нужно его расположить в памяти в конце прошивки. Мы создаем секцию checksum за 3 байта до последнего байта прошивки (т.е. за 4 до конца).
Теперь программа. Я буду пользоваться периферийной библиотекой ST.
Для того, чтобы вычитать посчитанную IAR контрольную сумму в переменную:
// для импорта контрольной суммы extern uint32_t __checksum;
Также нам будут нужны данные о секциях:
// для использования информации о секциях #pragma section=".intvec" #pragma section=".checksum"
и переменная для хранения CRC посчитанного контролером:
// для хранения подсчитанной контрольной суммы uint32_t calculated_crc;
При старте программы вычитаваем CRC из созданной нами секции памяти и получаем данные о начале и конце прошивки:
// читаем CRC в локальную переменную uint32_t read_crc = __checksum; // информация о границах памяти uint32_t* begin = (uint32_t*)__section_begin(".intvec"); // получаем начало прошивки uint32_t* end = (uint32_t*)__section_begin(".checksum"); // получаем начало CRC (конец прошивки) uint32_t* ptr = begin;
Для работы с модулем расчета CRC в stm32 его необходимо затактировать:
// включаем модуль CRC RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_CRC, ENABLE);
Дальше у нас два пути.
В принципе регистры работы с CRC очень простые и можно использовать обычные структуры:
// сбрасываем регистр данных CRC CRC->CR |= CRC_CR_RESET; // считаем CRC while (ptr != end) { CRC->DR = *ptr++; } // записываем результат в переменную calculated_crc = CRC->DR;
Но рас уж у нас есть библиотеки ST можно сделать и так:
// тот же подсчет CRC через библиотеку ST CRC_ResetDR(); calculated_crc = CRC_CalcBlockCRC(begin, end - begin);
Вот и все. Остается только сравнить полученные данные. Если все ок включим светодиод, ну а если нет — выключим.
// настраиваем светодиод STM_EVAL_LEDInit(LED3); while(1) { if (calculated_crc == read_crc) { // если CRC совпало - зажигаем STM_EVAL_LEDOn(LED3); } else { // если нет - гасим STM_EVAL_LEDOff(LED3); } }
Прикладываю проект для stm32f4 discovery https://www.dropbox.com/s/clvgdyafg2gqixe/iar_crc_calculate.rar
Если не использовать переменную __checksum, и будет включена оптимизация, компилятор выдаст ошибку ielftool error: The string '__checksum' was not found in the string table. Простая строка volatile uint32_t *pCrc = &__checksum; или иное использование в коде __checksum решает проблему.
Отличная статья.
В Project->Options…->Linker->Extra Options поставить галку и добавить —keep __checksum.
Даже не используя рекомендации Олега от 12/02/2016 ошибки линкера не будет.
ps EWARM 7.50