STM32CubeMX学习笔记(1)--SPI接口使用(读取MT6701位置传感器)

1,675 阅读6分钟

1.MT6701简介

MT6701是麦歌恩微电子推出的新一代基于差分水平霍尔磁感应原理的角度位置传感器芯片。

1.工作电压:3.3-5.0V
2.支持最高转速55000转/分钟
3.角度输出的系统延时<5us
4.分辨率:14bit
5.增量UVW输出支持1-16极对数
6.支持的通信协议:IIC、SPI

1.1 IIC接口应用电路

是否需要加上拉电阻取决于MCU,一般情况下推荐加上上拉电阻。
image.png

1.2 SSI接口应用电路

MCU可以通过SSI接口接收14位的角度数据、状态位以及对应的校验位数据。
image.png

1.2.1 SSI时序图

image.png

1.通信起始于CSN的下降沿,在CLK的上升沿发送数据,CSN拉高后停止通信。
2.DO信号是MT6701的输出数据,在CLK的上升沿发生变化,建议在CLK的下降沿采集数据。

位0-13:14位角度数据 D[13:0]
位14-17:4位磁场状态数据 Mg[3:0]
位18-23:6位CRC校验码 CRC[5:0]

D[13:0]按照以下公式来计算0-360°绝对角度:
θ=i=013D<i>2i16384360_{\theta=\frac{\sum_{i=0}^{13}\mathrm{D}<i>\bullet2^i}{16384}\bullet360^\circ}

2.STM32CubeMX配置

主控使用STM32F401RCT6
RCC时钟配置:

image.png
配置调试模式:

image.png
时钟树配置:

image.png
配置SPI串口:

image.png

image.png

MSB First:高位先行,LSB First,低位先行。
CPOL = 0,CLK在空闲状态为低电平。CPOL = 1,CLK在空闲状态为高电平
CPHA = 0,在CLK时钟线的“奇数边沿”进行数据采样,CPHA = 1,在CLK时钟线的“偶数边沿”进行数据采样。

image.png

配置片选引脚CS配置:

image.png

3.编写业务代码-SPI

```c++
// SPI发送数据
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef * hspi, uint8_t * pData, uint16_t Size, uint32_t Timeout);

// SPI接收数据
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef * hspi, uint8_t * pData, uint16_t Size, uint32_t Timeout);

// SPI同时发送和接收数据
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef * hspi, uint8_t * pTxData, uint8_t * pRxData, uint16_t Size,
                                          uint32_t Timeout);
```

查询SPI通信的标志位:
数据发送过程:数据从发送缓冲器传输到移位寄存器时,TXE标志将被置位。
数据接收过程:传输移位寄存器里的数据到接收缓冲器,RXNE标志被置位。
1. SPI_FLAG_RXNE:接收缓冲区非空标志位,表示可以读取接收到的数据。
2. SPI_FLAG_TXE:传输缓冲区为空标志位,表示可以发送数据。
3. SPI_FLAG_BSY:忙标志位,表示SPI总线正在传输数据。
4.SPI_FLAG_OVR:溢出标志位,表示在接收数据时发生了溢出。
5.SPI_FLAG_MODF:模式错误标志位,表示SPI总线处于错误模式。
6.SPI_FLAG_CRCERR: CRC校验错误标志位,表示SPI传输过程中发生了CRC校验错误。

 /** @brief  Check whether the specified SPI flag is set or not.
  * @param  __HANDLE__ specifies the SPI Handle.
  *         This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
  * @param  __FLAG__ specifies the flag to check.
  *         This parameter can be one of the following values:
  *            @arg SPI_FLAG_RXNE: Receive buffer not empty flag
  *            @arg SPI_FLAG_TXE: Transmit buffer empty flag
  *            @arg SPI_FLAG_CRCERR: CRC error flag
  *            @arg SPI_FLAG_MODF: Mode fault flag
  *            @arg SPI_FLAG_OVR: Overrun flag
  *            @arg SPI_FLAG_BSY: Busy flag
  *            @arg SPI_FLAG_FRE: Frame format error flag
  * @retval The new state of __FLAG__ (TRUE or FALSE).
  */
#define __HAL_SPI_GET_FLAG(__HANDLE__, __FLAG__) ((((__HANDLE__)->Instance->SR) & (__FLAG__)) == (__FLAG__))   
    // 使用SPIx一次读取16bits数据
void MT6701_GetRawData(uint16_t *rawData)
{
    uint16_t txData = 0xFFFF;
    uint16_t timeOut = 200;

    while (HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY)
    {
        if (timeOut-- == 0)
        {
            FOC_log("SPI state error!\r\n");
            return; // 在超时时直接返回,避免继续执行后续代码
        }
    }

    MT6701_CS_Enable();

    HAL_StatusTypeDef spiStatus = HAL_SPI_TransmitReceive(&hspi1, (uint8_t *)&txData, (uint8_t *)rawData, 1, HAL_MAX_DELAY);
    if (spiStatus != HAL_OK)
    {
        MT6701_CS_Disable();
        FOC_log("MT6701 read data error!\r\n");
        return; // 在SPI传输错误时直接返回,避免继续执行后续代码
    }

    MT6701_CS_Disable();

    *rawData >>= 2; // 右移2位,取高位置14位角度数据,将结果写回rawData指向的内存
}
    // 返回值:转子角度,无圈数累加
