STM32--CRC学习笔记

进行CRC校验的原因

CRC(循环冗余校验)是一种在数据传输或存储过程中常用的错误检测技术。它通过计算数据的校验值,并将其附加到原始数据中,以便在接收端验证数据的完整性和准确性。
以下是一些进行CRC校验的原因:
1. 数据完整性验证:在数据传输过程中,数据可能会受到干扰、损坏或篡改,而CRC校验可以帮助检测这些错误,确保数据在传输过程中没有发生变化。
2. 错误检测:CRC校验可以有效地检测到数据传输过程中发生的单比特错误、多比特错误或位错。
3. 数据识别:CRC校验码可以帮助识别数据是否在传输过程中发生了意外的变化,从而防止数据被恶意篡改。
4. 数据完整性保护:通过在数据中添加CRC校验码,可以提高数据的完整性和可靠性,确保数据在传输或存储过程中不会受到破坏。
总的来说,CRC校验是一种有效的数据完整性保护机制,可以在数据传输或存储过程中提供额外的安全性和可靠性保障。

一、奇偶校验

奇校验为例,如果数据中1的个数为奇数,则奇校验位0,否则为1。
例如原始数据为:0001 0011,数据中1的个数(或各位相加)为3,所以奇校验位为0。这种校验方法很简单,但这种校验方法有很大的误码率。假设由于传输过程中的干扰,接收端接收到的数据是0010 0011,通过奇校验运算,得到奇校验位的值为0,虽然校验通过,但是数据已经发生了错误。

二、循环冗余校验(CRC)

循环冗余校验的核心数学算法原理基于循环码,在不增加原始数据的信息基础上扩展了信息,以极小的存储代价存储其冗余特征。

41721202d65c4742a439442e103e2f2f.png

  • 这里的n位二进制数据为有效信息载荷。(可能是传输或存储的有用信息)
  • 根据CRC算法计算出m位冗余码,即根据该CRC校验多项式结合CRC算法从前面有效数据中提取出特征冗余码,这就是冗余的真实含义。
  • 实际传输或者存储的就是n+m位二进制数据。
  • 发送码串的时候,在原始码串后面加上CRC。CRC一般有8bit,16bit,和32bit。

1.算法原理及处理过程

多项式

5622142deb4242f98dcef7bfe2bdf192.png

其本质就是多进制的数学表示法,这里是二进制,故X的值为2。

XOR异或操作

8711632d3f48414d88e6a00aac9133ab.png

模二加法:0+0=0 0+1=1 1+0=1 1+1=0;

模二减法:0-0=0 0-1=1 1-0=1 1-1=0;

算法处理过程

cb4f1ed9ccc741ddbdc8d7dad31b6a8d.png

待发送有效数据为二进制多项式M(x),而校验多项式P(x)为收发双方约定好了的,双方已知。

计算过程

17e9d7a708514fa7a913c467029448c7.png

2.计算实例

计算冗余码C(X)

16b0a82d35844b97a60ef9f09f285e0d.png

验证

余数为0则校验通过,否则校验不通过

5bcc15d31eaa498a986f72e10225394a.png

三、硬件CRC校验(32位)

以下部分未声明的都基于硬件:STM32F103C8T6   软件:Keil5   函数:HAL库
也有可能会使用其他硬件或者不同的库编程,我会提前注明。

1.硬件CRC校验的定义

硬件CRC校验是一种通过专用的硬件模块来计算数据的循环冗余校验(CRC)值的方法。在微控制器中,通常会集成一个硬件CRC模块,它可以在硬件层面上执行CRC校验,而不需要CPU的直接干预。
硬件CRC校验通常具有以下优点:
1. 高效性:硬件CRC模块通常能够以较高的速度执行CRC校验操作,这是因为它们使用了专用的硬件电路来执行计算,而不需要CPU参与。
2. 低功耗:由于硬件CRC模块可以在不占用CPU资源的情况下执行计算,因此它们通常能够在低功耗模式下执行CRC校验操作。
3. 精确性:硬件CRC模块通常能够以固定的时钟速率执行CRC校验操作,这有助于提高校验结果的准确性。
4. 灵活性:一些硬件CRC模块允许用户配置CRC多项式、初始值和输入/输出数据的位序,从而使得它们适用于不同的应用场景。
在嵌入式系统中,硬件CRC校验通常被用于数据通信、存储器校验、数据完整性验证等方面。通过利用硬件CRC模块,可以加速CRC校验过程,并减少对CPU资源的占用,从而提高系统性能和效率。

2.硬件CRC单次校验计算

实验步骤

本节实验利用硬件CRC计算单元,使用HAL_CRC_Calculate(按字传)函数,对数据进行单次计算,然后在网上找个在线的CRC计算工具(CRC(循环冗余校验)在线计算_ip33.com),二者数据进行对比,看是否一样。

代码

crc.c

#include "stm32f1xx_hal.h"

// 定义一个CRC_HandleTypeDef结构体的实例,用于管理CRC模块
CRC_HandleTypeDef crc;

// CRC初始化函数,用于配置CRC模块的参数并初始化
void CRC_Init(void)
{
    // 设置CRC_HandleTypeDef结构体实例的Instance成员为CRC外设的地址
    crc.Instance = CRC;
    // 调用HAL库提供的函数,初始化CRC模块
    HAL_CRC_Init(&crc);
}
 
// CRC模块的初始化回调函数
void HAL_CRC_MspInit(CRC_HandleTypeDef *hcrc)
{
    // 启用CRC外设的时钟,以便可以使用CRC功能
    __HAL_RCC_CRC_CLK_ENABLE();
    // 这个回调函数会在HAL_CRC_Init()函数中被调用
    // 在这里,我们只需要开启CRC的时钟即可,不需要做其他的初始化工作
}

