Jump to content

    
Димон Безпарольный

Помогите по CRC32. На компьютерном Си работает, в микроконтролере дает неверный результат

Recommended Posts

Считаю контрольную сумму 16-ти байт

Quote

01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

Формирую бинарный файл с этими данными, получаю результат 0xСECEE288(crc файла в Проводнике). Контроллер выдает 0x31311D77. Загнал алгоритм (код ниже) в C-Free компилятор. Тоже получил 0xСECEE288. Сравнил сформированные таблицы - одинаковые. Закипел.

Код формирования таблицы unsigned long crc_table[256]; Таблица глобальная

void Crc32Init(void)
{
unsigned long crc;
	for (int i = 0; i < 256; i++)								//инициализируем таблицу расчёта Crc32
		{
			crc = i;
			for (int j = 0; j < 8; j++)							//цикл перебора полинома
			crc = crc & 1 ? (crc >> 1) ^ 0xEDB88320UL : crc >> 1;
			crc_table[i] = crc;
		}
	inCRC = 0xFFFFFFFF;
}

Код самого подсчета

unsigned long int Crc32(unsigned char Byte)
{
	inCRC = (inCRC >> 8) ^ crc_table[(inCRC ^ Byte) & 0xFF];
	return inCRC ^ 0xFFFFFFFF;
}

inCRC - глобальная переменная unsigned long int inCRC = 0xFFFFFFFF; в которой накапливается сумма. Может подскажет кто, где я накосячил?

 

 

 

 

 

 

 

 

 

 

Edited by Димон Безпарольный

Share this post


Link to post
Share on other sites

2 * 2, конечно, "сэм-восэм", но c 1 до 15 никак не 16 чисел, входные байты могут и "зеркалиться" относительно их середины.
А "если мы с вами посмотрим, то мы с вами увидим" ((с) моя школьная историчка): 0xСECEE288 инверсия 0x31311D77.

Share this post


Link to post
Share on other sites
8 hours ago, Obam said:

0xСECEE288 инверсия 0x31311D77

Точно инверсия. Буду разбираться откуда она берется. Сам алгоритм выдает на двух компиляторах данные без инверсии.

Share this post


Link to post
Share on other sites

Нашел причину. Выполнение строки:

 

return inCRC ^ 0xFFFFFFFF;

Дает разный результат на компьютерном С++ и в камне. В камне return inCRC ^ 0xFFFFFFFF; и return inCRC; дает одинаковый результат! На компьютерном С++ - дает инверсию.

Так например Crc32(0x55); дает число 0x36fcb509 в камне независимо от варианта команды return. На С-Free на компьютере будет 0xC9034AF6 при команде  return inCRC ^ 0xFFFFFFFF; и 0x36fcb509 при команде return inCRC;

 

Почему в камне вычисление значения 0x36fcb509 ^ 0xFFFFFFFF; не инвертирует число, а на компьютере происходит инверсия? Подозреваю что что - то с разрядностью но не могу понять что.

Edited by Димон Безпарольный

Share this post


Link to post
Share on other sites
14.01.2021 в 00:49, Димон Безпарольный сказал:

Может подскажет кто, где я накосячил?

Построение функций принципиально косячное. Не используйте в них глобальные переменные! Использование такого стиля дает сплошные побочные эффекты. Функции зависят черт-знает от чего и портят кучу всего в программе.

Всё необходимое для своей работы функция должна получать через параметры. Нужны дополнительные хранилища вспомогательных данных - используйте локальные переменные. Нужно, чтобы локальные переменные сохраняли значения между вызовами - делайте их локальными статическими.

По существу: как вы проверяли результат? Приведите полный текст тестовой программы. Вы, случаем, не значение вашей глобальной переменной inCRC32 проверяли?

А вот эта функция

14.01.2021 в 00:49, Димон Безпарольный сказал:

unsigned long int Crc32(unsigned char Byte)

мне вообще непонятна. Зачем считать CRC32 от одного байта? А еще её результат зависит от двух глобальных объектов программы, один из которых ещё и изменяется ей же самой. Просто набор штампов, как не следует писать программы.

