forked from espressif/esp-idf
feat(lp_core): added lp-spi example for lp core
Added an example of using lp-spi from lp-core to read sensor data from a BME280 sensor.
This commit is contained in:
@@ -327,6 +327,12 @@ examples/system/ulp/lp_core/lp_i2c:
|
||||
depends_components:
|
||||
- ulp
|
||||
|
||||
examples/system/ulp/lp_core/lp_spi:
|
||||
enable:
|
||||
- if: SOC_LP_SPI_SUPPORTED == 1 and SOC_DEEP_SLEEP_SUPPORTED == 1
|
||||
depends_components:
|
||||
- ulp
|
||||
|
||||
examples/system/ulp/lp_core/lp_uart/lp_uart_echo:
|
||||
disable:
|
||||
- if: (SOC_ULP_LP_UART_SUPPORTED != 1) or (SOC_DEEP_SLEEP_SUPPORTED != 1)
|
||||
|
6
examples/system/ulp/lp_core/lp_spi/CMakeLists.txt
Normal file
6
examples/system/ulp/lp_core/lp_spi/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(lp_spi)
|
54
examples/system/ulp/lp_core/lp_spi/README.md
Normal file
54
examples/system/ulp/lp_core/lp_spi/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
| Supported Targets | ESP32-P4 |
|
||||
| ----------------- | -------- |
|
||||
|
||||
# LP I2C Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
## Overview
|
||||
|
||||
This example demonstrates the basic usage of the LP SPI driver from the LP core by reading to and writing from a sensor connected over SPI. The ULP will periodically read temperature and humidity measurements from the sensor and wake up the HP CPU if `WAKEUP_HP_CPU_LIMIT_CELSIUS` (30 degrees by default) is exceeded.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
To run this example, you should have an ESP based development board that supports the LP SPI peripheral on the LP Core as well as a BME280 sensor. BME280 is a combined temperature, humidity and pressure sensor. More information about it can be found in at [BME280](https://www.bosch-sensortec.com/products/environmental-sensors/humidity-sensors-bme280/).
|
||||
|
||||
#### Pin Assignment:
|
||||
|
||||
**Note:** The following pin assignments are used by default.
|
||||
|
||||
| | SDI(MISO) | SDO(MOSI) | SCK | CSB (CS) |
|
||||
| ----------------------- | ----------| ----------| ----- | -------- |
|
||||
| ESP32-P4 LP SPI Master | GPIO6 | GPIO7 | GPIO8 | GPIO4 |
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Enter `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
The log output should indicate that the LP core and the LP SPI peripheral have been successfully initialized. The main CPU would then enter deep sleep mode.
|
||||
|
||||
```bash
|
||||
Not an LP core wakeup. Cause = 0
|
||||
Initializing...
|
||||
LP SPI initialized successfully
|
||||
LP core loaded with firmware successfully
|
||||
Entering deep sleep...
|
||||
|
||||
(When the BME280 sensor is exposed to a temperature above normal room temperature, defined as 30 degree by default in the ULP code, it will wake up the HP CPU)
|
||||
|
||||
LP core woke up the main CPU
|
||||
Temperature 31.31 degree celsius, humidity 66.01%RH
|
||||
Entering deep sleep...
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)
|
25
examples/system/ulp/lp_core/lp_spi/main/CMakeLists.txt
Normal file
25
examples/system/ulp/lp_core/lp_spi/main/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
# Register the component
|
||||
idf_component_register(SRCS "lp_spi_main.c"
|
||||
INCLUDE_DIRS ""
|
||||
REQUIRES ulp)
|
||||
|
||||
#
|
||||
# ULP support additions to component CMakeLists.txt.
|
||||
#
|
||||
# 1. The LP Core app name must be unique (if multiple components use LP Core).
|
||||
set(ulp_app_name lp_core_${COMPONENT_NAME})
|
||||
#
|
||||
# 2. Specify all C files.
|
||||
# Files should be placed into a separate directory (in this case, lp_core/),
|
||||
# which should not be added to COMPONENT_SRCS.
|
||||
set(ulp_lp_core_sources "lp_core/main.c")
|
||||
|
||||
#
|
||||
# 3. List all the component source files which include automatically
|
||||
# generated LP Core export file, ${ulp_app_name}.h:
|
||||
set(ulp_exp_dep_srcs "lp_spi_main.c")
|
||||
|
||||
#
|
||||
# 4. Call function to build ULP binary and embed in project using the argument
|
||||
# values above.
|
||||
ulp_embed_binary(${ulp_app_name} "${ulp_lp_core_sources}" "${ulp_exp_dep_srcs}")
|
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// Contains only the subset of registers used in this example
|
||||
|
||||
#define BME280_CHIP_ID_REG 0xD0 // Chip ID Register
|
||||
|
||||
#define BME280_RESET_REG 0xE0 // Soft Reset Register
|
||||
|
||||
#define BME280_CTRL_HUM_REG 0xF2 // Humidity control
|
||||
#define BME280_HUM_OVERSAMPLING_BIT 0 // Bit position for humidity oversampling
|
||||
#define BME280_HUM_OVERSAMPLING_1X 0x1 // Value for 1x oversampling
|
||||
|
||||
#define BME280_STATUS_REG 0xF3 // Status Register
|
||||
#define BME280_MEASURING_BIT 3 // Bit position for measuring in progress status
|
||||
|
||||
#define BME280_CTRL_MEAS_REG 0xF4 // Measurement Control Register
|
||||
#define BME280_MODE_BIT 0 // Bit position for mode
|
||||
#define BME280_MODE_FORCED 0x1 // Value for setting forced mode
|
||||
#define BME280_TEMP_OVERSAMPLING_BIT 5 // Bit position for temperature oversampling
|
||||
#define BME280_TEMP_OVERSAMPLING_1X 0x1 // Value for 1x oversampling
|
||||
|
||||
#define BME280_CONFIG_REG 0xF5 // Configuration Register
|
||||
|
||||
#define BME280_TEMPERATURE_MSB_REG 0xFA // Temperature data MSB
|
||||
#define BME280_TEMPERATURE_LSB_REG 0xFB // Temperature data LSB
|
||||
#define BME280_TEMPERATURE_XLSB_REG 0xFC // Temperature data XLSB
|
||||
#define BME280_HUMIDITY_MSB_REG 0xFD // Humidity data MSB
|
||||
#define BME280_HUMIDITY_LSB_REG 0xFE // Humidity data LSB
|
||||
|
||||
#define BME280_TRIM_PARAM_TEMP_1_REG 0x88 // Trimming Parameter T1
|
||||
#define BME280_TRIM_PARAM_HUM_1_REG 0xA1 // Trimming Parameter H1
|
||||
#define BME280_TRIM_PARAM_HUM_2_REG 0xE1 // Trimming Parameter H2
|
||||
|
||||
#define BME280_RESET_VAL 0xB6 // Write value to trigger a reset
|
||||
#define BME280_CHIP_ID_VAL 0x60 // Chip ID
|
186
examples/system/ulp/lp_core/lp_spi/main/lp_core/main.c
Normal file
186
examples/system/ulp/lp_core/lp_spi/main/lp_core/main.c
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "ulp_lp_core_spi.h"
|
||||
#include "ulp_lp_core_utils.h"
|
||||
#include "ulp_lp_core_print.h"
|
||||
#include "bme280_defs.h"
|
||||
|
||||
#define LP_SPI_TRANS_WAIT_FOREVER -1
|
||||
#define WAKEUP_HP_CPU_LIMIT_CELSIUS 30
|
||||
|
||||
// Uncomment to print debug logs
|
||||
// #define DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DEBUG_LOG lp_core_printf
|
||||
#else
|
||||
#define DEBUG_LOG (void)
|
||||
#endif
|
||||
|
||||
static bool start_up = true;
|
||||
|
||||
// Misc values used to compensate the measurements
|
||||
static uint16_t dig_T1;
|
||||
static int16_t dig_T2;
|
||||
static int16_t dig_T3;
|
||||
static int32_t t_fine;
|
||||
|
||||
static uint16_t dig_H1;
|
||||
static int16_t dig_H2;
|
||||
static uint16_t dig_H3;
|
||||
static int16_t dig_H4;
|
||||
static int16_t dig_H5;
|
||||
static int16_t dig_H6;
|
||||
|
||||
|
||||
static void bme280_write(uint8_t reg_addr, uint8_t data)
|
||||
{
|
||||
lp_spi_transaction_t trans_desc = {
|
||||
.tx_buffer = &data,
|
||||
.tx_length = 1,
|
||||
.address = reg_addr & ~(1 << 7), // Clear MSB of register addr to indicate it is a write
|
||||
.address_bits = 8,
|
||||
};
|
||||
|
||||
esp_err_t err = lp_core_lp_spi_master_transfer(&trans_desc, LP_SPI_TRANS_WAIT_FOREVER);
|
||||
if(err != ESP_OK) {
|
||||
DEBUG_LOG("Failed to write register: 0x%X, with data = 0x%X\n", reg_addr, data);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void bme280_read(uint8_t reg_addr, uint8_t* read_data, size_t read_len)
|
||||
{
|
||||
lp_spi_transaction_t trans_desc = {
|
||||
.tx_buffer = read_data,
|
||||
.tx_length = read_len,
|
||||
.rx_buffer = read_data,
|
||||
.rx_length = read_len,
|
||||
.address = reg_addr,
|
||||
.address_bits = 8,
|
||||
};
|
||||
|
||||
|
||||
esp_err_t err = lp_core_lp_spi_master_transfer(&trans_desc, LP_SPI_TRANS_WAIT_FOREVER);
|
||||
if(err != ESP_OK) {
|
||||
DEBUG_LOG("Failed to read register: 0x%X, with len = 0x%X\n", reg_addr, read_len);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void bme280_read_compensation_params()
|
||||
{
|
||||
uint8_t param_buf[6];
|
||||
// Temperature compensation params are all in consecutive registers, read them all in one go
|
||||
bme280_read(BME280_TRIM_PARAM_TEMP_1_REG, param_buf, 6);
|
||||
dig_T1 = (param_buf[1] << 8) | param_buf[0];
|
||||
dig_T2 = (param_buf[3] << 8) | param_buf[2];
|
||||
dig_T3 = (param_buf[5] << 8) | param_buf[4];
|
||||
|
||||
// Humidity compensation params are in two separate regions, read twice
|
||||
bme280_read(BME280_TRIM_PARAM_HUM_1_REG, param_buf, 1);
|
||||
dig_H1 = param_buf[0];
|
||||
|
||||
bme280_read(BME280_TRIM_PARAM_HUM_2_REG, param_buf, 7);
|
||||
dig_H2 = (param_buf[1] << 8) | param_buf[0];
|
||||
dig_H3 = param_buf[2];
|
||||
dig_H4 = (param_buf[3] << 4) | (param_buf[4] & 0xF);
|
||||
dig_H5 = (param_buf[4] >> 4) | (param_buf[5] << 4);
|
||||
dig_H6 = param_buf[6];
|
||||
|
||||
}
|
||||
|
||||
// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.
|
||||
// t_fine carries fine temperature as global value
|
||||
// Function is taken from BME280 datasheet
|
||||
static int32_t convert_temp(int32_t adc_t)
|
||||
{
|
||||
int32_t var1, var2, T;
|
||||
var1 = ((((adc_t >> 3) - ((int32_t)dig_T1<<1)))*((int32_t)dig_T2)) >> 11;
|
||||
var2 = (((((adc_t>>4) - ((int32_t)dig_T1)) * ((adc_t>>4) - ((int32_t)dig_T1))) >> 12) * ((int32_t)dig_T3)) >> 14;
|
||||
t_fine = var1 + var2;
|
||||
T = (t_fine * 5 + 128) >> 8;
|
||||
return T;
|
||||
}
|
||||
|
||||
// Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format (22 integer and 10 fractional bits).
|
||||
// Output value of “47445” represents 47445/1024 = 46.333 %RH
|
||||
// Function is taken from BME280 datasheet
|
||||
static uint32_t convert_humidity(int32_t adc_h)
|
||||
{
|
||||
int32_t v_x1_u32r;
|
||||
v_x1_u32r = (t_fine - ((int32_t)76800));
|
||||
v_x1_u32r = (((((adc_h << 14) - (((int32_t)dig_H4) << 20) - (((int32_t)dig_H5) * v_x1_u32r)) + ((int32_t)16384)) >> 15) * (((((((v_x1_u32r * ((int32_t)dig_H6)) >> 10) * (((v_x1_u32r * ((int32_t)dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + ((int32_t)2097152)) * ((int32_t)dig_H2) + 8192) >> 14));
|
||||
v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((int32_t)dig_H1)) >> 4));
|
||||
v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
|
||||
v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
|
||||
return (uint32_t)(v_x1_u32r>>12);
|
||||
}
|
||||
|
||||
static void bme280_read_environment_data(int32_t *temperature, uint32_t *humidity)
|
||||
{
|
||||
uint8_t ctrl_hum = BME280_HUM_OVERSAMPLING_1X << BME280_HUM_OVERSAMPLING_BIT;
|
||||
bme280_write(BME280_CTRL_HUM_REG, ctrl_hum);
|
||||
|
||||
uint8_t ctrl_meas = (BME280_TEMP_OVERSAMPLING_1X << BME280_TEMP_OVERSAMPLING_BIT) | (BME280_MODE_FORCED << BME280_MODE_BIT);
|
||||
bme280_write(BME280_CTRL_MEAS_REG, ctrl_meas);
|
||||
|
||||
bool measuring = true;
|
||||
while(measuring) {
|
||||
uint8_t status = 0;
|
||||
bme280_read(BME280_STATUS_REG, &status, 1);
|
||||
measuring = status & (1 << BME280_MEASURING_BIT);
|
||||
}
|
||||
|
||||
uint8_t env_data_buf[8] = {};
|
||||
bme280_read(BME280_TEMPERATURE_MSB_REG, env_data_buf, 5);
|
||||
uint32_t adc_temp = (env_data_buf[0] << 12) | (env_data_buf[1] << 4) | (env_data_buf[2] >> 4);
|
||||
uint32_t adc_hum = (env_data_buf[3] << 8) | (env_data_buf[4]);
|
||||
*temperature = convert_temp(adc_temp);
|
||||
*humidity = convert_humidity(adc_hum);
|
||||
}
|
||||
|
||||
|
||||
static void init_sensor(void)
|
||||
{
|
||||
bme280_write(BME280_RESET_REG, BME280_RESET_VAL);
|
||||
// Give the sensor some time to reset
|
||||
ulp_lp_core_delay_us(2000);
|
||||
|
||||
uint8_t chip_id = 0;
|
||||
bme280_read(BME280_CHIP_ID_REG, &chip_id, sizeof(chip_id));
|
||||
DEBUG_LOG("Read chip id = 0x%X, expected 0x%X\n", chip_id, BME280_CHIP_ID_VAL);
|
||||
if(chip_id != BME280_CHIP_ID_VAL) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
int32_t temperature;
|
||||
uint32_t humidity;
|
||||
|
||||
int main (void)
|
||||
{
|
||||
if(start_up) {
|
||||
init_sensor();
|
||||
bme280_read_compensation_params();
|
||||
start_up = false;
|
||||
}
|
||||
|
||||
bme280_read_environment_data(&temperature, &humidity);
|
||||
DEBUG_LOG("Temperature: %d.%d degree Celsius, humidity: %d.%d\%%RH\n", temperature / 100, temperature % 100, humidity / 1024, humidity % 1024);
|
||||
|
||||
if(temperature/100 > WAKEUP_HP_CPU_LIMIT_CELSIUS) {
|
||||
ulp_lp_core_wakeup_main_processor();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
102
examples/system/ulp/lp_core/lp_spi/main/lp_spi_main.c
Normal file
102
examples/system/ulp/lp_core/lp_spi/main/lp_spi_main.c
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "esp_sleep.h"
|
||||
#include "lp_core_main.h"
|
||||
#include "ulp_lp_core.h"
|
||||
#include "lp_core_spi.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
extern const uint8_t lp_core_main_bin_start[] asm("_binary_lp_core_main_bin_start");
|
||||
extern const uint8_t lp_core_main_bin_end[] asm("_binary_lp_core_main_bin_end");
|
||||
|
||||
#define LP_SPI_MOSI_PIN 7
|
||||
#define LP_SPI_MISO_PIN 6
|
||||
#define LP_SPI_SCLK_PIN 8
|
||||
#define LP_SPI_CS_PIN 4
|
||||
|
||||
#define LP_CORE_WAKEUP_PERIOD_US 1*1000*1000
|
||||
|
||||
static void lp_core_init(void)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
ulp_lp_core_cfg_t cfg = {
|
||||
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER,
|
||||
.lp_timer_sleep_duration_us = LP_CORE_WAKEUP_PERIOD_US
|
||||
};
|
||||
|
||||
ret = ulp_lp_core_load_binary(lp_core_main_bin_start, (lp_core_main_bin_end - lp_core_main_bin_start));
|
||||
if (ret != ESP_OK) {
|
||||
printf("LP Core load failed\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
ret = ulp_lp_core_run(&cfg);
|
||||
if (ret != ESP_OK) {
|
||||
printf("LP Core run failed\n");
|
||||
abort();
|
||||
}
|
||||
printf("LP core loaded with firmware successfully\n");
|
||||
}
|
||||
|
||||
static void lp_spi_init(void)
|
||||
{
|
||||
lp_spi_host_t host_id = 0;
|
||||
|
||||
lp_spi_bus_config_t bus_config = {
|
||||
.miso_io_num = LP_SPI_MISO_PIN,
|
||||
.mosi_io_num = LP_SPI_MOSI_PIN,
|
||||
.sclk_io_num = LP_SPI_SCLK_PIN,
|
||||
};
|
||||
|
||||
/* Base LP SPI device settings */
|
||||
lp_spi_device_config_t device = {
|
||||
.cs_io_num = LP_SPI_CS_PIN,
|
||||
.clock_speed_hz = 10 * 1000, // 10 MHz
|
||||
.duty_cycle = 128, // 50% duty cycle
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(lp_core_lp_spi_bus_initialize(host_id, &bus_config));
|
||||
|
||||
/* Add LP SPI device */
|
||||
ESP_ERROR_CHECK(lp_core_lp_spi_bus_add_device(host_id, &device));
|
||||
printf("LP SPI initialized successfully\n");
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/* If user is using USB-serial-jtag then idf monitor needs some time to
|
||||
* re-connect to the USB port. We wait 1 sec here to allow for it to make the reconnection
|
||||
* before we print anything. Otherwise the chip will go back to sleep again before the user
|
||||
* has time to monitor any output.
|
||||
*/
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
|
||||
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
|
||||
if (cause != ESP_SLEEP_WAKEUP_ULP) {
|
||||
printf("Not an LP core wakeup. Cause = %d\n", cause);
|
||||
printf("Initializing...\n");
|
||||
|
||||
/* Initialize LP_SPI from the main processor */
|
||||
lp_spi_init();
|
||||
|
||||
/* Load LP Core binary and start the coprocessor */
|
||||
lp_core_init();
|
||||
} else if (cause == ESP_SLEEP_WAKEUP_ULP) {
|
||||
printf("LP core woke up the main CPU\n");
|
||||
printf("Temperature %.2f degree celsius, humidity %.2f%%RH\n", ulp_temperature / 100.0, ulp_humidity / 1024.0);
|
||||
}
|
||||
|
||||
/* Setup wakeup triggers */
|
||||
ESP_ERROR_CHECK(esp_sleep_enable_ulp_wakeup());
|
||||
|
||||
/* Enter Deep Sleep */
|
||||
printf("Entering deep sleep...\n");
|
||||
esp_deep_sleep_start();
|
||||
}
|
10
examples/system/ulp/lp_core/lp_spi/sdkconfig.defaults
Normal file
10
examples/system/ulp/lp_core/lp_spi/sdkconfig.defaults
Normal file
@@ -0,0 +1,10 @@
|
||||
# Enable LP Core
|
||||
CONFIG_ULP_COPROC_ENABLED=y
|
||||
CONFIG_ULP_COPROC_TYPE_LP_CORE=y
|
||||
CONFIG_ULP_COPROC_RESERVE_MEM=8128
|
||||
|
||||
# Set log level to Warning to produce clean output
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL=2
|
||||
CONFIG_LOG_DEFAULT_LEVEL_WARN=y
|
||||
CONFIG_LOG_DEFAULT_LEVEL=2
|
Reference in New Issue
Block a user