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);