Files
libopencm3-examples/examples/stm32/f4/stm32f429i-discovery/spi/spi-mems.c
2021-06-19 02:19:47 -07:00

309 lines
7.4 KiB
C

/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2013 Chuck McManis <cmcmanis@mcmanis.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
/*
* SPI Port example
*/
#include <stdint.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/spi.h>
#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);
}
}