找回密码
立即注册
搜索
热搜: 活动 交友 discuz
发新帖

143

积分

0

好友

12

主题
发表于 2023-9-15 13:52:04 | 查看: 441| 回复: 2 IP:广东省 广电网
最近用到串口通信(UART),需要保证数据完整和准确,怎么做呢,怎么实现数据校验和纠正呢?
用在单片机上的,通过串口控制Lora模块来发送和接收数据。
发表于 2023-9-15 13:56:15 IP:广东省 广电网

我这给你个示例代码, 你可以参考一下,用的CRC校验。

// 定义一个用于计算CRC校验码的函数
// 参数:data是要发送或接收的数据,len是数据的长度
// 返回值:CRC校验码
unsigned char crc8(unsigned char *data, int len) {
  unsigned char crc = 0; // 初始化CRC为0
  int i, j; // 循环变量
  for (i = 0; i < len; i++) { // 遍历每个字节
    crc ^= data[i]; // CRC与当前字节异或
    for (j = 0; j < 8; j++) { // 遍历每个位
      if (crc & 0x80) { // 如果最高位为1
        crc = (crc << 1) ^ 0x31; // 左移一位并与多项式0x31异或
      } else { // 如果最高位为0
        crc = crc << 1; // 左移一位
      }
    }
  }
  return crc; // 返回CRC校验码
}

// 定义一个用于发送数据的函数
// 参数:data是要发送的数据,len是数据的长度
// 返回值:无
void send_data(unsigned char *data, int len) {
  unsigned char crc = crc8(data, len); // 计算CRC校验码
  int i; // 循环变量
  for (i = 0; i < len; i++) { // 遍历每个字节
    send_byte(data[i]); // 调用发送单个字节的函数(需要自己实现)
  }
  send_byte(crc); // 发送CRC校验码
}

// 定义一个用于接收数据的函数
// 参数:data是用于存储接收到的数据的数组,len是数组的长度
// 返回值:接收到的数据的实际长度,如果出错则返回-1
int receive_data(unsigned char *data, int len) {
  unsigned char crc; // 定义一个变量用于存储接收到的CRC校验码
  int i = 0; // 定义一个变量用于记录接收到的数据的长度,并初始化为0
  while (i < len) { // 当没有超过数组长度时循环
    data[i] = receive_byte(); // 调用接收单个字节的函数(需要自己实现)并存储到数组中
    if (data[i] == EOF) { // 如果接收到文件结束符(EOF)
      break; // 跳出循环
    }
    i++; // 数据长度加一
  }
  if (i == len) { // 如果数组已满
    return -1; // 返回-1表示出错
  }
  crc = receive_byte(); // 接收CRC校验码
  if (crc != crc8(data, i)) { // 如果计算出的CRC校验码与接收到的不一致
    return -1; // 返回-1表示出错
  }
  return i; // 返回接收到的数据的实际长度
}
发表于 2023-9-15 17:46:50 IP:中国广东省 中国电信公众宽带

在串口通信中,常用的数据校验和纠错算法包括CRC校验、奇偶校验和循环冗余校验(CRC)等。本文以CRC校验为例,给出单片机上串口发送和接收的代码实现。

CRC校验是一种循环冗余校验算法,常用于数据通信的完整性验证。具体步骤如下:

  1. 首先,确定一个生成多项式G(x)。这里我们选用CRC16-CCITT生成多项式,其二进制表示为:x^16 + x^12 + x^5 + 1,即0x1021。
  2. 对待校验数据进行处理,首先在数据末尾添加16个0,得到新的数据位串M(x)。
  3. 将生成多项式G(x)的最高次数项的系数置为1,并将其与M(x)进行除法运算,得到余数R(x)。
  4. 将余数R(x)添加到原始数据的末尾,得到新的数据位串T(x)。
  5. 在发送数据和接收数据时,将CRC校验和添加到数据的末尾,接收方在接收到数据后,计算CRC校验和,如果计算出来的CRC校验和与接收到的CRC校验和不一致,则说明数据发生错误。

下面是用于串口发送和接收的数据校验纠错代码,以STM32为例:

#include "stm32f10x.h"
#include "stdio.h"

// 定义生成多项式G(x)
#define CRC16_CCITT 0x1021

// 初始化CRC寄存器
void CRC_Init(void)
{
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE);
}

// 计算CRC校验和
uint16_t CRC_Calculate(uint8_t *pBuffer, uint16_t length)
{
    uint16_t i, j;
    uint16_t crc = 0xFFFF;
    for (i = 0; i < length; i++)
    {
        crc ^= pBuffer[i];
        for (j = 0; j < 8; j++)
        {
            if ((crc & 0x0001) != 0)
            {
                crc = (crc >> 1) ^ CRC16_CCITT;
            }
            else
            {
                crc = crc >> 1;
            }
        }
    }
    return crc;
}

// 串口发送函数
void UART_SendData(uint8_t *pBuffer, uint16_t length)
{
    uint16_t crc = CRC_Calculate(pBuffer, length); // 计算CRC校验和
    pBuffer[length] = (crc >> 8) & 0xFF; // 添加高8位校验和
    pBuffer[length + 1] = crc & 0xFF; // 添加低8位校验和
    length += 2; // 更新数据长度
    USART_SendData(USART1, 0x55); // 发送帧头
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    for (uint16_t i = 0; i < length; i++)
    {
        USART_SendData(USART1, pBuffer[i]); // 发送数据
        while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    }
    USART_SendData(USART1, 0xAA); // 发送帧尾
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

// 串口接收函数
uint16_t UART_ReceiveData(uint8_t *pBuffer)
{
    uint16_t length = 0;
    uint8_t data;
    uint16_t crc = 0xFFFF;
    while (1)
    {
        // 等待帧头
        while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
        data = USART_ReceiveData(USART1);
        if (data != 0x55)
        {
            continue; // 如果不是帧头,继续等待
        }
        // 接收数据
        while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
        data = USART_ReceiveData(USART1);
        pBuffer[length++] = data;
        crc ^= data;
        while (length < 255)
        {
            while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
            data = USART_ReceiveData(USART1);
            if (data == 0xAA)
            {
                // 如果是帧尾,计算CRC校验和
                uint16_t crc_received = 0;
                while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
                data = USART_ReceiveData(USART1);
                crc_received = (data << 8) & 0xFF00;
                while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
                data = USART_ReceiveData(USART1);
                crc_received |= data & 0x00FF;
                if (crc == crc_received)
                {
                    // 如果CRC校验和正确,则说明数据完整无误
                    return length - 2;
                }
                else
                {
                    // 如果CRC校验和错误,则说明数据有误,丢弃数据
                    length = 0;
                    break;
                }
            }
            pBuffer[length++] = data;
            crc ^= data;
        }
        // 如果数据长度超过255,则说明数据有误,丢弃数据
        length = 0;
    }
}

在上述代码中,CRC_Init()函数用于初始化CRC计算器,CRC_Calculate()函数用于计算CRC校验和,UART_SendData()函数用于发送带有CRC校验和的数据,UART_ReceiveData()函数用于接收数据并进行CRC校验。需要注意的是,由于CRC校验和占用了2个字节,因此在发送和接收数据时需要在原始数据的末尾添加2个字节的校验和。

以上代码仅供参考,具体实现还需要根据具体应用场景进行调整。

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|国产电子社区 ( 沪ICP备2023018578号-1|

苏公网安备 32011102010465号


)|网站地图

GMT+8, 2024-7-27 17:50 , Processed in 0.063528 second(s), 25 queries , MemCached On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表