Нормальные функции подсчета CRC32 произвольного объекта данных data размером size байт выглядят примерно так:

uint32_t Crc32(const void *data, uint32_t size);

Да, для платформонезависимости результата пользуйтесь именами типов из файла stdint.h. Иначе один и тот же ваш код будет давать разный результат на разных архитектурах: на 8-битных один, на 16-битных другой, на 32-битных третий, а на 64-битных четвертый.

Share this post


Link to post
Share on other sites
1 hour ago, Darth Vader said:

Зачем считать CRC32 от одного байта?

Функция работает получая данные как с массивов, так и на лету с приема байт. Согласен что написано криво, надо причесать. Но это не решит проблемы потому что на глюк не похоже. Если инвертировать результат, функция считает CRC правильно на лету, из EEPROM 2580, из ФЛЭШ контроллера и еще бог знает откуда - проблем нет. Для проверки работы функции я написал вызов:

	Crc32Init();
	Crc32(0x55);

Так вот. В коде функции:

unsigned long int Crc32(unsigned char Byte)
{
	inCRC = (inCRC >> 8) ^ crc_table[(inCRC ^ Byte) & 0xFF];
	return inCRC ^ 0xFFFFFFFF;
}

Есть строка     return inCRC ^ 0xFFFFFFFF;. Она ничего не меняет. Т.е. возвращаемый результат будет тем же, что и при  return inCRC; Это в камне. Приходится дополнительно инвертировать результат чтобы совпало с вариантом на ПК. А вот в варианте на ПК return inCRC ^ 0xFFFFFFFF; как раз инвертирует результат как и должно быть. Что мне пока непонятно. Один и тот же код работает по разному.

 

Нашел причину. Вызов функции был неправильным. Правильно надо присваивать значение CRC локальной переменной uint32_t .  CRCSUM = Crc32(Chr); Тогда результат подсчета будет правильный. Всем спасибо за советы. Пошел причесывать код.

Edited by Димон Безпарольный

Share this post


Link to post
Share on other sites
1 hour ago, Darth Vader said:

Не используйте в них глобальные переменные!

Перед подсчетом CRC32 нужно вызвать Crc32Init(); Она в частности проинициализирует inCRC = 0xFFFFFFFF;. Переменная  inCRC используется в двух функциях. Поэтому она глобальна. Как тогда реализовать иначе?

Share this post


Link to post
Share on other sites
1 час назад, Димон Безпарольный сказал:

Как тогда реализовать иначе?

Сделать ОДНУ функцию, которая всё делает сама. 

Например так:

// Реализация функции расчёта CRC-32
// data_p - указатель на произвольный объект
// len - размер объекта в байтах
uint32_t Crc32(const void *data_p, uint32_t len) {
  static const uint32_t Crc32Table[256] = {
    0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
    0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
    0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
    0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
    0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
    0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
    0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
    0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
    0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
    0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
    0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
    0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
    0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
    0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
    0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
    0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
    0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
    0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
    0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
    0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
    0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
    0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
    0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
    0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
    0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
    0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
    0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
    0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
    0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
    0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
    0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
    0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
    0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
    0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
    0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
    0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
    0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
    0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
    0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
    0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
    0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
    0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
    0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
    0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
    0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
    0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
    0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
    0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
    0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
    0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
    0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
    0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
    0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
    0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
    0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
    0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
    0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
    0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
    0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
    0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
    0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
    0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
    0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
    0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
  };
    const uint8_t   *buf  = (const uint8_t*)data_p;
    uint32_t  crc   = 0xFFFFFFFF;
    
    while (len--) {
        crc = (crc >> 8) ^ Crc32Table[(crc ^ *buf++) & 0xFF];
    }
    return ~crc;
};

Это быстрый табличный расчёт CRC32 с полиномом 0x04C11DB7. Для другого полинома будут другие значения в массиве Crc32Table[256].

Взято отсюда.

Проведен небольшой рефакторинг: тип входного параметра-указателя для универсальности заменен на void*, константный массив спрятан внутрь функции, типы заменены на более конкретные.

