简介:例程包以STM32F103C8T6为平台,演示通过SPI接口驱动SCL3300角度传感器并利用UART串口输出数据的完整工程,面向正在学习STM32外设通信或需要快速完成传感器选型验证的嵌入式开发者。压缩包内含194个文件,以C源码、H头文件、Keil工程文件为主,另附HEX固件、AXF调试文件及部分编译中间产物,整体约6.06MB,工程目录结构清晰,可直接打开编译烧录。代码从GPIO引脚映射、SPI时钟极性与相位配置,到命令字节收发和串口打印均给出完整实现,模块拆分明确,便于对照SCL3300数据手册逐段理解寄存器操作。整个例程覆盖了主从设备通信时序、SPI读写常见错误排查思路,以及如何借助串口助手验证传感器数据有效性。已有1998人学习,可作为嵌入式SPI/UART综合实践与传感器驱动开发的入门参考。 很多人第一次接触SCL3300,多半是在找高精度倾角传感器的时候。我当时做云台姿态反馈,先用的是MPU6050,静态角度还能看,可环境温度一变,零点就慢慢飘,数据里还夹着明显噪声。后来换成Murata的SCL3300,这颗三轴倾角传感器配合手头的stm32f103c8t6最小系统板,我花了两个晚上把SPI例程调通,角度输出稳定到小数点后一位。这篇就把接线、寄存器操作、数据滤波和排坑过程完整记录下来,给正在搜scl3300例程的朋友一份可以直接照抄的参考。

1. 为什么偏要用SCL3300做倾角测量

1.1 它和普通加速度计的核心区别

很多新手会问:MPU6050里不是也有加速度计吗,凑合用用不行吗?能用,但不好用。普通加速度计测的是重力矢量在三个轴上的分量,静态倾角确实能用atan2算出来,可芯片本身没做温度补偿,标定精度也一般,零点随温度漂移几十甚至上百个LSB都很正常。更麻烦的是,加速度计同时感知振动和运动加速度,你在一个电机旁边测静态倾角,数据会被高频噪声盖住。

SCL3300的定位不一样。它内部同样是MEMS电容式敏感结构,但出厂前经过逐颗标定和温补,输出的是经过内部计算的角度值或者正弦值,而不是让你拿原始加速度去慢慢换算。它的角度噪声低、重复性好,专门面向塔筒倾斜、天线对星、云台调平、工程机械姿态这类"要的是稳定准确的角度"的场景。

1.2 这颗芯片的关键参数

我整理了一下实际用得上的参数,方便你对照选型:

参数项 典型值 备注
工作电压 3.0V ~ 3.6V 推荐3.3V,不能直接上5V
接口 4线SPI 支持Mode 0和Mode 3
测量轴 X / Y / Z 三轴倾角
X/Y量程 ±90° 常用垂直安装
Z量程 ±180° Z轴可覆盖全向
角度分辨率 0.01°/LSB 角度输出寄存器
温度范围 -40℃ ~ +85℃ 工业级常用

注意"0.01°/LSB"指的是角度输出寄存器,如果你的程序读的是正弦值输出,那比例因子就完全是另一套了。我建议刚上手直接读角度寄存器,先把数据显示出来,再考虑要不要用正弦值自己做高级换算。

1.3 到手先怎么接线

我用的是最常见的蓝色stm32f103c8t6最小系统板,用SPI1驱动SCL3300。接线非常简单,传感器端我接的是7个引脚:

SCL3300引脚 接F103C8T6 说明
VDD 3.3V 传感器供电
VDDIO 3.3V IO参考电压,必须接
GND GND 共地
CSB PA4 片选,用GPIO控制
SCK PA5 SPI时钟
SDI/MOSI PA7 主发从收
SDO/MISO PA6 主收从发

我这里没有把DRDY中断脚接出来,因为例程用轮询读角度已经够用。如果你要做低功耗,可以接一个GPIO检测数据就绪。还有一点必须强调:VDDIO也要接3.3V,不接的话SPI电平会乱,读回来的数据千奇百怪。

2. SPI通信帧与CRC校验的实际处理

2.1 寄存器读写帧结构

SCL3300走的是标准4线SPI,通信时由主机发起。每笔操作都会发送一个寄存器命令,命令里包含读写标志位和寄存器地址,传感器在时钟节拍下把对应数据从SDO上回传。不同批次芯片的命令帧长度可能略有差异,所以拿到芯片以后第一件事是打开官方datasheet,翻到SPI协议部分核对一遍。

我自己习惯把所有寄存器地址统一用宏管理,这样后面读代码不用到处翻数字:

#define SCL3300_REG_WHOAMI    0x00u
#define SCL3300_REG_MODE      0x01u
#define SCL3300_REG_ANGL_X    0x02u
#define SCL3300_REG_ANGL_Y    0x03u
#define SCL3300_REG_ANGL_Z    0x04u

注意:不同版本的datasheet对寄存器地址的编号不完全一样,这组宏是我手头芯片对应的地址。如果你的板子读出来不对,优先核对官方手册,别一上来就怀疑芯片坏了。

