CRC
循环冗余校验(CRC)计算单元是根据固定的生成多项式得到任一32位全字的CRC计算结果。
使用CRC-32(以太网)多项式:0x4C11DB7
CRC计算单元含有1个32位数据寄存器:
- 对该寄存器进行写操作时,作为输入寄存器,可以输入要进行CRC计算的新数据。
- 对该寄存器进行读操作时,返回上一次CRC计算的结果。
每一次写入数据寄存器,其计算结果是前一次CRC计算结果和新计算结果的组合(对整个32位字进行CRC计算,而不是逐字节地计算)。
在CRC计算期间会暂停CPU的写操作,因此可以对寄存器CRC_DR进行背靠背写入或者连续地写-读操作。
可以通过设置寄存器CRC_CR的RESET位来重置寄存器CRC_DR为0xFFFF FFFF。该操作不影响寄存器CRC_IDR内的数据。
CRC在线计算网站链接
STM32的CRC校验等同于
对STM32的CRC解释
CRC的DR寄存器如下
字节1 | 字节2 | 字节3 | 字节4 |
---|---|---|---|
XX | XX | XX | XX |
假设我们需要发送字符串ABC
按照4字节对齐,那么校验的字符串为ABC(0x00)
,发送与校验的字符串如下
字节1 | 字节2 | 字节3 | 字节4 |
---|---|---|---|
A | B | C | 0x00 |
并且在写入CRC的DR寄存器前,需要对写入的字进行高低字节转换,先写入高字节即 0x00 最后写入A
也就是unsigned int num = 0x00434241; CRC->DR = num
这样才是对字符串ABC(0x00)
进行了CRC校验,否则是对(0x00)CBA
进行校验。
CRC校验demo
采用STM32F103C8T6单片机,KeilMDK5.32版本
在串口输出数据之前,对数据进行CRC校验,并将数据进行4字节对齐(因为CRC校验是对字进行校验)
STM32CubeMX配置
主程序代码
串口加CRC函数
extern CRC_HandleTypeDef hcrc;
/**
* @brief UART 仿printf发送并增加CRC校验
* @param huart 指向串口结构体的指针
* @param pBuff 指向发送缓冲区的指针
* @param pBuff 是否需要进行CRC校验
* @param format 输出的字符串
* @retval 成功:返回写入的字符总数
* 失败:返回 -1
*/
int USARTPrintfCRCEx(UART_HandleTypeDef *huart, unsigned char *pBuff, char isEnableCRC, const char *format, ...)
{
int bytes = 0;
va_list list;
/* format 字符串写入 pBuff 中 */
va_start(list, format);
bytes = vsprintf((void*)pBuff, format, list);
va_end(list);
/* 判断写入字节数目是否超过串口发送缓冲区的大小 */
if(bytes > USART_SENDBUFF_MAX_BYTES)
{
return -1;
}
/* 对字符串进行 CRC 校验并且字符串长度得大于 0 */
if(isEnableCRC && (bytes >= 0))
{
/* 检查 CRC 是否开启 */
if(READ_BIT(RCC->AHBENR, RCC_AHBENR_CRCEN))
{
uint32_t CRCValue = 0U;
uint32_t *temp = (void*)pBuff;
uint8_t mode = bytes % 4;
/* 将源数据按照 4 字节对齐 */
if(mode)
{
/* 根据取模结果将多余无效的字节用0代替 */
for(int i = bytes; i < bytes + 4 - mode; i++)
{
pBuff[i] = 0U;
}
bytes += (4 - mode);//增加无效字节的字节数
}
/* 复位 CRC 的 DR 寄存器 */
__HAL_CRC_DR_RESET(&hcrc);
/* 对数据进行 CRC 校验 */
for(int i = 0; i < bytes / 4; i++)
{
CRCValue = *temp;//暂存数据,避免破坏源数据
CRCValue = BytesSwap32(CRCValue);//高低字节转换
WRITE_REG(hcrc.Instance->DR, CRCValue);
temp = (void*)&pBuff[(i + 1) * 4];
}
CRCValue = READ_REG(hcrc.Instance->DR);//读取 CRC 计算的结果
/* 主机序转换网络序 */
CRCValue = BytesSwap32(CRCValue);
/* 将计算得出 CRC 结果添加到串口发送缓冲区末尾 */
memcpy(pBuff + bytes, &CRCValue, sizeof(CRCValue));
bytes += 4;
}
else
{
return -1;
}
}
/* DMA式发送数据,该函数里会先清除TC位再使能串口 */
if(HAL_UART_Transmit_DMA(huart, pBuff, bytes) != HAL_OK)
{
return -1;
}
return bytes;
}
高低字节转换宏定义
#define BytesSwap16(num) \
( ((num) & 0xFF00U) >> 8U | \
((num) & 0x00FFU) << 8U )
#define BytesSwap32(num) \
( ((num) & 0xFF000000U) >> 24U | \
((num) & 0x00FF0000U) >> 8U | \
((num) & 0x0000FF00U) << 8U | \
((num) & 0x000000FFU) << 24U )
效果
工程文件下载链接