Edited by Darth Vader

Share this post


Link to post
Share on other sites
12 minutes ago, Darth Vader said:

Сделать ОДНУ функцию, которая всё делает сама. 

Спасибо. Хочу только заметить что мной пишется бутлоадер для камней с большим количеством ОЗУ. Ему неважно сколько ОЗУ будет съедено. Да и ПЗУ тоже. Но вот ПЗУ желательно поменьше. Чисто субъективно.

Также непонятно как налету считать CRC. Файл прошивки может быть гораздо больше чем ОЗУ. А вот потеря времени на расчет таблицы малозначительна. Принятые данные напрямую пишутся в 2580 и налету считается CRC. Данные пишутся по 256 байт за раз. Как вариант можно CRC считать тоже по 256 байт.

 

Edited by Димон Безпарольный

Share this post


Link to post
Share on other sites
47 минут назад, Димон Безпарольный сказал:

Переменная  inCRC используется в двух функциях. Поэтому она глобальна.

Ситуация ничего не напоминает?

В С++ именно для таких ситуаций придуманы классы. Общие данные - это данные (поля) класса. Функции, оперирующие этими общими данными - это функции-члены класса. Так что в некоторых случаях, если нужна большая гибкость расчета, удобнее сделать класс CRC32.

Share this post


Link to post
Share on other sites
1 minute ago, Darth Vader said:

Ситуация ничего не напоминает?

В С++ именно для таких ситуаций придуманы классы. Общие данные - это данные (поля) класса. Функции, оперирующие этими общими данными - это функции-члены класса. Так что в некоторых случаях, если нужна большая гибкость расчета, удобнее сделать класс CRC32.

Код написан на Си.

Share this post


Link to post
Share on other sites
35 минут назад, Димон Безпарольный сказал:

Код написан на Си.

Код может быть написан на чем угодно. Один файл на Си, другой на С++, третий на ассемблере. Всё равно компилируются они раздельно. А линкеру всё равно, на чем были написаны исходные коды объектных модулей. Так что писать стоит на том, на чем удобней в данный конкретный момент.

Но уж если хочется чистого Си, то можно сделать закат солнца вручную и изобрести понятие класса и указателя this средствами Си:

- описываем тип структуры с полями-данными, необходимыми для работы всех функций

- во все функции добавляем в качестве первого параметра указатель на эту структуру с именем this

- внутри функций обращаетесь к необходимым общим данным через этот указатель this

- перед началом нового расчета создаете эту структуру. Причем она может быть как глобальной, так и локальной.

- вызываете ваши функции, передавая им в качестве первого параметра указатель на структуру.

 

Edited by Darth Vader

Share this post


Link to post
Share on other sites
On 2/19/2021 at 9:29 PM, Darth Vader said:

В С++ именно для таких ситуаций придуманы классы.

да и ты чтобы сбросить CRC предлагаешь создавать новый экземпляр класса?

On 2/19/2021 at 6:46 PM, Darth Vader said:

Построение функций принципиально косячное. Не используйте в них глобальные переменные! Использование такого стиля дает сплошные побочные эффекты. Функции зависят черт-знает от чего и портят кучу всего в программе.

Всё необходимое для своей работы функция должна получать через параметры. Нужны дополнительные хранилища вспомогательных данных - используйте локальные переменные. Нужно, чтобы локальные переменные сохраняли значения между вызовами - делайте их локальными статическими.

Набор штампов как не следует давать советы, подробнее о побочных эффектах пожалуйста и чем это локальная статическая отличается от глобальной кроме зоны видимости? 

Share this post


Link to post
Share on other sites

А можно задать нескромный вопрос: что за камень используется, если у него нет блока аппаратного вычисления CRC?

Зачем на STM32 вычислять CRC программно? Может, вы еще и БПФ будете вручную считать вместо использования fftw3?

 

Примера ради открываю RM на STM32F072. В регистр CRC_POL заносим полином. В CRC_CR - порядок бит, размер полинома и т.п. Что там сложного-то?

Edited by Eddy_Em

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.