crc.h

#ifndef __CRC_H
#define __CRC_H

void CRC_Init(void);                // 声明 CRC 初始化函数

extern CRC_HandleTypeDef crc;      // 声明 CRC 外设的句柄变量,该变量在其他文件中定义

#endif 

main.c

#include "stm32f1xx_hal.h"   // 包含STM32 HAL库的头文件
#include "rcc.h"             // 包含RCC模块的头文件
#include "uart.h"            // 包含UART模块的头文件
#include "crc.h"             // 包含CRC模块的头文件

uint32_t buff[4] = {0x01020304, 0x05060708, 0x090A0B0C, 0x0D0E0F00}; // 定义一个包含4个32位数据的数组

int main(void)
{
    HAL_Init();                 // 初始化HAL库
    RccClock_Init();            // 初始化系统时钟
    U1_Init(9600);              // 初始化UART1串口,波特率设置为9600
    CRC_Init();                 // 初始化CRC模块

    // 使用CRC模块计算数据缓冲区buff的CRC校验值,并通过UART打印输出
    u1_printf("%x\r\n", HAL_CRC_Calculate(&crc, buff, 4));
    
    while (1)
    {
        // 程序进入主循环,持续运行
    }   
}

实验结果

每按下一次复位键都会输出一次CRC的校验结果,对比之后发现CRC结果与CRC计算器()一致。

fbe216221ed743afb7fd9fa07c6991b1.png

b86f1fb2c92a43e4b2e3be37c698e001.png

3.硬件CRC连续校验计算

内存量不够的时候分多次连续校验。

实验步骤

本节实验我们在上一节单次计算的基础上,利用HAL_CRC_Accumulate函数进行连续多次计算。
注意:连续多次计算时,第一次用:HAL CRC Calculate函数(初始值就是硬件CRC的固定初始值)后面计算用HAL CRC Accumulate函数(将上一次的计算结果作为本次初始值)。

代码

只需要修改main.c部分。

main.c

#include "stm32f1xx_hal.h"   // 包含STM32 HAL库的头文件
#include "rcc.h"             // 包含RCC模块的头文件
#include "uart.h"            // 包含UART模块的头文件
#include "crc.h"             // 包含CRC模块的头文件

uint32_t buff[4] = {0x01020304, 0x05060708, 0x090A0B0C, 0x0D0E0F00}; // 四个字

int main(void)
{
    HAL_Init();             // 初始化HAL库
    RccClock_Init();        // 初始化系统时钟
    U1_Init(9600);          // 初始化UART1串口,波特率设置为9600
    CRC_Init();             // 初始化CRC模块
    
    // 单次校验
    u1_printf("%x\r\n", HAL_CRC_Calculate(&crc, buff, 4));
    
    // 重置CRC模块的状态
    crc.Instance->CR |= CRC_CR_RESET;
    
    // 连续校验
    HAL_CRC_Calculate(&crc, &buff[0], 1);        // 对第一个数据进行单独校验
    HAL_CRC_Accumulate(&crc, &buff[1], 1);       // 累加第二个数据
    HAL_CRC_Accumulate(&crc, &buff[2], 1);       // 累加第三个数据
    u1_printf("%x\r\n", HAL_CRC_Accumulate(&crc, &buff[3], 1)); // 对第四个数据进行校验并输出结果
    
    while (1)
    {
        // 主循环,程序会一直停留在这里
    }
}

实验结果
8cf5c7b40eee4299b67767609fea1954.png

4.硬件CRC校验(接收数据校验后再发送)

试验步骤

在程序中添加函数,我们手动发送数据,串口接收之后进行CRC硬件校验,然后再将其校验结果发送出来。

代码

只需要修改main.c部分即可

main.c

#include "stm32f1xx_hal.h"   // 包含STM32 HAL库的头文件
#include "rcc.h"             // 包含RCC模块的头文件
#include "uart.h"            // 包含UART模块的头文件
#include "crc.h"             // 包含CRC模块的头文件

#define BUFFER_SIZE 4        // 定义数据缓冲区大小为4字节

uint32_t buff[BUFFER_SIZE];  // 数据缓冲区,存放接收到的数据

// 将32位数据的字节顺序调整为小端序,并打印出来
void print_little_endian(uint32_t data) {
    uint8_t *ptr = (uint8_t *)&data;
    u1_printf("%02X%02X%02X%02X ", ptr[0], ptr[1], ptr[2], ptr[3]);
}

int main(void)
{
    HAL_Init();               // 初始化HAL库
    RccClock_Init();          // 初始化系统时钟
    U1_Init(9600);            // 初始化UART1串口,波特率设置为9600
    CRC_Init();               // 初始化CRC模块
    
    while (1)
    {
        // 等待接收数据
        while (HAL_UART_Receive(&huart1, (uint8_t *)buff, sizeof(buff), HAL_MAX_DELAY) != HAL_OK);
        
        // 打印接收到的数据(按照小端序)
        for (int i = 0; i < BUFFER_SIZE; i++) {
            print_little_endian(buff[i]);
        }
        u1_printf("\r\n");
        
        // 将小端序数据转换为大端序
        for (int i = 0; i < BUFFER_SIZE; i++) {
            buff[i] = __REV(buff[i]);
        }
        
        // 计算CRC校验值并通过UART发送
        u1_printf("CRC: %x\r\n", HAL_CRC_Calculate(&crc, (uint32_t *)buff, BUFFER_SIZE));
    }
}

实验结果 

接收数据和CRC校验结果都正确。

8d514e69fe284ff28714a3893ee0e3e1.png

