156 lines
4.8 KiB
C
156 lines
4.8 KiB
C
/**
|
|
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "pico/stdlib.h"
|
|
#include "pico/binary_info.h"
|
|
#include "hardware/spi.h"
|
|
|
|
/* Example code to talk to a MPU9250 MEMS accelerometer and gyroscope.
|
|
Ignores the magnetometer, that is left as a exercise for the reader.
|
|
|
|
This is taking to simple approach of simply reading registers. It's perfectly
|
|
possible to link up an interrupt line and set things up to read from the
|
|
inbuilt FIFO to make it more useful.
|
|
|
|
NOTE: Ensure the device is capable of being driven at 3.3v NOT 5v. The Pico
|
|
GPIO (and therefor SPI) cannot be used at 5v.
|
|
|
|
You will need to use a level shifter on the I2C lines if you want to run the
|
|
board at 5v.
|
|
|
|
Connections on Raspberry Pi Pico board and a generic MPU9250 board, other
|
|
boards may vary.
|
|
|
|
GPIO 4 (pin 6) MISO/spi0_rx-> ADO on MPU9250 board
|
|
GPIO 5 (pin 7) Chip select -> NCS on MPU9250 board
|
|
GPIO 6 (pin 9) SCK/spi0_sclk -> SCL on MPU9250 board
|
|
GPIO 7 (pin 10) MOSI/spi0_tx -> SDA on MPU9250 board
|
|
3.3v (pin 36) -> VCC on MPU9250 board
|
|
GND (pin 38) -> GND on MPU9250 board
|
|
|
|
Note: SPI devices can have a number of different naming schemes for pins. See
|
|
the Wikipedia page at https://en.wikipedia.org/wiki/Serial_Peripheral_Interface
|
|
for variations.
|
|
The particular device used here uses the same pins for I2C and SPI, hence the
|
|
using of I2C names
|
|
*/
|
|
|
|
#define PIN_MISO 4
|
|
#define PIN_CS 5
|
|
#define PIN_SCK 6
|
|
#define PIN_MOSI 7
|
|
|
|
#define SPI_PORT spi0
|
|
#define READ_BIT 0x80
|
|
|
|
static inline void cs_select() {
|
|
asm volatile("nop \n nop \n nop");
|
|
gpio_put(PIN_CS, 0); // Active low
|
|
asm volatile("nop \n nop \n nop");
|
|
}
|
|
|
|
static inline void cs_deselect() {
|
|
asm volatile("nop \n nop \n nop");
|
|
gpio_put(PIN_CS, 1);
|
|
asm volatile("nop \n nop \n nop");
|
|
}
|
|
|
|
static void mpu9250_reset() {
|
|
// Two byte reset. First byte register, second byte data
|
|
// There are a load more options to set up the device in different ways that could be added here
|
|
uint8_t buf[] = {0x6B, 0x00};
|
|
cs_select();
|
|
spi_write_blocking(SPI_PORT, buf, 2);
|
|
cs_deselect();
|
|
}
|
|
|
|
|
|
static void read_registers(uint8_t reg, uint8_t *buf, uint16_t len) {
|
|
// For this particular device, we send the device the register we want to read
|
|
// first, then subsequently read from the device. The register is auto incrementing
|
|
// so we don't need to keep sending the register we want, just the first.
|
|
|
|
reg |= READ_BIT;
|
|
cs_select();
|
|
spi_write_blocking(SPI_PORT, ®, 1);
|
|
sleep_ms(10);
|
|
spi_read_blocking(SPI_PORT, 0, buf, len);
|
|
cs_deselect();
|
|
sleep_ms(10);
|
|
}
|
|
|
|
|
|
static void mpu9250_read_raw(int16_t accel[3], int16_t gyro[3], int16_t *temp) {
|
|
uint8_t buffer[6];
|
|
|
|
// Start reading acceleration registers from register 0x3B for 6 bytes
|
|
read_registers(0x3B, buffer, 6);
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
accel[i] = (buffer[i * 2] << 8 | buffer[(i * 2) + 1]);
|
|
}
|
|
|
|
// Now gyro data from reg 0x43 for 6 bytes
|
|
read_registers(0x43, buffer, 6);
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
gyro[i] = (buffer[i * 2] << 8 | buffer[(i * 2) + 1]);;
|
|
}
|
|
|
|
// Now temperature from reg 0x41 for 2 bytes
|
|
read_registers(0x41, buffer, 2);
|
|
|
|
*temp = buffer[0] << 8 | buffer[1];
|
|
}
|
|
|
|
int main() {
|
|
stdio_init_all();
|
|
|
|
printf("Hello, MPU9250! Reading raw data from registers via SPI...\n");
|
|
|
|
// This example will use SPI0 at 0.5MHz.
|
|
spi_init(SPI_PORT, 500 * 1000);
|
|
gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);
|
|
gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);
|
|
gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
|
|
// Make the SPI pins available to picotool
|
|
bi_decl(bi_3pins_with_func(PIN_MISO, PIN_MOSI, PIN_SCK, GPIO_FUNC_SPI));
|
|
|
|
// Chip select is active-low, so we'll initialise it to a driven-high state
|
|
gpio_init(PIN_CS);
|
|
gpio_set_dir(PIN_CS, GPIO_OUT);
|
|
gpio_put(PIN_CS, 1);
|
|
// Make the CS pin available to picotool
|
|
bi_decl(bi_1pin_with_name(PIN_CS, "SPI CS"));
|
|
|
|
mpu9250_reset();
|
|
|
|
// See if SPI is working - interrograte the device for its I2C ID number, should be 0x71
|
|
uint8_t id;
|
|
read_registers(0x75, &id, 1);
|
|
printf("I2C address is 0x%x\n", id);
|
|
|
|
int16_t acceleration[3], gyro[3], temp;
|
|
|
|
while (1) {
|
|
mpu9250_read_raw(acceleration, gyro, &temp);
|
|
|
|
// These are the raw numbers from the chip, so will need tweaking to be really useful.
|
|
// See the datasheet for more information
|
|
printf("Acc. X = %d, Y = %d, Z = %d\n", acceleration[0], acceleration[1], acceleration[2]);
|
|
printf("Gyro. X = %d, Y = %d, Z = %d\n", gyro[0], gyro[1], gyro[2]);
|
|
// Temperature is simple so use the datasheet calculation to get deg C.
|
|
// Note this is chip temperature.
|
|
printf("Temp. = %f\n", (temp / 340.0) + 36.53);
|
|
|
|
sleep_ms(100);
|
|
}
|
|
|
|
return 0;
|
|
}
|