2.2 CRC8校验可以晚点再啃

SCL3300为了可靠性,支持在SPI帧里附加CRC校验。这对量产项目很重要,但调试初期会把人绕晕。我的经验是:第一步先把不带CRC的裸通信调通,能稳定读回WHOAMI和角度值以后,再回头把CRC加上。一上来就带着CRC调,出了问题很难分清是时序问题还是校验算法问题。

CRC计算用的是多项式0x31,初值0xFF,算法代码可以这样写:

uint8_t scl3300_crc8(uint8_t *data, uint8_t len)
{
    uint8_t crc = 0xFF;
    for (uint8_t i = 0; i < len; i++)
    {
        crc ^= data[i];
        for (uint8_t bit = 0; bit < 8; bit++)
        {
            crc = (crc & 0x80) ? (crc << 1) ^ 0x31 : (crc << 1);
        }
    }
    return crc;
}

如果你调通了无CRC模式,再用这段代码去附加校验字节,基本上不会卡太久。测试CRC正确性时,可以在发送命令后对比传感器回传的CRC位,逐位一致才算通过。

2.3 HAL库底层收发函数怎么写

在STM32CubeMX里把SPI1配成主机模式,8位数据长度,软件NSS,CPOL和CPHA先都设为低电平,也就是SPI Mode 0。波特率预分频先用64,72MHz主频下SPI时钟约1.125MHz,这个速度调试很稳。先把低速调通,再慢慢提速。

底层收发我用一个函数封装:

static uint16_t scl3300_spi_transfer(uint16_t cmd)
{
    uint16_t rx = 0;
    SCL3300_CS_LOW();
    HAL_SPI_TransmitReceive(&hspi1, (uint8_t *)&cmd, (uint8_t *)&rx, 2, 100);
    SCL3300_CS_HIGH();
    return rx;
}

读取寄存器的函数就很简单了:

uint16_t scl3300_read_reg(uint8_t reg)
{
    uint16_t cmd = (0x8000u | ((uint16_t)reg << 8));
    return scl3300_spi_transfer(cmd);
}

写寄存器时把最高位变成0,数据段放在低字节。你在自己的工程里要根据手册确认每个寄存器的数据位位置,但整个封装思路是一样的。CS引脚操作必须放在SPI收发前后,不能用硬件NSS,否则片选毛刺会影响通信。

3. 初始化流程和测量模式配置

3.1 上电后的复位与自检

SCL3300上电后不能马上读角度,它自己需要一小段时间完成内部上电初始化。我的初始化函数里第一步就是延时50ms,然后把CS拉高,让SPI总线处于空闲状态。接下来做一次软复位操作,把传感器内部状态清干净。

复位之后,我强烈建议先读WHOAMI寄存器。这个寄存器返回的ID值每个芯片基本固定,只要能读回来一个既不是0x0000也不是0xFFFF的数,就说明SPI接线和时序已经对了。这一步是整个调试过程中最值钱的判断点。

3.2 配置测量模式

SCL3300支持不同的测量模式和滤波配置,通过模式寄存器选择。官方手册里会给出对应数值,比如把传感器切到高精度测量模式,或者切到高速低功耗模式。我实际测试下来,普通倾角测量用高精度模式加片上滤波就够了,角度数据非常平滑。

初始化代码大约长这样:

void scl3300_init(void)
{
    SCL3300_CS_HIGH();
    HAL_Delay(50);

    // 软复位
    scl3300_write_reg(SCL3300_REG_RST, 0x01);
    HAL_Delay(20);

    // 读WHOAMI确认通信
    uint16_t id = scl3300_read_reg(SCL3300_REG_WHOAMI);
    if (id == 0x0000 || id == 0xFFFF)
    {
        // 通信失败,检查接线或SPI时序
        while (1);
    }

    // 设置测量模式
    scl3300_write_reg(SCL3300_REG_MODE, SCL3300_MODE_HIGH_PRECISION);
    HAL_Delay(10);
}

写寄存器函数和读寄存器类似,只要把命令字的读写位改成0,数据放进数据段就行。如果写寄存器后读回内容不对,别急着怀疑函数,先看看时序里的CS有没有在每笔操作之间保持高电平,很多怪问题都是片选控制不当引起的。

3.3 主循环读三轴角度

初始化完成以后,主循环里直接读角度寄存器:

while (1)
{
    int16_t raw_x = (int16_t)scl3300_read_reg(SCL3300_REG_ANGL_X);
    int16_t raw_y = (int16_t)scl3300_read_reg(SCL3300_REG_ANGL_Y);
    int16_t raw_z = (int16_t)scl3300_read_reg(SCL3300_REG_ANGL_Z);

    float angle_x = raw_x * 0.01f;
    float angle_y = raw_y * 0.01f;
    float angle_z = raw_z * 0.01f;

    printf("X: %.2f  Y: %.2f  Z: %.2f\r\n", angle_x, angle_y, angle_z);
    HAL_Delay(10);
}