数据的处理过程

  1. 初始化

    • 调用 HAL_Init() 初始化 HAL 库。
    • 调用 RccClock_Init() 初始化系统时钟。
    • 调用 U1_Init(9600) 初始化 UART1 串口,设置波特率为 9600。
    • 调用 CRC_Init() 初始化 CRC 模块。
  2. 接收数据

    • 进入 while(1) 主循环后,通过 HAL_UART_Receive() 函数等待接收数据。一旦接收到数据,将其存储到名为 buff 的数据缓冲区中。
    • 接收的数据长度为 BUFFER_SIZE 个 32 位字(4字节),这是通过 sizeof(buff) 的方式得到的。
    • 此循环使用 HAL_MAX_DELAY 作为超时参数,表示无限等待,直到成功接收到数据为止。
  3. 打印接收到的数据

    • 使用 print_little_endian() 函数按照小端序打印接收到的数据。在这个函数中,将每个 32 位字节数据拆分为 4 个字节,并按照小端序顺序打印。
    • 每个字节打印为两位十六进制数,并以空格分隔,形成一行数据。
    • 在每次接收到完整的数据后,会打印一行数据,然后换行。
  4. 进行 CRC 校验

    • 使用 HAL_CRC_Calculate() 函数对 buff 数组中的数据进行 CRC 校验。
    • 将计算得到的 CRC 校验值通过 UART 发送出去。

5.CubeMax配置硬件CRC校验

在此只需要配置好时钟、串口和CRC即可。

创建工程
3574766db64d4345b0941a685d6b35bf.png
1e50301bbdba43369140cf0ceebdd77a.png

配置时钟RCC81925f7a400e48a5a5c89cb4a8cb838c.png
d65c2fc4f8214d88b36f853d0eba2f5a.png

SYS0ac16c14d9024e3f9881d0a4f8000aae.png

UART
9f922861fbb94227b2ab07aafc2a25aa.png3cf09ccedf694eca808aec426e4baffa.png

CRC
7bf4347e40814cfbbd6cdfc3fda57f54.png

文件配置
d14a933d9ba7487e9037cf65f6f691a0.png
1d131af98ebe403bbf093bfb67f00730.png
86e3bc87178d4957ae9d0bb8cd6110a5.png

代码

在程序中我只修改了main.c部分,把打印函数插入生成的程序中
注意:自己写的程序写入在有begin和end的之间,否则再次用Cubemax时会删除我们所写的程序。

