I'm quite new in STM32 world, and I have a problem with the on-board L3GD20 gyroscope on teh STM32F429-DISC1 board.
I had troubles in getting it running (the gyroscope was constantly sending the same data, even after reset or power down) and after I finally managed to get it working (by sending instructions a couple of times), I saw strange results (both on x-axis, y and z) (see graph below).
Am I missing something, or should I do something with the raw data, that will smooth it? Is there a possibility, that the IC is defective?
I am using Atollic TrueStudio v9.0 for STM32 with STM32F429-DISC1.
Here is my code:
#include "stm32f4xx.h"
#include "stm32f429i_discovery.h"
#include "stdio.h"
volatile uint32_t elapsed = 0;
#define CS_gyro_start GPIO_ResetBits( GPIOC, GPIO_Pin_1 )
#define CS_gyro_stop GPIO_SetBits( GPIOC, GPIO_Pin_1 )
void DelayMS( int time ){
elapsed = time;
while( elapsed > 0 );
}
void SysTick_Handler(){
if( elapsed > 0 ) --elapsed;
}
void SendChar( char ch ){
while( USART_GetFlagStatus( USART1, USART_FLAG_TXE ) == RESET ){}
USART_SendData( USART1, ch );
}
void sendString( const char *s ){
while( *s ){
SendChar( *s++ );
}
}
int _write( int file, char *ptr, int len ){
sendString( ptr );
return len;
} //sadly this doesn't work with float variables
void initialize( void ){
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOA |
RCC_AHB1Periph_GPIOC |
RCC_AHB1Periph_GPIOF, ENABLE );
RCC_APB2PeriphClockCmd( RCC_APB2Periph_SPI5 |
RCC_APB2Periph_SYSCFG |
RCC_APB2Periph_USART1, ENABLE );
GPIO_InitTypeDef gpio;
USART_InitTypeDef usart;
SPI_InitTypeDef spi;
GPIO_StructInit( &gpio );
USART_StructInit( &usart );
SPI_StructInit( &spi );
//usart
GPIO_PinAFConfig( GPIOA, GPIO_PinSource10, GPIO_AF_USART1 );
GPIO_PinAFConfig( GPIOA, GPIO_PinSource9, GPIO_AF_USART1 );
gpio.GPIO_Mode = GPIO_Mode_AF;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_Init( GPIOA, &gpio );
usart.USART_BaudRate = 115200;
USART_Init( USART1, &usart );
USART_Cmd( USART1, ENABLE );
//spi
GPIO_PinAFConfig( GPIOF, GPIO_PinSource7, GPIO_AF_SPI5 );
GPIO_PinAFConfig( GPIOF, GPIO_PinSource9, GPIO_AF_SPI5 );
GPIO_PinAFConfig( GPIOF, GPIO_PinSource8, GPIO_AF_SPI5 );
//SS
gpio.GPIO_Pin = GPIO_Pin_1;
gpio.GPIO_Mode = GPIO_Mode_OUT;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init( GPIOC, &gpio );
GPIO_SetBits( GPIOC, GPIO_Pin_1 );
//SCK, MOSI
gpio.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_7;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_Mode = GPIO_Mode_AF;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init( GPIOF, &gpio );
//MISO
gpio.GPIO_Pin = GPIO_Pin_8;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_Mode = GPIO_Mode_AF;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init( GPIOF, &gpio );
spi.SPI_Mode = SPI_Mode_Master;
spi.SPI_NSS = SPI_NSS_Soft;
spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
SPI_Init( SPI5, &spi );
SPI_Cmd( SPI5, ENABLE );
}
uint8_t SPI_sendByte( uint8_t byte_ ){
while( SPI_I2S_GetFlagStatus( SPI5, SPI_I2S_FLAG_TXE ) == RESET ){}
SPI_I2S_SendData( SPI5, byte_ );
while( SPI_I2S_GetFlagStatus( SPI5, SPI_I2S_FLAG_RXNE ) == RESET ){}
return SPI_I2S_ReceiveData( SPI5 );
}
void SPI_writeData( uint8_t address, uint8_t byteToWrite ){
CS_gyro_start;
SPI_sendByte( address );
SPI_sendByte( byteToWrite );
CS_gyro_stop;
}
void GetGyroValues( uint16_t *x, uint16_t *y, uint16_t *z ){
CS_gyro_start;
SPI_sendByte( 0x29 | 0x80 );
*x = SPI_sendByte( 0xff );
CS_gyro_stop;
CS_gyro_start;
SPI_sendByte( 0x28 | 0x80 );
*x |= (SPI_sendByte( 0xff ) << 8);
CS_gyro_stop;
CS_gyro_start;
SPI_sendByte( 0x2B | 0x80 );
*y = SPI_sendByte( 0xff );
CS_gyro_stop;
CS_gyro_start;
SPI_sendByte( 0x2A | 0x80 );
*y |= (SPI_sendByte( 0xff ) << 8);
CS_gyro_stop;
CS_gyro_start;
SPI_sendByte( 0x2D | 0x80 );
*z = SPI_sendByte( 0xff );
CS_gyro_stop;
CS_gyro_start;
SPI_sendByte( 0x2C | 0x80 );
*z |= (SPI_sendByte( 0xff ) << 8);
CS_gyro_stop;
}
int main( void ){
SysTick_Config( SystemCoreClock / 1000 );
initialize();
SPI_writeData(0x20, 0xff); //power on, settings from TM-library & datasheet
SPI_writeData(0x21, 0x00); //high-pass filter settings
SPI_writeData(0x24, 0x10); //high-pass filter en
SPI_writeData(0x23, 0x20); //scale 2000
uint16_t x, y, z;
while( 1 ){
GetGyroValues( &x, &y, &z );
printf( "x: %d\r\n", x );
DelayMS( 100 );
}
}
uint32_t sEE_TIMEOUT_UserCallback(void)
{
/* TODO, implement your code here */
while (1)
{
}
}// This is required by Atollic
Here is a sample graph showing the changes on x-axis. (the discovery board was more or less at 45 degrees, when it's at 0 degrees - laying down, then the output is a stable 65000)
From the L3GD20 app note:
Note the last part "expressed as a two’s complement number"; you are incorrectly interpreting the data as unsigned. It looks like the value is hovering around zero; or actually it looks like you are holding it rather unsteadily in a position, but certainly not rotating it continuously as a steady rate. The device is a gyroscope not an accelerometer. It measures angular velocity not acceleration (or tilt - i.e. acceleration due to gravity). While stationary you would expect zero on all axes. What your graph shows is probably your hand shaking trying to hold it at 45 degrees.
void GetGyroValues( int16_t *x, int16_t *y, int16_t *z )
should be more sucessful, and of course:
int16_t x, y, z;
You can get an approximate measure of change of angle by integrating angular velocity, but not absolute angle. Even then you may have to calibrate frequently for zero - a small non-zero bias in integration will manifest itself as a false slow rotation.