ZHCU875Z August 2001 – October 2023 SM320F28335-EP
/*****************************************************************************/
/* CRC 计算函数的参考实现 */
/* */
/* gen_crc 是应从应用程序调用的接口函数。 */
/* 如果定义了 _RUN_MAIN,还可以使用独立的测试模式。 */
/* */
/*****************************************************************************/
/*---------------------------------------------------------------------------*/
/* 该文件未采用通用 CRC 函数。 */
/* 具体而言,它不通过初始值、位反射或最终 XOR 值处理参数化。 */
/* */
/* 此设计旨在实现链接器用于 C28x CRC 表的 CRC 函数。 */
/* 选择链接器使用的算法以匹配 */
/* PRIME 和 IEEE 802.15.4-2006 标准中的 CRC 算法, */
/* 这些标准使用由 C28x VCU 硬件支持的多项式。 若要从大体上了解 CRC, */
/* 尤其是存在哪些其他参数,请参阅: */
/* */
/* "A Painless Guide To CRC Error Detection Algorithms" likely at: */
/* http://www.ross.net/crc/download/crc_v3.txt */
/* Author : Ross Williams (ross@guest.adelaide.edu.au.). */
/* Date : 3 June 1993. */
/* Status : Public domain (C code). */
/*---------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
/*---------------------------------------------------------------------------*/
/* 这些是由链接器支持的 CRC 算法, */
/* 与 C28x VCU 硬件中支持的多项式相匹配, */
/* 符合 PRIME 和 IEEE 802.15.4-2006 标准。 这些必须与 crc_tbl.h 中的值相匹配。 */
/*---------------------------------------------------------------------------*/
#define CRC32_PRIME 0
#define CRC16_802_15_4 1
#define CRC16_ALT 2
#define CRC8_PRIME 3
typedef struct crc_config_t
{
int id;
int degree;
unsigned long poly;
} crc_config_t;
const crc_config_t crc_config[] = { { CRC32_PRIME, 32, 0x04c11db7 },
{ CRC16_802_15_4, 16, 0x1021 },
{ CRC16_ALT, 16, 0x8005 },
{ CRC8_PRIME, 8, 0x07 } };
unsigned long crc_table[256] = { 0 };
const crc_config_t *find_config(int id) {
size_t i;
for (i = 0; i < sizeof(crc_config) / sizeof(*crc_config); i++)
if (crc_config[i].id == id)
return &crc_config[i];
fprintf(stderr, "invalid config id %d\n", id);
exit(EXIT_FAILURE);
return NULL;
}
/*---------------------------------------------------------------------------*/
/* 表驱动版本 */
/*---------------------------------------------------------------------------*/
unsigned long generate_mask(int degree)
{
unsigned long half = (1ul << (degree / 2)) - 1;
return half << (degree / 2) | half;
}
void generate_crc_table(const crc_config_t *config)
{
int i, j;
unsigned long bit, crc;
unsigned long high_bit = (1ul << (config->degree - 1));
unsigned long mask = generate_mask(config->degree);
for (i = 0; i < 256; i++)
{
crc = (unsigned long)i << config->degree - 8;
for (j = 0; j < 8; j++)
{
bit = crc & high_bit;
crc <<= 1;
if (bit) crc^= config->poly;
}
crc_table[i] = crc & mask;
}
}
/*****************************************************************************/
/* gen_crc - 使用给定的 CRC 算法返回数据的 CRC 值 */
/* int id : 标识 CRC 算法 */
/* char *data : 数据 */
/* size_t len : 数据的大小 */
/*****************************************************************************/
unsigned long gen_crc(int id, const unsigned char *data, size_t len)
{
/*-----------------------------------------------------------------------*/
/* 注意:这不是通用 CRC 函数。 它不通过 */
/* 初始值、位反射或最终 XOR 值处理参数化。 */
/* 此 CRC 函数专门针对链接器中用于 */
/* C28x CRC 表的 CRC 算法。 */
/*-----------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*/
/* 此 CRC 函数不求最优化; */
/* 其编写原则是可以在所有 8 位和 16 位目标(包括 C28x、其他 TI DSP 和典型桌面)上起作用 */
/* 并产生相同的结果。 */
/*-----------------------------------------------------------------------*/
const crc_config_t *config = find_config(id);
unsigned long crc = 0;
unsigned long mask = generate_mask(config->degree);
size_t i;
generate_crc_table(config);
for (i = 0; i < len; i++)
{
unsigned int datum = data[i];
/*--------------------------------------------------------------------*/
/* 当我们在 16 位机器上进行编译时,这个循环处理 16 位字符。*/
/*--------------------------------------------------------------------*/
int n;
for (n = 0; n < (CHAR_BIT / 8); n++)
{
/*----------------------------------------------------------------*/
/* 对于 16 位机器,我们需要以任意顺序输入八位字节。 */
/* 对于 C2000, */
/*我们选择的任意顺序是首先输入 char 0 的最低有效八位字节 */
/* 输入到 CRC 的第一个八位字节是 char 0 的最低有效八位字节; */
/* 第二个八位字节是 char 0 的最高有效八位字节。 */
/* 请参阅《汇编语言工具用户指南》 */
/* 中的“关于 16 位字符的特别说明”一节。 */
/*----------------------------------------------------------------*/
#if __TMS320C28XX__
/*----------------------------------------------------------------*/
/* 未必要使用 __byte;在这里我们用它来说明它与八位字节顺序的关系。 */
/* */
/*----------------------------------------------------------------*/
unsigned long octet = __byte((int*)&datum, n);
#else
unsigned long octet = ((datum >> (8 * n)) & 0xff);
#endif
unsigned long term1 = (crc << 8);
int idx = ((crc >> (config->degree - 8)) & 0xff) ^ octet;
crc = term1 ^ crc_table[idx];
}
}
return crc & mask;
}
#ifdef _RUN_MAIN
/*****************************************************************************/
/* main - 如有需要,请使用每种算法计算测试数据的 CRC。 */
/*****************************************************************************/
int main(void)
{
#if CHAR_BIT == 16
const unsigned char data[] = { 'a', 'b', 'c', 'd' };
#elif CHAR_BIT == 8
/*-----------------------------------------------------------------------*/
/* 这表示“abcd”,因为如果我们将 C2000 内存视为八位字节, */
/* 它将出现在 C2000 内存中,其中最低有效八位字节在前; */
/* 请参阅《汇编语言工具用户指南》中的“关于 16 位字符的特别说明”一节。 */
/*-----------------------------------------------------------------------*/
const unsigned char data[] = { 'a', 0, 'b', 0, 'c', 0, 'd', 0 };
#endif
/* CRC_8_PRIME: 0x70 */
/* CRC_16_802: 0x1bd3 */
/* CRC_32_PRIME: 0x4beab53b */
const unsigned char *p = (const unsigned char *)data;
unsigned long crc;
crc = gen_crc(CRC32_PRIME, p, sizeof data);
printf("CRC_32_PRIME: %08lx\n", crc);
crc = gen_crc(CRC8_PRIME, p, sizeof data);
printf("CRC_8_PRIME: %02lx\n", crc);
crc = gen_crc(CRC16_802_15_4, p, sizeof data);
printf("CRC16_802_15_4: %04lx\n", crc);
return 0;
}
#endif