main.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint32_t buff[4] = {0x01020304, 0x05060708, 0x090A0B0C, 0x0D0E0F00}; // 四个字
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
    u1_printf("%x\r\n", HAL_CRC_Calculate(&crc, buff, 4));
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{ 
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

实验结果
660a48a4f1a843c988793de5ef1bd46e.png

注意:串口波特率这次设置的是115200,把串口助手的波特率设置的与程序一致才可以试验成功。

四、软件CRC校验

软件CRC校验的原因

在本文章中我们使用的硬件为STM32F103C8T6,在这个芯片中,硬件CRC校验只可以实现32位的CRC校验,如果工程的通讯要求是16位或者8位的,就只能用软件CRC校验了,以下我也做了16位和8位的程序烧录,在过程中会改变多项式、初始值、异或值或者输入输出的数据反转。
当然如果选用的芯片本就可以实现16位或者8位的CRC校验来满足要求,那就可以直接配置硬件CRC来完成,毕竟硬件CRC的数据校验要更快。

1.软件CRC单次校验计算

实验步骤

按字节计算,利用while循环一个字节一个字节计算。(硬件CRC校验是按字来校验)
软件CRC校验计算多项式初值可以自由设定,灵活性高。
代码步骤:
(1)取一个字节数据,因为是CRC32,将该字节左移到最高处。
(2)左移到最高处后和初值异或,结果变为新初值。
(3)利用for循环,循环8次,处理新初值中每个二进制位,如果是1,新初值左移1位后和多项式异或,结果是再新一次的初值,如果是0,仅左移,不同多项式异或。
(4)再去取下一个字节重复操作,直到while循环处理完所有数据,将最终的新初值最为校验结果值返回。

代码

crc.c

#include "stm32f1xx_hal.h"  

// 初始化CRC
CRC_HandleTypeDef crc;
void CRC_Init(void)
{
    crc.Instance = CRC;
	HAL_CRC_Init(&crc);
}

// CRC模块的初始化回调函数
void HAL_CRC_MspInit(CRC_HandleTypeDef *hcrc)
{
    __HAL_RCC_CRC_CLK_ENABLE(); // 开启CRC模块的时钟
}

// 计算CRC32校验值
// 参数:
//   - data: 待计算CRC的数据指针
//   - len: 数据长度
//   - init: CRC初始值
// 返回值:
//   - uint32_t类型的CRC32校验值
uint32_t CRC32(uint8_t *data, uint16_t len, uint32_t init)
{
    uint32_t poly = 0x04C11DB7; // CRC32多项式
    uint8_t i;
	
    // 遍历数据字节,计算CRC32校验值
    while (len--)
    {
        init = init ^ (*data << 24);
        for (i = 0; i < 8; i++)
        {
            if (init & 0x80000000) // 如果当前最高位是1
            {
                init = (init << 1) ^ poly; // 左移一位并与多项式异或
            }
            else // 如果当前最高位是0
            {
                init = (init << 1); // 左移一位
            }
        }
        data++; // 指向下一个数据字节
    }
    return init; // 返回计算得到的CRC32校验值
}

crc.h

#ifndef __CRC_H
#define __CRC_H

#include "stm32f1xx_hal_crc.h" 

extern CRC_HandleTypeDef crc; // CRC模块句柄声明

// CRC模块初始化函数声明
void CRC_Init(void);

// CRC32校验函数声明
// 参数:
//   - data: 待计算CRC的数据指针
//   - len: 数据长度
//   - init: CRC初始值
// 返回值:
//   - uint32_t类型的CRC32校验值
uint32_t CRC32(uint8_t *, uint16_t, uint32_t);

#endif

main.c

#include "stm32f1xx_hal.h"  // 包含STM32 HAL库的头文件
#include "rcc.h"            // 包含RCC模块的头文件
#include "uart.h"           // 包含UART模块的头文件
#include "crc.h"            // 包含CRC模块的头文件

uint32_t buff[4] = {0x01020304, 0x05060708, 0x090A0B0C, 0x0D0E0F00}; // 定义一个包含4个32位数据的数组
uint8_t buff2[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,0}; // 定义一个包含16个字节数据的数组

int main(void)
{
    HAL_Init();          // 初始化HAL库
    RccClock_Init();     // 初始化系统时钟
    U1_Init(9600);       // 初始化UART1串口,波特率设置为9600
    CRC_Init();          // 初始化CRC模块
    
    // 使用CRC模块计算数据缓冲区buff的CRC校验值,并通过UART打印输出
    u1_printf("单次校验结果:%x\r\n", HAL_CRC_Calculate(&crc, buff, 4));
    
    // 对数据缓冲区buff的每个元素逐个进行CRC校验累加
    HAL_CRC_Calculate(&crc, &buff[0], 1); // 对第1个元素进行CRC校验
    HAL_CRC_Accumulate(&crc, &buff[1], 1); // 对第2个元素进行CRC校验累加
    HAL_CRC_Accumulate(&crc, &buff[2], 1); // 对第3个元素进行CRC校验累加
    u1_printf("连续校验结果:%x\r\n", HAL_CRC_Accumulate(&crc, &buff[3], 1)); // 对第4个元素进行CRC校验累加并打印输出
    
    // 使用CRC32函数计算数据缓冲区buff2的CRC32校验值,并通过UART打印输出
    u1_printf("CRC32校验结果:%x\r\n", CRC32(buff2, 16, 0xFFFFFFFF));
    
    while (1)
    {
        // 程序进入主循环,持续运行
    }
}

实验结果

在软件校验的初始值和生成多项式和硬件CRC相同时,软件CRC校验的结果和硬件CRC校验以及CRC计算器校验结果相同。

701c7c0db3aa4d81ba0ca82aca402b22.png

2.软件CRC连续校验计算

代码

只需要修改main.c部分即可。

main.c

#include "stm32f1xx_hal.h"  // 包含STM32 HAL库的头文件
#include "rcc.h"            // 包含RCC模块的头文件
#include "uart.h"           // 包含UART模块的头文件
#include "crc.h"            // 包含CRC模块的头文件

uint32_t buff[4] = {0x01020304, 0x05060708, 0x090A0B0C, 0x0D0E0F00}; // 定义一个包含4个32位数据的数组
uint8_t buff2[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0}; // 定义一个包含16个字节数据的数组
uint32_t res; // 存储CRC32计算结果

int main(void)
{
    HAL_Init();          // 初始化HAL库
    RccClock_Init();     // 初始化系统时钟
    U1_Init(9600);       // 初始化UART1串口,波特率设置为9600
    CRC_Init();          // 初始化CRC模块
    
    // 使用CRC模块计算数据缓冲区buff的CRC校验值,并通过UART打印输出
    u1_printf("单次校验结果:%x\r\n", HAL_CRC_Calculate(&crc, buff, 4));
    
    // 对数据缓冲区buff的每个元素逐个进行CRC校验累加
    HAL_CRC_Calculate(&crc, &buff[0], 1); // 对第1个元素进行CRC校验
    HAL_CRC_Accumulate(&crc, &buff[1], 1); // 对第2个元素进行CRC校验累加
    HAL_CRC_Accumulate(&crc, &buff[2], 1); // 对第3个元素进行CRC校验累加
    u1_printf("连续校验结果:%x\r\n", HAL_CRC_Accumulate(&crc, &buff[3], 1)); // 对第4个元素进行CRC校验累加并打印输出
    
    // 使用CRC32函数计算数据缓冲区buff2的CRC32校验值,并通过UART打印输出
    u1_printf("软件CRC32单次校验结果:%x\r\n", CRC32(buff2, 16, 0xFFFFFFFF));
    
    // 对数据缓冲区buff2进行分块CRC32校验
    res = CRC32(&buff2[0], 4, 0xFFFFFFFF); // 对前4个字节进行CRC32校验
    res = CRC32(&buff2[4], 4, res); // 对接下来的4个字节进行CRC32校验并累加到前面的结果中
    res = CRC32(&buff2[8], 4, res); // 对接下来的4个字节进行CRC32校验并累加到前面的结果中
    u1_printf("软件CRC连续校验结果:%x\r\n", CRC32(&buff2[12], 4, res)); // 对最后的4个字节进行CRC32校验并打印输出
    
    while (1)
    {
        // 程序进入主循环,持续运行
    }
}

实验结果

c70211ef270e4d889bfbab2a31d8309c.png

3.使用软件CRC校验16位CRC

实验步骤

修改生成多项式和初始值,即可进行16位CRC的校验

代码

crc.c

#include "stm32f1xx_hal.h"  

CRC_HandleTypeDef crc;

// CRC初始化函数
void CRC_Init(void)
{
    crc.Instance = CRC;
	HAL_CRC_Init(&crc);
}

// CRC模块的初始化回调函数
void HAL_CRC_MspInit(CRC_HandleTypeDef *hcrc)
{
    __HAL_RCC_CRC_CLK_ENABLE(); // 开启CRC模块的时钟
}

// 计算CRC16校验值
// 参数:
//   - data: 待计算CRC的数据指针
//   - len: 数据长度
//   - init: CRC初始值
// 返回值:
//   - uint16_t类型的16位CRC16校验值
uint16_t CRC16(uint8_t *data, uint16_t len, uint16_t init)
{
    uint16_t poly = 0x8005; // CRC16多项式
    uint8_t i;

    // 遍历数据字节,计算CRC16校验值
    while (len--)
    {
        init ^= (*data << 8);
        for (i = 0; i < 8; i++)
        {
            if (init & 0x8000) // 如果当前最高位是1
            {
                init = (init << 1) ^ poly; // 左移一位并与多项式异或
            }
            else // 如果当前最高位是0
            {
                init = (init << 1); // 左移一位
            }
        }
        data++; // 指向下一个数据字节
    }
    return init; // 返回计算得到的CRC16校验值
}

crc.h

#ifndef __CRC_H
#define __CRC_H

#include "stm32f1xx_hal_crc.h" 

extern CRC_HandleTypeDef crc;

void CRC_Init(void);

uint16_t CRC16(uint8_t *data, uint16_t len, uint16_t init);

#endif

main.c

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "uart.h"
#include "crc.h"

// 声明变量buff2
uint8_t buff2[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};

int main(void)
{
    HAL_Init();
    RccClock_Init();
    U1_Init(9600);
    CRC_Init();
    
	u1_printf("%x\r\n", CRC16(buff2, 16, 0xFFFF)); 
    
    while(1)
    {
    }
}

实验结果

在上程序中未添加输入数据输出数据反转的功能,故在CRC计算器中我先自定义了计算方法如下图发现校验结果一致,因此代码实验正确。

cf2ec046309543ed85e5ab63b7427dd4.png

153d5e2eabae4312a4a63e32aea994d8.png

4.使用软件CRC校验8位CRC

实验步骤

修改生成多项式和初始值,即可进行8位CRC的校验

代码

crc.c

#include "stm32f1xx_hal.h"  

CRC_HandleTypeDef crc;

// CRC初始化函数
void CRC_Init(void)
{
    crc.Instance = CRC;
	HAL_CRC_Init(&crc);
}

// CRC模块的初始化回调函数
void HAL_CRC_MspInit(CRC_HandleTypeDef *hcrc)
{
    __HAL_RCC_CRC_CLK_ENABLE(); // 开启CRC模块的时钟
}

// 计算CRC-8校验值
// 参数:
//   - data: 待计算CRC的数据指针
//   - len: 数据长度
//   - init: CRC初始值
// 返回值:
//   - uint8_t类型的8位CRC-8校验值
uint8_t CRC8(uint8_t *data, uint16_t len, uint8_t init)
{
    uint8_t poly = 0x07; // CRC-8多项式
    uint8_t i;

    // 遍历数据字节,计算CRC-8校验值
    while (len--)
    {
        init ^= *data++;
        for (i = 0; i < 8; i++)
        {
            if (init & 0x80) // 如果当前最高位是1
            {
                init = (init << 1) ^ poly; // 左移一位并与多项式异或
            }
            else // 如果当前最高位是0
            {
                init <<= 1; // 左移一位
            }
        }
    }
    return init; // 返回计算得到的CRC-8校验值
}

crc.h

#ifndef __CRC_H
#define __CRC_H

#include "stm32f1xx_hal_crc.h" 

extern CRC_HandleTypeDef crc;

void CRC_Init(void);

uint8_t CRC8(uint8_t *data, uint16_t len, uint8_t init);

#endif

main.c

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "uart.h"
#include "crc.h"

// 声明变量buff2
uint8_t buff2[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};

int main(void)
{
    HAL_Init();
    RccClock_Init();
    U1_Init(9600);
    CRC_Init();
    
	u1_printf("%x\r\n", CRC8(buff2, 16, 0)); 
    
    while(1)
    {
    }
}

实验结果
2609639983054c2bac993e860f7bf906.png
dca3923b3ff246368085a246779cf7fd.png

5.32位软件CRC校验的数据反转

原因

在工程中我想使用CRC-16/MODBUS进行485通讯,很明显在第三节我的初始值、多项式以及异或值都和CRC-16/MODBUS一致,但是CRC-16/MODBUS有输入数据输出数据反转,故此节来写输入数据输出数据反转的功能。
10ee3504732046708d9de4beb3f73b59.png

实验步骤

本节实验我们在上一节软件CRC32校验计算程序基础上,增加数据反转函数,适用于更多种情形下的CRC32校验方式。
注意:数据翻转包含:
(1)待校验的数据,先反转后再参与计算
(2)校验后的结果数据,先反转后再return返回
注意:反转指的是低位变高位,高位变低位,比如按字节翻转,原来的位0成为了位7位1成为位6,原来高位的位7成为位0,位6变成位1,以此类推。

2870731475d045b189d224baf5bda039.png

代码

crr.c

#include "stm32f1xx_hal.h" 
#include "crc.h" 

// 初始化CRC
CRC_HandleTypeDef crc;

void CRC_Init(void)
{
    crc.Instance = CRC;
	HAL_CRC_Init(&crc);
}

// CRC模块的初始化回调函数
void HAL_CRC_MspInit(CRC_HandleTypeDef *hcrc)
{
    __HAL_RCC_CRC_CLK_ENABLE(); // 开启CRC模块的时钟
}

// 计算CRC32校验值
// 参数:
//   - data: 待计算CRC的数据指针
//   - len: 数据长度
//   - init: CRC初始值
// 返回值:
//   - uint32_t类型的CRC32校验值
uint32_t CRC32(uint8_t *data, uint16_t len, uint32_t init)
{
	uint32_t poly = 0x04C11DB7; // CRC32多项式
	uint8_t i;
	
	while(len--)
	{
		//init = init ^ (*data<<24); // 用这个输入数据不反转
		init = init ^ (InverUint8(*data)<<24); // 初始值异或输入数据,同时进行输入反转,用这个输入数据反转
		for(i=0;i<8;i++)
		{
			if(init&0x80000000) // 如果当前最高位是1
			{
				init =(init<<1)^poly; // 左移一位并与多项式异或
			}
			else // 如果当前最高位是0
			{
				init =(init<<1); // 左移一位
			}
		}
		data++; // 指向下一个数据字节
	}
//	return init;//用这个输出数据不反转
	return InverUint32(init); // 返回计算得到的CRC32校验值,同时进行输出反转,用这个输出数据反转
}

// 反转8位数据
uint8_t InverUint8(uint8_t data)
{
	uint8_t i;
	uint8_t temp;
	
	temp = 0;
	for(i = 0; i < 8; i++)
	{
		if(data & (1 << i))
		{
			temp |= 1 << (7 - i);
		}
	}
	return temp ;
}

// 反转32位数据
uint32_t InverUint32(uint32_t data)
{
	uint8_t i;
	uint32_t temp;
	
	temp = 0;
	for(i = 0; i < 32; i++)
	{
		if(data & (1 << i))
		{
			temp |= 1 << (31 - i);
		}
	}
	return temp ;
}

crc.h

#ifndef __CRC_H
#define __CRC_H

#include "stm32f1xx_hal_crc.h" 

extern CRC_HandleTypeDef crc;

void CRC_Init(void);

uint32_t CRC32(uint8_t *,uint16_t ,uint32_t );
uint8_t InverUint8(uint8_t data);
uint32_t InverUint32(uint32_t data);

#endif

main.c

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "uart.h"
#include "crc.h"

uint8_t buff2[16]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0};

