RT-Thread SPI Tutorial
The experimental environment uses the Pandora development board of punctual atom.
The SPI slave device uses the BMP280 temperature, humidity and atmospheric pressure sensor.
Use RT-Thread Studio to build basic functions.
1. Create a project
Use RT-Thread Studio IDE to create chip-level projects. After the creation is complete, you can directly compile and download for testing.
2. Add driver
2.1 Project configuration
After the project is created, 组建和服务层/Drivers/SPI
open the SPI driver in RT-Thread Studio.
Then configure the SPI:
After the configuration is complete, Ctrl+S
saving the configuration will automatically update the project code.
After completing the configuration, you also need to board.h
open the macro of the SPI that needs to be used in .
Then board.c
add STM32 SPI initialization code, you can generate code by configuring CubeMX:
SPI_HandleTypeDef hspi2;
/* SPI2 init function */
void MX_SPI2_Init(void)
{
hspi2.Instance = SPI2;
hspi2.Init.Mode = SPI_MODE_MASTER;
hspi2.Init.Direction = SPI_DIRECTION_2LINES;
hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi2.Init.NSS = SPI_NSS_SOFT;
hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi2.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi2) != HAL_OK)
{
Error_Handler();
}
}
void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {
0};
if(spiHandle->Instance==SPI2)
{
/* USER CODE BEGIN SPI2_MspInit 0 */
/* USER CODE END SPI2_MspInit 0 */
/* SPI2 clock enable */
__HAL_RCC_SPI2_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**SPI2 GPIO Configuration
PB13 ------> SPI2_SCK
PB14 ------> SPI2_MISO
PB15 ------> SPI2_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE BEGIN SPI2_MspInit 1 */
/* USER CODE END SPI2_MspInit 1 */
}
}
void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
{
if(spiHandle->Instance==SPI2)
{
/* USER CODE BEGIN SPI2_MspDeInit 0 */
/* USER CODE END SPI2_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_SPI2_CLK_DISABLE();
/**SPI2 GPIO Configuration
PB13 ------> SPI2_SCK
PB14 ------> SPI2_MISO
PB15 ------> SPI2_MOSI
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15);
/* USER CODE BEGIN SPI2_MspDeInit 1 */
/* USER CODE END SPI2_MspDeInit 1 */
}
}
After completion, the SPI driver is added.
2.2 Code Analysis
2.2.1 SPI driver usage process
- The SPI bus device
rt_spi_bus_register()
is registered in the SPI device driver framework through the interface. - The SPI device driver framework
rt_device_register()
registers the SPI bus device in the I/O device manager through the interface. - The SPI slave driver
rt_spi_bus_attach_device()
mounts the slave device on the SPI bus device through the interface, and registers it in the SPI device driver framework. - The SPI slave driver accesses the SPI slave device hardware through the SPI device interface.
2.2.2 Code
In the drivers group drv_spi.c
:
int rt_hw_spi_init(void)
{
stm32_get_dma_info();
return rt_hw_spi_bus_init();
}
INIT_BOARD_EXPORT(rt_hw_spi_init);
With the declaration here INIT_BOARD_EXPORT()
, add initialization code to .rti_fn.1
the section:
#define INIT_EXPORT(fn, level) \
RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn." level) = fn
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")
Then focus on initializing the device driver board.c
in rt_hw_board_init()
-> in.rt_components_board_init()
void rt_components_board_init(void)
{
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
{
(*fn_ptr)();
}
#endif
}
rt_hw_spi_bus_init()
Called again rt_spi_bus_register()
, rt_spi_bus_register()
call rt_spi_bus_device_init()
to call to rt_device_register()
complete the registration.
static rt_err_t spi_configure(struct rt_spi_device *device,
struct rt_spi_configuration *configuration)
{
RT_ASSERT(device != RT_NULL);
RT_ASSERT(configuration != RT_NULL);
struct stm32_spi *spi_drv = rt_container_of(device->bus, struct stm32_spi, spi_bus);
spi_drv->cfg = configuration;
return stm32_spi_init(spi_drv, configuration);
}
static const struct rt_spi_ops stm_spi_ops =
{
.configure = spi_configure,
.xfer = spixfer,
};
HAL_SPI_Init()
Initialization stm32_spi_init()
is done in and registered in ops. After attach, call directly rt_spi_configure()
to complete the initialization.
Note that unlike the use of i2c, SPI must be bound through attach to use the SPI device interface.
3. Using SPI
Complete the bmp280 code to read the Device ID, add it to the file bmp280.c
, and then add the file to the project:
#include <rtthread.h>
#include <rtdevice.h>
#include <drv_spi.h>
#define BME280_SPI_DEVICE_NAME "spi20"
#define BEM280_REG_ID 0XD0
rt_bool_t initialnized = RT_FALSE;
static void spi_bme280_demo(void)
{
uint8_t data = BEM280_REG_ID | (1 << 7);
rt_err_t err;
struct rt_spi_device * spi_bme280;
if (!initialnized) {
initialnized = RT_TRUE;
err = rt_hw_spi_device_attach("spi2", BME280_SPI_DEVICE_NAME, GPIOB, GPIO_PIN_12);
if (err) {
rt_kprintf("attach device error\r\n");
return ;
}
}
spi_bme280 = (struct rt_spi_device *)rt_device_find(BME280_SPI_DEVICE_NAME);
if (spi_bme280 == RT_NULL) {
rt_kprintf("find %s error\r\n", BME280_SPI_DEVICE_NAME);
return ;
}
struct rt_spi_configuration cfg = {
.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB,
.data_width = 8,
.max_hz = 1 * 1000 * 1000
};
err = rt_spi_configure(spi_bme280, &cfg);
if (err != RT_NULL) {
rt_kprintf("spi configurate error\r\n");
return ;
}
uint8_t send_buf[5] = {
data, 0xff};
uint8_t recv_buf[5];
if (rt_spi_transfer(spi_bme280, send_buf, recv_buf, 2) == 0) {
rt_kprintf("spi transfer error\r\n");
}
rt_kprintf("bme280 id: 0x%02x\r\n", recv_buf[1]);
}
MSH_CMD_EXPORT(spi_bme280_demo, read bme280 id);
The CS Pin I use here is PB12, pay attention to the attach interface parameters.
4. Test
The Device ID of BMP280 is 0x58
, BME280 is 0x60
.
Compile and burn the above project, and enter the command to verify: