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, "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;
 | |
| }
 |