int main(void)
{
	HAL_Init();
	RccClock_Init();
	U1_Init(9600);
	CRC_Init();
	
	u1_printf("%x\r\n",CRC32(buff2,16,0xFFFFFFFF));
	
	while(1)
	{
	}
	
}

实验结果

分别进行了输入数据和输出数据反转的实验,结果都一致。

8e31a8dbdd054f46965f248ede50cfd1.png
输入数据和输出数据都不反转
59c170a285474f199ab43e3a0447d6d3.png
输入数据反转
b8483adefc8a42b6a08ee623020e6764.png
输出数据反转
ed2243d824e64ea784138e249000b55b.png
输入数据和输出数据都反转
97b0e14bef6a4dfab55e9285ff6fa331.png

6.16位软件CRC校验的数据反转

实验步骤

对第三节的16位CRC校验程序改写

代码

crc.c

#include "stm32f1xx_hal.h"  
#include "crc.h" 

CRC_HandleTypeDef crc;

// CRC初始化函数
void CRC_Init(void)
{
    crc.Instance = CRC;
	HAL_CRC_Init(&crc);
}

// CRC模块的初始化回调函数
void HAL_CRC_MspInit(CRC_HandleTypeDef *hcrc)
{
    __HAL_RCC_CRC_CLK_ENABLE(); // 开启CRC模块的时钟
}

