/*
Configurare il giroscopio per misurare la velocità angolare del solo asse Z, con fondo scala +-(2000) dps.
Utilizzare un output data rate a 760 Hz e una frequenza di cut-off a 50 Hz.
Acquisire un campione ogni 10 ms, e utilizzare un buffer circolare per la memorizzazione. Esprimere il valore in dps (°C/s).
Raccogliere 100 campioni.
Visualizzare tale buffer nel Live Watch.
*/
#include "stm32f4xx.h"
#define MS_COUNT 42000
#define PERIOD_MS 10
#define N 100
#define GPIOAEN 1
#define GPIOEEN 1 << 4
#define TIM2EN 1
#define SPI1EN 1 << 12
#define GPIOA_PA5_AF 1 << 11
#define GPIOA_PA6_AF 1 << 13
#define GPIOA_PA7_AF 1 << 15
#define GPIOA_PA5_OSPEED_MAX 0x3 << 10
#define GPIOA_PA6_OSPEED_MAX 0x3 << 12
#define GPIOA_PA7_OSPEED_MAX 0x3 << 14
#define GPIOA_PA5_SPI1_SCK 0x5 << 20
#define GPIOA_PA6_SPI1_MISO 0x5 << 24
#define GPIOA_PA7_SPI1_MOSI 0x5 << 28
#define GPIOA_PA5_PD 1 << 11
#define GPIOA_PA6_PD 1 << 13
#define GPIOA_PA7_PD 1 << 15
#define GPIOE_PE3_OUT 1 << 6
#define SPI1_SSM 1 << 9
#define SPI1_SSI 1 << 8
#define SPI1_SPE 1 << 6
#define SPI1_BR 0x3 << 3
#define SPI1_MSTR 1 << 2
#define SPI1_TXE 1 << 1
#define SPI1_RXNE 1
#define TIM2_ARR (MS_COUNT * PERIOD_MS) - 1
#define TIM2_PSC 0
#define TIM2_UIE 1
#define TIM2_CEN 1
#define TIM2_UIF 0
#define TIM2_INT 1 << 28
#define PE3_CHS 1 << 3
#define CTRL_REG1_ADDR 0x20
#define CTRL_REG1_CONF 0xED
#define CTRL_REG4_ADDR 0x23
#define CTRL_REG4_CONF 0x30
#define OUT_Z_L_ADDR 0x2C
#define RD 1 << 7
#define DUMMY 0x00
#define COMP_8BIT_MAX 256
#define SENSITIVITY 70
short int data = 0;
short int z_l = 0;
short int z_h = 0;
short int z = 0;
short int buffer[N] = {0};
int cnt = 0;
int main( )
{
RCC->AHB1ENR |= (GPIOAEN | GPIOEEN); // abilito le due porte (A ed E)
RCC->APB1ENR |= TIM2EN; // abilito il timer TIM2
RCC->APB2ENR |= SPI1EN; // abilito SPI1
GPIOA->MODER |= (GPIOA_PA5_AF | GPIOA_PA6_AF | GPIOA_PA7_AF); // PA5, PA6 E PA7 con Alternate Functions
GPIOA->OSPEEDR |= (GPIOA_PA5_OSPEED_MAX | GPIOA_PA6_OSPEED_MAX | GPIOA_PA7_OSPEED_MAX); // PA5, PA6 e PA7 con velocità di output massime
GPIOA->AFR[0] |= (GPIOA_PA5_SPI1_SCK | GPIOA_PA6_SPI1_MISO | GPIOA_PA7_SPI1_MOSI); // PA5, PA6 e PA7 come SPI1_SCK, SPI1_MISO e SPI1_MOSI
GPIOA->PUPDR |= (GPIOA_PA5_PD | GPIOA_PA6_PD | GPIOA_PA7_PD); // PA5, PA6 e PA7 in pull-down
GPIOE->MODER |= GPIOE_PE3_OUT; // PE3 come output (Chip Select)
SPI1->CR1 |= (SPI1_SSM | SPI1_SSI | SPI1_BR | SPI1_MSTR); // SSM = no pin NSS; SSI = sostituisce il pin NSS; BR = Baud Rate; MSTR = Master
TIM2->ARR = TIM2_ARR; // viene generato un Update Event ogni (TIM_ARR + 1) conteggi
TIM2->PSC = TIM2_PSC; // Prescaler
TIM2->DIER |= TIM2_UIE; // viene generata una interrupt per ogni Update Event
SPI1->CR1 |= SPI1_SPE; // abilito la comunicazione con il giroscopio
// Configurazione CTRL_REG1
GPIOE->ODR &= !(PE3_CHS); // abbasso il Chip Select
while (!(SPI1->SR & SPI1_TXE) & SPI1_TXE); // attendo che il buffer di trasmissione sia libero (!empty)
SPI1->DR = CTRL_REG1_ADDR; // punto il registro CTRL_REG1 in modalità scrittura
while (!(SPI1->SR & SPI1_RXNE) & SPI1_RXNE); // attendo che il buffer di ricezione sia non vuoto (!notEmpty)
data = SPI1->DR; // serve solo a resettare i bit dello SR
while (!(SPI1->SR & SPI1_TXE) & SPI1_TXE); // attendo che il buffer di trasmissione sia libero (!empty)
SPI1->DR = CTRL_REG1_CONF; // scrivo la configurazione in CTRL_REG1 impostando l'output data rate e la banda passante richiesti (vedi man. Giroscopio)
// abilito anche l'asse Z
while (!(SPI1->SR & SPI1_RXNE) & SPI1_RXNE); // attendo che il buffer di ricezione sia non vuoto (!notEmpty)
data = SPI1->DR; // serve solo a resettare i bit dello SR
GPIOE->ODR |= PE3_CHS; // alzo il Chip Select
// Configurazione CTRL_REG4
GPIOE->ODR &= !(PE3_CHS); // abbasso il Chip Select
while (!(SPI1->SR & SPI1_TXE) & SPI1_TXE); // attendo che il buffer di trasmissione sia libero (!empty)
SPI1->DR = CTRL_REG4_ADDR; // punto il registro CTRL_REG4 in modalità scrittura
while (!(SPI1->SR & SPI1_RXNE) & SPI1_RXNE); // attendo che il buffer di ricezione sia non vuoto (!notEmpty)
data = SPI1->DR; // serve solo a resettare i bit dello SR
while (!(SPI1->SR & SPI1_TXE) & SPI1_TXE); // attendo che il buffer di trasmissione sia libero (!empty)
SPI1->DR = CTRL_REG4_CONF; // scrivo la configurazione in CTRL_REG4 impostando il fondo scala specificato
while (!(SPI1->SR & SPI1_RXNE) & SPI1_RXNE); // attendo che il buffer di ricezione sia non vuoto (!notEmpty)
data = SPI1->DR; // serve solo a resettare i bit dello SR
GPIOE->ODR |= PE3_CHS; // alzo il Chip Select
NVIC->ISER[0] |= TIM2_INT; // abilito la routine di servizio per le interrupt generate da TIM2
TIM2->CR1 |= TIM2_CEN; // abilito il conteggio del timer TIM2
while (1);
}
void TIM2_IRQHandler( )
{
TIM2->SR &= TIM2_UIF; // abbasso il flag Update Interrupt
// Lettura della velocità angolare dall'asse Z
GPIOE->ODR &= !(PE3_CHS); // abbasso il Chip Select
while (!(SPI1->SR & SPI1_TXE) & SPI1_TXE); // attendo che il buffer di trasmissione sia libero (!empty)
SPI1->DR = (OUT_Z_L_ADDR | RD); // punto il registro OUT_Z_L in modalità lettura
while (!(SPI1->SR & SPI1_RXNE) & SPI1_RXNE); // attendo che il buffer di ricezione sia non vuoto (!notEmpty)
data = SPI1->DR; // serve solo a resettare i bit dello SR
while (!(SPI1->SR & SPI1_TXE) & SPI1_TXE); // attendo che il buffer di trasmissione sia libero (!empty)
SPI1->DR = DUMMY; // invio un DUMMY, autorizzando il giroscopio a inviare i dati
while (!(SPI1->SR & SPI1_RXNE) & SPI1_RXNE); // attendo che il buffer di ricezione sia non vuoto (!notEmpty)
z_l = SPI1->DR; // prelevo i primi 8 bit di Z (sono i meno significativi)
while (!(SPI1->SR & SPI1_TXE) & SPI1_TXE); // attendo che il buffer di trasmissione sia libero (!empty)
SPI1->DR = DUMMY; // invio un DUMMY, autorizzando il giroscopio a inviare i dati
while (!(SPI1->SR & SPI1_RXNE) & SPI1_RXNE); // attendo che il buffer di ricezione sia non vuoto (!notEmpty)
z_h = SPI1->DR; // prelevo gli ultimi 8 bit di Z (sono i più significativi)
GPIOE->ODR |= PE3_CHS; // alzo il Chip Select
z_l = (z_l > COMP_8BIT_MAX - 1 ? -(COMP_8BIT_MAX - z_l) : z_l); // z_l è in complemento a 2
z_h = (z_h > COMP_8BIT_MAX - 1 ? -(COMP_8BIT_MAX - z_h) : z_h); // z_h è in complemento a 2
z = ((z_l + (z_h << 8)) * SENSITIVITY) / 1000; // il parametro So (sensitivity) è riportato nel manuale del Giroscopio, e varia in base al fondo scala (FS)
// inoltre, la grandezza è espressa in milligradi/secondo (noi la vogliamo in gradi/secondo)
buffer[cnt++] = z; // memorizzo il campione raccolto nel buffer
cnt %= N; // per la memorizzazione circolare (quando cnt diventa N, si azzera, e si ricomincia da capo)
}