STM32F429I-Discovery board has an L3GD20 3-axis gyroscope onboard connected to SPI channel 5. In this article we'll try to get it up and running. I will continue where we left off last time - a working example with blinking LEDs and UART.
So, let us look at the devkit connections, as defined in Discovery board documentation:
- MOSI is on PF9
- MISO is on PF8
- SCK is on PF7
- Chip-select (CS) is in PC1
- Interrupt 1 is on PA1
- Interrupt 2 is on PA2
#define SPI5_SCK_Pin GPIO_PIN_7 #define SPI5_SCK_GPIO_Port GPIOF #define SPI5_MISO_Pin GPIO_PIN_8 #define SPI5_MISO_GPIO_Port GPIOF #define SPI5_MOSI_Pin GPIO_PIN_9 #define SPI5_MOSI_GPIO_Port GPIOF .. #define NCS_MEMS_SPI_Pin GPIO_PIN_1 #define NCS_MEMS_SPI_GPIO_Port GPIOC .. #define MEMS_INT1_Pin GPIO_PIN_1 #define MEMS_INT1_GPIO_Port GPIOA #define MEMS_INT2_Pin GPIO_PIN_2 #define MEMS_INT2_GPIO_Port GPIOA
Another thing we should worry about (it's a pretty common source of headache) are SPI bus setup. Generated setup in spi.c states:
hspi5.Instance = SPI5; hspi5.Init.Mode = SPI_MODE_MASTER; hspi5.Init.Direction = SPI_DIRECTION_2LINES; hspi5.Init.DataSize = SPI_DATASIZE_8BIT; hspi5.Init.CLKPolarity = SPI_POLARITY_HIGH; hspi5.Init.CLKPhase = SPI_PHASE_2EDGE; hspi5.Init.NSS = SPI_NSS_SOFT; hspi5.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; hspi5.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi5.Init.TIMode = SPI_TIMODE_DISABLE;
- Instance: SPI5 is what we want, yes
- Mode: Master mode - we will be initiating the communications
- Direction: 2 Lines. That's what we have and want. Apparently, there's a support for single-wire half-duplex comms as well, but that's not for us
- Datasize: 8bit. True. All the registers are 8bit, wider words are sent as pairs of high/low bytes
- Clock polarity (CPOL): Idle state is high, so polarity is high
- Clock phase (CPHA): Sampling seems to be done on the trailing edge, so this should be SPI_PHASE_2EDGE
- NSS (Slave select) will be done in software for now, by manually pulling CS pin low before transfer, with subsequent pull back up once transfer is done. It is possible to use hardware NSS, but it requires disabling the SPI master after transfer to pull it back up (just an implementation in STM32 HAL)
- Baud Rate Prescaler is a clock divider, which sets the transfer speed. Leave it be for now
- First Bit should be the most significant bit (MSB) as per gyro datasheet
- We are not interested in TI mode for now, so leave it disabled
void vGyroTesterTask(void const * argument) { HAL_StatusTypeDef response = HAL_ERROR; // default to error
// 0x0F is WHO_AM_I register, 0x80 read bit, should return 0b11010100 or 0xD4
uint8_t txbuf[3] = {0x0F | 0x80, 0x00}; uint8_t rxbuf[3] = {0x00, 0x00}; HAL_GPIO_WritePin(NCS_MEMS_SPI_GPIO_Port, NCS_MEMS_SPI_Pin, GPIO_PIN_RESET); response = HAL_SPI_TransmitReceive(&hspi5, txbuf, rxbuf, 2, 1000); HAL_GPIO_WritePin(NCS_MEMS_SPI_GPIO_Port, NCS_MEMS_SPI_Pin, GPIO_PIN_SET); if (response == HAL_OK) { printf("Sent: %02x %02x Got: %02x %02x\r\n", txbuf[0], txbuf[1], rxbuf[0], rxbuf[1]); } else { printf("Got error response as %d\r\n", response); } osThreadTerminate(gyroTaskHandle); }
- We define default response as error, just to be sure, that it gets changed to HAL_OK
- define a 2 byte wide transmit buffer (with last spot as NULL terminator) First is the byte (command) we send. We want to read (0x80) register 0x0F, then we send bunch of zeros as a dummy payload to give chance to the chip to respond.
- define a 2 byte wide receive buffer (with last spot as NULL terminator). First byte is dummy receive, normally filled with 0xFF (no data), the second one will contain the response from the chip
- pull down our CS pin, to indicate to the slave, that we are talking to it
- Do the actual transceiving of the data
- pull back up the CS pin, to tell the chip, that we are done with it
- check the response status code and print out the data or code received
- kill the thread, for we are done with it
All that's remaining, is just starting the task and let it run. I will add a new handle and reduce the priorities of blinky tasks to avoid them interrupting our transmit, since it is in blocking mode, that might mess things up:
osThreadId defaultTaskHandle, blinkyTaskHandle, gyroTaskHandle; .. void MX_FREERTOS_Init(void) { osThreadDef(defaultTask, StartDefaultTask, osPriorityLow, 0, 1000); defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL); osThreadDef(blinkyTask, vBlinkyTask, osPriorityLow, 0, 1000); blinkyTaskHandle = osThreadCreate(osThread(blinkyTask), NULL); osThreadDef(gyroTask, vGyroTesterTask, osPriorityHigh, 0, 1000); gyroTaskHandle = osThreadCreate(osThread(gyroTask), NULL); }
As expected, we get our notification, that system is up. First goes high-priority SPI task and then it continues with low priority blinking. The printout shows, that MCU sent 0x8F to the gyro and got back it's chip identification byte 0xD4, just as expected.
I'm not particularly interested in implementing full driver for the gyro, that can be left as an exercise for the reader. There should be plenty of those already available online.
As usual, sources on GitHub have been updated to include all of the above. Next up: Part V: SPI with DMA
No comments:
Post a Comment