void MT6701_GetRawAngle(float *shatfAngle)
{
    uint16_t rawData = 0;
    MT6701_GetRawData(&rawData);
    *shatfAngle = (float)rawData / (float)COUNT_PER_ROTATE * _2PI;
}

// 返回值:转过的总角度,有圈数累加
void MT6701_GetSumAngle(float *sumAngle, int32_t *rotationCount)
{
    float angle = 0.0f;            // 当前轴角度
    static float angleLast = 0.0f; // 上次轴角度
    MT6701_GetRawAngle(&angle);
    float deltaAngle = angle - angleLast;
    if (fabs(deltaAngle) > (0.8f * _2PI))
        *rotationCount += (deltaAngle > 0.0f) ? -1 : 1;
    angleLast = angle;
    *sumAngle = *rotationCount * _2PI + angle;
}

// 电角度 = 机械角度 * 极对数
void MT6701_GetElectricalAngle(float *el_Angle)
{
    float shaftAngle = 0.0f;
    MT6701_GetRawAngle(&shaftAngle);
    *el_Angle = shaftAngle * pole_pairs;
}

// 计算转速(角速度)
void MT6701_GetVelocity(float *vel)
{
    float sumAngle = 0.0f; // 转过的总角度
    static float sumAngleLast = 0.0f;
    static uint32_t rotationCount = 0; // 转过的圈数

    MT6701_GetSumAngle(&sumAngle, &rotationCount);

    *vel = (sumAngle - sumAngleLast) * 1000.0f; //  *vel = (sumAngle - sumAngleLast) / 0.001f;
    sumAngleLast = sumAngle;
}

4.MT6701编码器程序优化

#include "spi.h"
#include "Serial.h"
#include <stdint.h>
#include "math_utils.h"
#include "FOC.h"

#define MT6701_CS_Enable() HAL_GPIO_WritePin(MT6701_CS_GPIO_Port, MT6701_CS_Pin, GPIO_PIN_RESET)
#define MT6701_CS_Disable() HAL_GPIO_WritePin(MT6701_CS_GPIO_Port, MT6701_CS_Pin, GPIO_PIN_SET)

const uint8_t pole_pairs = 7; // 极对数
float zeroElectricAngleOffset = 0.0f;

int rotationCount = 0;  // 旋转过的圈数
int rotationCount_Last; // 上一次循环时转过的圈数

// 获取MT6701原始数据
uint16_t MT6701_GetRawData(void)
{
    uint16_t rawData;
    uint16_t txData = 0xFFFF;
    uint16_t timeOut = 200;

    while (HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY)
    {
        if (timeOut-- == 0)
        {
            FOC_log("SPI state error!\r\n");
            return 0; // 在超时时直接返回,避免继续执行后续代码
        }
    }

    MT6701_CS_Enable();

    HAL_StatusTypeDef spiStatus = HAL_SPI_TransmitReceive(&hspi1, (uint8_t *)&txData, (uint8_t *)&rawData, 1, HAL_MAX_DELAY);
    if (spiStatus != HAL_OK)
    {
        MT6701_CS_Disable();
        FOC_log("MT6701 read data error!\r\n");
        return 0; // 在SPI传输错误时直接返回,避免继续执行后续代码
    }

    MT6701_CS_Disable();

    return rawData >> 2; // 取高14位的角度数据
}

// 获得原始角度,无圈数累加
float MT6701_GetRawAngle(void)
{
    uint16_t rawData = MT6701_GetRawData();
    return (float)rawData / 16384.0f * _2PI;
}

// 获得转过的总角度,有圈数累加
float MT6701_GetFullAngle(void)
{
    static float angle_Last = 0.0f;     // 上次的轴角度,范围0~6.28
    float angle = MT6701_GetRawAngle(); // 当前角度,范围0~6.28
    float deltaAngle = angle - angle_Last;

    // 计算旋转的总圈数
    // 通过判断角度变化是否大于80%的一圈(0.8f*6.28318530718f)来判断是否发生了溢出,如果发生了,则将full_rotations增加1(如果d_angle小于0)或减少1(如果d_angle大于0)。

    if (fabsf(deltaAngle) > (0.8f * 6.28318530718f))
    {
        rotationCount += (deltaAngle > 0) ? -1 : 1; // 圈数计算
        rotationCount_Last = rotationCount;
    }

    angle_Last = angle;
    return rotationCount * 6.28318530718f + angle_Last; // 转过的圈数 * 2pi + 未转满一圈的角度值
}

// 计算转速
float MT6701_GetVelocity(void)
{
    static float full_Angle_Last = 0.0f; // 记录上次转过的总角度
    float full_Angle = MT6701_GetFullAngle();
    float delta_Angle = (rotationCount - rotationCount_Last) * _2PI + (full_Angle - full_Angle_Last);
    float vel = delta_Angle * 1000.0f; // Ts = 1ms

    // 更新变量值
    full_Angle_Last = full_Angle;
    return vel;
}

// 获得电角度
float MT6701_GetElectricalAngle(void)
{
    return _normalizeAngle(pole_pairs * MT6701_GetRawAngle() - zeroElectricAngleOffset);
}

// 电角度零位校准
void Align_Sensor(void)
{
    setPhaseVoltage(3.0f, 0.0f, _3PI_2);
    HAL_Delay(2000);
    zeroElectricAngleOffset = MT6701_GetElectricalAngle();
    setPhaseVoltage(0, 0, _3PI_2);
    FOC_log("[zeroAngle]:%f\r\n", zeroElectricAngleOffset);
}