// 计算CRC16校验值
// 参数:
//   - data: 待计算CRC的数据指针
//   - len: 数据长度
//   - init: CRC初始值
// 返回值:
//   - uint16_t类型的16位CRC16校验值
uint16_t CRC16(uint8_t *data, uint16_t len, uint16_t init)
{
    uint16_t poly = 0x8005; // CRC16多项式
    uint8_t i;

    // 遍历数据字节,计算CRC16校验值
    while (len--)
    {
		// init ^= (*data << 8);//不反转
		init ^= (InverUint8(*data) << 8);//反转
        for (i = 0; i < 8; i++)
        {
            if (init & 0x8000) // 如果当前最高位是1
            {
                init = (init << 1) ^ poly; // 左移一位并与多项式异或
            }
            else // 如果当前最高位是0
            {
                init = (init << 1); // 左移一位
            }
        }
        data++; // 指向下一个数据字节
    }
	// return init; // 返回计算得到的CRC16校验值,不反转
	return InverUint16(init);//反转
}

// 反转8位数据
uint8_t InverUint8(uint8_t data)
{
	uint8_t i;
	uint8_t temp;
	
	temp = 0;
	for(i = 0; i < 8; i++)
	{
		if(data & (1 << i))
		{
			temp |= 1 << (7 - i);
		}
	}
	return temp ;
}

// 反转32位数据
uint16_t InverUint16(uint16_t data)
{
	uint8_t i;
	uint16_t temp;
	
	temp = 0;
	for(i = 0; i < 16; i++)
	{
		if(data & (1 << i))
		{
			temp |= 1 << (15 - i);
		}
	}
	return temp ;
}

crc.h

#ifndef __CRC_H
#define __CRC_H

#include "stm32f1xx_hal_crc.h" 

extern CRC_HandleTypeDef crc;

void CRC_Init(void);

uint16_t CRC16(uint8_t *data, uint16_t len, uint16_t init);
uint8_t InverUint8(uint8_t data);
uint16_t InverUint16(uint16_t data);

#endif

main.c

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "uart.h"
#include "crc.h"

// 声明变量buff2
uint8_t buff2[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};

int main(void)
{
    HAL_Init();
    RccClock_Init();
    U1_Init(9600);
    CRC_Init();
    
	u1_printf("%x\r\n", CRC16(buff2, 16, 0xFFFF)); 
    
    while(1)
    {
    }
}

