欢迎光临
我们一直在努力

第 2 篇:SPI 通信协议深度解析|40 位数据包 + STM32 代码实现

#TMC2240 #SPI通信 #STM32实战 #电机驱动 #嵌入式避坑

作者:BackCatK Chen 厦门市电子工程中级工程师

(承接第 1 篇《软件视角扫盲》,关注我解锁全套 TMC2240 驱动实战教程!)


👋 SPI 通信为什么总掉坑?

在 TMC2240 开发中,SPI 通信是新手的 “第一道拦路虎”:

  • 明明接线没错,却读不到寄存器值;

  • 写进去的参数不生效,排查半天找不到原因;

  • 不知道 40 位数据包怎么拼接,读写标识位搞反;

  • SPI 的 CPOL/CPHA 参数配置错误,直接通信失败。

作为深耕 TMC 系列芯片的工程师,我太懂这种 “代码写半天,通信没反应” 的崩溃!这篇文章承接上一篇的软件核心特性,从协议原理到代码实现,再到实战验证,全程保姆级拆解 —— 不用死记硬背时序图,不用纠结寄存器格式,跟着步骤走,就能独立实现 TMC2240 的 SPI 通信,成功读写寄存器,为后续电机控制打牢基础!


✅ 一、TMC2240 SPI 通信核心原理(只讲有用的,不堆砌理论)

要搞定 SPI 通信,关键是吃透 TMC2240 的专属规则 —— 它和普通 SPI 设备的最大区别是40 位数据包格式和读写标识位定义,这两点是通信成功的核心。

1.1 先搞懂 3 个基础概念(新手必懂)

SPI 是 “同步串行通信协议”,靠主设备(STM32)产生的时钟信号(SCK)同步数据传输,TMC2240 作为从设备,只需遵循主设备的时钟节奏即可,核心要点:

  • 通信引脚(STM32 ↔ TMC2240):
STM32(主设备)TMC2240(从设备)功能说明接线注意事项
SPIx_SCK SCK 时钟线(主设备产生) 必须接,时钟同步的核心
SPIx_MOSI MOSI 主发从收(写数据到芯片) 数据从 STM32 传到 TMC2240
SPIx_MISO MISO 主收从发(读芯片数据) 数据从 TMC2240 传到 STM32
GPIO(自定义) CS 片选线(低电平有效) 必须接,选中指定 TMC2240 芯片
GND GND 共地 必接!否则数据乱码、通信失败
  • 时钟极性 / 相位(CPOL/CPHA):

    TMC2240 芯片手册强制要求:CPOL=1,CPHA=1(记死!错了必失败):

    • CPOL=1:时钟线空闲时为高电平;

    • CPHA=1:数据在时钟的第二个边沿(下降沿)采样。

  • 通信速率:TMC2240 支持最高 10MHz,新手建议先设 1MHz~4.5MHz(稳定性优先),后续再根据需求调高。

1.2 核心重点:TMC2240 40 位数据包结构

普通 SPI 通信多是 8 位 / 16 位数据包,但 TMC2240 固定用40 位(5 字节) 数据包,格式和读写规则是核心易错点,用表格和示例讲清楚:

(1)数据包整体格式

40位 = 1字节地址(Addr) + 4字节数据(Data32)

  • 地址字节(8 位):最高位(bit7)是「读写标识位」,低 7 位是「寄存器实际地址」;

  • 数据字节(32 位):大端模式(高位先传),对应 TMC2240 的 32 位寄存器值(比如电流、微步等参数)。

(2)读写标识规则(关键!)
操作类型地址字节 bit7规则说明示例(配置 GCONF 寄存器 0x00)
写寄存器 0 bit7=0 表示写操作,低 7 位为寄存器地址 地址字节 = 0x00(0000 0000),后续跟 4 字节配置数据
读寄存器 1 bit7=1 表示读操作,低 7 位为寄存器地址 地址字节 = 0x80(1000 0000),后续跟 4 字节空数据