这里有一个很关键的细节:角度寄存器是有符号数,必须强转成int16_t再乘比例因子。如果直接用uint16_t去算,负角度会变成一个很大的正数,怎么调都是错的。

4. 角度值换算、滤波与零点校准

4.1 角度寄存器和正弦值模式怎么选

SCL3300既能输出角度,也能输出正弦值。读角度寄存器最省事,比例因子就是0.01°/LSB,直接乘。如果读正弦值,公式里要套反正弦函数:

角度 = asin(正弦值 / 比例因子) * 180 / PI

这个比例因子和选择的量程有关,得查手册确认。我的建议是:刚上手别碰正弦值模式,先用角度模式把整套流程跑通。角度模式的内部处理已经帮你补偿了一部分非线性误差,对做工程的人来说,少一道换算就少一个出错点。

4.2 滑动平均滤波的实现

SCL3300本身噪声已经很低,但如果你把传感器装在电机旁边,或者供电纹波偏大,角度数据还是会轻微抖动。我习惯在读出角度后再做一层滑动平均滤波,窗口取16次采样,效果很好,代码也简单:

#define FILTER_SIZE 16

static float filter_buf_x[FILTER_SIZE];
static uint8_t filter_index_x;

float scl3300_filter(float input, float *buf)
{
    static float sum = 0.0f;
    sum -= buf[filter_index_x];
    buf[filter_index_x] = input;
    sum += input;
    filter_index_x = (filter_index_x + 1) % FILTER_SIZE;
    return sum / FILTER_SIZE;
}

实际使用中,窗口大小的选取要权衡:窗口太大,数据平滑但响应变慢;窗口太小,滤波效果不明显。静止测量用16到32都行,云台这类需要快速响应的场景,窗口不建议超过8。

4.3 零点校准到底校的是什么

SCL3300出厂精度已经不错,但安装到你的板子上以后,PCB焊接应力、外壳安装角度、轻微倾斜都会引入固定零点偏移。零点校准的本质就是把传感器放在你定义的"零度基准面"上,静止采集一段时间取平均,把这个平均值作为零偏记录下来。

校准代码可以这样写:

static float offset_x, offset_y, offset_z;

void scl3300_calibrate(void)
{
    float sum_x = 0, sum_y = 0, sum_z = 0;
    uint8_t i;

    for (i = 0; i < 100; i++)
    {
        sum_x += read_angle_x();
        sum_y += read_angle_y();
        sum_z += read_angle_z();
        HAL_Delay(10);
    }

    offset_x = sum_x / 100;
    offset_y = sum_y / 100;
    offset_z = sum_z / 100;
}

校准完成后,实际输出角度等于原始角度减去零偏。注意校准过程中传感器必须完全静止,任何一点手动触碰都会污染平均值。如果是做批量产品,这个校准步骤可以做成产线工位,让板子在标准治具上自动完成。

5. 实测最容易翻车的四个问题

5.1 读回数据全是0xFFFF

这个问题九成出在接线和SPI配置上。先量MISO引脚有没有波形,没有波形就检查PA6和SDO是否真的连通。再用示波器看CS信号,CS必须在SPI时钟之前拉低,在时钟结束之后拉高,时序反了传感器就不响应。还有一个很隐蔽的点:VDDIO没接3.3V的时候,SPI引脚电平会异常,数据读回来经常是全FF或者全00。

5.2 SPI Mode 0和Mode 3的选择

SCL3300手册说支持Mode 0和Mode 3,但我实际测试中发现,个别模块在Mode 0下不稳定,而切到Mode 3以后通信变得非常干净。如果你SPI时钟、接线、地址都对,数据却偶尔多一个字节或者漏一个字节,试着把CPOL和CPHA改成高电平,很轻松就能解决。这个经验在调其他MEMS传感器时也常常遇到。

5.3 角度值突然跳变一大截

跳变一般不是芯片问题,而是供电纹波太大。SCL3300内部虽然做了滤波,但电源噪声会直接影响MEMS检测。我踩过一次坑:把传感器和电机驱动的电源接在同一个LDO后面,电机一转角度就乱跳,后来在传感器电源脚上加了一个10uF陶瓷电容和一个100nF高频去耦电容,问题立刻消失。

5.4 上电后第一帧数据不可信

芯片刚上电、刚切换测量模式的前几帧数据,内部滤波还未收敛,直接拿来用会把系统状态带偏。我现在的做法是:初始化完成后连续读十次角度,丢掉前几次,从有效帧开始才进入控制逻辑。这招在几乎所有数字传感器上都适用,能少踩很多坑。


最后分享一点个人体会:调试这类SPI传感器,一定不要急着加功能。先把WHOAMI读出来,再把三轴角度串口打印出来,确认数据在合理范围内跳动,然后才做滤波、校准和上层逻辑。每一步都确认过,后面出问题时你才能快速定位。我还会在工程里加一个原始寄存器数据的调试命令,接串口后随时可以看裸值,这个习惯救了我很多次。SCL3300这颗芯片性能确实不错,只要硬件和时序处理干净,它能给到的稳定性远超普通加速度计。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

更多推荐