mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
ulp: Added support for RTC I2C driver for ULP RISC-V on esp32s2 and esp32s3
This commit adds support for using the RTC I2C peripheral on the ULP RISC-V core for esp32s2 and esp32s3. It also adds an example to demonstrate the usage of the RTC I2C peripheral. This commit also modifies the rtc_i2c register structure files to enable the use of bitfields in the ULP RISC-V RTC I2C driver.
This commit is contained in:
parent
6193e4c8e8
commit
4fde033a5f
@ -165,7 +165,11 @@ typedef volatile struct rtc_i2c_dev_s {
|
||||
} fifo_data;
|
||||
union {
|
||||
struct {
|
||||
uint32_t command0: 14; /*command0*/
|
||||
uint32_t byte_num: 8;
|
||||
uint32_t ack_en: 1;
|
||||
uint32_t ack_exp: 1;
|
||||
uint32_t ack_val: 1;
|
||||
uint32_t op_code: 3;
|
||||
uint32_t reserved14: 17;
|
||||
uint32_t done: 1; /*command0_done*/
|
||||
};
|
||||
|
@ -166,9 +166,13 @@ typedef volatile struct rtc_i2c_dev_s {
|
||||
} fifo_data;
|
||||
union {
|
||||
struct {
|
||||
uint32_t command0: 14; /*command0*/
|
||||
uint32_t byte_num: 8;
|
||||
uint32_t ack_en: 1;
|
||||
uint32_t ack_exp: 1;
|
||||
uint32_t ack_val: 1;
|
||||
uint32_t op_code: 3;
|
||||
uint32_t reserved14: 17;
|
||||
uint32_t done: 1; /*command0_done*/
|
||||
uint32_t done: 1;
|
||||
};
|
||||
uint32_t val;
|
||||
} command[16];
|
||||
|
@ -11,6 +11,7 @@ PROVIDE ( SDM = 0x3f404f00 );
|
||||
PROVIDE ( RTCCNTL = 0x3f408000 );
|
||||
PROVIDE ( RTCIO = 0x3f408400 );
|
||||
PROVIDE ( SENS = 0x3f408800 );
|
||||
PROVIDE ( RTC_I2C = 0x3f408C00 );
|
||||
PROVIDE ( HINF = 0x3f40B000 );
|
||||
PROVIDE ( I2S0 = 0x3f40F000 );
|
||||
PROVIDE ( UART1 = 0x3f410000 );
|
||||
|
@ -190,294 +190,27 @@ typedef union {
|
||||
uint32_t val;
|
||||
} rtc_i2c_data_reg_t;
|
||||
|
||||
/** Type of i2c_cmd0 register
|
||||
* i2c commond0 register
|
||||
/** Type of i2c_cmd register
|
||||
* i2c command register
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** i2c_command0 : R/W; bitpos: [13:0]; default: 2307;
|
||||
* command0
|
||||
/** i2c_command : R/W; bitpos: [13:0]; default: 2307;
|
||||
* command
|
||||
*/
|
||||
uint32_t i2c_command0:14;
|
||||
uint32_t reserved_14:17;
|
||||
/** i2c_command0_done : RO; bitpos: [31]; default: 0;
|
||||
uint32_t i2c_byte_num:8;
|
||||
uint32_t i2c_ack_en:1;
|
||||
uint32_t i2c_ack_exp:1;
|
||||
uint32_t i2c_ack_val:1;
|
||||
uint32_t i2c_op_code:3;
|
||||
uint32_t reserved14:17;
|
||||
/** i2c_command_done : RO; bitpos: [31]; default: 0;
|
||||
* command0_done
|
||||
*/
|
||||
uint32_t i2c_command0_done:1;
|
||||
uint32_t i2c_command_done:1;
|
||||
};
|
||||
uint32_t val;
|
||||
} rtc_i2c_cmd0_reg_t;
|
||||
|
||||
/** Type of i2c_cmd1 register
|
||||
* i2c commond1 register
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** i2c_command1 : R/W; bitpos: [13:0]; default: 6401;
|
||||
* command1
|
||||
*/
|
||||
uint32_t i2c_command1:14;
|
||||
uint32_t reserved_14:17;
|
||||
/** i2c_command1_done : RO; bitpos: [31]; default: 0;
|
||||
* command1_done
|
||||
*/
|
||||
uint32_t i2c_command1_done:1;
|
||||
};
|
||||
uint32_t val;
|
||||
} rtc_i2c_cmd1_reg_t;
|
||||
|
||||
/** Type of i2c_cmd2 register
|
||||
* i2c commond2 register
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** i2c_command2 : R/W; bitpos: [13:0]; default: 2306;
|
||||
* command2
|
||||
*/
|
||||
uint32_t i2c_command2:14;
|
||||
uint32_t reserved_14:17;
|
||||
/** i2c_command2_done : RO; bitpos: [31]; default: 0;
|
||||
* command2_done
|
||||
*/
|
||||
uint32_t i2c_command2_done:1;
|
||||
};
|
||||
uint32_t val;
|
||||
} rtc_i2c_cmd2_reg_t;
|
||||
|
||||
/** Type of i2c_cmd3 register
|
||||
* i2c commond3 register
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** i2c_command3 : R/W; bitpos: [13:0]; default: 257;
|
||||
* command3
|
||||
*/
|
||||
uint32_t i2c_command3:14;
|
||||
uint32_t reserved_14:17;
|
||||
/** i2c_command3_done : RO; bitpos: [31]; default: 0;
|
||||
* command3_done
|
||||
*/
|
||||
uint32_t i2c_command3_done:1;
|
||||
};
|
||||
uint32_t val;
|
||||
} rtc_i2c_cmd3_reg_t;
|
||||
|
||||
/** Type of i2c_cmd4 register
|
||||
* i2c commond4 register
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** i2c_command4 : R/W; bitpos: [13:0]; default: 2305;
|
||||
* command4
|
||||
*/
|
||||
uint32_t i2c_command4:14;
|
||||
uint32_t reserved_14:17;
|
||||
/** i2c_command4_done : RO; bitpos: [31]; default: 0;
|
||||
* command4_done
|
||||
*/
|
||||
uint32_t i2c_command4_done:1;
|
||||
};
|
||||
uint32_t val;
|
||||
} rtc_i2c_cmd4_reg_t;
|
||||
|
||||
/** Type of i2c_cmd5 register
|
||||
* i2c commond5_register
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** i2c_command5 : R/W; bitpos: [13:0]; default: 5889;
|
||||
* command5
|
||||
*/
|
||||
uint32_t i2c_command5:14;
|
||||
uint32_t reserved_14:17;
|
||||
/** i2c_command5_done : RO; bitpos: [31]; default: 0;
|
||||
* command5_done
|
||||
*/
|
||||
uint32_t i2c_command5_done:1;
|
||||
};
|
||||
uint32_t val;
|
||||
} rtc_i2c_cmd5_reg_t;
|
||||
|
||||
/** Type of i2c_cmd6 register
|
||||
* i2c commond6 register
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** i2c_command6 : R/W; bitpos: [13:0]; default: 6401;
|
||||
* command6
|
||||
*/
|
||||
uint32_t i2c_command6:14;
|
||||
uint32_t reserved_14:17;
|
||||
/** i2c_command6_done : RO; bitpos: [31]; default: 0;
|
||||
* command6_done
|
||||
*/
|
||||
uint32_t i2c_command6_done:1;
|
||||
};
|
||||
uint32_t val;
|
||||
} rtc_i2c_cmd6_reg_t;
|
||||
|
||||
/** Type of i2c_cmd7 register
|
||||
* i2c commond7 register
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** i2c_command7 : R/W; bitpos: [13:0]; default: 2308;
|
||||
* command7
|
||||
*/
|
||||
uint32_t i2c_command7:14;
|
||||
uint32_t reserved_14:17;
|
||||
/** i2c_command7_done : RO; bitpos: [31]; default: 0;
|
||||
* command7_done
|
||||
*/
|
||||
uint32_t i2c_command7_done:1;
|
||||
};
|
||||
uint32_t val;
|
||||
} rtc_i2c_cmd7_reg_t;
|
||||
|
||||
/** Type of i2c_cmd8 register
|
||||
* i2c commond8 register
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** i2c_command8 : R/W; bitpos: [13:0]; default: 6401;
|
||||
* command8
|
||||
*/
|
||||
uint32_t i2c_command8:14;
|
||||
uint32_t reserved_14:17;
|
||||
/** i2c_command8_done : RO; bitpos: [31]; default: 0;
|
||||
* command8_done
|
||||
*/
|
||||
uint32_t i2c_command8_done:1;
|
||||
};
|
||||
uint32_t val;
|
||||
} rtc_i2c_cmd8_reg_t;
|
||||
|
||||
/** Type of i2c_cmd9 register
|
||||
* i2c commond9 register
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** i2c_command9 : R/W; bitpos: [13:0]; default: 2307;
|
||||
* command9
|
||||
*/
|
||||
uint32_t i2c_command9:14;
|
||||
uint32_t reserved_14:17;
|
||||
/** i2c_command9_done : RO; bitpos: [31]; default: 0;
|
||||
* command9_done
|
||||
*/
|
||||
uint32_t i2c_command9_done:1;
|
||||
};
|
||||
uint32_t val;
|
||||
} rtc_i2c_cmd9_reg_t;
|
||||
|
||||
/** Type of i2c_cmd10 register
|
||||
* i2c commond10 register
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** i2c_command10 : R/W; bitpos: [13:0]; default: 257;
|
||||
* command10
|
||||
*/
|
||||
uint32_t i2c_command10:14;
|
||||
uint32_t reserved_14:17;
|
||||
/** i2c_command10_done : RO; bitpos: [31]; default: 0;
|
||||
* command10_done
|
||||
*/
|
||||
uint32_t i2c_command10_done:1;
|
||||
};
|
||||
uint32_t val;
|
||||
} rtc_i2c_cmd10_reg_t;
|
||||
|
||||
/** Type of i2c_cmd11 register
|
||||
* i2c commond11 register
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** i2c_command11 : R/W; bitpos: [13:0]; default: 2305;
|
||||
* command11
|
||||
*/
|
||||
uint32_t i2c_command11:14;
|
||||
uint32_t reserved_14:17;
|
||||
/** i2c_command11_done : RO; bitpos: [31]; default: 0;
|
||||
* command11_done
|
||||
*/
|
||||
uint32_t i2c_command11_done:1;
|
||||
};
|
||||
uint32_t val;
|
||||
} rtc_i2c_cmd11_reg_t;
|
||||
|
||||
/** Type of i2c_cmd12 register
|
||||
* i2c commond12 register
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** i2c_command12 : R/W; bitpos: [13:0]; default: 5889;
|
||||
* command12
|
||||
*/
|
||||
uint32_t i2c_command12:14;
|
||||
uint32_t reserved_14:17;
|
||||
/** i2c_command12_done : RO; bitpos: [31]; default: 0;
|
||||
* command12_done
|
||||
*/
|
||||
uint32_t i2c_command12_done:1;
|
||||
};
|
||||
uint32_t val;
|
||||
} rtc_i2c_cmd12_reg_t;
|
||||
|
||||
/** Type of i2c_cmd13 register
|
||||
* i2c commond13 register
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** i2c_command13 : R/W; bitpos: [13:0]; default: 6401;
|
||||
* command13
|
||||
*/
|
||||
uint32_t i2c_command13:14;
|
||||
uint32_t reserved_14:17;
|
||||
/** i2c_command13_done : RO; bitpos: [31]; default: 0;
|
||||
* command13_done
|
||||
*/
|
||||
uint32_t i2c_command13_done:1;
|
||||
};
|
||||
uint32_t val;
|
||||
} rtc_i2c_cmd13_reg_t;
|
||||
|
||||
/** Type of i2c_cmd14 register
|
||||
* i2c commond14 register
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** i2c_command14 : R/W; bitpos: [13:0]; default: 0;
|
||||
* command14
|
||||
*/
|
||||
uint32_t i2c_command14:14;
|
||||
uint32_t reserved_14:17;
|
||||
/** i2c_command14_done : RO; bitpos: [31]; default: 0;
|
||||
* command14_done
|
||||
*/
|
||||
uint32_t i2c_command14_done:1;
|
||||
};
|
||||
uint32_t val;
|
||||
} rtc_i2c_cmd14_reg_t;
|
||||
|
||||
/** Type of i2c_cmd15 register
|
||||
* i2c commond15 register
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
/** i2c_command15 : R/W; bitpos: [13:0]; default: 0;
|
||||
* command15
|
||||
*/
|
||||
uint32_t i2c_command15:14;
|
||||
uint32_t reserved_14:17;
|
||||
/** i2c_command15_done : RO; bitpos: [31]; default: 0;
|
||||
* command15_done
|
||||
*/
|
||||
uint32_t i2c_command15_done:1;
|
||||
};
|
||||
uint32_t val;
|
||||
} rtc_i2c_cmd15_reg_t;
|
||||
|
||||
} rtc_i2c_cmd_reg_t;
|
||||
|
||||
/** Group: status register */
|
||||
/** Type of i2c_status register
|
||||
@ -750,29 +483,15 @@ typedef struct {
|
||||
volatile rtc_i2c_int_st_reg_t i2c_int_st;
|
||||
volatile rtc_i2c_int_ena_reg_t i2c_int_ena;
|
||||
volatile rtc_i2c_data_reg_t i2c_data;
|
||||
volatile rtc_i2c_cmd0_reg_t i2c_cmd0;
|
||||
volatile rtc_i2c_cmd1_reg_t i2c_cmd1;
|
||||
volatile rtc_i2c_cmd2_reg_t i2c_cmd2;
|
||||
volatile rtc_i2c_cmd3_reg_t i2c_cmd3;
|
||||
volatile rtc_i2c_cmd4_reg_t i2c_cmd4;
|
||||
volatile rtc_i2c_cmd5_reg_t i2c_cmd5;
|
||||
volatile rtc_i2c_cmd6_reg_t i2c_cmd6;
|
||||
volatile rtc_i2c_cmd7_reg_t i2c_cmd7;
|
||||
volatile rtc_i2c_cmd8_reg_t i2c_cmd8;
|
||||
volatile rtc_i2c_cmd9_reg_t i2c_cmd9;
|
||||
volatile rtc_i2c_cmd10_reg_t i2c_cmd10;
|
||||
volatile rtc_i2c_cmd11_reg_t i2c_cmd11;
|
||||
volatile rtc_i2c_cmd12_reg_t i2c_cmd12;
|
||||
volatile rtc_i2c_cmd13_reg_t i2c_cmd13;
|
||||
volatile rtc_i2c_cmd14_reg_t i2c_cmd14;
|
||||
volatile rtc_i2c_cmd15_reg_t i2c_cmd15;
|
||||
volatile rtc_i2c_cmd_reg_t i2c_cmd[16];
|
||||
uint32_t reserved_078[33];
|
||||
volatile rtc_i2c_date_reg_t i2c_date;
|
||||
} rtc_dev_t;
|
||||
} rtc_i2c_dev_t;
|
||||
extern rtc_i2c_dev_t RTC_I2C;
|
||||
|
||||
|
||||
#ifndef __cplusplus
|
||||
_Static_assert(sizeof(rtc_dev_t) == 0x100, "Invalid size of rtc_dev_t structure");
|
||||
_Static_assert(sizeof(rtc_i2c_dev_t) == 0x100, "Invalid size of rtc_i2c_dev_t structure");
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -12,6 +12,7 @@ PROVIDE ( EFUSE = 0x60007000 );
|
||||
PROVIDE ( RTCCNTL = 0x60008000 );
|
||||
PROVIDE ( RTCIO = 0x60008400 );
|
||||
PROVIDE ( SENS = 0x60008800 );
|
||||
PROVIDE ( RTC_I2C = 0x60008C00 );
|
||||
PROVIDE ( HINF = 0x6000B000 );
|
||||
PROVIDE ( I2S0 = 0x6000F000 );
|
||||
PROVIDE ( I2S1 = 0x6002D000 );
|
||||
|
@ -25,7 +25,8 @@ if(CONFIG_SOC_ULP_SUPPORTED OR CONFIG_SOC_RISCV_COPROC_SUPPORTED)
|
||||
list(APPEND srcs
|
||||
"ulp_riscv/ulp_riscv.c"
|
||||
"ulp_riscv/ulp_riscv_lock.c"
|
||||
"ulp_riscv/ulp_riscv_adc.c")
|
||||
"ulp_riscv/ulp_riscv_adc.c"
|
||||
"ulp_riscv/ulp_riscv_i2c.c")
|
||||
|
||||
list(APPEND includes
|
||||
ulp_riscv/include
|
||||
|
@ -76,6 +76,7 @@ if(ULP_COCPU_IS_RISCV)
|
||||
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_lock.c"
|
||||
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_uart.c"
|
||||
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_print.c"
|
||||
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c"
|
||||
"${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_utils.c")
|
||||
|
||||
target_link_options(${ULP_APP_NAME} PRIVATE "-nostartfiles")
|
||||
|
@ -7,3 +7,4 @@
|
||||
PROVIDE ( RTCCNTL = 0x8000 );
|
||||
PROVIDE ( RTCIO = 0xA400 );
|
||||
PROVIDE ( SENS = 0xC800 );
|
||||
PROVIDE ( RTC_I2C = 0x8C00 );
|
||||
|
@ -7,3 +7,4 @@
|
||||
PROVIDE ( RTCCNTL = 0x8000 );
|
||||
PROVIDE ( RTCIO = 0xA400 );
|
||||
PROVIDE ( SENS = 0xC800 );
|
||||
PROVIDE ( RTC_I2C = 0xEC00 );
|
||||
|
99
components/ulp/ulp_riscv/include/ulp_riscv_i2c.h
Normal file
99
components/ulp/ulp_riscv/include/ulp_riscv_i2c.h
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "hal/gpio_types.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t sda_io_num; // GPIO pin for SDA signal. Only GPIO#1 or GPIO#3 can be used as the SDA pin.
|
||||
uint32_t scl_io_num; // GPIO pin for SCL signal. Only GPIO#0 or GPIO#2 can be used as the SCL pin.
|
||||
bool sda_pullup_en; // SDA line enable internal pullup. Can be configured if external pullup is not used.
|
||||
bool scl_pullup_en; // SCL line enable internal pullup. Can be configured if external pullup is not used.
|
||||
} ulp_riscv_i2c_pin_cfg_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t scl_low_period; // SCL low period
|
||||
uint32_t scl_high_period; // SCL high period
|
||||
uint32_t sda_duty_period; // Period between the SDA switch and the falling edge of SCL
|
||||
uint32_t scl_start_period; // Waiting time after the START condition
|
||||
uint32_t scl_stop_period; // Waiting time before the END condition
|
||||
uint32_t i2c_trans_timeout; // I2C transaction timeout
|
||||
} ulp_riscv_i2c_timing_cfg_t;
|
||||
|
||||
typedef struct {
|
||||
ulp_riscv_i2c_pin_cfg_t i2c_pin_cfg; // RTC I2C pin configuration
|
||||
ulp_riscv_i2c_timing_cfg_t i2c_timing_cfg; // RTC I2C timing configuration
|
||||
} ulp_riscv_i2c_cfg_t;
|
||||
|
||||
/* Nominal default GPIO settings and timing parametes */
|
||||
#define ULP_RISCV_I2C_DEFAULT_CONFIG() \
|
||||
{ \
|
||||
.i2c_pin_cfg.sda_io_num = GPIO_NUM_3, \
|
||||
.i2c_pin_cfg.scl_io_num = GPIO_NUM_2, \
|
||||
.i2c_pin_cfg.sda_pullup_en = true, \
|
||||
.i2c_pin_cfg.scl_pullup_en = true, \
|
||||
.i2c_timing_cfg.scl_low_period = 5, \
|
||||
.i2c_timing_cfg.scl_high_period = 5, \
|
||||
.i2c_timing_cfg.sda_duty_period = 2, \
|
||||
.i2c_timing_cfg.scl_start_period = 3, \
|
||||
.i2c_timing_cfg.scl_stop_period = 6, \
|
||||
.i2c_timing_cfg.i2c_trans_timeout = 20, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the I2C slave device address
|
||||
*
|
||||
* @param slave_addr I2C slave address (7 bit)
|
||||
*/
|
||||
void ulp_riscv_i2c_master_set_slave_addr(uint8_t slave_addr);
|
||||
|
||||
/**
|
||||
* @brief Set the I2C slave device sub register address
|
||||
*
|
||||
* @param slave_reg_addr I2C slave sub register address
|
||||
*/
|
||||
void ulp_riscv_i2c_master_set_slave_reg_addr(uint8_t slave_reg_addr);
|
||||
|
||||
/**
|
||||
* @brief Read from I2C slave device
|
||||
*
|
||||
* @note The I2C slave device address must be configured at least once before invoking this API.
|
||||
*
|
||||
* @param data_rd Buffer to hold data to be read
|
||||
* @param size Size of data to be read in bytes
|
||||
*/
|
||||
void ulp_riscv_i2c_master_read_from_device(uint8_t *data_rd, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Write to I2C slave device
|
||||
*
|
||||
* @note The I2C slave device address must be configured at least once before invoking this API.
|
||||
*
|
||||
* @param data_wr Buffer which holds the data to be written
|
||||
* @param size Size of data to be written in bytes
|
||||
*/
|
||||
void ulp_riscv_i2c_master_write_to_device(uint8_t *data_wr, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Initialize and configure the RTC I2C for use by ULP RISC-V
|
||||
* Currently RTC I2C can only be used in master mode
|
||||
*
|
||||
* @param cfg Configuration parameters
|
||||
* @return esp_err_t ESP_OK when successful
|
||||
*/
|
||||
esp_err_t ulp_riscv_i2c_master_init(const ulp_riscv_i2c_cfg_t *cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Set the I2C slave device address
|
||||
*
|
||||
* @param slave_addr I2C slave address (7 bit)
|
||||
*/
|
||||
void ulp_riscv_i2c_master_set_slave_addr(uint8_t slave_addr);
|
||||
|
||||
/**
|
||||
* @brief Set the I2C slave device sub register address
|
||||
*
|
||||
* @param slave_reg_addr I2C slave register address
|
||||
*/
|
||||
void ulp_riscv_i2c_master_set_slave_reg_addr(uint8_t slave_reg_addr);
|
||||
|
||||
/**
|
||||
* @brief Read from I2C slave device
|
||||
*
|
||||
* @note The I2C slave device address must be configured at least once before invoking this API.
|
||||
*
|
||||
* @param data_rd Buffer to hold data to be read
|
||||
* @param size Size of data to be read in bytes
|
||||
*/
|
||||
void ulp_riscv_i2c_master_read_from_device(uint8_t *data_rd, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Write to I2C slave device
|
||||
*
|
||||
* @note The I2C slave device address must be configured at least once before invoking this API.
|
||||
*
|
||||
* @param data_wr Buffer which holds the data to be written
|
||||
* @param size Size of data to be written in bytes
|
||||
*/
|
||||
void ulp_riscv_i2c_master_write_to_device(uint8_t *data_wr, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
225
components/ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c
Normal file
225
components/ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c
Normal file
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "ulp_riscv_i2c_ulp_core.h"
|
||||
#include "ulp_riscv_utils.h"
|
||||
#include "soc/rtc_i2c_reg.h"
|
||||
#include "soc/rtc_i2c_struct.h"
|
||||
#include "soc/rtc_io_reg.h"
|
||||
#include "soc/sens_reg.h"
|
||||
#include "hal/i2c_ll.h"
|
||||
|
||||
#define I2C_CTRL_SLAVE_ADDR_MASK (0xFF << 0)
|
||||
#define I2C_CTRL_SLAVE_REG_ADDR_MASK (0xFF << 11)
|
||||
#define I2C_CTRL_MASTER_TX_DATA_MASK (0xFF << 19)
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
#define ULP_I2C_CMD_RESTART 0 /*!<I2C restart command */
|
||||
#define ULP_I2C_CMD_WRITE 1 /*!<I2C write command */
|
||||
#define ULP_I2C_CMD_READ 2 /*!<I2C read command */
|
||||
#define ULP_I2C_CMD_STOP 3 /*!<I2C stop command */
|
||||
#define ULP_I2C_CMD_END 4 /*!<I2C end command */
|
||||
#else
|
||||
#define ULP_I2C_CMD_RESTART I2C_LL_CMD_RESTART /*!<I2C restart command */
|
||||
#define ULP_I2C_CMD_WRITE I2C_LL_CMD_WRITE /*!<I2C write command */
|
||||
#define ULP_I2C_CMD_READ I2C_LL_CMD_READ /*!<I2C read command */
|
||||
#define ULP_I2C_CMD_STOP I2C_LL_CMD_STOP /*!<I2C stop command */
|
||||
#define ULP_I2C_CMD_END I2C_LL_CMD_END /*!<I2C end command */
|
||||
#endif // CONFIG_IDF_TARGET_ESP32S3
|
||||
|
||||
/*
|
||||
* The RTC I2C controller follows the I2C command registers to perform read/write operations.
|
||||
* The cmd registers have the following format:
|
||||
*
|
||||
* 31 30:14 13:11 10 9 8 7:0
|
||||
* |----------|----------|---------|---------|----------|------------|---------|
|
||||
* | CMD_DONE | Reserved | OPCODE |ACK Value|ACK Expect|ACK Check En|Byte Num |
|
||||
* |----------|----------|---------|---------|----------|------------|---------|
|
||||
*/
|
||||
static void ulp_riscv_i2c_format_cmd(uint32_t cmd_idx, uint8_t op_code, uint8_t ack_val,
|
||||
uint8_t ack_expected, uint8_t ack_check_en, uint8_t byte_num)
|
||||
{
|
||||
uint32_t reg_addr = RTC_I2C_CMD0_REG + 4*cmd_idx;
|
||||
|
||||
CLEAR_PERI_REG_MASK(reg_addr, 0xFFFFFFFF);
|
||||
|
||||
WRITE_PERI_REG(reg_addr,
|
||||
(0 << 31) | // CMD Done
|
||||
|
||||
((op_code & 0x3) << 11) | // Opcode
|
||||
|
||||
((ack_val & 0x1) << 10) | // ACK bit sent by I2C controller during READ.
|
||||
// Ignored during RSTART, STOP, END and WRITE cmds.
|
||||
|
||||
((ack_expected & 0x1) << 9) | // ACK bit expected by I2C controller during WRITE.
|
||||
// Ignored during RSTART, STOP, END and READ cmds.
|
||||
|
||||
((ack_check_en & 0x1) << 8) | // I2C controller verifies that the ACK bit sent by the slave device matches
|
||||
// the ACK expected bit during WRITE.
|
||||
// Ignored during RSTART, STOP, END and READ cmds.
|
||||
((byte_num & 0xFF) << 0)); // Byte Num
|
||||
}
|
||||
|
||||
void ulp_riscv_i2c_master_set_slave_addr(uint8_t slave_addr)
|
||||
{
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, I2C_CTRL_SLAVE_ADDR_MASK);
|
||||
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0xFF, slave_addr, 0);
|
||||
}
|
||||
|
||||
void ulp_riscv_i2c_master_set_slave_reg_addr(uint8_t slave_reg_addr)
|
||||
{
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, I2C_CTRL_SLAVE_REG_ADDR_MASK);
|
||||
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0xFF, slave_reg_addr, 11);
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C transactions when master reads one byte of data from the slave device:
|
||||
*
|
||||
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|
|
||||
* | Master | START | SAD + W | | SUB | | SR | SAD + R | | | NACK | STOP |
|
||||
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|
|
||||
* | Slave | | | ACK | | ACK | | | ACK | DATA | | |
|
||||
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|
|
||||
*
|
||||
* I2C transactions when master reads multiple bytes of data from the slave device:
|
||||
*
|
||||
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|--------|--------|
|
||||
* | Master | START | SAD + W | | SUB | | SR | SAD + R | | | ACK | | NACK | STOP |
|
||||
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|--------|--------|
|
||||
* | Slave | | | ACK | | ACK | | | ACK | DATA | | DATA | | |
|
||||
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|--------|--------|
|
||||
*/
|
||||
void ulp_riscv_i2c_master_read_from_device(uint8_t *data_rd, size_t size)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
uint32_t cmd_idx = 0;
|
||||
|
||||
if (size == 0) {
|
||||
// Quietly return
|
||||
return;
|
||||
}
|
||||
|
||||
/* By default, RTC I2C controller is hard wired to use CMD2 register onwards for read operations */
|
||||
cmd_idx = 2;
|
||||
|
||||
/* Write slave addr */
|
||||
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_WRITE, 0, 0, 1, 2);
|
||||
|
||||
/* Repeated START */
|
||||
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_RESTART, 0, 0, 0, 0);
|
||||
|
||||
/* Write slave register addr */
|
||||
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_WRITE, 0, 0, 1, 1);
|
||||
|
||||
if (size > 1) {
|
||||
/* Read n - 1 bytes */
|
||||
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_READ, 0, 0, 1, size - 1);
|
||||
}
|
||||
|
||||
/* Read last byte + NACK */
|
||||
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_READ, 1, 1, 1, 1);
|
||||
|
||||
/* STOP */
|
||||
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_STOP, 0, 0, 0, 0);
|
||||
|
||||
/* Configure the RTC I2C controller in read mode */
|
||||
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0x1, 0, 27);
|
||||
|
||||
/* Enable Rx data interrupt */
|
||||
SET_PERI_REG_MASK(RTC_I2C_INT_ENA_REG, RTC_I2C_RX_DATA_INT_ENA);
|
||||
|
||||
/* Start RTC I2C transmission */
|
||||
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE);
|
||||
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
/* Poll for RTC I2C Rx Data interrupt bit to be set */
|
||||
while (!REG_GET_FIELD(RTC_I2C_INT_ST_REG, RTC_I2C_RX_DATA_INT_ST)) { }
|
||||
|
||||
/* Read the data
|
||||
*
|
||||
* Unfortunately, the RTC I2C has no fifo buffer to help us with reading and storing
|
||||
* multiple bytes of data. Therefore, we need to read one byte at a time and clear the
|
||||
* Rx interrupt to get ready for the next byte.
|
||||
*/
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
data_rd[i] = REG_GET_FIELD(RTC_I2C_DATA_REG, RTC_I2C_RDATA);
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
data_rd[i] = REG_GET_FIELD(RTC_I2C_DATA_REG, RTC_I2C_I2C_RDATA);
|
||||
#endif // CONFIG_IDF_TARGET_ESP32S2
|
||||
|
||||
/* Clear the Rx data interrupt bit */
|
||||
SET_PERI_REG_MASK(RTC_I2C_INT_CLR_REG, RTC_I2C_RX_DATA_INT_CLR);
|
||||
}
|
||||
|
||||
/* Clear the RTC I2C transmission bits */
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE);
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C transactions when master writes one byte of data to the slave device:
|
||||
*
|
||||
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|
|
||||
* | Master | START | SAD + W | | SUB | | DATA | | STOP |
|
||||
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|
|
||||
* | Slave | | | ACK | | ACK | | ACK | |
|
||||
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|
|
||||
*
|
||||
* I2C transactions when master writes multiple bytes of data to the slave device:
|
||||
*
|
||||
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|--------|--------|
|
||||
* | Master | START | SAD + W | | SUB | | DATA | | DATA | | STOP |
|
||||
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|--------|--------|
|
||||
* | Slave | | | ACK | | ACK | | ACK | | ACK | |
|
||||
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|--------|--------|
|
||||
*/
|
||||
void ulp_riscv_i2c_master_write_to_device(uint8_t *data_wr, size_t size)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
uint32_t cmd_idx = 0;
|
||||
|
||||
if (size == 0) {
|
||||
// Quietly return
|
||||
return;
|
||||
}
|
||||
|
||||
/* By default, RTC I2C controller is hard wired to use CMD0 and CMD1 registers for write operations */
|
||||
cmd_idx = 0;
|
||||
|
||||
/* Write slave addr + reg addr + data */
|
||||
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_WRITE, 0, 0, 1, 2 + size);
|
||||
|
||||
/* Stop */
|
||||
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_STOP, 0, 0, 0, 0);
|
||||
|
||||
/* Configure the RTC I2C controller in write mode */
|
||||
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0x1, 1, 27);
|
||||
|
||||
/* Enable Tx data interrupt */
|
||||
SET_PERI_REG_MASK(RTC_I2C_INT_ENA_REG, RTC_I2C_TX_DATA_INT_ENA);
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
/* Write the data to be transmitted */
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, I2C_CTRL_MASTER_TX_DATA_MASK);
|
||||
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0xFF, data_wr[i], 19);
|
||||
|
||||
if (i == 0) {
|
||||
/* Start RTC I2C transmission. (Needn't do it for every byte) */
|
||||
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE);
|
||||
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
|
||||
}
|
||||
|
||||
/* Poll for RTC I2C Tx Data interrupt bit to be set */
|
||||
while (!REG_GET_FIELD(RTC_I2C_INT_ST_REG, RTC_I2C_TX_DATA_INT_ST)) { }
|
||||
|
||||
/* Clear the Tx data interrupt bit */
|
||||
SET_PERI_REG_MASK(RTC_I2C_INT_CLR_REG, RTC_I2C_TX_DATA_INT_CLR);
|
||||
}
|
||||
|
||||
/* Clear the RTC I2C transmission bits */
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE);
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
|
||||
}
|
441
components/ulp/ulp_riscv/ulp_riscv_i2c.c
Normal file
441
components/ulp/ulp_riscv/ulp_riscv_i2c.c
Normal file
@ -0,0 +1,441 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "ulp_riscv_i2c.h"
|
||||
#include "esp_check.h"
|
||||
#include "soc/rtc_i2c_reg.h"
|
||||
#include "soc/rtc_i2c_struct.h"
|
||||
#include "soc/rtc_io_struct.h"
|
||||
#include "soc/sens_reg.h"
|
||||
#include "soc/clk_tree_defs.h"
|
||||
#include "hal/i2c_ll.h"
|
||||
#include "driver/rtc_io.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
static const char *RTCI2C_TAG = "ulp_riscv_i2c";
|
||||
|
||||
#define I2C_CTRL_SLAVE_ADDR_MASK (0xFF << 0)
|
||||
#define I2C_CTRL_SLAVE_REG_ADDR_MASK (0xFF << 11)
|
||||
#define I2C_CTRL_MASTER_TX_DATA_MASK (0xFF << 19)
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
#define ULP_I2C_CMD_RESTART 0 /*!<I2C restart command */
|
||||
#define ULP_I2C_CMD_WRITE 1 /*!<I2C write command */
|
||||
#define ULP_I2C_CMD_READ 2 /*!<I2C read command */
|
||||
#define ULP_I2C_CMD_STOP 3 /*!<I2C stop command */
|
||||
#define ULP_I2C_CMD_END 4 /*!<I2C end command */
|
||||
#else
|
||||
#define ULP_I2C_CMD_RESTART I2C_LL_CMD_RESTART /*!<I2C restart command */
|
||||
#define ULP_I2C_CMD_WRITE I2C_LL_CMD_WRITE /*!<I2C write command */
|
||||
#define ULP_I2C_CMD_READ I2C_LL_CMD_READ /*!<I2C read command */
|
||||
#define ULP_I2C_CMD_STOP I2C_LL_CMD_STOP /*!<I2C stop command */
|
||||
#define ULP_I2C_CMD_END I2C_LL_CMD_END /*!<I2C end command */
|
||||
#endif // CONFIG_IDF_TARGET_ESP32S3
|
||||
|
||||
/* Use the register structure to access RTC_I2C and RTCIO module registers */
|
||||
rtc_i2c_dev_t *i2c_dev = &RTC_I2C;
|
||||
rtc_io_dev_t *rtc_io_dev = &RTCIO;
|
||||
|
||||
#define MICROSEC_TO_RTC_FAST_CLK(period) (period) * ((SOC_CLK_RC_FAST_FREQ_APPROX) / (1000000))
|
||||
|
||||
static esp_err_t i2c_gpio_is_cfg_valid(gpio_num_t sda_io_num, gpio_num_t scl_io_num)
|
||||
{
|
||||
/* Verify that the SDA and SCL GPIOs are valid RTC I2C io pins */
|
||||
ESP_RETURN_ON_ERROR(!rtc_gpio_is_valid_gpio(sda_io_num), RTCI2C_TAG, "RTC I2C SDA GPIO invalid");
|
||||
ESP_RETURN_ON_ERROR(!rtc_gpio_is_valid_gpio(scl_io_num), RTCI2C_TAG, "RTC I2C SCL GPIO invalid");
|
||||
|
||||
/* Verify that the SDA and SCL line belong to the RTC IO I2C function group */
|
||||
if ((sda_io_num != GPIO_NUM_1) && (sda_io_num != GPIO_NUM_3)) {
|
||||
ESP_LOGE(RTCI2C_TAG, "SDA pin can only be configured as GPIO#1 or GPIO#3");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if ((scl_io_num != GPIO_NUM_0) && (scl_io_num != GPIO_NUM_2)) {
|
||||
ESP_LOGE(RTCI2C_TAG, "SCL pin can only be configured as GPIO#0 or GPIO#2");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_configure_io(gpio_num_t io_num, bool pullup_en)
|
||||
{
|
||||
/* Initialize IO Pin */
|
||||
ESP_RETURN_ON_ERROR(rtc_gpio_init(io_num), RTCI2C_TAG, "RTC GPIO Init failed for GPIO %d", io_num);
|
||||
/* Set direction to input+output */
|
||||
ESP_RETURN_ON_ERROR(rtc_gpio_set_direction(io_num, RTC_GPIO_MODE_INPUT_OUTPUT), RTCI2C_TAG, "RTC GPIO Set direction failed for %d", io_num);
|
||||
/* Disable pulldown on the io pin */
|
||||
ESP_RETURN_ON_ERROR(rtc_gpio_pulldown_dis(io_num), RTCI2C_TAG, "RTC GPIO pulldown disable failed for %d", io_num);
|
||||
/* Enable pullup based on pullup_en flag */
|
||||
if (pullup_en) {
|
||||
ESP_RETURN_ON_ERROR(rtc_gpio_pullup_en(io_num), RTCI2C_TAG, "RTC GPIO pullup enable failed for %d", io_num);
|
||||
} else {
|
||||
ESP_RETURN_ON_ERROR(rtc_gpio_pullup_dis(io_num), RTCI2C_TAG, "RTC GPIO pullup disable failed for %d", io_num);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_set_pin(const ulp_riscv_i2c_cfg_t *cfg)
|
||||
{
|
||||
gpio_num_t sda_io_num = cfg->i2c_pin_cfg.sda_io_num;
|
||||
gpio_num_t scl_io_num = cfg->i2c_pin_cfg.scl_io_num;
|
||||
bool sda_pullup_en = cfg->i2c_pin_cfg.sda_pullup_en;
|
||||
bool scl_pullup_en = cfg->i2c_pin_cfg.scl_pullup_en;
|
||||
|
||||
/* Verify that the I2C GPIOs are valid */
|
||||
ESP_RETURN_ON_ERROR(i2c_gpio_is_cfg_valid(sda_io_num, scl_io_num), RTCI2C_TAG, "RTC I2C GPIO config invalid");
|
||||
|
||||
/* Initialize SDA Pin */
|
||||
ESP_RETURN_ON_ERROR(i2c_configure_io(sda_io_num, sda_pullup_en), RTCI2C_TAG, "RTC I2C SDA pin config failed");
|
||||
|
||||
/* Initialize SCL Pin */
|
||||
ESP_RETURN_ON_ERROR(i2c_configure_io(scl_io_num, scl_pullup_en), RTCI2C_TAG, "RTC I2C SCL pin config failed");
|
||||
|
||||
/* Route SDA IO signal to the RTC subsystem */
|
||||
rtc_io_dev->touch_pad[sda_io_num].mux_sel = 1;
|
||||
|
||||
/* Route SCL IO signal to the RTC subsystem */
|
||||
rtc_io_dev->touch_pad[scl_io_num].mux_sel = 1;
|
||||
|
||||
/* Select RTC I2C function for SDA pin */
|
||||
rtc_io_dev->touch_pad[sda_io_num].fun_sel = 3;
|
||||
|
||||
/* Select RTC I2C function for SCL pin */
|
||||
rtc_io_dev->touch_pad[scl_io_num].fun_sel = 3;
|
||||
|
||||
/* Map the SDA and SCL signals to the RTC I2C controller */
|
||||
if (sda_io_num == GPIO_NUM_1) {
|
||||
rtc_io_dev->sar_i2c_io.sda_sel = 0;
|
||||
} else {
|
||||
rtc_io_dev->sar_i2c_io.sda_sel = 1;
|
||||
}
|
||||
|
||||
if (scl_io_num == GPIO_NUM_0) {
|
||||
rtc_io_dev->sar_i2c_io.scl_sel = 0;
|
||||
} else {
|
||||
rtc_io_dev->sar_i2c_io.scl_sel = 1;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_set_timing(const ulp_riscv_i2c_cfg_t *cfg)
|
||||
{
|
||||
/* Convert all timing parameters from micro-seconds to period in RTC_FAST_CLK cycles.
|
||||
* RTC_FAST_CLK = 8.5 MHz for esp32s2 and 17.5 MHz for esp32s3.
|
||||
* The following calculations approximate the period for each parameter.
|
||||
*/
|
||||
uint32_t scl_low_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_low_period);
|
||||
uint32_t scl_high_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_high_period);
|
||||
uint32_t sda_duty_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.sda_duty_period);
|
||||
uint32_t scl_start_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_start_period);
|
||||
uint32_t scl_stop_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_stop_period);
|
||||
uint32_t i2c_trans_timeout = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.i2c_trans_timeout);
|
||||
uint32_t setup_time_start = (cfg->i2c_timing_cfg.scl_high_period + cfg->i2c_timing_cfg.sda_duty_period);
|
||||
uint32_t hold_time_start = (cfg->i2c_timing_cfg.scl_start_period - cfg->i2c_timing_cfg.sda_duty_period);
|
||||
uint32_t setup_time_data = (cfg->i2c_timing_cfg.scl_low_period - cfg->i2c_timing_cfg.sda_duty_period);
|
||||
|
||||
/* Verify timing constraints */
|
||||
ESP_RETURN_ON_FALSE((float)cfg->i2c_timing_cfg.scl_low_period > 1.3, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL low period cannot be less than 1.3 micro seconds");
|
||||
ESP_RETURN_ON_FALSE((float)cfg->i2c_timing_cfg.scl_high_period > 0.6, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL high period cannot be less than 0.6 micro seconds");
|
||||
ESP_RETURN_ON_FALSE((float)setup_time_start > 0.6, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Setup time cannot be less than 0.6 micro seconds");
|
||||
ESP_RETURN_ON_FALSE((float)hold_time_start > 0.6, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Data hold time cannot be less than 0.6 micro seconds");
|
||||
ESP_RETURN_ON_FALSE((float)cfg->i2c_timing_cfg.scl_stop_period > 0.6, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Setup time cannot be less than 0.6 micro seconds");
|
||||
ESP_RETURN_ON_FALSE((float)cfg->i2c_timing_cfg.sda_duty_period < 3.45, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Data hold time cannot be greater than 3.45 micro seconds");
|
||||
ESP_RETURN_ON_FALSE((float)(setup_time_data * 1000) > 250, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Data setup time cannot be less than 250 nano seconds");
|
||||
|
||||
/* Verify filtering constrains
|
||||
*
|
||||
* I2C may have glitches on the transition edge, so the edge will be filtered in the design,
|
||||
* which will also affect the value of the timing parameter register.
|
||||
* Therefore, the following filtering constraints must be followed:
|
||||
*/
|
||||
ESP_RETURN_ON_FALSE(scl_stop_period > scl_high_period, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL Stop period cannot be greater than SCL high period");
|
||||
ESP_RETURN_ON_FALSE(sda_duty_period < scl_low_period, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SDA duty period cannot be less than the SCL low period");
|
||||
ESP_RETURN_ON_FALSE(scl_start_period > 8, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL start period must be greater than 8 RTC_FAST_CLK cycles");
|
||||
ESP_RETURN_ON_FALSE((scl_low_period + scl_high_period - sda_duty_period) > 8, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL low + SCL high - SDA duty must be greater than 8 RTC_FAST_CLK cycles");
|
||||
|
||||
/* Verify SDA duty num constraints */
|
||||
ESP_RETURN_ON_FALSE(sda_duty_period > 14, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SDA duty period must be greater than 14 RTC_FAST_CLK cycles");
|
||||
|
||||
/* Set the RTC I2C timing parameters */
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
i2c_dev->scl_low.val = scl_low_period; // SCL low period
|
||||
i2c_dev->scl_high.val = scl_high_period; // SCL high period
|
||||
i2c_dev->sda_duty.val = sda_duty_period; // SDA duty cycle
|
||||
i2c_dev->scl_start_period.val = scl_start_period; // Wait time after START condition
|
||||
i2c_dev->scl_stop_period.val = scl_stop_period; // Wait time before END condition
|
||||
i2c_dev->timeout.val = i2c_trans_timeout; // I2C transaction timeout
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
i2c_dev->i2c_scl_low.val = scl_low_period; // SCL low period
|
||||
i2c_dev->i2c_scl_high.val = scl_high_period; // SCL high period
|
||||
i2c_dev->i2c_sda_duty.val = sda_duty_period; // SDA duty cycle
|
||||
i2c_dev->i2c_scl_start_period.val = scl_start_period; // Wait time after START condition
|
||||
i2c_dev->i2c_scl_stop_period.val = scl_stop_period; // Wait time before END condition
|
||||
i2c_dev->i2c_to.val = i2c_trans_timeout; // I2C transaction timeout
|
||||
#endif // CONFIG_IDF_TARGET_ESP32S2
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* The RTC I2C controller follows the I2C command registers to perform read/write operations.
|
||||
* The cmd registers have the following format:
|
||||
*
|
||||
* 31 30:14 13:11 10 9 8 7:0
|
||||
* |----------|----------|---------|---------|----------|------------|---------|
|
||||
* | CMD_DONE | Reserved | OPCODE |ACK Value|ACK Expect|ACK Check En|Byte Num |
|
||||
* |----------|----------|---------|---------|----------|------------|---------|
|
||||
*/
|
||||
static void ulp_riscv_i2c_format_cmd(uint32_t cmd_idx, uint8_t op_code, uint8_t ack_val,
|
||||
uint8_t ack_expected, uint8_t ack_check_en, uint8_t byte_num)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
/* Reset cmd register */
|
||||
i2c_dev->command[cmd_idx].val = 0;
|
||||
|
||||
/* Write new command to cmd register */
|
||||
i2c_dev->command[cmd_idx].done = 0; // CMD Done
|
||||
i2c_dev->command[cmd_idx].op_code = op_code; // Opcode
|
||||
i2c_dev->command[cmd_idx].ack_val = ack_val; // ACK bit sent by I2C controller during READ.
|
||||
// Ignored during RSTART, STOP, END and WRITE cmds.
|
||||
i2c_dev->command[cmd_idx].ack_exp = ack_expected; // ACK bit expected by I2C controller during WRITE.
|
||||
// Ignored during RSTART, STOP, END and READ cmds.
|
||||
i2c_dev->command[cmd_idx].ack_en = ack_check_en; // I2C controller verifies that the ACK bit sent by the
|
||||
// slave device matches the ACK expected bit during WRITE.
|
||||
// Ignored during RSTART, STOP, END and READ cmds.
|
||||
i2c_dev->command[cmd_idx].byte_num = byte_num; // Byte Num
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
/* Reset cmd register */
|
||||
i2c_dev->i2c_cmd[cmd_idx].val = 0;
|
||||
|
||||
/* Write new command to cmd register */
|
||||
i2c_dev->i2c_cmd[cmd_idx].i2c_command_done = 0; // CMD Done
|
||||
i2c_dev->i2c_cmd[cmd_idx].i2c_op_code = op_code; // Opcode
|
||||
i2c_dev->i2c_cmd[cmd_idx].i2c_ack_val = ack_val; // ACK bit sent by I2C controller during READ.
|
||||
// Ignored during RSTART, STOP, END and WRITE cmds.
|
||||
i2c_dev->i2c_cmd[cmd_idx].i2c_ack_exp = ack_expected; // ACK bit expected by I2C controller during WRITE.
|
||||
// Ignored during RSTART, STOP, END and READ cmds.
|
||||
i2c_dev->i2c_cmd[cmd_idx].i2c_ack_en = ack_check_en; // I2C controller verifies that the ACK bit sent by the
|
||||
// slave device matches the ACK expected bit during WRITE.
|
||||
// Ignored during RSTART, STOP, END and READ cmds.
|
||||
i2c_dev->i2c_cmd[cmd_idx].i2c_byte_num = byte_num; // Byte Num
|
||||
#endif // CONFIG_IDF_TARGET_ESP32S2
|
||||
}
|
||||
|
||||
void ulp_riscv_i2c_master_set_slave_addr(uint8_t slave_addr)
|
||||
{
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, I2C_CTRL_SLAVE_ADDR_MASK);
|
||||
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0xFF, slave_addr, 0);
|
||||
}
|
||||
|
||||
void ulp_riscv_i2c_master_set_slave_reg_addr(uint8_t slave_reg_addr)
|
||||
{
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, I2C_CTRL_SLAVE_REG_ADDR_MASK);
|
||||
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0xFF, slave_reg_addr, 11);
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C transactions when master reads one byte of data from the slave device:
|
||||
*
|
||||
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|
|
||||
* | Master | START | SAD + W | | SUB | | SR | SAD + R | | | NACK | STOP |
|
||||
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|
|
||||
* | Slave | | | ACK | | ACK | | | ACK | DATA | | |
|
||||
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|
|
||||
*
|
||||
* I2C transactions when master reads multiple bytes of data from the slave device:
|
||||
*
|
||||
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|--------|--------|
|
||||
* | Master | START | SAD + W | | SUB | | SR | SAD + R | | | ACK | | NACK | STOP |
|
||||
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|--------|--------|
|
||||
* | Slave | | | ACK | | ACK | | | ACK | DATA | | DATA | | |
|
||||
* |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|--------|--------|
|
||||
*/
|
||||
void ulp_riscv_i2c_master_read_from_device(uint8_t *data_rd, size_t size)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
uint32_t cmd_idx = 0;
|
||||
|
||||
if (size == 0) {
|
||||
// Quietly return
|
||||
return;
|
||||
}
|
||||
|
||||
/* By default, RTC I2C controller is hard wired to use CMD2 register onwards for read operations */
|
||||
cmd_idx = 2;
|
||||
|
||||
/* Write slave addr */
|
||||
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_WRITE, 0, 0, 1, 2);
|
||||
|
||||
/* Repeated START */
|
||||
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_RESTART, 0, 0, 0, 0);
|
||||
|
||||
/* Write slave register addr */
|
||||
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_WRITE, 0, 0, 1, 1);
|
||||
|
||||
if (size > 1) {
|
||||
/* Read n - 1 bytes */
|
||||
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_READ, 0, 0, 1, size - 1);
|
||||
}
|
||||
|
||||
/* Read last byte + NACK */
|
||||
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_READ, 1, 1, 1, 1);
|
||||
|
||||
/* STOP */
|
||||
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_STOP, 0, 0, 0, 0);
|
||||
|
||||
/* Configure the RTC I2C controller in read mode */
|
||||
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0x1, 0, 27);
|
||||
|
||||
/* Enable Rx data interrupt */
|
||||
SET_PERI_REG_MASK(RTC_I2C_INT_ENA_REG, RTC_I2C_RX_DATA_INT_ENA);
|
||||
|
||||
/* Start RTC I2C transmission */
|
||||
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE);
|
||||
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
/* Poll for RTC I2C Rx Data interrupt bit to be set */
|
||||
while (!REG_GET_FIELD(RTC_I2C_INT_ST_REG, RTC_I2C_RX_DATA_INT_ST)) {
|
||||
/* Minimal delay to avoid hogging the CPU */
|
||||
vTaskDelay(1);
|
||||
}
|
||||
|
||||
/* Read the data
|
||||
*
|
||||
* Unfortunately, the RTC I2C has no fifo buffer to help us with reading and storing
|
||||
* multiple bytes of data. Therefore, we need to read one byte at a time and clear the
|
||||
* Rx interrupt to get ready for the next byte.
|
||||
*/
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
data_rd[i] = REG_GET_FIELD(RTC_I2C_DATA_REG, RTC_I2C_RDATA);
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
data_rd[i] = REG_GET_FIELD(RTC_I2C_DATA_REG, RTC_I2C_I2C_RDATA);
|
||||
#endif // CONFIG_IDF_TARGET_ESP32S2
|
||||
|
||||
/* Clear the Rx data interrupt bit */
|
||||
SET_PERI_REG_MASK(RTC_I2C_INT_CLR_REG, RTC_I2C_RX_DATA_INT_CLR);
|
||||
}
|
||||
|
||||
/* Clear the RTC I2C transmission bits */
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE);
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C transactions when master writes one byte of data to the slave device:
|
||||
*
|
||||
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|
|
||||
* | Master | START | SAD + W | | SUB | | DATA | | STOP |
|
||||
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|
|
||||
* | Slave | | | ACK | | ACK | | ACK | |
|
||||
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|
|
||||
*
|
||||
* I2C transactions when master writes multiple bytes of data to the slave device:
|
||||
*
|
||||
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|--------|--------|
|
||||
* | Master | START | SAD + W | | SUB | | DATA | | DATA | | STOP |
|
||||
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|--------|--------|
|
||||
* | Slave | | | ACK | | ACK | | ACK | | ACK | |
|
||||
* |--------|--------|---------|--------|--------|--------|--------|--------|--------|--------|--------|
|
||||
*/
|
||||
void ulp_riscv_i2c_master_write_to_device(uint8_t *data_wr, size_t size)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
uint32_t cmd_idx = 0;
|
||||
|
||||
if (size == 0) {
|
||||
// Quietly return
|
||||
return;
|
||||
}
|
||||
|
||||
/* By default, RTC I2C controller is hard wired to use CMD0 and CMD1 registers for write operations */
|
||||
cmd_idx = 0;
|
||||
|
||||
/* Write slave addr + reg addr + data */
|
||||
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_WRITE, 0, 0, 1, 2 + size);
|
||||
|
||||
/* Stop */
|
||||
ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_STOP, 0, 0, 0, 0);
|
||||
|
||||
/* Configure the RTC I2C controller in write mode */
|
||||
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0x1, 1, 27);
|
||||
|
||||
/* Enable Tx data interrupt */
|
||||
SET_PERI_REG_MASK(RTC_I2C_INT_ENA_REG, RTC_I2C_TX_DATA_INT_ENA);
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
/* Write the data to be transmitted */
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, I2C_CTRL_MASTER_TX_DATA_MASK);
|
||||
SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0xFF, data_wr[i], 19);
|
||||
|
||||
if (i == 0) {
|
||||
/* Start RTC I2C transmission. (Needn't do it for every byte) */
|
||||
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE);
|
||||
SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
|
||||
}
|
||||
|
||||
/* Poll for RTC I2C Tx Data interrupt bit to be set */
|
||||
while (!REG_GET_FIELD(RTC_I2C_INT_ST_REG, RTC_I2C_TX_DATA_INT_ST)) {
|
||||
/* Minimal delay to avoid hogging the CPU */
|
||||
vTaskDelay(1);
|
||||
}
|
||||
|
||||
/* Clear the Tx data interrupt bit */
|
||||
SET_PERI_REG_MASK(RTC_I2C_INT_CLR_REG, RTC_I2C_TX_DATA_INT_CLR);
|
||||
}
|
||||
|
||||
/* Clear the RTC I2C transmission bits */
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE);
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START);
|
||||
}
|
||||
|
||||
esp_err_t ulp_riscv_i2c_master_init(const ulp_riscv_i2c_cfg_t *cfg)
|
||||
{
|
||||
/* Reset RTC I2C */
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
i2c_dev->ctrl.i2c_reset = 1;
|
||||
esp_rom_delay_us(20);
|
||||
i2c_dev->ctrl.i2c_reset = 0;
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
SET_PERI_REG_MASK(SENS_SAR_PERI_RESET_CONF_REG, SENS_RTC_I2C_RESET);
|
||||
i2c_dev->i2c_ctrl.i2c_i2c_reset = 1;
|
||||
esp_rom_delay_us(20);
|
||||
i2c_dev->i2c_ctrl.i2c_i2c_reset = 0;
|
||||
CLEAR_PERI_REG_MASK(SENS_SAR_PERI_RESET_CONF_REG, SENS_RTC_I2C_RESET);
|
||||
#endif // CONFIG_IDF_TARGET_ESP32S2
|
||||
|
||||
/* Verify that the input cfg param is valid */
|
||||
ESP_RETURN_ON_FALSE(cfg, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "RTC I2C configuration is NULL");
|
||||
|
||||
/* Configure RTC I2C GPIOs */
|
||||
ESP_RETURN_ON_ERROR(i2c_set_pin(cfg), RTCI2C_TAG, "Failed to configure RTC I2C GPIOs");
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
/* Configure the RTC I2C controller in master mode */
|
||||
i2c_dev->ctrl.ms_mode = 1;
|
||||
|
||||
/* Enable RTC I2C Clock gate */
|
||||
i2c_dev->ctrl.i2c_ctrl_clk_gate_en = 1;
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
/* For esp32s3, we need to enable the rtc_i2c clock gate before accessing rtc i2c registers */
|
||||
SET_PERI_REG_MASK(SENS_SAR_PERI_CLK_GATE_CONF_REG, SENS_RTC_I2C_CLK_EN);
|
||||
|
||||
/* Configure the RTC I2C controller in master mode */
|
||||
i2c_dev->i2c_ctrl.i2c_ms_mode = 1;
|
||||
|
||||
/* Enable RTC I2C Clock gate */
|
||||
i2c_dev->i2c_ctrl.i2c_i2c_ctrl_clk_gate_en = 1;
|
||||
#endif // CONFIG_IDF_TARGET_ESP32S2
|
||||
|
||||
/* Configure RTC I2C timing paramters */
|
||||
ESP_RETURN_ON_ERROR(i2c_set_timing(cfg), RTCI2C_TAG, "Failed to configure RTC I2C timing");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
@ -194,6 +194,10 @@ examples/system/ulp_riscv/gpio_interrupt:
|
||||
temporary: true
|
||||
reason: the other targets are not tested yet
|
||||
|
||||
examples/system/ulp_riscv/i2c:
|
||||
enable:
|
||||
- if: SOC_RISCV_COPROC_SUPPORTED == 1
|
||||
|
||||
examples/system/ulp_riscv/uart_print:
|
||||
enable:
|
||||
- if: SOC_RISCV_COPROC_SUPPORTED == 1
|
||||
|
6
examples/system/ulp_riscv/i2c/CMakeLists.txt
Normal file
6
examples/system/ulp_riscv/i2c/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
# The following 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(ulp-riscv-rtc-i2c-example)
|
76
examples/system/ulp_riscv/i2c/README.md
Normal file
76
examples/system/ulp_riscv/i2c/README.md
Normal file
@ -0,0 +1,76 @@
|
||||
| Supported Targets | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
||||
# ULP RISC-V I2C Example
|
||||
|
||||
This example demonstrates how to use the RTC I2C peripheral from the ULP RISC-V coprocessor in deep sleep.
|
||||
|
||||
The ULP program is based on the BMP180 Temperature and Pressure sensor (https://cz.mouser.com/datasheet/2/783/BST-BMP180-DS000-1509579.pdf) which has an I2C interface. The main CPU initializes the RTC I2C peripheral, the BMP180 sensor and loads the ULP program. It then goes into deep sleep.
|
||||
|
||||
The ULP program periodically measures the temperature and pressure values from the BMP180 sensor and wakesup the main CPU when the values are above a certain thershold.
|
||||
### Hardware Required
|
||||
|
||||
* A development board with a SOC which has a RISC-V ULP coprocessor (e.g., ESP32-S2 Saola)
|
||||
* A BMP180 sensor module
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
## Example output
|
||||
|
||||
Below is the output from this example.
|
||||
|
||||
```
|
||||
Not a ULP-RISC V wakeup (cause = 0)
|
||||
Initializing RTC I2C ...
|
||||
RTC_I2C_STATUS_REG = 0x00000000
|
||||
Reading calibration data from BMP180 ...
|
||||
ac1 = 7819
|
||||
ac2 = -1152
|
||||
ac3 = -14317
|
||||
ac4 = 34252
|
||||
ac5 = 25122
|
||||
ac6 = 14289
|
||||
b1 = 6515
|
||||
b2 = 44
|
||||
mb = -32768
|
||||
mc = -11786
|
||||
md = 2746
|
||||
|
||||
Reading initial uncompensated temperature and pressure data ...
|
||||
Uncompensated Temperature = 22865
|
||||
Uncompensated Pressure = 41768
|
||||
|
||||
Real Temperature = 24.900000 deg celcius
|
||||
Real Pressure = 990.640000 hPa
|
||||
|
||||
Entering in deep sleep
|
||||
|
||||
ESP-ROM:esp32s2-rc4-20191025
|
||||
Build:Oct 25 2019
|
||||
rst:0x5 (DSLEEP),boot:0x9 (SPI_FAST_FLASH_BOOT)
|
||||
SPIWP:0xee
|
||||
mode:DIO, clock div:1
|
||||
load:0x3ffe6108,len:0x1298
|
||||
load:0x4004c000,len:0x92c
|
||||
load:0x40050000,len:0x2f04
|
||||
entry 0x4004c154
|
||||
W (76) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
|
||||
ULP RISC-V woke up the main CPU
|
||||
Uncompensated Temperature = 22865
|
||||
Uncompensated Pressure = 41765
|
||||
Reading calibration data from BMP180 ...
|
||||
ac1 = 7819
|
||||
ac2 = -1152
|
||||
ac3 = -14317
|
||||
ac4 = 34252
|
||||
ac5 = 25122
|
||||
ac6 = 14289
|
||||
b1 = 6515
|
||||
b2 = 44
|
||||
mb = -32768
|
||||
mc = -11786
|
||||
md = 2746
|
||||
|
||||
New Real Temperature = 24.900000 deg celcius
|
||||
New Real Pressure = 990.550000 hPa
|
||||
Entering in deep sleep
|
||||
```
|
25
examples/system/ulp_riscv/i2c/main/CMakeLists.txt
Normal file
25
examples/system/ulp_riscv/i2c/main/CMakeLists.txt
Normal file
@ -0,0 +1,25 @@
|
||||
idf_component_register(SRCS "ulp_riscv_rtc_i2c_example_main.c"
|
||||
INCLUDE_DIRS ""
|
||||
REQUIRES soc ulp)
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
||||
|
||||
#
|
||||
# ULP support additions to component CMakeLists.txt.
|
||||
#
|
||||
# 1. The ULP app name must be unique (if multiple components use ULP).
|
||||
set(ulp_app_name ulp_${COMPONENT_NAME})
|
||||
#
|
||||
# 2. Specify all C and Assembly source files.
|
||||
# Files should be placed into a separate directory (in this case, ulp/),
|
||||
# which should not be added to COMPONENT_SRCS.
|
||||
set(ulp_riscv_sources "ulp/main.c")
|
||||
|
||||
#
|
||||
# 3. List all the component source files which include automatically
|
||||
# generated ULP export file, ${ulp_app_name}.h:
|
||||
set(ulp_exp_dep_srcs "ulp_riscv_rtc_i2c_example_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_riscv_sources}" "${ulp_exp_dep_srcs}")
|
110
examples/system/ulp_riscv/i2c/main/bmp180_defs.h
Normal file
110
examples/system/ulp_riscv/i2c/main/bmp180_defs.h
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/***************************************************
|
||||
* BMP180 Register Addresses
|
||||
***************************************************/
|
||||
#define BMP180_SENSOR_I2C_ADDR 0x77
|
||||
#define BMP180_SENSOR_REG_ADDR_WHO_AM_I 0xD0
|
||||
#define BMP180_SENSOR_REG_ADDR_SOFT_RESET 0xE0
|
||||
#define BMP180_SENSOR_REG_ADDR_AC1_MSB 0xAA
|
||||
#define BMP180_SENSOR_REG_ADDR_AC1_LSB 0xAB
|
||||
#define BMP180_SENSOR_REG_ADDR_AC2_MSB 0xAC
|
||||
#define BMP180_SENSOR_REG_ADDR_AC2_LSB 0xAD
|
||||
#define BMP180_SENSOR_REG_ADDR_AC3_MSB 0xAE
|
||||
#define BMP180_SENSOR_REG_ADDR_AC3_LSB 0xAF
|
||||
#define BMP180_SENSOR_REG_ADDR_AC4_MSB 0xB0
|
||||
#define BMP180_SENSOR_REG_ADDR_AC4_LSB 0xB1
|
||||
#define BMP180_SENSOR_REG_ADDR_AC5_MSB 0xB2
|
||||
#define BMP180_SENSOR_REG_ADDR_AC5_LSB 0xB3
|
||||
#define BMP180_SENSOR_REG_ADDR_AC6_MSB 0xB4
|
||||
#define BMP180_SENSOR_REG_ADDR_AC6_LSB 0xB5
|
||||
#define BMP180_SENSOR_REG_ADDR_B1_MSB 0xB6
|
||||
#define BMP180_SENSOR_REG_ADDR_B1_LSB 0xB7
|
||||
#define BMP180_SENSOR_REG_ADDR_B2_MSB 0xB8
|
||||
#define BMP180_SENSOR_REG_ADDR_B2_LSB 0xB9
|
||||
#define BMP180_SENSOR_REG_ADDR_MB_MSB 0xBA
|
||||
#define BMP180_SENSOR_REG_ADDR_MB_LSB 0xBB
|
||||
#define BMP180_SENSOR_REG_ADDR_MC_MSB 0xBC
|
||||
#define BMP180_SENSOR_REG_ADDR_MC_LSB 0xBD
|
||||
#define BMP180_SENSOR_REG_ADDR_MD_MSB 0xBE
|
||||
#define BMP180_SENSOR_REG_ADDR_MD_LSB 0xBF
|
||||
#define BMP180_SENSOR_REG_ADDR_CTRL_REG 0xF4
|
||||
#define BMP180_SENSOR_REG_ADDR_SENSOR_DATA_MSB 0xF6
|
||||
#define BMP180_SENSOR_REG_ADDR_SENSOR_DATA_LSB 0xF7
|
||||
#define BMP180_SENSOR_REG_ADDR_SENSOR_DATA_XLSB 0xF8
|
||||
|
||||
/***************************************************
|
||||
* BMP180 Control Commands
|
||||
***************************************************/
|
||||
#define BMP180_SENSOR_CMD_READ_TEMPERATURE 0x2E
|
||||
#define BMP180_SENSOR_CMD_READ_PRESSURE_OSS_0 0x34
|
||||
#define BMP180_SENSOR_CMD_READ_PRESSURE_OSS_1 0x74
|
||||
#define BMP180_SENSOR_CMD_READ_PRESSURE_OSS_2 0xB4
|
||||
#define BMP180_SENSOR_CMD_READ_PRESSURE_OSS_3 0xF4
|
||||
#define BMP180_SENSOR_CMD_SOFT_RESET 0xB6
|
||||
|
||||
/***************************************************
|
||||
* BMP180 Chip ID
|
||||
***************************************************/
|
||||
#define BMP180_SENSOR_CHIP_ID 0x55
|
||||
|
||||
/***************************************************
|
||||
* BMP180 Calibration Data
|
||||
***************************************************/
|
||||
typedef struct {
|
||||
int16_t ac1;
|
||||
int16_t ac2;
|
||||
int16_t ac3;
|
||||
uint16_t ac4;
|
||||
uint16_t ac5;
|
||||
uint16_t ac6;
|
||||
int16_t b1;
|
||||
int16_t b2;
|
||||
int16_t mb;
|
||||
int16_t mc;
|
||||
int16_t md;
|
||||
} bmp180_cal_data_t;
|
||||
|
||||
bmp180_cal_data_t bmp180_cal_data;
|
||||
|
||||
/***************************************************
|
||||
* BMP180 Oversampling setting to measure pressure
|
||||
***************************************************/
|
||||
typedef enum {
|
||||
OSS_0 = 0, // Ultra low power
|
||||
OSS_1 = 1, // Standard
|
||||
OSS_2 = 2, // High resolution
|
||||
OSS_3 = 3 // Ultra high resolution
|
||||
} oss_mode_t;
|
||||
|
||||
/***************************************************
|
||||
* BMP180 Interaction APIs
|
||||
***************************************************/
|
||||
static void bmp180_read_cal_data(void); // Read cal data
|
||||
static void bmp180_read_ut_data(int16_t *ut_data); // Read uncompensated temperature
|
||||
static void bmp180_read_up_data(int32_t *up_data, oss_mode_t oss_mode); // Read uncompensated pressure
|
||||
|
||||
/************************************************
|
||||
* BMP180 Utility APIs
|
||||
************************************************/
|
||||
static void bmp180_read16(uint16_t *data_out, uint32_t reg_msb, uint32_t reg_lsb);
|
||||
static int32_t bmp180_calculate_real_temp(int32_t ut_data);
|
||||
static int32_t bmp180_calculate_real_pressure(int32_t up_data, int32_t ut_data, oss_mode_t oss_mode);
|
||||
|
||||
/************************************************
|
||||
* Pressure measurement mode
|
||||
************************************************/
|
||||
#define EXAMPLE_OSS_MODE OSS_0
|
||||
|
||||
/************************************************
|
||||
* Temperature and Pressure thresholds (uncompensated) to wake up Main CPU
|
||||
* The threshold values have been selected for demp purposes and may not
|
||||
* represent real world use case.
|
||||
************************************************/
|
||||
#define EXAMPLE_UT_THRESHOLD 20000
|
||||
#define EXAMPLE_UP_THRESHOLD 40000
|
126
examples/system/ulp_riscv/i2c/main/ulp/main.c
Normal file
126
examples/system/ulp_riscv/i2c/main/ulp/main.c
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
/* ULP RISC-V RTC I2C example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
This code runs on ULP RISC-V coprocessor
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "ulp_riscv.h"
|
||||
#include "ulp_riscv_utils.h"
|
||||
#include "ulp_riscv_i2c_ulp_core.h"
|
||||
#include "../bmp180_defs.h"
|
||||
|
||||
/************************************************
|
||||
* Shared data between main CPU and ULP
|
||||
************************************************/
|
||||
int16_t ut_data = 0;
|
||||
int32_t up_data = 0;
|
||||
int32_t ut_threshold = EXAMPLE_UT_THRESHOLD;
|
||||
int32_t up_threshold = EXAMPLE_UP_THRESHOLD;
|
||||
oss_mode_t oss_mode = EXAMPLE_OSS_MODE;
|
||||
|
||||
int main (void)
|
||||
{
|
||||
/* Read uncompensated temperature */
|
||||
bmp180_read_ut_data(&ut_data);
|
||||
|
||||
/* Read uncompensated pressure */
|
||||
bmp180_read_up_data(&up_data, oss_mode);
|
||||
|
||||
/* Wakeup the main CPU if either the uncompensated temperature or uncompensated pressure values
|
||||
* are more than their respective threshold values.
|
||||
*/
|
||||
if ((ut_data > ut_threshold) || (up_data > up_threshold)) {
|
||||
ulp_riscv_wakeup_main_processor();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bmp180_read16(uint16_t *data_out, uint32_t reg_msb, uint32_t reg_lsb)
|
||||
{
|
||||
uint8_t data_rd = 0;
|
||||
*data_out = 0;
|
||||
|
||||
ulp_riscv_i2c_master_set_slave_reg_addr(reg_msb);
|
||||
ulp_riscv_i2c_master_read_from_device(&data_rd, 1);
|
||||
*data_out |= (uint16_t)(data_rd << 8);
|
||||
ulp_riscv_i2c_master_set_slave_reg_addr(reg_lsb);
|
||||
data_rd = 0;
|
||||
ulp_riscv_i2c_master_read_from_device(&data_rd, 1);
|
||||
*data_out |= (uint16_t)(data_rd);
|
||||
}
|
||||
|
||||
static void bmp180_read_ut_data(int16_t *ut_data)
|
||||
{
|
||||
/* Set slave register address to the control register */
|
||||
ulp_riscv_i2c_master_set_slave_reg_addr(BMP180_SENSOR_REG_ADDR_CTRL_REG);
|
||||
|
||||
/* Setup control register to read temperature */
|
||||
uint8_t cmd = BMP180_SENSOR_CMD_READ_TEMPERATURE;
|
||||
ulp_riscv_i2c_master_write_to_device(&cmd, 1);
|
||||
|
||||
/* Wait at least 4.5 milliseconds for the sensor to complete the reading */
|
||||
ulp_riscv_delay_cycles(5 * ULP_RISCV_CYCLES_PER_US * 1000);
|
||||
|
||||
/* Read uncompensated temperature data */
|
||||
bmp180_read16((uint16_t *)ut_data, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_MSB, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_LSB);
|
||||
}
|
||||
|
||||
static void bmp180_read_up_data(int32_t *up_data, oss_mode_t oss_mode)
|
||||
{
|
||||
uint16_t press_high;
|
||||
uint8_t press_low;
|
||||
|
||||
/* Set slave register address to the control register */
|
||||
ulp_riscv_i2c_master_set_slave_reg_addr(BMP180_SENSOR_REG_ADDR_CTRL_REG);
|
||||
|
||||
/* Setup control register to read pressure */
|
||||
uint8_t cmd = 0;
|
||||
uint8_t wait = 0;
|
||||
switch(oss_mode)
|
||||
{
|
||||
case OSS_0:
|
||||
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_0;
|
||||
wait = 5; // Wait atleast 4.5 msec
|
||||
break;
|
||||
case OSS_1:
|
||||
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_1;
|
||||
wait = 8; // Wait atleast 7.5 msec
|
||||
break;
|
||||
case OSS_2:
|
||||
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_2;
|
||||
wait = 14; // Wait atleast 13.5 msec
|
||||
break;
|
||||
case OSS_3:
|
||||
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_3;
|
||||
wait = 26; // Wait atleast 25.5 msec
|
||||
break;
|
||||
}
|
||||
|
||||
ulp_riscv_i2c_master_write_to_device(&cmd, 1);
|
||||
|
||||
/* Wait for the required amount of time for the sensor to complete the reading */
|
||||
ulp_riscv_delay_cycles(wait * ULP_RISCV_CYCLES_PER_US * 1000);
|
||||
|
||||
/* Read uncompensated temperature data */
|
||||
|
||||
/* Read MSB + LSB */
|
||||
bmp180_read16(&press_high, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_MSB, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_LSB);
|
||||
|
||||
/* Read XLSB */
|
||||
ulp_riscv_i2c_master_set_slave_reg_addr(BMP180_SENSOR_REG_ADDR_SENSOR_DATA_XLSB);
|
||||
ulp_riscv_i2c_master_read_from_device(&press_low, 1);
|
||||
|
||||
*up_data = (((uint32_t)press_high << 8) + (uint32_t)press_low) >> (8 - oss_mode);
|
||||
}
|
@ -0,0 +1,351 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
/* ULP RISC-V RTC I2C example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "esp_sleep.h"
|
||||
#include "ulp_riscv.h"
|
||||
#include "ulp_riscv_i2c.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "ulp_main.h"
|
||||
#include "bmp180_defs.h"
|
||||
|
||||
extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
|
||||
extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end");
|
||||
|
||||
/************************************************
|
||||
* ULP utility APIs
|
||||
************************************************/
|
||||
static void init_ulp_program(void);
|
||||
|
||||
/************************************************
|
||||
* RTC I2C utility APIs
|
||||
************************************************/
|
||||
static void init_i2c(void);
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
uint8_t data_rd = 0;
|
||||
int16_t ut_data = 0;
|
||||
int32_t up_data = 0;
|
||||
int32_t temperature = 0;
|
||||
int32_t pressure = 0;
|
||||
oss_mode_t oss_mode;
|
||||
|
||||
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
|
||||
|
||||
/* Not a wakeup from ULP
|
||||
* Initialize RTC I2C
|
||||
* Setup BMP180 sensor
|
||||
* Store current temperature and pressure values
|
||||
* Load the ULP firmware
|
||||
* Go to deep sleep
|
||||
*/
|
||||
if (cause != ESP_SLEEP_WAKEUP_ULP) {
|
||||
printf("Not a ULP-RISC V wakeup (cause = %d)\n", cause);
|
||||
|
||||
/* Initialize RTC I2C */
|
||||
init_i2c();
|
||||
|
||||
/* Configure I2C slave address */
|
||||
ulp_riscv_i2c_master_set_slave_addr(BMP180_SENSOR_I2C_ADDR);
|
||||
|
||||
/* Reset the BMP180 sensor*/
|
||||
ulp_riscv_i2c_master_set_slave_reg_addr(BMP180_SENSOR_REG_ADDR_SOFT_RESET);
|
||||
uint8_t data_wr = BMP180_SENSOR_CMD_SOFT_RESET;
|
||||
ulp_riscv_i2c_master_write_to_device(&data_wr, 1);
|
||||
|
||||
/* Confirm that the sensor is alive
|
||||
* The BMP180 returns the chip id 0x55 on quering reg addr 0xD0
|
||||
*/
|
||||
ulp_riscv_i2c_master_set_slave_reg_addr(BMP180_SENSOR_REG_ADDR_WHO_AM_I);
|
||||
ulp_riscv_i2c_master_read_from_device(&data_rd, 1);
|
||||
if (data_rd != BMP180_SENSOR_CHIP_ID) {
|
||||
printf("ERROR: Cannot communicate with I2C sensor\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Read the calibration data */
|
||||
printf("Reading calibration data from BMP180 ...\n");
|
||||
bmp180_read_cal_data();
|
||||
printf("\n");
|
||||
|
||||
/* Read uncompensated temperature and pressure */
|
||||
printf("Reading initial uncompensated temperature and pressure data ...\n");
|
||||
ut_data = 0;
|
||||
up_data = 0;
|
||||
oss_mode = EXAMPLE_OSS_MODE;
|
||||
bmp180_read_ut_data(&ut_data);
|
||||
bmp180_read_up_data(&up_data, oss_mode);
|
||||
printf("Uncompensated Temperature = %d\n", ut_data);
|
||||
printf("Uncompensated Pressure = %d\n", up_data);
|
||||
printf("\n");
|
||||
|
||||
/* Calculate real temperature value */
|
||||
temperature = bmp180_calculate_real_temp((int32_t)ut_data);
|
||||
printf("Real Temperature = %f deg celcius\n", (float)(temperature/10.0));
|
||||
|
||||
/* Calculate real pressure value */
|
||||
pressure = bmp180_calculate_real_pressure(up_data, (int32_t)ut_data, oss_mode);
|
||||
printf("Real Pressure = %f hPa\n", pressure / 100.0);
|
||||
printf("\n");
|
||||
|
||||
/* Load ULP firmware
|
||||
*
|
||||
* The ULP is responsible of monitoring the temperature and pressure values
|
||||
* periodically. It will wakeup the main CPU if the temperature and pressure
|
||||
* values are above a certain threshold.
|
||||
*/
|
||||
init_ulp_program();
|
||||
}
|
||||
|
||||
/* ULP RISC-V read and detected a temperature or pressure above the limit */
|
||||
if (cause == ESP_SLEEP_WAKEUP_ULP) {
|
||||
printf("ULP RISC-V woke up the main CPU\n");
|
||||
|
||||
/* Pause ULP while we are using the RTC I2C from the main CPU */
|
||||
ulp_timer_stop();
|
||||
ulp_riscv_halt();
|
||||
|
||||
printf("Uncompensated Temperature = %d\n", ulp_ut_data);
|
||||
printf("Uncompensated Pressure = %d\n", ulp_up_data);
|
||||
|
||||
/* Read the calibration data again */
|
||||
printf("Reading calibration data from BMP180 ...\n");
|
||||
bmp180_read_cal_data();
|
||||
printf("\n");
|
||||
|
||||
/* Calculate real temperature and pressure again */
|
||||
temperature = 0;
|
||||
temperature = bmp180_calculate_real_temp((int32_t)ulp_ut_data);
|
||||
printf("New Real Temperature = %f deg celcius\n", (float)(temperature/10.0));
|
||||
|
||||
/* Calculate real pressure value */
|
||||
pressure = 0;
|
||||
pressure = bmp180_calculate_real_pressure(ulp_up_data, (int32_t)ulp_ut_data, oss_mode);
|
||||
printf("New Real Pressure = %f hPa\n", pressure / 100.0);
|
||||
|
||||
/* Resume ULP and go to deep sleep again */
|
||||
ulp_timer_resume();
|
||||
}
|
||||
|
||||
|
||||
/* Add a delay for everything to the printed before heading in to light sleep */
|
||||
vTaskDelay(100);
|
||||
|
||||
/* Go back to sleep, only the ULP RISC-V will run */
|
||||
printf("Entering deep sleep\n\n");
|
||||
|
||||
/* RTC peripheral power domain needs to be kept on to keep RTC I2C related configs during sleep */
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
||||
|
||||
ESP_ERROR_CHECK(esp_sleep_enable_ulp_wakeup());
|
||||
|
||||
esp_deep_sleep_start();
|
||||
}
|
||||
|
||||
static void init_i2c(void)
|
||||
{
|
||||
/* Configure RTC I2C */
|
||||
printf("Initializing RTC I2C ...\n");
|
||||
ulp_riscv_i2c_cfg_t i2c_cfg = ULP_RISCV_I2C_DEFAULT_CONFIG();
|
||||
ulp_riscv_i2c_master_init(&i2c_cfg);
|
||||
}
|
||||
|
||||
static void bmp180_read16(uint16_t *data_out, uint32_t reg_msb, uint32_t reg_lsb)
|
||||
{
|
||||
uint8_t data_rd = 0;
|
||||
*data_out = 0;
|
||||
|
||||
ulp_riscv_i2c_master_set_slave_reg_addr(reg_msb);
|
||||
ulp_riscv_i2c_master_read_from_device(&data_rd, 1);
|
||||
*data_out |= (uint16_t)(data_rd << 8);
|
||||
ulp_riscv_i2c_master_set_slave_reg_addr(reg_lsb);
|
||||
data_rd = 0;
|
||||
ulp_riscv_i2c_master_read_from_device(&data_rd, 1);
|
||||
*data_out |= (uint16_t)(data_rd);
|
||||
}
|
||||
|
||||
static void bmp180_read_cal_data(void)
|
||||
{
|
||||
/* AC1 */
|
||||
bmp180_read16((uint16_t *)&bmp180_cal_data.ac1, BMP180_SENSOR_REG_ADDR_AC1_MSB, BMP180_SENSOR_REG_ADDR_AC1_LSB);
|
||||
printf("ac1 = %d\n", bmp180_cal_data.ac1);
|
||||
|
||||
/* AC2 */
|
||||
bmp180_read16((uint16_t *)&bmp180_cal_data.ac2, BMP180_SENSOR_REG_ADDR_AC2_MSB, BMP180_SENSOR_REG_ADDR_AC2_LSB);
|
||||
printf("ac2 = %d\n", bmp180_cal_data.ac2);
|
||||
|
||||
/* AC3 */
|
||||
bmp180_read16((uint16_t *)&bmp180_cal_data.ac3, BMP180_SENSOR_REG_ADDR_AC3_MSB, BMP180_SENSOR_REG_ADDR_AC3_LSB);
|
||||
printf("ac3 = %d\n", bmp180_cal_data.ac3);
|
||||
|
||||
/* AC4 */
|
||||
bmp180_read16(&bmp180_cal_data.ac4, BMP180_SENSOR_REG_ADDR_AC4_MSB, BMP180_SENSOR_REG_ADDR_AC4_LSB);
|
||||
printf("ac4 = %u\n", bmp180_cal_data.ac4);
|
||||
|
||||
/* AC5 */
|
||||
bmp180_read16(&bmp180_cal_data.ac5, BMP180_SENSOR_REG_ADDR_AC5_MSB, BMP180_SENSOR_REG_ADDR_AC5_LSB);
|
||||
printf("ac5 = %u\n", bmp180_cal_data.ac5);
|
||||
|
||||
/* AC6 */
|
||||
bmp180_read16(&bmp180_cal_data.ac6, BMP180_SENSOR_REG_ADDR_AC6_MSB, BMP180_SENSOR_REG_ADDR_AC6_LSB);
|
||||
printf("ac6 = %u\n", bmp180_cal_data.ac6);
|
||||
|
||||
/* B1 */
|
||||
bmp180_read16((uint16_t *)&bmp180_cal_data.b1, BMP180_SENSOR_REG_ADDR_B1_MSB, BMP180_SENSOR_REG_ADDR_B1_LSB);
|
||||
printf("b1 = %d\n", bmp180_cal_data.b1);
|
||||
|
||||
/* B2 */
|
||||
bmp180_read16((uint16_t *)&bmp180_cal_data.b2, BMP180_SENSOR_REG_ADDR_B2_MSB, BMP180_SENSOR_REG_ADDR_B2_LSB);
|
||||
printf("b2 = %d\n", bmp180_cal_data.b2);
|
||||
|
||||
/* MB */
|
||||
bmp180_read16((uint16_t *)&bmp180_cal_data.mb, BMP180_SENSOR_REG_ADDR_MB_MSB, BMP180_SENSOR_REG_ADDR_MB_LSB);
|
||||
printf("mb = %d\n", bmp180_cal_data.mb);
|
||||
|
||||
/* MC */
|
||||
bmp180_read16((uint16_t *)&bmp180_cal_data.mc, BMP180_SENSOR_REG_ADDR_MC_MSB, BMP180_SENSOR_REG_ADDR_MC_LSB);
|
||||
printf("mc = %d\n", bmp180_cal_data.mc);
|
||||
|
||||
/* MD */
|
||||
bmp180_read16((uint16_t *)&bmp180_cal_data.md, BMP180_SENSOR_REG_ADDR_MD_MSB, BMP180_SENSOR_REG_ADDR_MD_LSB);
|
||||
printf("md = %d\n", bmp180_cal_data.md);
|
||||
}
|
||||
|
||||
static void bmp180_read_ut_data(int16_t *ut_data)
|
||||
{
|
||||
/* Set slave register address to the control register */
|
||||
ulp_riscv_i2c_master_set_slave_reg_addr(BMP180_SENSOR_REG_ADDR_CTRL_REG);
|
||||
|
||||
/* Setup control register to read temperature */
|
||||
uint8_t cmd = BMP180_SENSOR_CMD_READ_TEMPERATURE;
|
||||
ulp_riscv_i2c_master_write_to_device(&cmd, 1);
|
||||
|
||||
/* Wait at least 4.5 milliseconds for the sensor to complete the reading */
|
||||
vTaskDelay(pdMS_TO_TICKS(5));
|
||||
|
||||
/* Read uncompensated temperature data */
|
||||
bmp180_read16((uint16_t *)ut_data, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_MSB, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_LSB);
|
||||
}
|
||||
|
||||
static int32_t computeb5(int32_t ut_data)
|
||||
{
|
||||
int32_t x1 = (ut_data - (int32_t)bmp180_cal_data.ac6) * ((int32_t)bmp180_cal_data.ac5) >> 15;
|
||||
int32_t x2 = ((int32_t)bmp180_cal_data.mc << 11) / (x1 + (int32_t)bmp180_cal_data.md);
|
||||
return x1 + x2;
|
||||
}
|
||||
|
||||
static int32_t bmp180_calculate_real_temp(int32_t ut_data)
|
||||
{
|
||||
int32_t b5 = computeb5(ut_data);
|
||||
int32_t t = (b5 + 8) >> 4;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static void bmp180_read_up_data(int32_t *up_data, oss_mode_t oss_mode)
|
||||
{
|
||||
uint16_t press_high;
|
||||
uint8_t press_low;
|
||||
|
||||
/* Set slave register address to the control register */
|
||||
ulp_riscv_i2c_master_set_slave_reg_addr(BMP180_SENSOR_REG_ADDR_CTRL_REG);
|
||||
|
||||
/* Setup control register to read pressure */
|
||||
uint8_t cmd = 0;
|
||||
uint8_t wait = 0;
|
||||
switch(oss_mode)
|
||||
{
|
||||
case OSS_0:
|
||||
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_0;
|
||||
wait = 5; // Wait atleast 4.5 msec
|
||||
break;
|
||||
case OSS_1:
|
||||
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_1;
|
||||
wait = 8; // Wait atleast 7.5 msec
|
||||
break;
|
||||
case OSS_2:
|
||||
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_2;
|
||||
wait = 14; // Wait atleast 13.5 msec
|
||||
break;
|
||||
case OSS_3:
|
||||
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_3;
|
||||
wait = 26; // Wait atleast 25.5 msec
|
||||
break;
|
||||
}
|
||||
|
||||
ulp_riscv_i2c_master_write_to_device(&cmd, 1);
|
||||
|
||||
/* Wait for the required amount of time for the sensor to complete the reading */
|
||||
vTaskDelay(pdMS_TO_TICKS(wait));
|
||||
|
||||
/* Read uncompensated temperature data */
|
||||
|
||||
/* Read MSB + LSB */
|
||||
bmp180_read16(&press_high, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_MSB, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_LSB);
|
||||
|
||||
/* Read XLSB */
|
||||
ulp_riscv_i2c_master_set_slave_reg_addr(BMP180_SENSOR_REG_ADDR_SENSOR_DATA_XLSB);
|
||||
ulp_riscv_i2c_master_read_from_device(&press_low, 1);
|
||||
|
||||
*up_data = (((uint32_t)press_high << 8) + (uint32_t)press_low) >> (8 - oss_mode);
|
||||
}
|
||||
|
||||
static int32_t bmp180_calculate_real_pressure(int32_t up_data, int32_t ut_data, oss_mode_t oss_mode)
|
||||
{
|
||||
int32_t p, x1, x2, x3, b3, b5, b6;
|
||||
uint32_t b4, b7;
|
||||
|
||||
b5 = computeb5(ut_data);
|
||||
b6 = b5 - 4000;
|
||||
x1 = (bmp180_cal_data.b2 * ((b6 * b6) >> 12)) >> 11;
|
||||
x2 = (bmp180_cal_data.ac2 * b6) >> 11;
|
||||
x3 = x1 + x2;
|
||||
b3 = (((((int32_t) bmp180_cal_data.ac1) * 4 + x3) << oss_mode) + 2) >> 2;
|
||||
x1 = (bmp180_cal_data.ac3 * b6) >> 13;
|
||||
x2 = (bmp180_cal_data.b1 * ((b6 * b6) >> 12)) >> 16;
|
||||
x3 = ((x1 + x2) + 2) >> 2;
|
||||
b4 = (bmp180_cal_data.ac4 * (uint32_t) (x3 + 32768)) >> 15;
|
||||
b7 = ((uint32_t) (up_data - b3) * (50000 >> oss_mode));
|
||||
|
||||
if (b7 < 0x80000000) {
|
||||
p = (b7 << 1) / b4;
|
||||
} else {
|
||||
p = (b7 / b4) << 1;
|
||||
}
|
||||
|
||||
x1 = (p >> 8) * (p >> 8);
|
||||
x1 = (x1 * 3038) >> 16;
|
||||
x2 = (-7357 * p) >> 16;
|
||||
p = p + ((x1 + x2 + 3791) >> 4);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static void init_ulp_program(void)
|
||||
{
|
||||
esp_err_t err = ulp_riscv_load_binary(ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start));
|
||||
ESP_ERROR_CHECK(err);
|
||||
|
||||
/* The first argument is the period index, which is not used by the ULP-RISC-V timer
|
||||
* The second argument is the period in microseconds, which gives a wakeup time period of: 20ms
|
||||
*/
|
||||
ulp_set_wakeup_period(0, 20000);
|
||||
|
||||
/* Start the program */
|
||||
err = ulp_riscv_run();
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
9
examples/system/ulp_riscv/i2c/sdkconfig.defaults
Normal file
9
examples/system/ulp_riscv/i2c/sdkconfig.defaults
Normal file
@ -0,0 +1,9 @@
|
||||
# Enable ULP
|
||||
CONFIG_ULP_COPROC_ENABLED=y
|
||||
CONFIG_ULP_COPROC_RISCV=y
|
||||
CONFIG_ULP_COPROC_RESERVE_MEM=4096
|
||||
# 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
|
Loading…
x
Reference in New Issue
Block a user