举个直观例子:

  • 要给 TMC2240 的 GCONF 寄存器(地址 0x00)写入数据 0x00000010(SPI 模式 + 诊断推挽输出),数据包为:0x00 + 0x00 + 0x00 + 0x00 + 0x10(5 字节);

  • 要读取 GCONF 寄存器的值,数据包为:0x80 + 0x00 + 0x00 + 0x00 + 0x00(5 字节),TMC2240 会通过 MISO 引脚返回 4 字节寄存器数据。

1.3 通信时序流程(一看就懂)

SPI 通信的核心是 “片选→传输→取消片选” 的流程,TMC2240 的时序要求如下(新手不用死记,代码会体现):

  • 拉低 CS 引脚(选中 TMC2240,低电平有效);

  • 主设备(STM32)通过 SCK 产生时钟,MOSI 引脚发送 40 位数据包;

  • 从设备(TMC2240)同步接收数据,若为读操作,通过 MISO 引脚返回 4 字节数据;

  • 传输完成后,拉高 CS 引脚(取消选中),通信结束。

  • ✨ 避坑提示:CS 引脚必须严格控制,未传输时保持高电平,同一时间只能选中一个从设备(多设备挂载时)。


    🛠️ 二、STM32 SPI 初始化配置(CubeMX + 代码双教程)

    以 STM32F103C8T6 为例,用 HAL 库实现,全程截图级描述,新手能一步步复刻,承接上一篇提到的开发环境(STM32CubeIDE + Keil MDK)。

    2.1 硬件接线(先接对,再谈代码)

    STM32F103 引脚TMC2240 引脚备注
    PA5(SPI1_SCK) SCK 时钟线
    PA7(SPI1_MOSI) MOSI 主发从收
    PA6(SPI1_MISO) MISO 主收从发
    PA4(GPIO_OUT) CS 片选线(自定义,推挽输出)
    GND GND 必须共地,否则数据乱码
    3.3V VCC 给 TMC2240 供电(勿接 5V!)

    📌 接线检查要点:用万用表测量杜邦线是否导通,避免断芯;CS 引脚默认拉高,确保未传输时 TMC2240 不被误选中。

    2.2 CubeMX 配置步骤(保姆级,附避坑)

  • 打开 STM32CubeMX,选择芯片STM32F103C8T6,新建工程(承接上一篇的工程框架);

  • 配置 RCC:选择「Crystal/Ceramic Resonator」(外部晶振),确保系统时钟为 72MHz;

  • 配置 SPI1(核心步骤):

    • 点击左侧「Pinout & Configuration」→ 找到「SPI1」→ Mode 选择「Full-Duplex Master」(全双工主模式);

    • 取消勾选「Hardware NSS Signal」(手动控制 CS 引脚,更灵活);

    • 点击「Configuration」→「Parameter Settings」,按以下参数配置:

    参数配置值说明
    Clock Prescaler SPI_BAUDRATEPRESCALER_16 72MHz/16=4.5MHz(新手推荐)
    CPOL SPI_POLARITY_HIGH 时钟极性高(CPOL=1)
    CPHA SPI_PHASE_2EDGE 第二个边沿采样(CPHA=1)
    Data Size SPI_DATASIZE_8BIT 按字节传输,后续拼接 40 位
    First Bit SPI_FIRSTBIT_MSB 大端模式(高位先传)
    CRC Calculation Disabled 禁用 CRC(TMC2240 不支持)
    • 其他参数默认,点击「OK」;
  • 配置 CS 引脚(PA4):
    • 找到 PA4 引脚,右键选择「GPIO_Output」;

    • 点击左侧「Configuration」→「GPIO Settings」:

      • Output Type:Push Pull(推挽输出);

      • Pull-up/Pull-down:No Pull-up and No Pull-down;

      • Maximum output speed:Low;

  • 配置 USART1(用于打印调试信息):
    • Mode 选择「Asynchronous」(异步);

    • 波特率 115200,数据位 8,停止位 1,无校验;

  • 生成代码:
    • Toolchain/IDE 选择「MDK-ARM」或「STM32CubeIDE」(按自己习惯);

    • 勾选「Generate peripheral initialization as a pair of ‘.c/.h’ files per peripheral」;

    • 点击「GENERATE CODE」,打开工程。

    ✨ 避坑提示:CubeMX 中 SPI 的 CPOL/CPHA 参数必须严格按上述配置,这是 TMC2240 的强制要求,错了会导致通信完全失败!

    2.3 手动补充初始化代码(CubeMX 生成后必加)

    CubeMX 生成的 SPI 代码缺少 CS 引脚初始化(默认高电平)和 SPI 模式确认,需在main.c中补充:

    // 1. 定义CS引脚宏(方便后续修改)
    #define TMC2240_CS_PIN GPIO_PIN_4
    #define TMC2240_CS_PORT GPIOA

    // 2. 在main函数中,HAL_Init()后、while(1)前添加
    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_SPI1_Init();
    MX_USART1_UART_Init();
    /* USER CODE BEGIN 2 */
    // 初始化CS引脚为高电平(未选中TMC2240)
    HAL_GPIO_WritePin(TMC2240_CS_PORT, TMC2240_CS_PIN, GPIO_PIN_SET);
    /* USER CODE END 2 */

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

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


    🚀 三、通用寄存器读写函数封装(可直接复用)

    核心目标:封装 2 个通用函数(读 / 写),自动处理 40 位数据包拼接、CS 引脚控制,适配所有 TMC2240 寄存器,后续开发直接调用即可。

    3.1 写寄存器函数(TMC2240_WriteReg)

    功能:将 32 位数据写入指定寄存器,自动拼接 40 位数据包,返回通信状态(成功 / 失败)。

    /**
    * @brief 向TMC2240写入32位寄存器值
    * @param regAddr:寄存器地址(低7位,如0x00=GCONF、0x10=IHOLD_IRUN)
    * @param data:要写入的32位数据
    * @retval HAL_StatusTypeDef:通信状态(HAL_OK=成功,其他为失败)
    */

    HAL_StatusTypeDef TMC2240_WriteReg(uint8_t regAddr, uint32_t data)
    {
    // 1. 定义40位数据包缓冲区(5字节:1字节地址+4字节数据)
    uint8_t txData[5] = {0};

    // 2. 拼接地址字节:bit7=0(写操作),低7位为寄存器地址
    txData[0] = regAddr & 0x7F; // 屏蔽bit7,确保是写操作

    // 3. 拼接32位数据(大端模式:高位先传)
    txData[1] = (data >> 24) & 0xFF; // 数据第31~24位
    txData[2] = (data >> 16) & 0xFF; // 数据第23~16位
    txData[3] = (data >> 8) & 0xFF; // 数据第15~8位
    txData[4] = data & 0xFF; // 数据第7~0位

    // 4. 拉低CS引脚,选中TMC2240
    HAL_GPIO_WritePin(TMC2240_CS_PORT, TMC2240_CS_PIN, GPIO_PIN_RESET);
    HAL_Delay(1); // 短暂延时,确保芯片稳定响应(避免传输失败)

    // 5. 通过SPI发送5字节数据包(超时时间100ms)
    HAL_StatusTypeDef status = HAL_SPI_Transmit(&hspi1, txData, 5, 100);

    // 6. 拉高CS引脚,结束通信
    HAL_Delay(1);
    HAL_GPIO_WritePin(TMC2240_CS_PORT, TMC2240_CS_PIN, GPIO_PIN_SET);

    return status;
    }

    3.2 读寄存器函数(TMC2240_ReadReg)

    功能:读取指定寄存器的 32 位值,自动处理读标识位,返回读取到的数据(失败返回 0xFFFFFFFF)。

    /**
    * @brief 从TMC2240读取32位寄存器值
    * @param regAddr:寄存器地址(低7位,如0x00=GCONF、0x6F=DRV_STATUS)
    * @retval uint32_t:读取到的寄存器值(0xFFFFFFFF=读取失败)
    */

    uint32_t TMC2240_ReadReg(uint8_t regAddr)
    {
    // 1. 定义发送/接收缓冲区(5字节)
    uint8_t txData[5] = {0};
    uint8_t rxData[5] = {0};
    uint32_t readData = 0xFFFFFFFF; // 初始化失败返回值

    // 2. 拼接地址字节:bit7=1(读操作),低7位为寄存器地址
    txData[0] = (regAddr & 0x7F) | 0x80; // bit7置1,标记为读操作

    // 3. 拉低CS引脚,选中TMC2240
    HAL_GPIO_WritePin(TMC2240_CS_PORT, TMC2240_CS_PIN, GPIO_PIN_RESET);
    HAL_Delay(1);

    // 4. 发送读指令+接收数据(SPI全双工传输)
    if(HAL_SPI_TransmitReceive(&hspi1, txData, rxData, 5, 100) == HAL_OK)
    {
    // 5. 拼接32位数据(大端模式→主机字节序)
    readData = ((uint32_t)rxData[1] << 24) | // 接收数据第31~24位
    ((uint32_t)rxData[2] << 16) | // 接收数据第23~16位
    ((uint32_t)rxData[3] << 8) | // 接收数据第15~8位
    (uint32_t)rxData[4]; // 接收数据第7~0位
    }

    // 6. 拉高CS引脚,结束通信
    HAL_Delay(1);
    HAL_GPIO_WritePin(TMC2240_CS_PORT, TMC2240_CS_PIN, GPIO_PIN_SET);

    return readData;
    }

    3.3 函数关键说明(避坑重点)

  • CS 引脚控制:必须先拉低再传输,传输后拉高,且加 1ms 延时 ——TMC2240 需要时间响应片选信号,无延时可能导致数据传输不完整;

  • 数据拼接:TMC2240 是大端模式(高位先传),所以 txData [1] 对应 32 位数据的最高位,rxData [1] 也是接收数据的最高位,不能搞反;

  • 超时设置:100ms 足够覆盖 TMC2240 的响应时间,避免程序卡死在 SPI 传输中;

  • 地址处理:用regAddr & 0x7F屏蔽高 7 位,确保寄存器地址不超过 0x7F(TMC2240 有效地址范围)。


  • 🎯 四、实战测试:读写 GCONF 寄存器(验证通信)

    所有代码写完后,最直接的验证方式是读写 TMC2240 的 GCONF 寄存器(地址 0x00)—— 这是全局配置寄存器,配置后可通过读取验证通信是否成功,步骤可复刻。

    4.1 测试代码(main 函数中)

    /* USER CODE BEGIN 2 */
    // 初始化CS引脚为高电平
    HAL_GPIO_WritePin(TMC2240_CS_PORT, TMC2240_CS_PIN, GPIO_PIN_SET);

    // 串口打印测试信息
    printf("===== TMC2240 SPI通信测试 =====\\r\\n");

    // 1. 向GCONF寄存器写入数据:0x00000010(SPI模式+诊断推挽输出)
    uint32_t writeData = 0x00000010;
    HAL_StatusTypeDef writeStatus = TMC2240_WriteReg(0x00, writeData);
    if(writeStatus == HAL_OK)
    {
    printf("GCONF寄存器写入成功!写入值:0x%08X\\r\\n", writeData);
    }
    else
    {
    printf("GCONF寄存器写入失败!错误代码:%d\\r\\n", writeStatus);
    }

    // 2. 读取GCONF寄存器值,验证是否写入成功
    uint32_t readData = TMC2240_ReadReg(0x00);
    printf("GCONF寄存器读取值:0x%08X\\r\\n", readData);

    // 3. 对比写入和读取的值,判断通信是否正常
    if(readData == writeData)
    {
    printf("===== SPI通信测试成功!=====\\r\\n");
    }
    else
    {
    printf("===== SPI通信测试失败!=====\\r\\n");
    }
    /* USER CODE END 2 */

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

    /* USER CODE BEGIN 3 */
    HAL_Delay(1000);
    }

    4.2 测试步骤(全程可复刻)

  • 接线:按第二部分的引脚对应关系接好线,检查 GND 和 3.3V 供电;

  • 烧录代码:将工程编译后,用 ST-Link 烧录到 STM32 开发板;

  • 串口调试:打开串口助手(如 SSCOM),配置波特率 115200,连接开发板的 USART1;

  • 运行程序:复位开发板,查看串口输出:

    • 成功:写入值 = 读取值(0x00000010),提示 “SPI 通信测试成功”;

    • 失败:排查接线、SPI 配置、函数代码。

    4.3 常见问题排查(90% 新手会踩)

    问题现象核心原因解决方法
    写入成功,读取值为 0xFFFFFFFF 1. MISO 引脚接线错误;2. SPI 接收配置错误 1. 检查 STM32_MISO 与 TMC2240_MISO 是否接反;2. 确认 SPI 数据位为 8 位、MSB 优先
    写入失败,错误代码≠HAL_OK 1. CS 引脚未拉低;2. SPI 时序错误 1. 检查 CS 引脚初始化和控制代码;2. 重新确认 CPOL=1、CPHA=1
    读取值与写入值不一致 1. 通信速率过高;2. 缺少延时 1. 将 SPI 速率降至 1MHz;2. 在 CS 拉低 / 拉高后增加 1~2ms 延时
    串口无输出 USART1 配置错误 检查串口波特率、引脚接线(TX/RX 交叉连接)

    📌 五、拓展:多寄存器读写实战(直接复用)

    掌握了通用读写函数后,可直接复用代码读写其他核心寄存器,比如配置 IHOLD_IRUN(电流配置)、CHOPCONF(微步配置):

    // 配置IHOLD_IRUN寄存器(地址0x10):运行电流100%,保持电流50%,升流延时4
    uint32_t iholdIrunData = 0x00070F1F;
    TMC2240_WriteReg(0x10, iholdIrunData);
    printf("IHOLD_IRUN寄存器配置值:0x%08X\\r\\n", iholdIrunData);

    // 读取DRV_STATUS寄存器(地址0x6F):查看电机驱动状态
    uint32_t drvStatus = TMC2240_ReadReg(0x6F);
    printf("DRV_STATUS寄存器值:0x%08X\\r\\n", drvStatus);



    🔜 下期预告

    下一篇《UART 通信协议实战|接线简化 + 地址配置 + 代码模板》UART 通信优势(仅需 2 根线)、UART_ADR 引脚地址配置(0-3 可选)、波特率与校验位设置、寄存器读写代码(中断 / 查询模式)、SPI vs UART 通信场景选择!

    ✨ 关注我 @BackCatK Chen,嵌入式开发少走 90% 的弯路!如果测试中遇到具体问题,可在评论区留言 “问题现象 + 硬件配置”,我会 1 对 1 提供排查方案!

    🎁欢迎关注,获取更多技术干货!

    无

    🎁资料包亮点

    这份资料包涵盖了从硬件电路设计到STM32单片机开发,再到Linux系统学习的全链路内容,适合不同阶段的学习者:

    • 硬件基础:包含硬件电路合集、硬件设计开发工具包,帮你打牢底层基础。
    • STM32专项:从环境搭建、开发工具、传感器模块到项目实战,还有书籍和芯片手册,一站式搞定STM32学习。
    • C语言进阶:C语言学习资料包,助你掌握嵌入式开发的核心语言。
    • 面试求职:嵌入式面试题合集,提前备战技术面试。
    • Linux拓展:Linux相关学习资料包,拓宽技术视野。
    📂资料包目录
    • 00-STM32单片机环境搭建
    • 01-硬件电路合集
    • 02-硬件设计开发工具包
    • 03-C语言学习资料包
    • 04-STM32单片机开发工具包
    • 05-STM32传感器模块合集
    • 06-STM32项目合集
    • 07-STM32单片机书籍&芯片手册
    • 08-Linux相关学习资料包
    赞(0)
    未经允许不得转载:171主机测评 » 第 2 篇:SPI 通信协议深度解析|40 位数据包 + STM32 代码实现
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址