实验结果

实验结果一致。

476fa9d930ca491eaa37452180e57901.png

aea237abf1714c28b472f940533095ac.png
9904a54c802e4a488bf59e2800cc45db.png
ed3e4c08120442de864cd85135ba4e39.png
3a860c4ec096492a893d880f6075b041.png

7.8位软件CRC校验的数据反转

实验步骤

同CRC16

代码

crc.c

#include "stm32f1xx_hal.h"  
#include "crc.h"  

CRC_HandleTypeDef crc;

// CRC初始化函数
void CRC_Init(void)
{
    crc.Instance = CRC;
	HAL_CRC_Init(&crc);
}

// CRC模块的初始化回调函数
void HAL_CRC_MspInit(CRC_HandleTypeDef *hcrc)
{
    __HAL_RCC_CRC_CLK_ENABLE(); // 开启CRC模块的时钟
}

// 计算CRC-8校验值
// 参数:
//   - data: 待计算CRC的数据指针
//   - len: 数据长度
//   - init: CRC初始值
// 返回值:
//   - uint8_t类型的8位CRC-8校验值
uint8_t CRC8(uint8_t *data, uint16_t len, uint8_t init)
{
    uint8_t poly = 0x07; // CRC-8多项式
    uint8_t i;

    // 遍历数据字节,计算CRC-8校验值
    while (len--)
    {
//        init ^= *data++;
		init ^= InverUint8(*data++);
        for (i = 0;i < 8; i++)
        {
            if (init & 0x80) // 如果当前最高位是1
            {
                init = (init << 1) ^ poly; // 左移一位并与多项式异或
            }
            else // 如果当前最高位是0
            {
                init <<= 1; // 左移一位
            }
        }
    }
//    return init; // 返回计算得到的CRC-8校验值
	return InverUint8(init); // 返回计算得到的CRC32校验值,同时进行输出反转,用这个输出数据反转
}

// 反转8位数据
uint8_t InverUint8(uint8_t data)
{
	uint8_t i;
	uint8_t temp;
	
	temp = 0;
	for(i = 0; i < 8; i++)
	{
		if(data & (1 << i))
		{
			temp |= 1 << (7 - i);
		}
	}
	return temp ;
}

crc.h

#ifndef __CRC_H
#define __CRC_H

#include "stm32f1xx_hal_crc.h" 

extern CRC_HandleTypeDef crc;

void CRC_Init(void);

uint8_t CRC8(uint8_t *data, uint16_t len, uint8_t init);
uint8_t InverUint8(uint8_t data);

#endif

main.c

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "uart.h"
#include "crc.h"

// 声明变量buff2
uint8_t buff2[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};

int main(void)
{
    HAL_Init();
    RccClock_Init();
    U1_Init(9600);
    CRC_Init();
    
	u1_printf("%x\r\n", CRC8(buff2, 16, 0)); 
    
    while(1)
    {
    }
}

实验结果

校验结果对照一致。

c80f39be028047e7b69fb25e14f33f9e.png
a9a7a39a359845179d57a8f96e96cb90.png
4a7305ef9ba94aa99f7cf7e49bd3377f.png
da95d25564aa42abb21e5fe4c1a1600f.png
2435cddf8b6a48698942a45cee3a7022.png

8.软件CRC校验数据反转(添加接收数据校验后再发送)

实验步骤

添加手动发送数据,接收数据之后把原始数据和CRC校验值都发送的功能。

代码

只修改main.c。

main.c

#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "uart.h"
#include "crc.h"

#define BUFFER_SIZE 16 // 定义数据缓冲区大小为16字节

uint8_t buff[BUFFER_SIZE];  // 数据缓冲区,存放接收到的数据

// 打印16位数据的字节顺序为小端序
void print_little_endian(uint16_t data) {
    uint8_t *ptr = (uint8_t *)&data;
    u1_printf("%02X%02X ", ptr[0], ptr[1]);
}

int main(void)
{
    HAL_Init();
    RccClock_Init();
    U1_Init(9600);
    CRC_Init();
    
    while(1)
    {      
       // 等待接收数据
        while (HAL_UART_Receive(&huart1, buff, BUFFER_SIZE, HAL_MAX_DELAY) != HAL_OK);
        
        // 打印接收到的数据(按照小端序)
        u1_printf("Received data: ");
        for (int i = 0; i < BUFFER_SIZE; i+=2) {
            print_little_endian(*(uint16_t*)&buff[i]);
        }
        u1_printf("\r\n");
        
        // 计算CRC16校验值并打印
        u1_printf("CRC16 result: %x\r\n", CRC16(buff, BUFFER_SIZE, 0xFFFF));
    }
}

实验结果

结果一致。

557439271b364d38bd545c1123e94470.png

五、处理过程

若要求某位的CRC校验(8bits\16bits\32bits)
从硬件出发:直接选用具有要求CRC校验功能的芯片(如CRC校验位数、多项式值、初始值、异或值等)。
从软件出发:若硬件配置有限,没有能够满足各项条件的CRC硬件校验,则可以选用软件校验,通过软件校验可以设置所要求的CRC校验位数、多项式值、初始值、异或值等。

