STM32 #4 SPI:以MPU 9250九軸慣性測量單元為例 - ntut-rf/mcu GitHub Wiki

MPU-9250是一九軸慣性測量單元(IMU, Inertial Measurement Unit)IC。九軸指的是三軸的陀螺儀,三個方向的加速度計,和三個方向的磁力計,來測量物體在三維空間中的角速度,加速度,以及磁場,並以此解算出物體的姿態。

在這個練習中,我們使用GY-91這個模組,裡面包含MPU-9250九軸IMU以及BMP-280氣壓計,以STM32 MCU讀取其量測資料,並透過UART顯示在終端機上。MPU-9250設計上對外的通訊介面有I2C和SPI兩種,我們先使用SPI為例。要能正確讀取MPU-9250資料,需了解STM32 SPI介面的使用方式,以及MPU-9250的詳細使用方式,首先收集需要的技術文件如下:

資料收集

SPI

SPI是一種4線同步序列資料協定。一端為一”主控裝置(Master)”,另一端為一或多個 ”從屬裝置(Slave)”。

1 Master 對 1 Slave:

1 Master 對 3 Slave:

SPI的四個訊號為:

  • MISO: Master In, Slave Out。Master透過此腳接收從slave傳來的資料。
  • MOSI: Master Out, Slave In。Master透過此腳接傳送資料給slave。
  • SCK: 由master提供clock輸出給slave當成通訊clock
  • SS: Slave select,又稱Chip Select(CS). 該腳可以被master當作要和哪個slave做溝通

SPI的通訊是由master主控。當master要與某一slave溝通的時候,會將與此slave連接的CS拉為低電位。在傳輸過程中CS保持低電位,直到此次傳輸完成。傳輸過程中,master打出SCK clock訊號,並同步打出MOSI輸出資料,以及同步從MISO接收資料。

SPI的傳輸有CPOL和CPHA兩設定參數控制取值的時間關係,總共有4種組合:

  • CPOL(clock polarity) 決定閒置時 clock 的電位。

    • CPOL = 0 表閒置時為低電位。
    • CPOL = 1 表閒置時為高電位。
  • CPHA(clock phase) 決定在 clock 的哪個 edge 取值。

    • CPHA = 0 表示在第一個 edge (Rising,when CPOL=0.Falling,when CPOL=1.)取值。
    • CPHA = 1 表示在第二個 edge (Falling,when CPOL=1.Rising,when CPOL=0.)取值。

訊號連接

STM32F103-Nucleo Pinouts

參考上圖可知:

  • CS: PA4
  • SCK: PA5
  • MISO: PA6
  • MOSI: PA7

GY-91 Pinouts

  • VIN: Voltage Supply Pin
  • 3V3: 3.3v Regulator output
  • GND: Ground
  • SCL: I2C Clock / SPI Clock
  • SDA: I2C Data or SPI Data Input
  • SDO/SAO: SPI Data output / I2C Slave Address configuration pin
  • NCS: Chip Select for SPI mode only for MPU-9250
  • CSB: Chip Select for BMP280

STM32F103-Nucleo -- GY-91連接:

STM32F103-Nucleo GY-91
GND GND
3V3 VIN
PA5 (SPI1 SCK) SCL
PA6 (SPI1 MISO) SDA
PA7 (SPI1 MOSI) SDO/SAO
PA4 (SPI1 SS) NCS

程式

程式結構

  • main.c: 主程式
  • uart-printf.h/.c: 提供printf至USART的功能
  • mpu9250.h/.c: 透過SPI讀寫MPU-9250

mpu9250.c

MPU9250_setup_SPI():設定STM32 SPI1符合MPU-9250的規範。此參考MPU-9250 Product Specification p.16 "3.6 SPI Timing Characterization" 以及 p.39 "7.5 SPI Interface"

void MPU9250_setup_SPI (void)
{
    /* Enable SPI1 Periph and gpio clocks */
    rcc_periph_clock_enable(RCC_GPIOA);
    rcc_periph_clock_enable(RCC_SPI1);

    /* Configure GPIOs: CS=PA4, SCK=PA5, MISO=PA6 and MOSI=PA7 */
    gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, SPI1_CS);
    gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI1_SCK | SPI1_MOSI );
    gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI1_MISO);

    /* Reset SPI, SPI_CR1 register cleared, SPI is disabled */
    spi_reset(SPI1);

    /* Set up SPI in Master mode with:
     * Clock baud rate: 1/128 of peripheral clock frequency
     * Clock polarity: Idle low
     * Clock phase: Data valid on 1st clock pulse
     * Data frame format: 8-bit
     * Frame format: MSB First
     */
     spi_init_master(SPI1, SPI_CR1_BAUDRATE_FPCLK_DIV_128, SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE,
                     SPI_CR1_CPHA_CLK_TRANSITION_1, SPI_CR1_DFF_8BIT, SPI_CR1_MSBFIRST);

    /* Set NSS management to software */
    spi_enable_software_slave_management(SPI1);
    spi_set_nss_high(SPI1);
    gpio_set(GPIOA, SPI1_CS);

    /* Enable SPI1 */
    spi_enable(SPI1);
}

MPU9250_read()

參考MPU-9250 Product Specification p.39 "7.5 SPI Interface"

uint8_t MPU9250_read (uint8_t addr)
{
    gpio_clear(GPIOA, SPI1_CS);
    spi_xfer(SPI1, 0x80 | addr);
    uint8_t rval = spi_xfer(SPI1, 0x00);
    gpio_set(GPIOA, SPI1_CS);
    return rval;
}

首先,將CS腳位拉到低電位,表示開始傳輸。

gpio_clear(GPIOA, SPI1_CS);

接著,傳輸要讀取的位置編號,並依照 p.39 "7.5 SPI Interface" 將最高位元設為1 (|0x80)。

spi_xfer(SPI1, 0x80 | addr);

接著,再傳輸一位元組,讀取結果:

uint8_t rval = spi_xfer(SPI1, 0x00);

最後,將CS腳位拉到高電位,表示結束傳輸。

gpio_set(GPIOA, SPI1_CS);