309 lines
7.4 KiB
C
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);
|
|
}
|
|
}
|