这篇笔记是我基于这个视频来学习记录的,特注明!

 STM32 CRC校验篇 章节大纲 HAL库精讲教程_哔哩哔哩_bilibili基础篇教程将完成700+例程源码的编写(HAL库程序),30+思维导图,力求测试HAL库中的每一个API函数以及每一个STM32单片机IO口上的各种复用功能。首先进行的基础篇教程大纲如下:【第 01 章】时钟篇【第 02 章】GPIO基本输入输出篇【第 03 章】串口异步通信篇(含双机+多机通信)【第 04 章】定时器篇【第 05 章】ADC模数转换(含双ADC模式)【第 06 章】I2C 通信(, 视频播放量 1475、弹幕量 0、点赞数 40、投硬币枚数 24、收藏人数 58、转发人数 5, 视频作者 超子说物联网, 作者简介 LoRa和4G旧的分享群满了,可加新分享群8群795476070,相关视频:STM32单片机 硬件CRC32 单次校验计算 HAL库精讲教程,STM32单片机 C语言 软件CRC16 CRC8校验 HAL库精讲教程,stm32 Keil程序源码 BootLoader程序思路流程讲解,STM32单片机 C语言 软件CRC32校验计算 HAL库精讲教程,【STM32+LoRa网关】Keil工程源码,Lora模块参数配置以及参数读取程序讲解,STM32单片机 C语言 软件CRC32 输入输出数据反转 HAL库精讲教程,手把手写【STM32/GD32 入门教程】OTA升级Boot程序 软件实现Xmodem协议CRC16校验,【开源群】【STM32+LoRa(E32)+WiFi+4G 物联网OTA升级】视频教程(1)应用简介,【开源群】STM32上手控制LoRa模块的第一步,STM32 HAL库汇 如何使用外部 HSE 晶振设置系统时钟icon-default.png?t=N7T8https://www.bilibili.com/video/BV1cU411F7T9/?spm_id_from=333.788&vd_source=a721d1e848b89861d89632a0232e63c4

  • 22
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: ​ STM32-STLink是一款常见的STM32单片机的USB调试工具,它可以方便地进行芯片的程序下载、烧录、调试和监测工作。在使用STM32-STLink进行调试工作前,需要先安装STM32-STLink驱动。 STM32-STLink驱动可以在ST官网或软件开发社区中获取。在安装驱动前需要确保电脑已经连接上STM32-STLink,并且已经准备好了相应的开发环境。建议先卸载旧版本的驱动再进行安装。 具体操作步骤如下: 1. 连接STM32-STLink到电脑,确保正常供电和通信。 2. 安装驱动,可以从ST官网或者软件开发社区中下载最新驱动程序。 3. 解压下载的驱动程序,双击运行安装程序。 4. 驱动安装过程中需要注意选择与自己操作系统版本相对应的驱动程序,勾选“核心组件”和“驱动程序”选项,进行安装。 5. 安装完成后,重启电脑,确认驱动程序已经成功安装。 6. 打开开发环境,并连接STM32-STLink,进行调试工作。在调试过程中,可以通过STLink Utility等工具进行芯片的操作。 总之,STM32-STLink驱动是STM32单片机开发必备的驱动程序之一。通过安装该驱动,可以方便地进行芯片的调试工作和程序烧录操作。 ### 回答2: stm32-stlink驱动是一种用于连接STM32单片机与电脑的驱动。STM32是一种低功耗、高性能的32位微控制器,而stlink是一种芯片级调试工具,用于将计算机与STM32单片机连接起来,进行程序的调试和下载。由于stm32-stlink驱动具备高性能、可靠性和灵活性等优势,因此在计算机与STM32单片机之间的数据传输过程中发挥了重要作用。 stm32-stlink驱动可以实现多种不同的功能。例如,在调试过程中,它可以帮助开发者调试程序,并实时监测STM32单片机的运行情况。此外,该驱动还可以实现对单片机的程序下载、烧录、刷写等操作,方便开发者进行快速迭代和测试。 除了实现上述功能之外,stm32-stlink驱动还具备多种其他特点。例如,该驱动可以长时间稳定运行,且不会对系统稳定性产生不利影响。此外,该驱动还具备自动适配的能力,可以适应不同版本的STM32单片机。 在使用stm32-stlink驱动之前,开发者需要根据自己的需要进行安装和配置。具体来说,需要下载适合自己系统版本的stm32-stlink驱动,并在电脑上安装。此外,在进行开发过程中,开发者还需要根据需要对该驱动进行相应的设置和调整,以保障程序的正常运行。 总的来说,stm32-stlink驱动是一种实现STM32单片机与电脑之间数据传输的重要工具,具备多种优势和特点。在进行相关开发工作时,合理配置和使用该驱动可以极大地提高开发效率和程序稳定性。 ### 回答3: STM32-STLINK驱动是用于连接计算机和STM32微控制器开发板之间的软件程序。STM32-STLINK驱动可用于下载代码,调试和仿真微控制器。STM32-STLINK驱动由STMicroelectronics提供,并且是免费提供的。该驱动程序可以在Windows和Linux操作系统上使用,并且支持多种开发工具,例如Keil,IAR和Atollic。 在使用STM32-STLINK驱动之前,需要先将开发板与计算机连接。连接过程可以通过USB连接或JTAG/SWD接口完成。当前,STMicroelectronics生产的大多数STM32微控制器都具有内置的ST-LINK / V2-1调试器,因此无需单独购买ST-LINK外设。只需使用USB线缆将STM32板连接到计算机上,即可使用STM32-STLINK驱动。 一旦STM32开发板和计算机连接成功并且安装了STM32-STLINK驱动,就可以开始进行下载、调试和仿真微控制器的工作了。STM32-STLINK驱动提供了基本的调试工具,例如设置断点、查看变量和跟踪程序执行等功能,以帮助开发人员进行程序调试。此外,该驱动程序还支持许多高级功能,例如实时追踪、功耗分析和定时器分析等,以帮助开发人员更深入地了解和优化代码。 综上所述,STM32-STLINK驱动是连接STM32微控制器和计算机之间的关键软件程序,用于下载、调试和仿真微控制器。它具有易于使用和灵活的优点,是STM32开发的重要工具之一。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值