/* * This file is part of the libopencm3 project. * * Copyright (C) 2013 Chuck McManis * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ /* * SPI Port example */ #include #include #include #include #include "clock.h" #include "console.h" /* * Functions defined for accessing the SPI port 8 bits at a time */ uint16_t read_reg(int reg); void write_reg(uint8_t reg, uint8_t value); uint8_t read_xyz(int16_t vecs[3]); void spi_init(void); /* * Chart of the various SPI ports (1 - 6) and where their pins can be: * * NSS SCK MISO MOSI * -------------- ------------------- ------------- --------------- * SPI1 PA4, PA15 PA5, PB3 PA6, PB4 PA7, PB5 * SPI2 PB9, PB12, PI0 PB10, PB13, PD3, PI1 PB14, PC2, PI2 PB15, PC3, PI3 * SPI3 PA15*, PA4* PB3*, PC10* PB4*, PC11* PB5*, PD6, PC12* * SPI4 PE4,PE11 PE2, PE12 PE5, PE13 PE6, PE14 * SPI5 PF6, PH5 PF7, PH6 PF8 PF9, PF11, PH7 * SPI6 PG8 PG13 PG12 PG14 * * Pin name with * is alternate function 6 otherwise use alternate function 5. * * MEMS uses SPI5 - SCK (PF7), MISO (PF8), MOSI (PF9), * MEMS CS* (PC1) -- GPIO * MEMS INT1 = PA1, MEMS INT2 = PA2 */ void put_status(char *m); /* * put_status(char *) * * This is a helper function I wrote to watch the status register * it decodes the bits and prints them on the console. Sometimes * the SPI port comes up with the MODF flag set, you have to re-read * the status port and re-write the control register to clear that. */ void put_status(char *m) { uint16_t stmp; console_puts(m); console_puts(" Status: "); stmp = SPI_SR(SPI5); if (stmp & SPI_SR_TXE) { console_puts("TXE, "); } if (stmp & SPI_SR_RXNE) { console_puts("RXNE, "); } if (stmp & SPI_SR_BSY) { console_puts("BUSY, "); } if (stmp & SPI_SR_OVR) { console_puts("OVERRUN, "); } if (stmp & SPI_SR_MODF) { console_puts("MODE FAULT, "); } if (stmp & SPI_SR_CRCERR) { console_puts("CRC, "); } if (stmp & SPI_SR_UDR) { console_puts("UNDERRUN, "); } console_puts("\n"); } /* * read_reg(int reg) * * This reads the MEMs registers. The chip registers are 16 bits * wide, but I read it as two 8 bit bytes. Originally I tried * swapping between an 8 bit and 16 bit wide bus but that confused * both my code and the chip after a while so this was found to * be a more stable solution. */ uint16_t read_reg(int reg) { uint16_t d1, d2; d1 = 0x80 | (reg & 0x3f); /* Read operation */ /* Nominallly a register read is a 16 bit operation */ gpio_clear(GPIOC, GPIO1); spi_send(SPI5, d1); d2 = spi_read(SPI5); d2 <<= 8; /* * You have to send as many bits as you want to read * so we send another 8 bits to get the rest of the * register. */ spi_send(SPI5, 0); d2 |= spi_read(SPI5); gpio_set(GPIOC, GPIO1); return d2; } /* * uint8_t status = read_xyz(int16_t []) * * This function exploits the fact that you can do a read + * auto increment of the SPI registers. It starts at the * address of the X register and reads 6 bytes. * * Then the status register is read and returned. */ uint8_t read_xyz(int16_t vecs[3]) { uint8_t buf[7]; int i; gpio_clear(GPIOC, GPIO1); /* CS* select */ spi_send(SPI5, 0xc0 | 0x28); (void) spi_read(SPI5); for (i = 0; i < 6; i++) { spi_send(SPI5, 0); buf[i] = spi_read(SPI5); } gpio_set(GPIOC, GPIO1); /* CS* deselect */ vecs[0] = (buf[1] << 8 | buf[0]); vecs[1] = (buf[3] << 8 | buf[2]); vecs[2] = (buf[5] << 8 | buf[4]); return read_reg(0x27); /* Status register */ } /* * void write_reg(uint8_t register, uint8_t value) * * This code then writes into a register on the chip first * selecting it and then writing to it. */ void write_reg(uint8_t reg, uint8_t value) { gpio_clear(GPIOC, GPIO1); /* CS* select */ spi_send(SPI5, reg); (void) spi_read(SPI5); spi_send(SPI5, value); (void) spi_read(SPI5); gpio_set(GPIOC, GPIO1); /* CS* deselect */ return; } int print_decimal(int); /* * int len = print_decimal(int value) * * Very simple routine to print an integer as a decimal * number on the console. */ int print_decimal(int num) { int ndx = 0; char buf[10]; int len = 0; char is_signed = 0; if (num < 0) { is_signed++; num = 0 - num; } buf[ndx++] = '\000'; do { buf[ndx++] = (num % 10) + '0'; num = num / 10; } while (num != 0); ndx--; if (is_signed != 0) { console_putc('-'); len++; } while (buf[ndx] != '\000') { console_putc(buf[ndx--]); len++; } return len; /* number of characters printed */ } char *axes[] = { "X: ", "Y: ", "Z: " }; /* * This then is the actual bit of example. It initializes the * SPI port, and then shows a continuous display of values on * the console once you start it. Typing ^C will reset it. */ int main(void) { int16_t vecs[3]; int16_t baseline[3]; int tmp, i; int count; uint32_t cr_tmp; clock_setup(); console_setup(115200); /* Enable the GPIO ports whose pins we are using */ rcc_periph_clock_enable(RCC_GPIOF | RCC_GPIOC); gpio_mode_setup(GPIOF, GPIO_MODE_AF, GPIO_PUPD_PULLDOWN, GPIO7 | GPIO8 | GPIO9); gpio_set_af(GPIOF, GPIO_AF5, GPIO7 | GPIO8 | GPIO9); gpio_set_output_options(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, GPIO7 | GPIO9); /* Chip select line */ gpio_set(GPIOC, GPIO1); gpio_mode_setup(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO1); rcc_periph_clock_enable(RCC_SPI5); cr_tmp = SPI_CR1_BAUDRATE_FPCLK_DIV_8 | SPI_CR1_MSTR | SPI_CR1_SPE | SPI_CR1_CPHA | SPI_CR1_CPOL_CLK_TO_1_WHEN_IDLE; put_status("\nBefore init: "); SPI_CR2(SPI5) |= SPI_CR2_SSOE; SPI_CR1(SPI5) = cr_tmp; put_status("After init: "); baseline[0] = 0; baseline[1] = 0; baseline[2] = 0; console_puts("MEMS demo (new version):\n"); console_puts("Press a key to read the registers\n"); console_getc(1); tmp = read_reg(0xf); if (tmp != 0xD4) { console_puts("Maybe this isn't a Gyroscope.\n"); } /* * These parameters are sort of random, clearly I need * set something. Based on the app note I reset the 'baseline' * values after 100 samples. But don't see a lot of change * when I move the board around. Z doesn't move at all but the * temperature reading is correct and the ID code returned is * as expected so the SPI code at least is working. */ write_reg(0x20, 0xcf); /* Normal mode */ write_reg(0x21, 0x07); /* standard filters */ write_reg(0x23, 0xb0); /* 250 dps */ tmp = (int) read_reg(0x26); console_puts("Temperature: "); print_decimal(tmp); console_puts(" C\n"); count = 0; while (1) { tmp = read_xyz(vecs); for (i = 0; i < 3; i++) { int pad; console_puts(axes[i]); tmp = vecs[i] - baseline[i]; pad = print_decimal(tmp); pad = 15 - pad; while (pad--) { console_puts(" "); } } console_putc('\r'); if (count == 100) { baseline[0] = vecs[0]; baseline[1] = vecs[1]; baseline[2] = vecs[2]; } else { count++; } msleep(100); } }