mirror of
https://gitee.com/beecue/fastbee.git
synced 2025-12-19 09:25:54 +08:00
添加智能灯固件代码
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
# Peripherals Examples
|
||||
|
||||
This section provides examples how to configure and use ESP32’s internal peripherals like GPIO, UART, I2C, SPI, timers, counters, ADC / DAC, PWM, etc.
|
||||
|
||||
See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples.
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(adc)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := adc
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# ADC1 Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example shows how to configure ADC1 and read the voltage connected to GPIO pin.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
In this example, we use `ADC_UNIT_1` by default, we need to connect a voltage source (0 ~ 3.3v) to GPIO34. If another ADC unit is selected in your application, you need to change the GPIO pin (please refer to Chapter 4.11 of the `ESP32 Technical Reference Manual`).
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
Running this example, you will see the following log output on the serial monitor:
|
||||
|
||||
```
|
||||
Raw: 486 Voltage: 189mV
|
||||
Raw: 435 Voltage: 177mV
|
||||
Raw: 225 Voltage: 128mV
|
||||
Raw: 18 Voltage: 79mV
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
* program upload failure
|
||||
|
||||
* Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
|
||||
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
|
||||
|
||||
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "adc1_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,108 @@
|
||||
/* ADC1 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 <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/adc.h"
|
||||
#include "esp_adc_cal.h"
|
||||
|
||||
#define DEFAULT_VREF 1100 //Use adc2_vref_to_gpio() to obtain a better estimate
|
||||
#define NO_OF_SAMPLES 64 //Multisampling
|
||||
|
||||
static esp_adc_cal_characteristics_t *adc_chars;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
static const adc_channel_t channel = ADC_CHANNEL_6; //GPIO34 if ADC1, GPIO14 if ADC2
|
||||
static const adc_bits_width_t width = ADC_WIDTH_BIT_12;
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
static const adc_channel_t channel = ADC_CHANNEL_6; // GPIO7 if ADC1, GPIO17 if ADC2
|
||||
static const adc_bits_width_t width = ADC_WIDTH_BIT_13;
|
||||
#endif
|
||||
static const adc_atten_t atten = ADC_ATTEN_DB_0;
|
||||
static const adc_unit_t unit = ADC_UNIT_1;
|
||||
|
||||
|
||||
static void check_efuse(void)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
//Check if TP is burned into eFuse
|
||||
if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK) {
|
||||
printf("eFuse Two Point: Supported\n");
|
||||
} else {
|
||||
printf("eFuse Two Point: NOT supported\n");
|
||||
}
|
||||
//Check Vref is burned into eFuse
|
||||
if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK) {
|
||||
printf("eFuse Vref: Supported\n");
|
||||
} else {
|
||||
printf("eFuse Vref: NOT supported\n");
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK) {
|
||||
printf("eFuse Two Point: Supported\n");
|
||||
} else {
|
||||
printf("Cannot retrieve eFuse Two Point calibration values. Default calibration values will be used.\n");
|
||||
}
|
||||
#else
|
||||
#error "This example is configured for ESP32/ESP32S2."
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void print_char_val_type(esp_adc_cal_value_t val_type)
|
||||
{
|
||||
if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
|
||||
printf("Characterized using Two Point Value\n");
|
||||
} else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
|
||||
printf("Characterized using eFuse Vref\n");
|
||||
} else {
|
||||
printf("Characterized using Default Vref\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
//Check if Two Point or Vref are burned into eFuse
|
||||
check_efuse();
|
||||
|
||||
//Configure ADC
|
||||
if (unit == ADC_UNIT_1) {
|
||||
adc1_config_width(width);
|
||||
adc1_config_channel_atten(channel, atten);
|
||||
} else {
|
||||
adc2_config_channel_atten((adc2_channel_t)channel, atten);
|
||||
}
|
||||
|
||||
//Characterize ADC
|
||||
adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t));
|
||||
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, width, DEFAULT_VREF, adc_chars);
|
||||
print_char_val_type(val_type);
|
||||
|
||||
//Continuously sample ADC1
|
||||
while (1) {
|
||||
uint32_t adc_reading = 0;
|
||||
//Multisampling
|
||||
for (int i = 0; i < NO_OF_SAMPLES; i++) {
|
||||
if (unit == ADC_UNIT_1) {
|
||||
adc_reading += adc1_get_raw((adc1_channel_t)channel);
|
||||
} else {
|
||||
int raw;
|
||||
adc2_get_raw((adc2_channel_t)channel, width, &raw);
|
||||
adc_reading += raw;
|
||||
}
|
||||
}
|
||||
adc_reading /= NO_OF_SAMPLES;
|
||||
//Convert adc_reading to voltage in mV
|
||||
uint32_t voltage = esp_adc_cal_raw_to_voltage(adc_reading, adc_chars);
|
||||
printf("Raw: %d\tVoltage: %dmV\n", adc_reading, voltage);
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(adc2)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := adc2
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# ADC2 Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
In this example, we use ADC2 to measure the output of DAC.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
#### ESP32 platform
|
||||
|
||||
* A development board with ESP32 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
We use ADC1_CHANNEL_7 (GPIO27) and DAC_CHANNEL_1 (GPIO25) by default, you need to short the two GPIOs (if you have changed the ADC2 and DAC channel, please refer to Chapter 4.11 of the `ESP32 Technical Reference Manual` to get the pin number).
|
||||
|
||||
#### ESP32-S2 platform
|
||||
|
||||
* A development board with ESP32S2 SoC
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
We use ADC1_CHANNEL_7 (GPIO18) and DAC_CHANNEL_1 (GPIO17) by default, you need to short the two GPIOs (if you have changed the ADC2 and DAC channel, please refer to the `ESP32S2 Technical Reference Manual` to get the pin number).
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
* Set ADC2 and DAC channel in "Example Configuration"
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
Running this example, you will see the following log output on the serial monitor:
|
||||
|
||||
### ESP32 platform
|
||||
|
||||
```
|
||||
ADC channel 7 @ GPIO 27, DAC channel 1 @ GPIO 25.
|
||||
adc2_init...
|
||||
start conversion.
|
||||
1: 150
|
||||
2: 203
|
||||
3: 250
|
||||
4: 300
|
||||
5: 351
|
||||
6: 400
|
||||
7: 441
|
||||
8: 491
|
||||
9: 547
|
||||
10: 595
|
||||
...
|
||||
```
|
||||
|
||||
#### ESP32-S2 platform
|
||||
|
||||
```
|
||||
ADC channel 7 @ GPIO 18, DAC channel 1 @ GPIO 17.
|
||||
adc2_init...
|
||||
start conversion.
|
||||
1: 150
|
||||
2: 203
|
||||
3: 250
|
||||
4: 300
|
||||
5: 351
|
||||
6: 400
|
||||
7: 441
|
||||
8: 491
|
||||
9: 547
|
||||
10: 595
|
||||
...
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
* program upload failure
|
||||
|
||||
* Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
|
||||
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
|
||||
|
||||
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "adc2_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,105 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
choice EXAMPLE_ADC2_CHANNEL
|
||||
bool "ADC2 Channel Num"
|
||||
depends on IDF_TARGET_ESP32
|
||||
default EXAMPLE_ADC2_CHANNEL_7
|
||||
help
|
||||
The channel of ADC2 used in this example.
|
||||
|
||||
config EXAMPLE_ADC2_CHANNEL_0
|
||||
bool "ADC2 Channel 0 (GPIO 4)"
|
||||
config EXAMPLE_ADC2_CHANNEL_1
|
||||
bool "ADC2 Channel 1 (GPIO 0)"
|
||||
config EXAMPLE_ADC2_CHANNEL_2
|
||||
bool "ADC2 Channel 2 (GPIO 2)"
|
||||
config EXAMPLE_ADC2_CHANNEL_3
|
||||
bool "ADC2 Channel 3 (GPIO 15)"
|
||||
config EXAMPLE_ADC2_CHANNEL_4
|
||||
bool "ADC2 Channel 4 (GPIO 13)"
|
||||
config EXAMPLE_ADC2_CHANNEL_5
|
||||
bool "ADC2 Channel 5 (GPIO 12)"
|
||||
config EXAMPLE_ADC2_CHANNEL_6
|
||||
bool "ADC2 Channel 6 (GPIO 14)"
|
||||
config EXAMPLE_ADC2_CHANNEL_7
|
||||
bool "ADC2 Channel 7 (GPIO 27)"
|
||||
config EXAMPLE_ADC2_CHANNEL_8
|
||||
bool "ADC2 Channel 8 (GPIO 25)"
|
||||
config EXAMPLE_ADC2_CHANNEL_9
|
||||
bool "ADC2 Channel 9 (GPIO 26)"
|
||||
endchoice
|
||||
|
||||
choice EXAMPLE_ADC2_CHANNEL
|
||||
bool "ADC2 Channel Num"
|
||||
depends on IDF_TARGET_ESP32S2
|
||||
default EXAMPLE_ADC2_CHANNEL_7
|
||||
help
|
||||
The channel of ADC2 used in this example.
|
||||
|
||||
config EXAMPLE_ADC2_CHANNEL_0
|
||||
bool "ADC2 Channel 0 (GPIO 11)"
|
||||
config EXAMPLE_ADC2_CHANNEL_1
|
||||
bool "ADC2 Channel 1 (GPIO 12)"
|
||||
config EXAMPLE_ADC2_CHANNEL_2
|
||||
bool "ADC2 Channel 2 (GPIO 13)"
|
||||
config EXAMPLE_ADC2_CHANNEL_3
|
||||
bool "ADC2 Channel 3 (GPIO 14)"
|
||||
config EXAMPLE_ADC2_CHANNEL_4
|
||||
bool "ADC2 Channel 4 (GPIO 15)"
|
||||
config EXAMPLE_ADC2_CHANNEL_5
|
||||
bool "ADC2 Channel 5 (GPIO 16)"
|
||||
config EXAMPLE_ADC2_CHANNEL_6
|
||||
bool "ADC2 Channel 6 (GPIO 17)"
|
||||
config EXAMPLE_ADC2_CHANNEL_7
|
||||
bool "ADC2 Channel 7 (GPIO 18)"
|
||||
config EXAMPLE_ADC2_CHANNEL_8
|
||||
bool "ADC2 Channel 8 (GPIO 19)"
|
||||
config EXAMPLE_ADC2_CHANNEL_9
|
||||
bool "ADC2 Channel 9 (GPIO 20)"
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_ADC2_CHANNEL
|
||||
int
|
||||
default 0 if EXAMPLE_ADC2_CHANNEL_0
|
||||
default 1 if EXAMPLE_ADC2_CHANNEL_1
|
||||
default 2 if EXAMPLE_ADC2_CHANNEL_2
|
||||
default 3 if EXAMPLE_ADC2_CHANNEL_3
|
||||
default 4 if EXAMPLE_ADC2_CHANNEL_4
|
||||
default 5 if EXAMPLE_ADC2_CHANNEL_5
|
||||
default 6 if EXAMPLE_ADC2_CHANNEL_6
|
||||
default 7 if EXAMPLE_ADC2_CHANNEL_7
|
||||
default 8 if EXAMPLE_ADC2_CHANNEL_8
|
||||
default 9 if EXAMPLE_ADC2_CHANNEL_9
|
||||
|
||||
choice EXAMPLE_DAC_CHANNEL
|
||||
bool "DAC Channel Num"
|
||||
depends on IDF_TARGET_ESP32
|
||||
default EXAMPLE_DAC_CHANNEL_1
|
||||
help
|
||||
The channel of DAC used in this example.
|
||||
|
||||
config EXAMPLE_DAC_CHANNEL_1
|
||||
bool "DAC Channel 1 (GPIO25)"
|
||||
config EXAMPLE_DAC_CHANNEL_2
|
||||
bool "DAC Channel 2 (GPIO26)"
|
||||
endchoice
|
||||
|
||||
choice EXAMPLE_DAC_CHANNEL
|
||||
bool "DAC Channel Num"
|
||||
depends on IDF_TARGET_ESP32S2
|
||||
default EXAMPLE_DAC_CHANNEL_1
|
||||
help
|
||||
The channel of DAC used in this example.
|
||||
|
||||
config EXAMPLE_DAC_CHANNEL_1
|
||||
bool "DAC Channel 1 (GPIO17)"
|
||||
config EXAMPLE_DAC_CHANNEL_2
|
||||
bool "DAC Channel 2 (GPIO18)"
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_DAC_CHANNEL
|
||||
int
|
||||
default 0 if EXAMPLE_DAC_CHANNEL_1
|
||||
default 1 if EXAMPLE_DAC_CHANNEL_2
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,69 @@
|
||||
/* ADC2 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 <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/adc.h"
|
||||
#include "driver/dac.h"
|
||||
#include "esp_system.h"
|
||||
|
||||
#define DAC_EXAMPLE_CHANNEL CONFIG_EXAMPLE_DAC_CHANNEL
|
||||
#define ADC2_EXAMPLE_CHANNEL CONFIG_EXAMPLE_ADC2_CHANNEL
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
static const adc_bits_width_t width = ADC_WIDTH_BIT_12;
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
static const adc_bits_width_t width = ADC_WIDTH_BIT_13;
|
||||
#endif
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
uint8_t output_data=0;
|
||||
int read_raw;
|
||||
esp_err_t r;
|
||||
|
||||
gpio_num_t adc_gpio_num, dac_gpio_num;
|
||||
|
||||
r = adc2_pad_get_io_num( ADC2_EXAMPLE_CHANNEL, &adc_gpio_num );
|
||||
assert( r == ESP_OK );
|
||||
r = dac_pad_get_io_num( DAC_EXAMPLE_CHANNEL, &dac_gpio_num );
|
||||
assert( r == ESP_OK );
|
||||
|
||||
printf("ADC2 channel %d @ GPIO %d, DAC channel %d @ GPIO %d.\n", ADC2_EXAMPLE_CHANNEL, adc_gpio_num,
|
||||
DAC_EXAMPLE_CHANNEL + 1, dac_gpio_num );
|
||||
|
||||
dac_output_enable( DAC_EXAMPLE_CHANNEL );
|
||||
|
||||
//be sure to do the init before using adc2.
|
||||
printf("adc2_init...\n");
|
||||
adc2_config_channel_atten( ADC2_EXAMPLE_CHANNEL, ADC_ATTEN_11db );
|
||||
|
||||
vTaskDelay(2 * portTICK_PERIOD_MS);
|
||||
|
||||
printf("start conversion.\n");
|
||||
while(1) {
|
||||
dac_output_voltage( DAC_EXAMPLE_CHANNEL, output_data++ );
|
||||
r = adc2_get_raw( ADC2_EXAMPLE_CHANNEL, width, &read_raw);
|
||||
if ( r == ESP_OK ) {
|
||||
printf("%d: %d\n", output_data, read_raw );
|
||||
} else if ( r == ESP_ERR_INVALID_STATE ) {
|
||||
printf("%s: ADC2 not initialized yet.\n", esp_err_to_name(r));
|
||||
} else if ( r == ESP_ERR_TIMEOUT ) {
|
||||
//This can not happen in this example. But if WiFi is in use, such error code could be returned.
|
||||
printf("%s: ADC2 is in use by Wi-Fi.\n", esp_err_to_name(r));
|
||||
} else {
|
||||
printf("%s\n", esp_err_to_name(r));
|
||||
}
|
||||
|
||||
vTaskDelay( 2 * portTICK_PERIOD_MS );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(gpio)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := gpio
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# Example: GPIO
|
||||
|
||||
This test code shows how to configure gpio and how to use gpio interrupt.
|
||||
|
||||
## GPIO functions:
|
||||
|
||||
* GPIO18: output
|
||||
* GPIO19: output
|
||||
* GPIO4: input, pulled up, interrupt from rising edge and falling edge
|
||||
* GPIO5: input, pulled up, interrupt from rising edge.
|
||||
|
||||
## Test:
|
||||
* Connect GPIO18 with GPIO4
|
||||
* Connect GPIO19 with GPIO5
|
||||
* Generate pulses on GPIO18/19, that triggers interrupt on GPIO4/5
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "gpio_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
@@ -0,0 +1,114 @@
|
||||
/* GPIO 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
/**
|
||||
* Brief:
|
||||
* This test code shows how to configure gpio and how to use gpio interrupt.
|
||||
*
|
||||
* GPIO status:
|
||||
* GPIO18: output
|
||||
* GPIO19: output
|
||||
* GPIO4: input, pulled up, interrupt from rising edge and falling edge
|
||||
* GPIO5: input, pulled up, interrupt from rising edge.
|
||||
*
|
||||
* Test:
|
||||
* Connect GPIO18 with GPIO4
|
||||
* Connect GPIO19 with GPIO5
|
||||
* Generate pulses on GPIO18/19, that triggers interrupt on GPIO4/5
|
||||
*
|
||||
*/
|
||||
|
||||
#define GPIO_OUTPUT_IO_0 18
|
||||
#define GPIO_OUTPUT_IO_1 19
|
||||
#define GPIO_OUTPUT_PIN_SEL ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1))
|
||||
#define GPIO_INPUT_IO_0 4
|
||||
#define GPIO_INPUT_IO_1 5
|
||||
#define GPIO_INPUT_PIN_SEL ((1ULL<<GPIO_INPUT_IO_0) | (1ULL<<GPIO_INPUT_IO_1))
|
||||
#define ESP_INTR_FLAG_DEFAULT 0
|
||||
|
||||
static xQueueHandle gpio_evt_queue = NULL;
|
||||
|
||||
static void IRAM_ATTR gpio_isr_handler(void* arg)
|
||||
{
|
||||
uint32_t gpio_num = (uint32_t) arg;
|
||||
xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
|
||||
}
|
||||
|
||||
static void gpio_task_example(void* arg)
|
||||
{
|
||||
uint32_t io_num;
|
||||
for(;;) {
|
||||
if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
|
||||
printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
gpio_config_t io_conf;
|
||||
//disable interrupt
|
||||
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
|
||||
//set as output mode
|
||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||
//bit mask of the pins that you want to set,e.g.GPIO18/19
|
||||
io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
|
||||
//disable pull-down mode
|
||||
io_conf.pull_down_en = 0;
|
||||
//disable pull-up mode
|
||||
io_conf.pull_up_en = 0;
|
||||
//configure GPIO with the given settings
|
||||
gpio_config(&io_conf);
|
||||
|
||||
//interrupt of rising edge
|
||||
io_conf.intr_type = GPIO_PIN_INTR_POSEDGE;
|
||||
//bit mask of the pins, use GPIO4/5 here
|
||||
io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
|
||||
//set as input mode
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
//enable pull-up mode
|
||||
io_conf.pull_up_en = 1;
|
||||
gpio_config(&io_conf);
|
||||
|
||||
//change gpio intrrupt type for one pin
|
||||
gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_ANYEDGE);
|
||||
|
||||
//create a queue to handle gpio event from isr
|
||||
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
|
||||
//start gpio task
|
||||
xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);
|
||||
|
||||
//install gpio isr service
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
|
||||
//hook isr handler for specific gpio pin
|
||||
gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
|
||||
//hook isr handler for specific gpio pin
|
||||
gpio_isr_handler_add(GPIO_INPUT_IO_1, gpio_isr_handler, (void*) GPIO_INPUT_IO_1);
|
||||
|
||||
//remove isr handler for gpio number.
|
||||
gpio_isr_handler_remove(GPIO_INPUT_IO_0);
|
||||
//hook isr handler for specific gpio pin again
|
||||
gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
|
||||
|
||||
int cnt = 0;
|
||||
while(1) {
|
||||
printf("cnt: %d\n", cnt++);
|
||||
vTaskDelay(1000 / portTICK_RATE_MS);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_0, cnt % 2);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_1, cnt % 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(i2c-example)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := i2c-example
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
# I2C Self-Test Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
## Overview
|
||||
|
||||
This example demonstrates basic usage of I2C driver by running two tasks on I2C bus:
|
||||
|
||||
1. Read external I2C sensor, here we take the BH1750 ambient light sensor (GY-30 module) for an example.
|
||||
2. Use one of ESP32’s I2C port (master mode) to read and write another I2C port (slave mode) in ESP32.
|
||||
|
||||
If you have a new I2C application to go (for example, read the temperature data from external sensor with I2C interface), try this as a basic template, then add your own code.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
To run this example, you should have one ESP32 dev board (e.g. ESP32-WROVER Kit) or ESP32 core board (e.g. ESP32-DevKitC). Optionally, you can also connect an external sensor, here we choose the BH1750 just for an example. BH1750 is a digital ambient light sensor, for more information about it, you can read the [PDF](http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1721fvc-e.pdf) of this sensor.
|
||||
|
||||
#### Pin Assignment:
|
||||
|
||||
**Note:** The following pin assignments are used by default, yout can change these in the `menuconfig` .
|
||||
|
||||
| | SDA | SCL |
|
||||
| ---------------- | ------ | ------ |
|
||||
| ESP32 I2C Master | GPIO18 | GPIO19 |
|
||||
| ESP32 I2C Slave | GPIO4 | GPIO5 |
|
||||
| BH1750 Sensor | SDA | SCL |
|
||||
|
||||
- slave:
|
||||
- GPIO4 is assigned as the data signal of I2C slave port
|
||||
- GPIO5 is assigned as the clock signal of I2C slave port
|
||||
- master:
|
||||
- GPIO18 is assigned as the data signal of I2C master port
|
||||
- GPIO19 is assigned as the clock signal of I2C master port
|
||||
|
||||
- Connection:
|
||||
- connect GPIO18 with GPIO4
|
||||
- connect GPIO19 with GPIO5
|
||||
- connect SDA/SCL of BH1750 sensor with GPIO18/GPIO19
|
||||
|
||||
**Note: ** There’s no need to add an external pull-up resistors for SDA/SCL pin, because the driver will enable the internal pull-up resistors.
|
||||
|
||||
### Configure the project
|
||||
|
||||
Open the project configuration menu (`idf.py menuconfig`). Then go into `Example Configuration` menu.
|
||||
|
||||
- In the `I2C Master` submenu, you can set the pin number of SDA/SCL according to your board. Also you can modify the I2C port number and freauency of the master.
|
||||
- In the `I2C Slave` submenu, you can set the pin number of SDA/SCL according to your board. Also you can modify the I2C port number and address of the slave.
|
||||
- In the `BH1750 Sensor` submenu, you can choose the slave address of BH1750 accroding to the pin level of ADDR pin (if the pin level of ADDR is low then the address is `0x23`, otherwise it is `0x5c`). Here you can also control the operation mode of BH1750, each mode has a different resolution and measurement time. For example, in the `One Time L-Resolution` mode, the resolution is 4 Lux and measurement time is typically 16ms (higher resolution means longer measurement time). For more information, you can consult the datasheet of BH1750.
|
||||
|
||||
|
||||
### 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
|
||||
|
||||
```bash
|
||||
I (6495) i2c-example: TASK[1] test cnt: 1
|
||||
*******************
|
||||
TASK[1] MASTER READ SENSOR( BH1750 )
|
||||
*******************
|
||||
data_h: 01
|
||||
data_l: d0
|
||||
sensor val: 386.67 [Lux]
|
||||
I (6695) i2c-example: TASK[0] test cnt: 2
|
||||
*******************
|
||||
TASK[0] MASTER READ SENSOR( BH1750 )
|
||||
*******************
|
||||
data_h: 01
|
||||
data_l: d0
|
||||
sensor val: 386.67 [Lux]
|
||||
*******************
|
||||
TASK[0] MASTER READ FROM SLAVE
|
||||
*******************
|
||||
====TASK[0] Slave buffer data ====
|
||||
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
|
||||
10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
|
||||
20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
|
||||
30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
|
||||
40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
|
||||
50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
|
||||
60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
|
||||
70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f
|
||||
|
||||
====TASK[0] Master read ====
|
||||
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
|
||||
10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
|
||||
20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
|
||||
30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
|
||||
40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
|
||||
50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
|
||||
60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
|
||||
70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f
|
||||
|
||||
*******************
|
||||
TASK[1] MASTER READ FROM SLAVE
|
||||
*******************
|
||||
====TASK[1] Slave buffer data ====
|
||||
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
|
||||
10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
|
||||
20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
|
||||
30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
|
||||
40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
|
||||
50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
|
||||
60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
|
||||
70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f
|
||||
|
||||
====TASK[1] Master read ====
|
||||
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
|
||||
10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
|
||||
20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
|
||||
30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
|
||||
40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
|
||||
50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
|
||||
60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
|
||||
70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f
|
||||
|
||||
*******************
|
||||
TASK[0] MASTER WRITE TO SLAVE
|
||||
*******************
|
||||
----TASK[0] Master write ----
|
||||
0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19
|
||||
1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29
|
||||
2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39
|
||||
3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49
|
||||
4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59
|
||||
5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69
|
||||
6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79
|
||||
7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89
|
||||
|
||||
----TASK[0] Slave read: [128] bytes ----
|
||||
0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19
|
||||
1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29
|
||||
2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39
|
||||
3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49
|
||||
4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59
|
||||
5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69
|
||||
6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79
|
||||
7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- BH1750 has two I2C address, which is decided by the voltage level of `ADDR` pin at start up. Make sure to check your schemetic before run this example.
|
||||
|
||||
(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.)
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "i2c_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,124 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
menu "I2C Master"
|
||||
config I2C_MASTER_SCL
|
||||
int "SCL GPIO Num"
|
||||
default 19
|
||||
help
|
||||
GPIO number for I2C Master clock line.
|
||||
|
||||
config I2C_MASTER_SDA
|
||||
int "SDA GPIO Num"
|
||||
default 18
|
||||
help
|
||||
GPIO number for I2C Master data line.
|
||||
|
||||
config I2C_MASTER_PORT_NUM
|
||||
int "Port Number"
|
||||
default 1
|
||||
help
|
||||
Port number for I2C Master device.
|
||||
|
||||
config I2C_MASTER_FREQUENCY
|
||||
int "Master Frequency"
|
||||
default 100000
|
||||
help
|
||||
I2C Speed of Master device.
|
||||
endmenu
|
||||
|
||||
menu "I2C Slave"
|
||||
config I2C_SLAVE_SCL
|
||||
int "SCL GPIO Num"
|
||||
default 5
|
||||
help
|
||||
GPIO number for I2C Slave clock line.
|
||||
|
||||
config I2C_SLAVE_SDA
|
||||
int "SDA GPIO Num"
|
||||
default 4
|
||||
help
|
||||
GPIO number for I2C Slave data line.
|
||||
|
||||
config I2C_SLAVE_PORT_NUM
|
||||
int "Port Number"
|
||||
default 0
|
||||
help
|
||||
Port number for I2C Slave device.
|
||||
|
||||
config I2C_SLAVE_ADDRESS
|
||||
hex "ESP Slave Address"
|
||||
default 0x28
|
||||
help
|
||||
Hardware Address of I2C Slave Port.
|
||||
endmenu
|
||||
|
||||
menu "BH1750 Sensor"
|
||||
choice BH1750_ADDR
|
||||
prompt "BH1750 I2C Address"
|
||||
default BH1750_I2C_ADDRESS_LOW
|
||||
help
|
||||
Hardware address of BH1750, which is 2 types, and determined by ADDR terminal.
|
||||
|
||||
config BH1750_I2C_ADDRESS_LOW
|
||||
bool "BH1750 I2C Address(ADDR=0)"
|
||||
help
|
||||
I2C Address of BH1750 Sensor according to your schemetic configuration.
|
||||
|
||||
config BH1750_I2C_ADDRESS_High
|
||||
bool "BH1750 I2C Address(ADDR=1)"
|
||||
help
|
||||
I2C Address of BH1750 Sensor according to your schemetic configuration.
|
||||
endchoice
|
||||
|
||||
config BH1750_ADDR
|
||||
hex
|
||||
default 0x5C if BH1750_I2C_ADDRESS_High
|
||||
default 0x23 if BH1750_I2C_ADDRESS_LOW
|
||||
|
||||
choice BH1750_MODE
|
||||
prompt "BH1750 Operation Mode"
|
||||
default BH1750_ONETIME_L_RESOLUTION
|
||||
help
|
||||
Operation Mode of BH1750.
|
||||
Different mode means different resolution and measurement time.
|
||||
config BH1750_CONTINU_H_RESOLUTION
|
||||
bool "Continuously H-Resolution Mode"
|
||||
help
|
||||
Resolution is 1lx, measurement time is typically 120ms.
|
||||
config BH1750_CONTINU_H_RESOLUTION2
|
||||
bool "Continuously H-Resolution Mode2"
|
||||
help
|
||||
Resolution is 0.5lx, measurement time is typically 120ms.
|
||||
config BH1750_CONTINU_L_RESOLUTION
|
||||
bool "Continuously L-Resolution Mode"
|
||||
help
|
||||
Resolution is 4lx, measurement time is typically 16ms.
|
||||
config BH1750_ONETIME_H_RESOLUTION
|
||||
bool "One Time H-Resolution Mode"
|
||||
help
|
||||
Resolution is 1lx, measurement time is typically 120ms.
|
||||
It is automatically set to Power Down mode after measurement.
|
||||
config BH1750_ONETIME_H_RESOLUTION2
|
||||
bool "One Time H-Resolution Mode2"
|
||||
help
|
||||
Resolution is 0.5lx, measurement time is typically 120ms.
|
||||
It is automatically set to Power Down mode after measurement.
|
||||
config BH1750_ONETIME_L_RESOLUTION
|
||||
bool "One Time L-Resolution Mode"
|
||||
help
|
||||
Resolution is 4lx, measurement time is typically 16ms.
|
||||
It is automatically set to Power Down mode after measurement.
|
||||
endchoice
|
||||
|
||||
config BH1750_OPMODE
|
||||
hex
|
||||
default 0x10 if BH1750_CONTINU_H_RESOLUTION
|
||||
default 0x11 if BH1750_CONTINU_H_RESOLUTION2
|
||||
default 0x13 if BH1750_CONTINU_L_RESOLUTION
|
||||
default 0x20 if BH1750_ONETIME_H_RESOLUTION
|
||||
default 0x21 if BH1750_ONETIME_H_RESOLUTION2
|
||||
default 0x23 if BH1750_ONETIME_L_RESOLUTION
|
||||
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
@@ -0,0 +1,287 @@
|
||||
/* i2c - Example
|
||||
|
||||
For other examples please check:
|
||||
https://github.com/espressif/esp-idf/tree/master/examples
|
||||
|
||||
See README.md file to get detailed usage of this 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 "esp_log.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static const char *TAG = "i2c-example";
|
||||
|
||||
#define _I2C_NUMBER(num) I2C_NUM_##num
|
||||
#define I2C_NUMBER(num) _I2C_NUMBER(num)
|
||||
|
||||
#define DATA_LENGTH 512 /*!< Data buffer length of test buffer */
|
||||
#define RW_TEST_LENGTH 128 /*!< Data length for r/w test, [0,DATA_LENGTH] */
|
||||
#define DELAY_TIME_BETWEEN_ITEMS_MS 1000 /*!< delay time between different test items */
|
||||
|
||||
#define I2C_SLAVE_SCL_IO CONFIG_I2C_SLAVE_SCL /*!< gpio number for i2c slave clock */
|
||||
#define I2C_SLAVE_SDA_IO CONFIG_I2C_SLAVE_SDA /*!< gpio number for i2c slave data */
|
||||
#define I2C_SLAVE_NUM I2C_NUMBER(CONFIG_I2C_SLAVE_PORT_NUM) /*!< I2C port number for slave dev */
|
||||
#define I2C_SLAVE_TX_BUF_LEN (2 * DATA_LENGTH) /*!< I2C slave tx buffer size */
|
||||
#define I2C_SLAVE_RX_BUF_LEN (2 * DATA_LENGTH) /*!< I2C slave rx buffer size */
|
||||
|
||||
#define I2C_MASTER_SCL_IO CONFIG_I2C_MASTER_SCL /*!< gpio number for I2C master clock */
|
||||
#define I2C_MASTER_SDA_IO CONFIG_I2C_MASTER_SDA /*!< gpio number for I2C master data */
|
||||
#define I2C_MASTER_NUM I2C_NUMBER(CONFIG_I2C_MASTER_PORT_NUM) /*!< I2C port number for master dev */
|
||||
#define I2C_MASTER_FREQ_HZ CONFIG_I2C_MASTER_FREQUENCY /*!< I2C master clock frequency */
|
||||
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
|
||||
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
|
||||
|
||||
#define BH1750_SENSOR_ADDR CONFIG_BH1750_ADDR /*!< slave address for BH1750 sensor */
|
||||
#define BH1750_CMD_START CONFIG_BH1750_OPMODE /*!< Operation mode */
|
||||
#define ESP_SLAVE_ADDR CONFIG_I2C_SLAVE_ADDRESS /*!< ESP32 slave address, you can set any 7bit value */
|
||||
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
|
||||
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
|
||||
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
|
||||
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
|
||||
#define ACK_VAL 0x0 /*!< I2C ack value */
|
||||
#define NACK_VAL 0x1 /*!< I2C nack value */
|
||||
|
||||
SemaphoreHandle_t print_mux = NULL;
|
||||
|
||||
/**
|
||||
* @brief test code to read esp-i2c-slave
|
||||
* We need to fill the buffer of esp slave device, then master can read them out.
|
||||
*
|
||||
* _______________________________________________________________________________________
|
||||
* | start | slave_addr + rd_bit +ack | read n-1 bytes + ack | read 1 byte + nack | stop |
|
||||
* --------|--------------------------|----------------------|--------------------|------|
|
||||
*
|
||||
*/
|
||||
static esp_err_t i2c_master_read_slave(i2c_port_t i2c_num, uint8_t *data_rd, size_t size)
|
||||
{
|
||||
if (size == 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (ESP_SLAVE_ADDR << 1) | READ_BIT, ACK_CHECK_EN);
|
||||
if (size > 1) {
|
||||
i2c_master_read(cmd, data_rd, size - 1, ACK_VAL);
|
||||
}
|
||||
i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL);
|
||||
i2c_master_stop(cmd);
|
||||
esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test code to write esp-i2c-slave
|
||||
* Master device write data to slave(both esp32),
|
||||
* the data will be stored in slave buffer.
|
||||
* We can read them out from slave buffer.
|
||||
*
|
||||
* ___________________________________________________________________
|
||||
* | start | slave_addr + wr_bit + ack | write n bytes + ack | stop |
|
||||
* --------|---------------------------|----------------------|------|
|
||||
*
|
||||
*/
|
||||
static esp_err_t i2c_master_write_slave(i2c_port_t i2c_num, uint8_t *data_wr, size_t size)
|
||||
{
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (ESP_SLAVE_ADDR << 1) | WRITE_BIT, ACK_CHECK_EN);
|
||||
i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief test code to operate on BH1750 sensor
|
||||
*
|
||||
* 1. set operation mode(e.g One time L-resolution mode)
|
||||
* _________________________________________________________________
|
||||
* | start | slave_addr + wr_bit + ack | write 1 byte + ack | stop |
|
||||
* --------|---------------------------|---------------------|------|
|
||||
* 2. wait more than 24 ms
|
||||
* 3. read data
|
||||
* ______________________________________________________________________________________
|
||||
* | start | slave_addr + rd_bit + ack | read 1 byte + ack | read 1 byte + nack | stop |
|
||||
* --------|---------------------------|--------------------|--------------------|------|
|
||||
*/
|
||||
static esp_err_t i2c_master_sensor_test(i2c_port_t i2c_num, uint8_t *data_h, uint8_t *data_l)
|
||||
{
|
||||
int ret;
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, BH1750_SENSOR_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, BH1750_CMD_START, ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
vTaskDelay(30 / portTICK_RATE_MS);
|
||||
cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, BH1750_SENSOR_ADDR << 1 | READ_BIT, ACK_CHECK_EN);
|
||||
i2c_master_read_byte(cmd, data_h, ACK_VAL);
|
||||
i2c_master_read_byte(cmd, data_l, NACK_VAL);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief i2c master initialization
|
||||
*/
|
||||
static esp_err_t i2c_master_init(void)
|
||||
{
|
||||
int i2c_master_port = I2C_MASTER_NUM;
|
||||
i2c_config_t conf;
|
||||
conf.mode = I2C_MODE_MASTER;
|
||||
conf.sda_io_num = I2C_MASTER_SDA_IO;
|
||||
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
conf.scl_io_num = I2C_MASTER_SCL_IO;
|
||||
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
conf.master.clk_speed = I2C_MASTER_FREQ_HZ;
|
||||
i2c_param_config(i2c_master_port, &conf);
|
||||
return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief i2c slave initialization
|
||||
*/
|
||||
static esp_err_t i2c_slave_init(void)
|
||||
{
|
||||
int i2c_slave_port = I2C_SLAVE_NUM;
|
||||
i2c_config_t conf_slave;
|
||||
conf_slave.sda_io_num = I2C_SLAVE_SDA_IO;
|
||||
conf_slave.sda_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
conf_slave.scl_io_num = I2C_SLAVE_SCL_IO;
|
||||
conf_slave.scl_pullup_en = GPIO_PULLUP_ENABLE;
|
||||
conf_slave.mode = I2C_MODE_SLAVE;
|
||||
conf_slave.slave.addr_10bit_en = 0;
|
||||
conf_slave.slave.slave_addr = ESP_SLAVE_ADDR;
|
||||
i2c_param_config(i2c_slave_port, &conf_slave);
|
||||
return i2c_driver_install(i2c_slave_port, conf_slave.mode, I2C_SLAVE_RX_BUF_LEN, I2C_SLAVE_TX_BUF_LEN, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief test function to show buffer
|
||||
*/
|
||||
static void disp_buf(uint8_t *buf, int len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
printf("%02x ", buf[i]);
|
||||
if ((i + 1) % 16 == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void i2c_test_task(void *arg)
|
||||
{
|
||||
int i = 0;
|
||||
int ret;
|
||||
uint32_t task_idx = (uint32_t)arg;
|
||||
uint8_t *data = (uint8_t *)malloc(DATA_LENGTH);
|
||||
uint8_t *data_wr = (uint8_t *)malloc(DATA_LENGTH);
|
||||
uint8_t *data_rd = (uint8_t *)malloc(DATA_LENGTH);
|
||||
uint8_t sensor_data_h, sensor_data_l;
|
||||
int cnt = 0;
|
||||
while (1) {
|
||||
ESP_LOGI(TAG, "TASK[%d] test cnt: %d", task_idx, cnt++);
|
||||
ret = i2c_master_sensor_test(I2C_MASTER_NUM, &sensor_data_h, &sensor_data_l);
|
||||
xSemaphoreTake(print_mux, portMAX_DELAY);
|
||||
if (ret == ESP_ERR_TIMEOUT) {
|
||||
ESP_LOGE(TAG, "I2C Timeout");
|
||||
} else if (ret == ESP_OK) {
|
||||
printf("*******************\n");
|
||||
printf("TASK[%d] MASTER READ SENSOR( BH1750 )\n", task_idx);
|
||||
printf("*******************\n");
|
||||
printf("data_h: %02x\n", sensor_data_h);
|
||||
printf("data_l: %02x\n", sensor_data_l);
|
||||
printf("sensor val: %.02f [Lux]\n", (sensor_data_h << 8 | sensor_data_l) / 1.2);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "%s: No ack, sensor not connected...skip...", esp_err_to_name(ret));
|
||||
}
|
||||
xSemaphoreGive(print_mux);
|
||||
vTaskDelay((DELAY_TIME_BETWEEN_ITEMS_MS * (task_idx + 1)) / portTICK_RATE_MS);
|
||||
//---------------------------------------------------
|
||||
for (i = 0; i < DATA_LENGTH; i++) {
|
||||
data[i] = i;
|
||||
}
|
||||
xSemaphoreTake(print_mux, portMAX_DELAY);
|
||||
size_t d_size = i2c_slave_write_buffer(I2C_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS);
|
||||
if (d_size == 0) {
|
||||
ESP_LOGW(TAG, "i2c slave tx buffer full");
|
||||
ret = i2c_master_read_slave(I2C_MASTER_NUM, data_rd, DATA_LENGTH);
|
||||
} else {
|
||||
ret = i2c_master_read_slave(I2C_MASTER_NUM, data_rd, RW_TEST_LENGTH);
|
||||
}
|
||||
|
||||
if (ret == ESP_ERR_TIMEOUT) {
|
||||
ESP_LOGE(TAG, "I2C Timeout");
|
||||
} else if (ret == ESP_OK) {
|
||||
printf("*******************\n");
|
||||
printf("TASK[%d] MASTER READ FROM SLAVE\n", task_idx);
|
||||
printf("*******************\n");
|
||||
printf("====TASK[%d] Slave buffer data ====\n", task_idx);
|
||||
disp_buf(data, d_size);
|
||||
printf("====TASK[%d] Master read ====\n", task_idx);
|
||||
disp_buf(data_rd, d_size);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "TASK[%d] %s: Master read slave error, IO not connected...\n",
|
||||
task_idx, esp_err_to_name(ret));
|
||||
}
|
||||
xSemaphoreGive(print_mux);
|
||||
vTaskDelay((DELAY_TIME_BETWEEN_ITEMS_MS * (task_idx + 1)) / portTICK_RATE_MS);
|
||||
//---------------------------------------------------
|
||||
int size;
|
||||
for (i = 0; i < DATA_LENGTH; i++) {
|
||||
data_wr[i] = i + 10;
|
||||
}
|
||||
xSemaphoreTake(print_mux, portMAX_DELAY);
|
||||
//we need to fill the slave buffer so that master can read later
|
||||
ret = i2c_master_write_slave(I2C_MASTER_NUM, data_wr, RW_TEST_LENGTH);
|
||||
if (ret == ESP_OK) {
|
||||
size = i2c_slave_read_buffer(I2C_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS);
|
||||
}
|
||||
if (ret == ESP_ERR_TIMEOUT) {
|
||||
ESP_LOGE(TAG, "I2C Timeout");
|
||||
} else if (ret == ESP_OK) {
|
||||
printf("*******************\n");
|
||||
printf("TASK[%d] MASTER WRITE TO SLAVE\n", task_idx);
|
||||
printf("*******************\n");
|
||||
printf("----TASK[%d] Master write ----\n", task_idx);
|
||||
disp_buf(data_wr, RW_TEST_LENGTH);
|
||||
printf("----TASK[%d] Slave read: [%d] bytes ----\n", task_idx, size);
|
||||
disp_buf(data, size);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "TASK[%d] %s: Master write slave error, IO not connected....\n",
|
||||
task_idx, esp_err_to_name(ret));
|
||||
}
|
||||
xSemaphoreGive(print_mux);
|
||||
vTaskDelay((DELAY_TIME_BETWEEN_ITEMS_MS * (task_idx + 1)) / portTICK_RATE_MS);
|
||||
}
|
||||
vSemaphoreDelete(print_mux);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
print_mux = xSemaphoreCreateMutex();
|
||||
ESP_ERROR_CHECK(i2c_slave_init());
|
||||
ESP_ERROR_CHECK(i2c_master_init());
|
||||
xTaskCreate(i2c_test_task, "i2c_test_task_0", 1024 * 2, (void *)0, 10, NULL);
|
||||
xTaskCreate(i2c_test_task, "i2c_test_task_1", 1024 * 2, (void *)1, 10, NULL);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# 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.5)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/system/console/components)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(i2c-tools)
|
||||
@@ -0,0 +1,11 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := i2c-tools
|
||||
|
||||
EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/system/console/components
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# I2C Tools Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
## Overview
|
||||
|
||||
[I2C Tools](https://i2c.wiki.kernel.org/index.php/I2C_Tools) is a simple but very useful tool for developing I2C related applications, which is also famous in Linux platform. This example just implements some of basic features of [I2C Tools](https://i2c.wiki.kernel.org/index.php/I2C_Tools) based on [esp32 console component](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/console.html). As follows, this example supports five command-line tools:
|
||||
|
||||
1. `i2cconfig`: It will configure the I2C bus with specific GPIO number, port number and frequency.
|
||||
2. `i2cdetect`: It will scan an I2C bus for devices and output a table with the list of detected devices on the bus.
|
||||
3. `i2cget`: It will read registers visible through the I2C bus.
|
||||
4. `i2cset`: It will set registers visible through the I2C bus.
|
||||
5. `i2cdump`: It will examine registers visible through the I2C bus.
|
||||
|
||||
If you have some trouble in developing I2C related applications, or just want to test some functions of one I2C device, you can play with this example first.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
To run this example, you should have one ESP32 dev board (e.g. ESP32-WROVER Kit) or ESP32 core board (e.g. ESP32-DevKitC). For test purpose, you should have a kind of device with I2C interface as well. Here we will take the CCS811 sensor as an example to show how to test the function of this sensor without writing any code (just use the command-line tools supported by this example). For more information about CCS811, you can consult the [online datasheet](http://ams.com/ccs811).
|
||||
|
||||
#### Pin Assignment:
|
||||
|
||||
**Note:** The following pin assignments are used by default, you can change them with `i2cconfig` command at any time.
|
||||
|
||||
| | SDA | SCL | GND | Other | VCC |
|
||||
| ---------------- | ------ | ------ | ---- | ----- | ---- |
|
||||
| ESP32 I2C Master | GPIO18 | GPIO19 | GND | GND | 3.3V |
|
||||
| Sensor | SDA | SCL | GND | WAK | VCC |
|
||||
|
||||
**Note: ** There’s no need to add an external pull-up resistors for SDA/SCL pin, because the driver will enable the internal pull-up resistors itself.
|
||||
|
||||
### Configure the project
|
||||
|
||||
Open the project configuration menu (`idf.py menuconfig`). Then go into `Example Configuration` menu.
|
||||
|
||||
- You can choose whether or not to save command history into flash in `Store command history in flash` option.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT flash monitor` to build and flash 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
|
||||
|
||||
### Check all supported commands and their usages
|
||||
|
||||
```bash
|
||||
==============================================================
|
||||
| Steps to Use i2c-tools on ESP32 |
|
||||
| |
|
||||
| 1. Try 'help', check all supported commands |
|
||||
| 2. Try 'i2cconfig' to configure your I2C bus |
|
||||
| 3. Try 'i2cdetect' to scan devices on the bus |
|
||||
| 4. Try 'i2cget' to get the content of specific register |
|
||||
| 5. Try 'i2cset' to set the value of specific register |
|
||||
| 6. Try 'i2cdump' to dump all the register (Experiment) |
|
||||
| |
|
||||
==============================================================
|
||||
|
||||
esp32> help
|
||||
help
|
||||
Print the list of registered commands
|
||||
|
||||
i2cconfig [--port=<0|1>] [--freq=<Hz>] --sda=<gpio> --scl=<gpio>
|
||||
Config I2C bus
|
||||
--port=<0|1> Set the I2C bus port number
|
||||
--freq=<Hz> Set the frequency(Hz) of I2C bus
|
||||
--sda=<gpio> Set the gpio for I2C SDA
|
||||
--scl=<gpio> Set the gpio for I2C SCL
|
||||
|
||||
i2cdetect
|
||||
Scan I2C bus for devices
|
||||
|
||||
i2cget -c <chip_addr> [-r <register_addr>] [-l <length>]
|
||||
Read registers visible through the I2C bus
|
||||
-c, --chip=<chip_addr> Specify the address of the chip on that bus
|
||||
-r, --register=<register_addr> Specify the address on that chip to read from
|
||||
-l, --length=<length> Specify the length to read from that data address
|
||||
|
||||
i2cset -c <chip_addr> [-r <register_addr>] [<data>]...
|
||||
Set registers visible through the I2C bus
|
||||
-c, --chip=<chip_addr> Specify the address of the chip on that bus
|
||||
-r, --register=<register_addr> Specify the address on that chip to read from
|
||||
<data> Specify the data to write to that data address
|
||||
|
||||
i2cdump -c <chip_addr> [-s <size>]
|
||||
Examine registers visible through the I2C bus
|
||||
-c, --chip=<chip_addr> Specify the address of the chip on that bus
|
||||
-s, --size=<size> Specify the size of each read
|
||||
|
||||
free
|
||||
Get the current size of free heap memory
|
||||
|
||||
heap
|
||||
Get minimum size of free heap memory that was available during program execu
|
||||
tion
|
||||
|
||||
version
|
||||
Get version of chip and SDK
|
||||
|
||||
restart
|
||||
Software reset of the chip
|
||||
|
||||
deep_sleep [-t <t>] [--io=<n>] [--io_level=<0|1>]
|
||||
Enter deep sleep mode. Two wakeup modes are supported: timer and GPIO. If no
|
||||
wakeup option is specified, will sleep indefinitely.
|
||||
-t, --time=<t> Wake up time, ms
|
||||
--io=<n> If specified, wakeup using GPIO with given number
|
||||
--io_level=<0|1> GPIO level to trigger wakeup
|
||||
|
||||
light_sleep [-t <t>] [--io=<n>]... [--io_level=<0|1>]...
|
||||
Enter light sleep mode. Two wakeup modes are supported: timer and GPIO. Mult
|
||||
iple GPIO pins can be specified using pairs of 'io' and 'io_level' arguments
|
||||
. Will also wake up on UART input.
|
||||
-t, --time=<t> Wake up time, ms
|
||||
--io=<n> If specified, wakeup using GPIO with given number
|
||||
--io_level=<0|1> GPIO level to trigger wakeup
|
||||
|
||||
tasks
|
||||
Get information about running tasks
|
||||
```
|
||||
|
||||
### Configure the I2C bus
|
||||
|
||||
```bash
|
||||
esp32> i2cconfig --port=0 --sda=18 --scl=19 --freq=100000
|
||||
```
|
||||
|
||||
* `--port` option to specify the port of I2C, here we choose port 0 for test.
|
||||
* `--sda` and `--scl` options to specify the gpio number used by I2C bus, here we choose GPIO18 as the SDA and GPIO19 as the SCL.
|
||||
* `--freq` option to specify the frequency of I2C bus, here we set to 100KHz.
|
||||
|
||||
### Check the I2C address (7 bits) on the I2C bus
|
||||
|
||||
```bash
|
||||
esp32> i2cdetect
|
||||
0 1 2 3 4 5 6 7 8 9 a b c d e f
|
||||
00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||
50: -- -- -- -- -- -- -- -- -- -- -- 5b -- -- -- --
|
||||
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||
```
|
||||
|
||||
* Here we found the address of CCS811 is 0x5b.
|
||||
|
||||
### Get the value of status register
|
||||
|
||||
```bash
|
||||
esp32> i2cget -c 0x5b -r 0x00 -l 1
|
||||
0x10
|
||||
```
|
||||
|
||||
* `-c` option to specify the address of I2C device (acquired from `i2cdetect` command).
|
||||
* `-r` option to specify the register address you want to inspect.
|
||||
* `-l` option to specify the length of the content.
|
||||
* Here the returned value 0x10 means that the sensor is just in the boot mode and is ready to go into application mode. For more information about CCS811 you should consult the [official website](http://ams.com/ccs811).
|
||||
|
||||
### Change the working mode
|
||||
|
||||
```bash
|
||||
esp32> i2cset -c 0x5b -r 0xF4
|
||||
I (734717) cmd_i2ctools: Write OK
|
||||
esp32> i2cset -c 0x5b -r 0x01 0x10
|
||||
I (1072047) cmd_i2ctools: Write OK
|
||||
esp32> i2cget -c 0x5b -r 0x00 -l 1
|
||||
0x98
|
||||
```
|
||||
|
||||
* Here we change the mode from boot to application and set a proper measure mode (by writing 0x10 to register 0x01)
|
||||
* Now the status value of the sensor is 0x98, which means a valid data is ready to read
|
||||
|
||||
### Read the sensor data
|
||||
|
||||
```bash
|
||||
esp32> i2cget -c 0x5b -r 0x02 -l 8
|
||||
0x01 0xb0 0x00 0x04 0x98 0x00 0x19 0x8f
|
||||
```
|
||||
|
||||
* The register 0x02 will output 8 bytes result, mainly including value of eCO~2~、TVOC and there raw value. So the value of eCO~2~ is 0x01b0 ppm and value of TVOC is 0x04 ppb.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
* I don’t find any available address when running `i2cdetect` command.
|
||||
* Make sure your wiring connection is right.
|
||||
* Some sensor will have a “wake up” pin, via which user can put the sensor into a sleep mode. So make sure your sensor in **not** in the sleep state.
|
||||
* Reset you I2C device, and then run `i2cdetect` again.
|
||||
* I can’t get the right content when running `i2cdump` command.
|
||||
* Currently the `i2cdump` only support those who have the same content length of registers inside the I2C device. For example, if a device have three register addresses, and the content length at these address are 1 byte, 2 bytes and 4 bytes. In this case you should not expect this command to dump the register correctly.
|
||||
|
||||
|
||||
(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.)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import ttfw_idf
|
||||
|
||||
EXPECT_TIMEOUT = 20
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_I2C_CCS811_SENSOR')
|
||||
def test_i2ctools_example(env, extra_data):
|
||||
# Get device under test, flash and start example. "i2ctool" must be defined in EnvConfig
|
||||
dut = env.get_dut('i2ctools', 'examples/peripherals/i2c/i2c_tools', dut_class=ttfw_idf.ESP32DUT)
|
||||
dut.start_app()
|
||||
dut.expect("i2c-tools>", timeout=EXPECT_TIMEOUT)
|
||||
# Get i2c address
|
||||
dut.write("i2cdetect")
|
||||
dut.expect("5b", timeout=EXPECT_TIMEOUT)
|
||||
# Get chip ID
|
||||
dut.write("i2cget -c 0x5b -r 0x20 -l 1")
|
||||
dut.expect("0x81", timeout=EXPECT_TIMEOUT)
|
||||
# Reset sensor
|
||||
dut.write("i2cset -c 0x5b -r 0xFF 0x11 0xE5 0x72 0x8A")
|
||||
dut.expect("OK", timeout=EXPECT_TIMEOUT)
|
||||
# Get status
|
||||
dut.write("i2cget -c 0x5b -r 0x00 -l 1")
|
||||
dut.expect_any("0x10", timeout=EXPECT_TIMEOUT)
|
||||
# Change work mode
|
||||
dut.write("i2cset -c 0x5b -r 0xF4")
|
||||
dut.expect("OK", timeout=EXPECT_TIMEOUT)
|
||||
dut.write("i2cset -c 0x5b -r 0x01 0x10")
|
||||
dut.expect("OK", timeout=EXPECT_TIMEOUT)
|
||||
# Get new status
|
||||
dut.write("i2cget -c 0x5b -r 0x00 -l 1")
|
||||
dut.expect_any("0x98", "0x90", timeout=EXPECT_TIMEOUT)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_i2ctools_example()
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "i2ctools_example_main.c"
|
||||
"cmd_i2ctools.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,10 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_STORE_HISTORY
|
||||
bool "Store command history in flash"
|
||||
default y
|
||||
help
|
||||
Linenoise line editing library provides functions to save and load
|
||||
command history. If this option is enabled, initalizes a FAT filesystem
|
||||
and uses it to store command history.
|
||||
endmenu
|
||||
@@ -0,0 +1,409 @@
|
||||
/* cmd_i2ctools.c
|
||||
|
||||
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 "argtable3/argtable3.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "esp_console.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
|
||||
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
|
||||
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
|
||||
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
|
||||
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
|
||||
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
|
||||
#define ACK_VAL 0x0 /*!< I2C ack value */
|
||||
#define NACK_VAL 0x1 /*!< I2C nack value */
|
||||
|
||||
static const char *TAG = "cmd_i2ctools";
|
||||
|
||||
static gpio_num_t i2c_gpio_sda = 18;
|
||||
static gpio_num_t i2c_gpio_scl = 19;
|
||||
static uint32_t i2c_frequency = 100000;
|
||||
static i2c_port_t i2c_port = I2C_NUM_0;
|
||||
|
||||
static esp_err_t i2c_get_port(int port, i2c_port_t *i2c_port)
|
||||
{
|
||||
if (port >= I2C_NUM_MAX) {
|
||||
ESP_LOGE(TAG, "Wrong port number: %d", port);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
switch (port) {
|
||||
case 0:
|
||||
*i2c_port = I2C_NUM_0;
|
||||
break;
|
||||
case 1:
|
||||
*i2c_port = I2C_NUM_1;
|
||||
break;
|
||||
default:
|
||||
*i2c_port = I2C_NUM_0;
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_master_driver_initialize(void)
|
||||
{
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = i2c_gpio_sda,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = i2c_gpio_scl,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = i2c_frequency
|
||||
};
|
||||
return i2c_param_config(i2c_port, &conf);
|
||||
}
|
||||
|
||||
static struct {
|
||||
struct arg_int *port;
|
||||
struct arg_int *freq;
|
||||
struct arg_int *sda;
|
||||
struct arg_int *scl;
|
||||
struct arg_end *end;
|
||||
} i2cconfig_args;
|
||||
|
||||
static int do_i2cconfig_cmd(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **)&i2cconfig_args);
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, i2cconfig_args.end, argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check "--port" option */
|
||||
if (i2cconfig_args.port->count) {
|
||||
if (i2c_get_port(i2cconfig_args.port->ival[0], &i2c_port) != ESP_OK) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/* Check "--freq" option */
|
||||
if (i2cconfig_args.freq->count) {
|
||||
i2c_frequency = i2cconfig_args.freq->ival[0];
|
||||
}
|
||||
/* Check "--sda" option */
|
||||
i2c_gpio_sda = i2cconfig_args.sda->ival[0];
|
||||
/* Check "--scl" option */
|
||||
i2c_gpio_scl = i2cconfig_args.scl->ival[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void register_i2cconfig(void)
|
||||
{
|
||||
i2cconfig_args.port = arg_int0(NULL, "port", "<0|1>", "Set the I2C bus port number");
|
||||
i2cconfig_args.freq = arg_int0(NULL, "freq", "<Hz>", "Set the frequency(Hz) of I2C bus");
|
||||
i2cconfig_args.sda = arg_int1(NULL, "sda", "<gpio>", "Set the gpio for I2C SDA");
|
||||
i2cconfig_args.scl = arg_int1(NULL, "scl", "<gpio>", "Set the gpio for I2C SCL");
|
||||
i2cconfig_args.end = arg_end(2);
|
||||
const esp_console_cmd_t i2cconfig_cmd = {
|
||||
.command = "i2cconfig",
|
||||
.help = "Config I2C bus",
|
||||
.hint = NULL,
|
||||
.func = &do_i2cconfig_cmd,
|
||||
.argtable = &i2cconfig_args
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&i2cconfig_cmd));
|
||||
}
|
||||
|
||||
static int do_i2cdetect_cmd(int argc, char **argv)
|
||||
{
|
||||
i2c_driver_install(i2c_port, I2C_MODE_MASTER, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
|
||||
i2c_master_driver_initialize();
|
||||
uint8_t address;
|
||||
printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\r\n");
|
||||
for (int i = 0; i < 128; i += 16) {
|
||||
printf("%02x: ", i);
|
||||
for (int j = 0; j < 16; j++) {
|
||||
fflush(stdout);
|
||||
address = i + j;
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (address << 1) | WRITE_BIT, ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 50 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if (ret == ESP_OK) {
|
||||
printf("%02x ", address);
|
||||
} else if (ret == ESP_ERR_TIMEOUT) {
|
||||
printf("UU ");
|
||||
} else {
|
||||
printf("-- ");
|
||||
}
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
|
||||
i2c_driver_delete(i2c_port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void register_i2cdectect(void)
|
||||
{
|
||||
const esp_console_cmd_t i2cdetect_cmd = {
|
||||
.command = "i2cdetect",
|
||||
.help = "Scan I2C bus for devices",
|
||||
.hint = NULL,
|
||||
.func = &do_i2cdetect_cmd,
|
||||
.argtable = NULL
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&i2cdetect_cmd));
|
||||
}
|
||||
|
||||
static struct {
|
||||
struct arg_int *chip_address;
|
||||
struct arg_int *register_address;
|
||||
struct arg_int *data_length;
|
||||
struct arg_end *end;
|
||||
} i2cget_args;
|
||||
|
||||
static int do_i2cget_cmd(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **)&i2cget_args);
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, i2cget_args.end, argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check chip address: "-c" option */
|
||||
int chip_addr = i2cget_args.chip_address->ival[0];
|
||||
/* Check register address: "-r" option */
|
||||
int data_addr = -1;
|
||||
if (i2cget_args.register_address->count) {
|
||||
data_addr = i2cget_args.register_address->ival[0];
|
||||
}
|
||||
/* Check data length: "-l" option */
|
||||
int len = 1;
|
||||
if (i2cget_args.data_length->count) {
|
||||
len = i2cget_args.data_length->ival[0];
|
||||
}
|
||||
uint8_t *data = malloc(len);
|
||||
|
||||
i2c_driver_install(i2c_port, I2C_MODE_MASTER, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
|
||||
i2c_master_driver_initialize();
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
if (data_addr != -1) {
|
||||
i2c_master_write_byte(cmd, chip_addr << 1 | WRITE_BIT, ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, data_addr, ACK_CHECK_EN);
|
||||
i2c_master_start(cmd);
|
||||
}
|
||||
i2c_master_write_byte(cmd, chip_addr << 1 | READ_BIT, ACK_CHECK_EN);
|
||||
if (len > 1) {
|
||||
i2c_master_read(cmd, data, len - 1, ACK_VAL);
|
||||
}
|
||||
i2c_master_read_byte(cmd, data + len - 1, NACK_VAL);
|
||||
i2c_master_stop(cmd);
|
||||
esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if (ret == ESP_OK) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
printf("0x%02x ", data[i]);
|
||||
if ((i + 1) % 16 == 0) {
|
||||
printf("\r\n");
|
||||
}
|
||||
}
|
||||
if (len % 16) {
|
||||
printf("\r\n");
|
||||
}
|
||||
} else if (ret == ESP_ERR_TIMEOUT) {
|
||||
ESP_LOGW(TAG, "Bus is busy");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Read failed");
|
||||
}
|
||||
free(data);
|
||||
i2c_driver_delete(i2c_port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void register_i2cget(void)
|
||||
{
|
||||
i2cget_args.chip_address = arg_int1("c", "chip", "<chip_addr>", "Specify the address of the chip on that bus");
|
||||
i2cget_args.register_address = arg_int0("r", "register", "<register_addr>", "Specify the address on that chip to read from");
|
||||
i2cget_args.data_length = arg_int0("l", "length", "<length>", "Specify the length to read from that data address");
|
||||
i2cget_args.end = arg_end(1);
|
||||
const esp_console_cmd_t i2cget_cmd = {
|
||||
.command = "i2cget",
|
||||
.help = "Read registers visible through the I2C bus",
|
||||
.hint = NULL,
|
||||
.func = &do_i2cget_cmd,
|
||||
.argtable = &i2cget_args
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&i2cget_cmd));
|
||||
}
|
||||
|
||||
static struct {
|
||||
struct arg_int *chip_address;
|
||||
struct arg_int *register_address;
|
||||
struct arg_int *data;
|
||||
struct arg_end *end;
|
||||
} i2cset_args;
|
||||
|
||||
static int do_i2cset_cmd(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **)&i2cset_args);
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, i2cset_args.end, argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check chip address: "-c" option */
|
||||
int chip_addr = i2cset_args.chip_address->ival[0];
|
||||
/* Check register address: "-r" option */
|
||||
int data_addr = 0;
|
||||
if (i2cset_args.register_address->count) {
|
||||
data_addr = i2cset_args.register_address->ival[0];
|
||||
}
|
||||
/* Check data: "-d" option */
|
||||
int len = i2cset_args.data->count;
|
||||
|
||||
i2c_driver_install(i2c_port, I2C_MODE_MASTER, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
|
||||
i2c_master_driver_initialize();
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, chip_addr << 1 | WRITE_BIT, ACK_CHECK_EN);
|
||||
if (i2cset_args.register_address->count) {
|
||||
i2c_master_write_byte(cmd, data_addr, ACK_CHECK_EN);
|
||||
}
|
||||
for (int i = 0; i < len; i++) {
|
||||
i2c_master_write_byte(cmd, i2cset_args.data->ival[i], ACK_CHECK_EN);
|
||||
}
|
||||
i2c_master_stop(cmd);
|
||||
esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Write OK");
|
||||
} else if (ret == ESP_ERR_TIMEOUT) {
|
||||
ESP_LOGW(TAG, "Bus is busy");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Write Failed");
|
||||
}
|
||||
i2c_driver_delete(i2c_port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void register_i2cset(void)
|
||||
{
|
||||
i2cset_args.chip_address = arg_int1("c", "chip", "<chip_addr>", "Specify the address of the chip on that bus");
|
||||
i2cset_args.register_address = arg_int0("r", "register", "<register_addr>", "Specify the address on that chip to read from");
|
||||
i2cset_args.data = arg_intn(NULL, NULL, "<data>", 0, 256, "Specify the data to write to that data address");
|
||||
i2cset_args.end = arg_end(2);
|
||||
const esp_console_cmd_t i2cset_cmd = {
|
||||
.command = "i2cset",
|
||||
.help = "Set registers visible through the I2C bus",
|
||||
.hint = NULL,
|
||||
.func = &do_i2cset_cmd,
|
||||
.argtable = &i2cset_args
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&i2cset_cmd));
|
||||
}
|
||||
|
||||
static struct {
|
||||
struct arg_int *chip_address;
|
||||
struct arg_int *size;
|
||||
struct arg_end *end;
|
||||
} i2cdump_args;
|
||||
|
||||
static int do_i2cdump_cmd(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **)&i2cdump_args);
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, i2cdump_args.end, argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check chip address: "-c" option */
|
||||
int chip_addr = i2cdump_args.chip_address->ival[0];
|
||||
/* Check read size: "-s" option */
|
||||
int size = 1;
|
||||
if (i2cdump_args.size->count) {
|
||||
size = i2cdump_args.size->ival[0];
|
||||
}
|
||||
if (size != 1 && size != 2 && size != 4) {
|
||||
ESP_LOGE(TAG, "Wrong read size. Only support 1,2,4");
|
||||
return 1;
|
||||
}
|
||||
i2c_driver_install(i2c_port, I2C_MODE_MASTER, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
|
||||
i2c_master_driver_initialize();
|
||||
uint8_t data_addr;
|
||||
uint8_t data[4];
|
||||
int32_t block[16];
|
||||
printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f"
|
||||
" 0123456789abcdef\r\n");
|
||||
for (int i = 0; i < 128; i += 16) {
|
||||
printf("%02x: ", i);
|
||||
for (int j = 0; j < 16; j += size) {
|
||||
fflush(stdout);
|
||||
data_addr = i + j;
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, chip_addr << 1 | WRITE_BIT, ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, data_addr, ACK_CHECK_EN);
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, chip_addr << 1 | READ_BIT, ACK_CHECK_EN);
|
||||
if (size > 1) {
|
||||
i2c_master_read(cmd, data, size - 1, ACK_VAL);
|
||||
}
|
||||
i2c_master_read_byte(cmd, data + size - 1, NACK_VAL);
|
||||
i2c_master_stop(cmd);
|
||||
esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 50 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
if (ret == ESP_OK) {
|
||||
for (int k = 0; k < size; k++) {
|
||||
printf("%02x ", data[k]);
|
||||
block[j + k] = data[k];
|
||||
}
|
||||
} else {
|
||||
for (int k = 0; k < size; k++) {
|
||||
printf("XX ");
|
||||
block[j + k] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf(" ");
|
||||
for (int k = 0; k < 16; k++) {
|
||||
if (block[k] < 0) {
|
||||
printf("X");
|
||||
}
|
||||
if ((block[k] & 0xff) == 0x00 || (block[k] & 0xff) == 0xff) {
|
||||
printf(".");
|
||||
} else if ((block[k] & 0xff) < 32 || (block[k] & 0xff) >= 127) {
|
||||
printf("?");
|
||||
} else {
|
||||
printf("%c", block[k] & 0xff);
|
||||
}
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
i2c_driver_delete(i2c_port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void register_i2cdump(void)
|
||||
{
|
||||
i2cdump_args.chip_address = arg_int1("c", "chip", "<chip_addr>", "Specify the address of the chip on that bus");
|
||||
i2cdump_args.size = arg_int0("s", "size", "<size>", "Specify the size of each read");
|
||||
i2cdump_args.end = arg_end(1);
|
||||
const esp_console_cmd_t i2cdump_cmd = {
|
||||
.command = "i2cdump",
|
||||
.help = "Examine registers visible through the I2C bus",
|
||||
.hint = NULL,
|
||||
.func = &do_i2cdump_cmd,
|
||||
.argtable = &i2cdump_args
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&i2cdump_cmd));
|
||||
}
|
||||
|
||||
void register_i2ctools(void)
|
||||
{
|
||||
register_i2cconfig();
|
||||
register_i2cdectect();
|
||||
register_i2cget();
|
||||
register_i2cset();
|
||||
register_i2cdump();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/* cmd_i2ctools.h
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void register_i2ctools(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
@@ -0,0 +1,71 @@
|
||||
/* i2c-tools 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 <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_console.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "cmd_system.h"
|
||||
#include "cmd_i2ctools.h"
|
||||
|
||||
static const char *TAG = "i2c-tools";
|
||||
|
||||
#if CONFIG_EXAMPLE_STORE_HISTORY
|
||||
|
||||
#define MOUNT_PATH "/data"
|
||||
#define HISTORY_PATH MOUNT_PATH "/history.txt"
|
||||
|
||||
static void initialize_filesystem(void)
|
||||
{
|
||||
static wl_handle_t wl_handle;
|
||||
const esp_vfs_fat_mount_config_t mount_config = {
|
||||
.max_files = 4,
|
||||
.format_if_mount_failed = true
|
||||
};
|
||||
esp_err_t err = esp_vfs_fat_spiflash_mount(MOUNT_PATH, "storage", &mount_config, &wl_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif // CONFIG_EXAMPLE_STORE_HISTORY
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_console_repl_t *repl = NULL;
|
||||
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
|
||||
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
|
||||
#if CONFIG_EXAMPLE_STORE_HISTORY
|
||||
initialize_filesystem();
|
||||
repl_config.history_save_path = HISTORY_PATH;
|
||||
#endif
|
||||
repl_config.prompt = "i2c-tools>";
|
||||
// init console REPL environment
|
||||
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));
|
||||
|
||||
register_i2ctools();
|
||||
register_system();
|
||||
|
||||
printf("\n ==============================================================\n");
|
||||
printf(" | Steps to Use i2c-tools |\n");
|
||||
printf(" | |\n");
|
||||
printf(" | 1. Try 'help', check all supported commands |\n");
|
||||
printf(" | 2. Try 'i2cconfig' to configure your I2C bus |\n");
|
||||
printf(" | 3. Try 'i2cdetect' to scan devices on the bus |\n");
|
||||
printf(" | 4. Try 'i2cget' to get the content of specific register |\n");
|
||||
printf(" | 5. Try 'i2cset' to set the value of specific register |\n");
|
||||
printf(" | 6. Try 'i2cdump' to dump all the register (Experiment) |\n");
|
||||
printf(" | |\n");
|
||||
printf(" ==============================================================\n\n");
|
||||
|
||||
// start console REPL
|
||||
ESP_ERROR_CHECK(esp_console_start_repl(repl));
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 1M,
|
||||
storage, data, fat, , 1M,
|
||||
|
@@ -0,0 +1,17 @@
|
||||
# Reduce bootloader log verbosity
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL=2
|
||||
|
||||
# Increase main task stack size
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=7168
|
||||
|
||||
# Enable filesystem
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
|
||||
|
||||
# Enable FreeRTOS stats formatting functions, needed for 'tasks' command
|
||||
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
|
||||
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
|
||||
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(esp32-i2s-driver-example)
|
||||
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
VERBOSE = 1
|
||||
PROJECT_NAME := esp32-i2s-driver-example
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# I2S Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
In this example, we generate a 100Hz triangle and sine wave and send it out from left and right channels at a sample rate of 36kHz through the I2S bus.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the Project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
Running this example, you will see the Bits per Sample changes every 5 seconds after you have run this example. You can use `i2s_set_clk` to change the Bits per Sample and the Sample Rate. The output log can be seen below:
|
||||
|
||||
```
|
||||
Test bits=24 free mem=293780, written data=2880
|
||||
I2S: DMA Malloc info, datalen=blocksize=480, dma_buf_count=6
|
||||
I2S: PLL_D2: Req RATE: 36000, real rate: 37878.000, BITS: 24, CLKM: 11, BCK: 8, MCLK: 13837837.838, SCLK: 1818144.000000, diva: 64, divb: 36
|
||||
|
||||
Test bits=32 free mem=292336, written data=2880
|
||||
I2S: DMA Malloc info, datalen=blocksize=480, dma_buf_count=6
|
||||
I2S: PLL_D2: Req RATE: 36000, real rate: 36764.000, BITS: 32, CLKM: 17, BCK: 4, MCLK: 9216921.692, SCLK: 2352896.000000, diva: 64, divb: 23
|
||||
|
||||
Test bits=16 free mem=293772, written data=1440
|
||||
I2S: DMA Malloc info, datalen=blocksize=240, dma_buf_count=6
|
||||
I2S: PLL_D2: Req RATE: 36000, real rate: 36764.000, BITS: 16, CLKM: 17, BCK: 8, MCLK: 9216921.692, SCLK: 1176448.000000, diva: 64, divb: 23
|
||||
```
|
||||
|
||||
If you have a logic analyzer, you can use a logic analyzer to grab online data. The following table describes the pins we use by default (Note that you can also use other pins for the same purpose).
|
||||
|
||||
| pin name| function | gpio_num |
|
||||
|:---:|:---:|:---:|
|
||||
| WS |word select| GPIO_NUM_15 |
|
||||
| SCK |continuous serial clock| GPIO_NUM_13 |
|
||||
| SD |serial data| GPIO_NUM_21 |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
* Program upload failure
|
||||
|
||||
* Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
|
||||
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
|
||||
|
||||
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "i2s_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,6 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
@@ -0,0 +1,119 @@
|
||||
/* I2S Example
|
||||
|
||||
This example code will output 100Hz sine wave and triangle wave to 2-channel of I2S driver
|
||||
Every 5 seconds, it will change bits_per_sample [16, 24, 32] for i2s data
|
||||
|
||||
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 "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/i2s.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_system.h"
|
||||
#include <math.h>
|
||||
|
||||
|
||||
#define SAMPLE_RATE (36000)
|
||||
#define I2S_NUM (0)
|
||||
#define WAVE_FREQ_HZ (100)
|
||||
#define PI (3.14159265)
|
||||
#define I2S_BCK_IO (GPIO_NUM_13)
|
||||
#define I2S_WS_IO (GPIO_NUM_15)
|
||||
#define I2S_DO_IO (GPIO_NUM_21)
|
||||
#define I2S_DI_IO (-1)
|
||||
|
||||
#define SAMPLE_PER_CYCLE (SAMPLE_RATE/WAVE_FREQ_HZ)
|
||||
|
||||
static void setup_triangle_sine_waves(int bits)
|
||||
{
|
||||
int *samples_data = malloc(((bits+8)/16)*SAMPLE_PER_CYCLE*4);
|
||||
unsigned int i, sample_val;
|
||||
double sin_float, triangle_float, triangle_step = (double) pow(2, bits) / SAMPLE_PER_CYCLE;
|
||||
size_t i2s_bytes_write = 0;
|
||||
|
||||
printf("\r\nTest bits=%d free mem=%d, written data=%d\n", bits, esp_get_free_heap_size(), ((bits+8)/16)*SAMPLE_PER_CYCLE*4);
|
||||
|
||||
triangle_float = -(pow(2, bits)/2 - 1);
|
||||
|
||||
for(i = 0; i < SAMPLE_PER_CYCLE; i++) {
|
||||
sin_float = sin(i * 2 * PI / SAMPLE_PER_CYCLE);
|
||||
if(sin_float >= 0)
|
||||
triangle_float += triangle_step;
|
||||
else
|
||||
triangle_float -= triangle_step;
|
||||
|
||||
sin_float *= (pow(2, bits)/2 - 1);
|
||||
|
||||
if (bits == 16) {
|
||||
sample_val = 0;
|
||||
sample_val += (short)triangle_float;
|
||||
sample_val = sample_val << 16;
|
||||
sample_val += (short) sin_float;
|
||||
samples_data[i] = sample_val;
|
||||
} else if (bits == 24) { //1-bytes unused
|
||||
samples_data[i*2] = ((int) triangle_float) << 8;
|
||||
samples_data[i*2 + 1] = ((int) sin_float) << 8;
|
||||
} else {
|
||||
samples_data[i*2] = ((int) triangle_float);
|
||||
samples_data[i*2 + 1] = ((int) sin_float);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
i2s_set_clk(I2S_NUM, SAMPLE_RATE, bits, 2);
|
||||
//Using push
|
||||
// for(i = 0; i < SAMPLE_PER_CYCLE; i++) {
|
||||
// if (bits == 16)
|
||||
// i2s_push_sample(0, &samples_data[i], 100);
|
||||
// else
|
||||
// i2s_push_sample(0, &samples_data[i*2], 100);
|
||||
// }
|
||||
// or write
|
||||
i2s_write(I2S_NUM, samples_data, ((bits+8)/16)*SAMPLE_PER_CYCLE*4, &i2s_bytes_write, 100);
|
||||
|
||||
free(samples_data);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
//for 36Khz sample rates, we create 100Hz sine wave, every cycle need 36000/100 = 360 samples (4-bytes or 8-bytes each sample)
|
||||
//depend on bits_per_sample
|
||||
//using 6 buffers, we need 60-samples per buffer
|
||||
//if 2-channels, 16-bit each channel, total buffer is 360*4 = 1440 bytes
|
||||
//if 2-channels, 24/32-bit each channel, total buffer is 360*8 = 2880 bytes
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX
|
||||
.sample_rate = SAMPLE_RATE,
|
||||
.bits_per_sample = 16,
|
||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels
|
||||
.communication_format = I2S_COMM_FORMAT_STAND_MSB,
|
||||
.dma_buf_count = 6,
|
||||
.dma_buf_len = 60,
|
||||
.use_apll = false,
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 //Interrupt level 1
|
||||
};
|
||||
i2s_pin_config_t pin_config = {
|
||||
.bck_io_num = I2S_BCK_IO,
|
||||
.ws_io_num = I2S_WS_IO,
|
||||
.data_out_num = I2S_DO_IO,
|
||||
.data_in_num = I2S_DI_IO //Not used
|
||||
};
|
||||
i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
|
||||
i2s_set_pin(I2S_NUM, &pin_config);
|
||||
|
||||
int test_bits = 16;
|
||||
while (1) {
|
||||
setup_triangle_sine_waves(test_bits);
|
||||
vTaskDelay(5000/portTICK_RATE_MS);
|
||||
test_bits += 8;
|
||||
if(test_bits > 32)
|
||||
test_bits = 16;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(i2s-adc-dac)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := i2s-adc-dac
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# I2S Built-in ADC/DAC Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
In this example, we configure I2S to work in I2S_ADC and I2S_DAC modes and then:
|
||||
* recording sound from ADC,
|
||||
* playing the recorded sound,
|
||||
* playing an audio file in flash via DAC.
|
||||
|
||||
#### Note:
|
||||
The `tools` directory contains `generate_audio_file.py` script for generating audio files:
|
||||
|
||||
* The script provides an example of generating audio tables from `.wav` files.
|
||||
* In this example, the `wav` file must be in 16k/16bit mono format.
|
||||
* The script will bundle the `wav` files into a single table named `audio_example_file.h`.
|
||||
* Since the ADC can only play 8-bit data, the script will scale each 16-bit value to a 8-bit value.
|
||||
* The script will covert all signed values into unsigned values because only positive values will be output by the ADC.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
* A microphone (with amplifier) and one or two speaker(s) for testing.
|
||||
|
||||
The following is the hardware connection:
|
||||
|
||||
|hardware|module|GPIO|
|
||||
|:---:|:---:|:---:|
|
||||
|Microphone|ADC1_CH0|GPIO36|
|
||||
|speaker(R)|DAC1|GPIO25|
|
||||
|speaker(L)|DAC2|GPIO26|
|
||||
|
||||
### Configure the Project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
* Set the flash size to 4 MB under Serial Flasher Options.
|
||||
* Select "Custom partition table CSV" and rename "Custom partition CSV file" to "partitions_adc_dac_example.csv".
|
||||
|
||||
(Note that you can use `sdkconfig.defaults`)
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
Reset your development board. The application it will first record the sound through the microphone. Then it will play the recorded sound, and finally a piece of audio stored in the flash. The following is the output log:
|
||||
|
||||
```
|
||||
partiton addr: 0x00110000; size: 2097152; label: storage
|
||||
Erasing flash
|
||||
partiton addr: 0x00110000; size: 2097152; label: storage
|
||||
Erase size: 323584 Bytes
|
||||
I2S: PLL_D2: Req RATE: 16000, real rate: 1004.000, BITS: 16, CLKM: 83, BCK: 60, MCLK: 83.333, SCLK: 32128.000000, diva: 64, divb: 21
|
||||
Sound recording 5%
|
||||
Sound recording 10%
|
||||
...
|
||||
Sound recording 97%
|
||||
Sound recording 102%
|
||||
playing: 0 %
|
||||
playing: 1 %
|
||||
playing: 2 %
|
||||
...
|
||||
playing: 96 %
|
||||
playing: 97 %
|
||||
playing: 98 %
|
||||
Playing file example:
|
||||
I2S: PLL_D2: Req RATE: 16000, real rate: 1004.000, BITS: 16, CLKM: 83, BCK: 60, MCLK: 83.333, SCLK: 32128.000000, diva: 64, divb: 21
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
* Program upload failure
|
||||
|
||||
* Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
|
||||
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
|
||||
|
||||
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "app_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,293 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_spi_flash.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_partition.h"
|
||||
#include "driver/i2s.h"
|
||||
#include "driver/adc.h"
|
||||
#include "audio_example_file.h"
|
||||
#include "esp_adc_cal.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
|
||||
static const char* TAG = "ad/da";
|
||||
#define V_REF 1100
|
||||
#define ADC1_TEST_CHANNEL (ADC1_CHANNEL_7)
|
||||
|
||||
#define PARTITION_NAME "storage"
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
EXAMPLE CONFIG
|
||||
---------------------------------------------------------------*/
|
||||
//enable record sound and save in flash
|
||||
#define RECORD_IN_FLASH_EN (1)
|
||||
//enable replay recorded sound in flash
|
||||
#define REPLAY_FROM_FLASH_EN (1)
|
||||
|
||||
//i2s number
|
||||
#define EXAMPLE_I2S_NUM (0)
|
||||
//i2s sample rate
|
||||
#define EXAMPLE_I2S_SAMPLE_RATE (16000)
|
||||
//i2s data bits
|
||||
#define EXAMPLE_I2S_SAMPLE_BITS (16)
|
||||
//enable display buffer for debug
|
||||
#define EXAMPLE_I2S_BUF_DEBUG (0)
|
||||
//I2S read buffer length
|
||||
#define EXAMPLE_I2S_READ_LEN (16 * 1024)
|
||||
//I2S data format
|
||||
#define EXAMPLE_I2S_FORMAT (I2S_CHANNEL_FMT_RIGHT_LEFT)
|
||||
//I2S channel number
|
||||
#define EXAMPLE_I2S_CHANNEL_NUM ((EXAMPLE_I2S_FORMAT < I2S_CHANNEL_FMT_ONLY_RIGHT) ? (2) : (1))
|
||||
//I2S built-in ADC unit
|
||||
#define I2S_ADC_UNIT ADC_UNIT_1
|
||||
//I2S built-in ADC channel
|
||||
#define I2S_ADC_CHANNEL ADC1_CHANNEL_0
|
||||
|
||||
//flash record size, for recording 5 seconds' data
|
||||
#define FLASH_RECORD_SIZE (EXAMPLE_I2S_CHANNEL_NUM * EXAMPLE_I2S_SAMPLE_RATE * EXAMPLE_I2S_SAMPLE_BITS / 8 * 5)
|
||||
#define FLASH_ERASE_SIZE (FLASH_RECORD_SIZE % FLASH_SECTOR_SIZE == 0) ? FLASH_RECORD_SIZE : FLASH_RECORD_SIZE + (FLASH_SECTOR_SIZE - FLASH_RECORD_SIZE % FLASH_SECTOR_SIZE)
|
||||
//sector size of flash
|
||||
#define FLASH_SECTOR_SIZE (0x1000)
|
||||
//flash read / write address
|
||||
#define FLASH_ADDR (0x200000)
|
||||
|
||||
|
||||
/**
|
||||
* @brief I2S ADC/DAC mode init.
|
||||
*/
|
||||
void example_i2s_init(void)
|
||||
{
|
||||
int i2s_num = EXAMPLE_I2S_NUM;
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN,
|
||||
.sample_rate = EXAMPLE_I2S_SAMPLE_RATE,
|
||||
.bits_per_sample = EXAMPLE_I2S_SAMPLE_BITS,
|
||||
.communication_format = I2S_COMM_FORMAT_STAND_MSB,
|
||||
.channel_format = EXAMPLE_I2S_FORMAT,
|
||||
.intr_alloc_flags = 0,
|
||||
.dma_buf_count = 2,
|
||||
.dma_buf_len = 1024,
|
||||
.use_apll = 1,
|
||||
};
|
||||
//install and start i2s driver
|
||||
i2s_driver_install(i2s_num, &i2s_config, 0, NULL);
|
||||
//init DAC pad
|
||||
i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
|
||||
//init ADC pad
|
||||
i2s_set_adc_mode(I2S_ADC_UNIT, I2S_ADC_CHANNEL);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief erase flash for recording
|
||||
*/
|
||||
void example_erase_flash(void)
|
||||
{
|
||||
#if RECORD_IN_FLASH_EN
|
||||
printf("Erasing flash \n");
|
||||
const esp_partition_t *data_partition = NULL;
|
||||
data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_DATA_FAT, PARTITION_NAME);
|
||||
if (data_partition != NULL) {
|
||||
printf("partiton addr: 0x%08x; size: %d; label: %s\n", data_partition->address, data_partition->size, data_partition->label);
|
||||
}
|
||||
printf("Erase size: %d Bytes\n", FLASH_ERASE_SIZE);
|
||||
ESP_ERROR_CHECK(esp_partition_erase_range(data_partition, 0, FLASH_ERASE_SIZE));
|
||||
#else
|
||||
printf("Skip flash erasing...\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief debug buffer data
|
||||
*/
|
||||
void example_disp_buf(uint8_t* buf, int length)
|
||||
{
|
||||
#if EXAMPLE_I2S_BUF_DEBUG
|
||||
printf("======\n");
|
||||
for (int i = 0; i < length; i++) {
|
||||
printf("%02x ", buf[i]);
|
||||
if ((i + 1) % 8 == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("======\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset i2s clock and mode
|
||||
*/
|
||||
void example_reset_play_mode(void)
|
||||
{
|
||||
i2s_set_clk(EXAMPLE_I2S_NUM, EXAMPLE_I2S_SAMPLE_RATE, EXAMPLE_I2S_SAMPLE_BITS, EXAMPLE_I2S_CHANNEL_NUM);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set i2s clock for example audio file
|
||||
*/
|
||||
void example_set_file_play_mode(void)
|
||||
{
|
||||
i2s_set_clk(EXAMPLE_I2S_NUM, 16000, EXAMPLE_I2S_SAMPLE_BITS, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Scale data to 16bit/32bit for I2S DMA output.
|
||||
* DAC can only output 8bit data value.
|
||||
* I2S DMA will still send 16 bit or 32bit data, the highest 8bit contains DAC data.
|
||||
*/
|
||||
int example_i2s_dac_data_scale(uint8_t* d_buff, uint8_t* s_buff, uint32_t len)
|
||||
{
|
||||
uint32_t j = 0;
|
||||
#if (EXAMPLE_I2S_SAMPLE_BITS == 16)
|
||||
for (int i = 0; i < len; i++) {
|
||||
d_buff[j++] = 0;
|
||||
d_buff[j++] = s_buff[i];
|
||||
}
|
||||
return (len * 2);
|
||||
#else
|
||||
for (int i = 0; i < len; i++) {
|
||||
d_buff[j++] = 0;
|
||||
d_buff[j++] = 0;
|
||||
d_buff[j++] = 0;
|
||||
d_buff[j++] = s_buff[i];
|
||||
}
|
||||
return (len * 4);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Scale data to 8bit for data from ADC.
|
||||
* Data from ADC are 12bit width by default.
|
||||
* DAC can only output 8 bit data.
|
||||
* Scale each 12bit ADC data to 8bit DAC data.
|
||||
*/
|
||||
void example_i2s_adc_data_scale(uint8_t * d_buff, uint8_t* s_buff, uint32_t len)
|
||||
{
|
||||
uint32_t j = 0;
|
||||
uint32_t dac_value = 0;
|
||||
#if (EXAMPLE_I2S_SAMPLE_BITS == 16)
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
dac_value = ((((uint16_t) (s_buff[i + 1] & 0xf) << 8) | ((s_buff[i + 0]))));
|
||||
d_buff[j++] = 0;
|
||||
d_buff[j++] = dac_value * 256 / 4096;
|
||||
}
|
||||
#else
|
||||
for (int i = 0; i < len; i += 4) {
|
||||
dac_value = ((((uint16_t)(s_buff[i + 3] & 0xf) << 8) | ((s_buff[i + 2]))));
|
||||
d_buff[j++] = 0;
|
||||
d_buff[j++] = 0;
|
||||
d_buff[j++] = 0;
|
||||
d_buff[j++] = dac_value * 256 / 4096;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2S ADC/DAC example
|
||||
* 1. Erase flash
|
||||
* 2. Record audio from ADC and save in flash
|
||||
* 3. Read flash and replay the sound via DAC
|
||||
* 4. Play an example audio file(file format: 8bit/8khz/single channel)
|
||||
* 5. Loop back to step 3
|
||||
*/
|
||||
void example_i2s_adc_dac(void*arg)
|
||||
{
|
||||
const esp_partition_t *data_partition = NULL;
|
||||
data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_DATA_FAT, PARTITION_NAME);
|
||||
if (data_partition != NULL) {
|
||||
printf("partiton addr: 0x%08x; size: %d; label: %s\n", data_partition->address, data_partition->size, data_partition->label);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Partition error: can't find partition name: %s\n", PARTITION_NAME);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
//1. Erase flash
|
||||
example_erase_flash();
|
||||
int i2s_read_len = EXAMPLE_I2S_READ_LEN;
|
||||
int flash_wr_size = 0;
|
||||
size_t bytes_read, bytes_written;
|
||||
|
||||
//2. Record audio from ADC and save in flash
|
||||
#if RECORD_IN_FLASH_EN
|
||||
char* i2s_read_buff = (char*) calloc(i2s_read_len, sizeof(char));
|
||||
uint8_t* flash_write_buff = (uint8_t*) calloc(i2s_read_len, sizeof(char));
|
||||
i2s_adc_enable(EXAMPLE_I2S_NUM);
|
||||
while (flash_wr_size < FLASH_RECORD_SIZE) {
|
||||
//read data from I2S bus, in this case, from ADC.
|
||||
i2s_read(EXAMPLE_I2S_NUM, (void*) i2s_read_buff, i2s_read_len, &bytes_read, portMAX_DELAY);
|
||||
example_disp_buf((uint8_t*) i2s_read_buff, 64);
|
||||
//save original data from I2S(ADC) into flash.
|
||||
esp_partition_write(data_partition, flash_wr_size, i2s_read_buff, i2s_read_len);
|
||||
flash_wr_size += i2s_read_len;
|
||||
ets_printf("Sound recording %u%%\n", flash_wr_size * 100 / FLASH_RECORD_SIZE);
|
||||
}
|
||||
i2s_adc_disable(EXAMPLE_I2S_NUM);
|
||||
free(i2s_read_buff);
|
||||
i2s_read_buff = NULL;
|
||||
free(flash_write_buff);
|
||||
flash_write_buff = NULL;
|
||||
#endif
|
||||
|
||||
uint8_t* flash_read_buff = (uint8_t*) calloc(i2s_read_len, sizeof(char));
|
||||
uint8_t* i2s_write_buff = (uint8_t*) calloc(i2s_read_len, sizeof(char));
|
||||
while (1) {
|
||||
|
||||
//3. Read flash and replay the sound via DAC
|
||||
#if REPLAY_FROM_FLASH_EN
|
||||
for (int rd_offset = 0; rd_offset < flash_wr_size; rd_offset += FLASH_SECTOR_SIZE) {
|
||||
//read I2S(ADC) original data from flash
|
||||
esp_partition_read(data_partition, rd_offset, flash_read_buff, FLASH_SECTOR_SIZE);
|
||||
//process data and scale to 8bit for I2S DAC.
|
||||
example_i2s_adc_data_scale(i2s_write_buff, flash_read_buff, FLASH_SECTOR_SIZE);
|
||||
//send data
|
||||
i2s_write(EXAMPLE_I2S_NUM, i2s_write_buff, FLASH_SECTOR_SIZE, &bytes_written, portMAX_DELAY);
|
||||
printf("playing: %d %%\n", rd_offset * 100 / flash_wr_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
//4. Play an example audio file(file format: 8bit/16khz/single channel)
|
||||
printf("Playing file example: \n");
|
||||
int offset = 0;
|
||||
int tot_size = sizeof(audio_table);
|
||||
example_set_file_play_mode();
|
||||
while (offset < tot_size) {
|
||||
int play_len = ((tot_size - offset) > (4 * 1024)) ? (4 * 1024) : (tot_size - offset);
|
||||
int i2s_wr_len = example_i2s_dac_data_scale(i2s_write_buff, (uint8_t*)(audio_table + offset), play_len);
|
||||
i2s_write(EXAMPLE_I2S_NUM, i2s_write_buff, i2s_wr_len, &bytes_written, portMAX_DELAY);
|
||||
offset += play_len;
|
||||
example_disp_buf((uint8_t*) i2s_write_buff, 32);
|
||||
}
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
example_reset_play_mode();
|
||||
}
|
||||
free(flash_read_buff);
|
||||
free(i2s_write_buff);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void adc_read_task(void* arg)
|
||||
{
|
||||
adc1_config_width(ADC_WIDTH_12Bit);
|
||||
adc1_config_channel_atten(ADC1_TEST_CHANNEL, ADC_ATTEN_11db);
|
||||
esp_adc_cal_characteristics_t characteristics;
|
||||
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, V_REF, &characteristics);
|
||||
while(1) {
|
||||
uint32_t voltage;
|
||||
esp_adc_cal_get_voltage(ADC1_TEST_CHANNEL, &characteristics, &voltage);
|
||||
ESP_LOGI(TAG, "%d mV", voltage);
|
||||
vTaskDelay(200 / portTICK_RATE_MS);
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t app_main(void)
|
||||
{
|
||||
example_i2s_init();
|
||||
esp_log_level_set("I2S", ESP_LOG_INFO);
|
||||
xTaskCreate(example_i2s_adc_dac, "example_i2s_adc_dac", 1024 * 2, NULL, 5, NULL);
|
||||
xTaskCreate(adc_read_task, "ADC read task", 2048, NULL, 5, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 1M,
|
||||
storage, data, fat, , 2M,
|
||||
|
@@ -0,0 +1,43 @@
|
||||
from __future__ import print_function
|
||||
from builtins import range
|
||||
import os
|
||||
import wave
|
||||
import struct
|
||||
|
||||
|
||||
def get_wave_array_str(filename, target_bits):
|
||||
wave_read = wave.open(filename, "r")
|
||||
array_str = ""
|
||||
nchannels, sampwidth, framerate, nframes, comptype, compname = wave_read.getparams()
|
||||
sampwidth *= 8
|
||||
for i in range(wave_read.getnframes()):
|
||||
val, = struct.unpack("<H", wave_read.readframes(1))
|
||||
scale_val = (1 << target_bits) - 1
|
||||
cur_lim = (1 << sampwidth) - 1
|
||||
# scale current data to 8-bit data
|
||||
val = val * scale_val / cur_lim
|
||||
val = int(val + ((scale_val + 1) // 2)) & scale_val
|
||||
array_str += "0x%x, " % (val)
|
||||
if (i + 1) % 16 == 0:
|
||||
array_str += "\n"
|
||||
return array_str
|
||||
|
||||
|
||||
def gen_wave_table(wav_file_list, target_file_name, scale_bits=8):
|
||||
with open(target_file_name, "w") as audio_table:
|
||||
print('#include <stdio.h>', file=audio_table)
|
||||
print('const unsigned char audio_table[] = {', file=audio_table)
|
||||
for wav in wav_file_list:
|
||||
print("processing: {}".format(wav))
|
||||
print(get_wave_array_str(filename=wav, target_bits=scale_bits), file=audio_table)
|
||||
print('};\n', file=audio_table)
|
||||
print("Done...")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Generating audio array...")
|
||||
wav_list = []
|
||||
for filename in os.listdir("./"):
|
||||
if filename.endswith(".wav"):
|
||||
wav_list.append(filename)
|
||||
gen_wave_table(wav_file_list=wav_list, target_file_name="audio_example_file.h")
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(ledc)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := ledc
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# _LEDC Example_
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example shows how to control intensity of LEDs using ESP32's on-board hardware LED PWM Controller module.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
Connect four LEDs to the following LEDC channels / individual GPIOs:
|
||||
|
||||
|ledc channel|GPIO|
|
||||
|:---:|:---:|
|
||||
|channel 0|GPIO18|
|
||||
|channel 1|GPIO19|
|
||||
|channel 2|GPIO4|
|
||||
|channel 3|GPIO5|
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
Running this example, you will see each ledc's brightness changes differently
|
||||
|
||||
* LEDC 1: Fade up / increase intensity
|
||||
* LEDC 2: Fade down / decrease intensity
|
||||
* LEDC 3: Keep a stable intensity
|
||||
* LEDC 4: LED is not on
|
||||
|
||||
you can also see the following output log on the serial monitor:
|
||||
|
||||
```
|
||||
1. LEDC fade up to duty = 4000
|
||||
2. LEDC fade down to duty = 0
|
||||
3. LEDC set duty = 4000 without fade
|
||||
4. LEDC set duty = 0 without fade
|
||||
...
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
* Programming fail
|
||||
|
||||
* Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
|
||||
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
|
||||
|
||||
For any technical queries, please open an [issue] (https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "ledc_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
@@ -0,0 +1,192 @@
|
||||
/* LEDC (LED Controller) fade 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 "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/ledc.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
/*
|
||||
* About this example
|
||||
*
|
||||
* 1. Start with initializing LEDC module:
|
||||
* a. Set the timer of LEDC first, this determines the frequency
|
||||
* and resolution of PWM.
|
||||
* b. Then set the LEDC channel you want to use,
|
||||
* and bind with one of the timers.
|
||||
*
|
||||
* 2. You need first to install a default fade function,
|
||||
* then you can use fade APIs.
|
||||
*
|
||||
* 3. You can also set a target duty directly without fading.
|
||||
*
|
||||
* 4. This example uses GPIO18/19/4/5 as LEDC output,
|
||||
* and it will change the duty repeatedly.
|
||||
*
|
||||
* 5. GPIO18/19 are from high speed channel group.
|
||||
* GPIO4/5 are from low speed channel group.
|
||||
*
|
||||
*/
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
#define LEDC_HS_TIMER LEDC_TIMER_0
|
||||
#define LEDC_HS_MODE LEDC_HIGH_SPEED_MODE
|
||||
#define LEDC_HS_CH0_GPIO (18)
|
||||
#define LEDC_HS_CH0_CHANNEL LEDC_CHANNEL_0
|
||||
#define LEDC_HS_CH1_GPIO (19)
|
||||
#define LEDC_HS_CH1_CHANNEL LEDC_CHANNEL_1
|
||||
#endif
|
||||
#define LEDC_LS_TIMER LEDC_TIMER_1
|
||||
#define LEDC_LS_MODE LEDC_LOW_SPEED_MODE
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32S2
|
||||
#define LEDC_LS_CH0_GPIO (18)
|
||||
#define LEDC_LS_CH0_CHANNEL LEDC_CHANNEL_0
|
||||
#define LEDC_LS_CH1_GPIO (19)
|
||||
#define LEDC_LS_CH1_CHANNEL LEDC_CHANNEL_1
|
||||
#endif
|
||||
#define LEDC_LS_CH2_GPIO (4)
|
||||
#define LEDC_LS_CH2_CHANNEL LEDC_CHANNEL_2
|
||||
#define LEDC_LS_CH3_GPIO (5)
|
||||
#define LEDC_LS_CH3_CHANNEL LEDC_CHANNEL_3
|
||||
|
||||
#define LEDC_TEST_CH_NUM (4)
|
||||
#define LEDC_TEST_DUTY (4000)
|
||||
#define LEDC_TEST_FADE_TIME (3000)
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
int ch;
|
||||
|
||||
/*
|
||||
* Prepare and set configuration of timers
|
||||
* that will be used by LED Controller
|
||||
*/
|
||||
ledc_timer_config_t ledc_timer = {
|
||||
.duty_resolution = LEDC_TIMER_13_BIT, // resolution of PWM duty
|
||||
.freq_hz = 5000, // frequency of PWM signal
|
||||
.speed_mode = LEDC_LS_MODE, // timer mode
|
||||
.timer_num = LEDC_LS_TIMER, // timer index
|
||||
.clk_cfg = LEDC_AUTO_CLK, // Auto select the source clock
|
||||
};
|
||||
// Set configuration of timer0 for high speed channels
|
||||
ledc_timer_config(&ledc_timer);
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
// Prepare and set configuration of timer1 for low speed channels
|
||||
ledc_timer.speed_mode = LEDC_HS_MODE;
|
||||
ledc_timer.timer_num = LEDC_HS_TIMER;
|
||||
ledc_timer_config(&ledc_timer);
|
||||
#endif
|
||||
/*
|
||||
* Prepare individual configuration
|
||||
* for each channel of LED Controller
|
||||
* by selecting:
|
||||
* - controller's channel number
|
||||
* - output duty cycle, set initially to 0
|
||||
* - GPIO number where LED is connected to
|
||||
* - speed mode, either high or low
|
||||
* - timer servicing selected channel
|
||||
* Note: if different channels use one timer,
|
||||
* then frequency and bit_num of these channels
|
||||
* will be the same
|
||||
*/
|
||||
ledc_channel_config_t ledc_channel[LEDC_TEST_CH_NUM] = {
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
{
|
||||
.channel = LEDC_HS_CH0_CHANNEL,
|
||||
.duty = 0,
|
||||
.gpio_num = LEDC_HS_CH0_GPIO,
|
||||
.speed_mode = LEDC_HS_MODE,
|
||||
.hpoint = 0,
|
||||
.timer_sel = LEDC_HS_TIMER
|
||||
},
|
||||
{
|
||||
.channel = LEDC_HS_CH1_CHANNEL,
|
||||
.duty = 0,
|
||||
.gpio_num = LEDC_HS_CH1_GPIO,
|
||||
.speed_mode = LEDC_HS_MODE,
|
||||
.hpoint = 0,
|
||||
.timer_sel = LEDC_HS_TIMER
|
||||
},
|
||||
#elif defined CONFIG_IDF_TARGET_ESP32S2
|
||||
{
|
||||
.channel = LEDC_LS_CH0_CHANNEL,
|
||||
.duty = 0,
|
||||
.gpio_num = LEDC_LS_CH0_GPIO,
|
||||
.speed_mode = LEDC_LS_MODE,
|
||||
.hpoint = 0,
|
||||
.timer_sel = LEDC_LS_TIMER
|
||||
},
|
||||
{
|
||||
.channel = LEDC_LS_CH1_CHANNEL,
|
||||
.duty = 0,
|
||||
.gpio_num = LEDC_LS_CH1_GPIO,
|
||||
.speed_mode = LEDC_LS_MODE,
|
||||
.hpoint = 0,
|
||||
.timer_sel = LEDC_LS_TIMER
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.channel = LEDC_LS_CH2_CHANNEL,
|
||||
.duty = 0,
|
||||
.gpio_num = LEDC_LS_CH2_GPIO,
|
||||
.speed_mode = LEDC_LS_MODE,
|
||||
.hpoint = 0,
|
||||
.timer_sel = LEDC_LS_TIMER
|
||||
},
|
||||
{
|
||||
.channel = LEDC_LS_CH3_CHANNEL,
|
||||
.duty = 0,
|
||||
.gpio_num = LEDC_LS_CH3_GPIO,
|
||||
.speed_mode = LEDC_LS_MODE,
|
||||
.hpoint = 0,
|
||||
.timer_sel = LEDC_LS_TIMER
|
||||
},
|
||||
};
|
||||
|
||||
// Set LED Controller with previously prepared configuration
|
||||
for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) {
|
||||
ledc_channel_config(&ledc_channel[ch]);
|
||||
}
|
||||
|
||||
// Initialize fade service.
|
||||
ledc_fade_func_install(0);
|
||||
|
||||
while (1) {
|
||||
printf("1. LEDC fade up to duty = %d\n", LEDC_TEST_DUTY);
|
||||
for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) {
|
||||
ledc_set_fade_with_time(ledc_channel[ch].speed_mode,
|
||||
ledc_channel[ch].channel, LEDC_TEST_DUTY, LEDC_TEST_FADE_TIME);
|
||||
ledc_fade_start(ledc_channel[ch].speed_mode,
|
||||
ledc_channel[ch].channel, LEDC_FADE_NO_WAIT);
|
||||
}
|
||||
vTaskDelay(LEDC_TEST_FADE_TIME / portTICK_PERIOD_MS);
|
||||
|
||||
printf("2. LEDC fade down to duty = 0\n");
|
||||
for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) {
|
||||
ledc_set_fade_with_time(ledc_channel[ch].speed_mode,
|
||||
ledc_channel[ch].channel, 0, LEDC_TEST_FADE_TIME);
|
||||
ledc_fade_start(ledc_channel[ch].speed_mode,
|
||||
ledc_channel[ch].channel, LEDC_FADE_NO_WAIT);
|
||||
}
|
||||
vTaskDelay(LEDC_TEST_FADE_TIME / portTICK_PERIOD_MS);
|
||||
|
||||
printf("3. LEDC set duty = %d without fade\n", LEDC_TEST_DUTY);
|
||||
for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) {
|
||||
ledc_set_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel, LEDC_TEST_DUTY);
|
||||
ledc_update_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel);
|
||||
}
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
|
||||
printf("4. LEDC set duty = 0 without fade\n");
|
||||
for (ch = 0; ch < LEDC_TEST_CH_NUM; ch++) {
|
||||
ledc_set_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel, 0);
|
||||
ledc_update_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel);
|
||||
}
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(mcpwm_basic_config)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := mcpwm_basic_config
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# MCPWM basic config Example
|
||||
|
||||
This example will show you how to use each submodule of MCPWM unit
|
||||
|
||||
The example can't be used without modifying the code first
|
||||
|
||||
Edit the macros at the top of mcpwm_example_basic_config.c to enable/disable the submodules which are used in the example
|
||||
|
||||
|
||||
## Step 1: Pin assignment
|
||||
* The gpio init function initializes:
|
||||
* six MCPWM output pins
|
||||
* three MCPWM fault input pins
|
||||
* three MCPWM sync input pins
|
||||
* three MCPWM capture input pins
|
||||
|
||||
|
||||
## Step 2: Connection
|
||||
* Six MCPWM output pins to motor driver input signals
|
||||
* Fault, sync, capture signals can be connected to respective signals
|
||||
|
||||
|
||||
## Step 3: Initialize MCPWM
|
||||
* You need to set the frequency and duty cycle of each three MCPWM timer along with other parameters mentioned
|
||||
* You need to set the MCPWM channel you want to use, with these timers
|
||||
|
||||
|
||||
## Step 4: Testing
|
||||
* The deadtime module, set deadtime type and with value as time*100ns
|
||||
* The sync module, synchonizes all the timer pulses
|
||||
* The fault module when enabled takes action on MCPWM signals when fault occurs
|
||||
* The capture module captures input signal(digital i.e. hall sensor value, etc), timing between two rising/falling edge
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "mcpwm_basic_config_example.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
@@ -0,0 +1,293 @@
|
||||
/* MCPWM basic config 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 example will show you how to use each submodule of MCPWM unit.
|
||||
* The example can't be used without modifying the code first.
|
||||
* Edit the macros at the top of mcpwm_example_basic_config.c to enable/disable the submodules which are used in the example.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "string.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_attr.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "driver/mcpwm.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
|
||||
#define MCPWM_EN_CARRIER 0 //Make this 1 to test carrier submodule of mcpwm, set high frequency carrier parameters
|
||||
#define MCPWM_EN_DEADTIME 0 //Make this 1 to test deadtime submodule of mcpwm, set deadtime value and deadtime mode
|
||||
#define MCPWM_EN_FAULT 0 //Make this 1 to test fault submodule of mcpwm, set action on MCPWM signal on fault occurence like overcurrent, overvoltage, etc
|
||||
#define MCPWM_EN_SYNC 0 //Make this 1 to test sync submodule of mcpwm, sync timer signals
|
||||
#define MCPWM_EN_CAPTURE 0 //Make this 1 to test capture submodule of mcpwm, measure time between rising/falling edge of captured signal
|
||||
#define MCPWM_GPIO_INIT 0 //select which function to use to initialize gpio signals
|
||||
#define CAP_SIG_NUM 3 //Three capture signals
|
||||
|
||||
#define CAP0_INT_EN BIT(27) //Capture 0 interrupt bit
|
||||
#define CAP1_INT_EN BIT(28) //Capture 1 interrupt bit
|
||||
#define CAP2_INT_EN BIT(29) //Capture 2 interrupt bit
|
||||
|
||||
|
||||
#define GPIO_PWM0A_OUT 19 //Set GPIO 19 as PWM0A
|
||||
#define GPIO_PWM0B_OUT 18 //Set GPIO 18 as PWM0B
|
||||
#define GPIO_PWM1A_OUT 17 //Set GPIO 17 as PWM1A
|
||||
#define GPIO_PWM1B_OUT 16 //Set GPIO 16 as PWM1B
|
||||
#define GPIO_PWM2A_OUT 15 //Set GPIO 15 as PWM2A
|
||||
#define GPIO_PWM2B_OUT 14 //Set GPIO 14 as PWM2B
|
||||
#define GPIO_CAP0_IN 23 //Set GPIO 23 as CAP0
|
||||
#define GPIO_CAP1_IN 25 //Set GPIO 25 as CAP1
|
||||
#define GPIO_CAP2_IN 26 //Set GPIO 26 as CAP2
|
||||
#define GPIO_SYNC0_IN 2 //Set GPIO 02 as SYNC0
|
||||
#define GPIO_SYNC1_IN 4 //Set GPIO 04 as SYNC1
|
||||
#define GPIO_SYNC2_IN 5 //Set GPIO 05 as SYNC2
|
||||
#define GPIO_FAULT0_IN 32 //Set GPIO 32 as FAULT0
|
||||
#define GPIO_FAULT1_IN 34 //Set GPIO 34 as FAULT1
|
||||
#define GPIO_FAULT2_IN 34 //Set GPIO 34 as FAULT2
|
||||
|
||||
typedef struct {
|
||||
uint32_t capture_signal;
|
||||
mcpwm_capture_signal_t sel_cap_signal;
|
||||
} capture;
|
||||
|
||||
xQueueHandle cap_queue;
|
||||
#if MCPWM_EN_CAPTURE
|
||||
static mcpwm_dev_t *MCPWM[2] = {&MCPWM0, &MCPWM1};
|
||||
#endif
|
||||
|
||||
static void mcpwm_example_gpio_initialize(void)
|
||||
{
|
||||
printf("initializing mcpwm gpio...\n");
|
||||
#if MCPWM_GPIO_INIT
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, GPIO_PWM0A_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0B, GPIO_PWM0B_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1A, GPIO_PWM1A_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1B, GPIO_PWM1B_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM2A, GPIO_PWM2A_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM2B, GPIO_PWM2B_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_0, GPIO_CAP0_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_1, GPIO_CAP1_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_2, GPIO_CAP2_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_SYNC_0, GPIO_SYNC0_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_SYNC_1, GPIO_SYNC1_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_SYNC_2, GPIO_SYNC2_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_FAULT_0, GPIO_FAULT0_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_FAULT_1, GPIO_FAULT1_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_FAULT_2, GPIO_FAULT2_IN);
|
||||
#else
|
||||
mcpwm_pin_config_t pin_config = {
|
||||
.mcpwm0a_out_num = GPIO_PWM0A_OUT,
|
||||
.mcpwm0b_out_num = GPIO_PWM0B_OUT,
|
||||
.mcpwm1a_out_num = GPIO_PWM1A_OUT,
|
||||
.mcpwm1b_out_num = GPIO_PWM1B_OUT,
|
||||
.mcpwm2a_out_num = GPIO_PWM2A_OUT,
|
||||
.mcpwm2b_out_num = GPIO_PWM2B_OUT,
|
||||
.mcpwm_sync0_in_num = GPIO_SYNC0_IN,
|
||||
.mcpwm_sync1_in_num = GPIO_SYNC1_IN,
|
||||
.mcpwm_sync2_in_num = GPIO_SYNC2_IN,
|
||||
.mcpwm_fault0_in_num = GPIO_FAULT0_IN,
|
||||
.mcpwm_fault1_in_num = GPIO_FAULT1_IN,
|
||||
.mcpwm_fault2_in_num = GPIO_FAULT2_IN,
|
||||
.mcpwm_cap0_in_num = GPIO_CAP0_IN,
|
||||
.mcpwm_cap1_in_num = GPIO_CAP1_IN,
|
||||
.mcpwm_cap2_in_num = GPIO_CAP2_IN
|
||||
};
|
||||
mcpwm_set_pin(MCPWM_UNIT_0, &pin_config);
|
||||
#endif
|
||||
gpio_pulldown_en(GPIO_CAP0_IN); //Enable pull down on CAP0 signal
|
||||
gpio_pulldown_en(GPIO_CAP1_IN); //Enable pull down on CAP1 signal
|
||||
gpio_pulldown_en(GPIO_CAP2_IN); //Enable pull down on CAP2 signal
|
||||
gpio_pulldown_en(GPIO_SYNC0_IN); //Enable pull down on SYNC0 signal
|
||||
gpio_pulldown_en(GPIO_SYNC1_IN); //Enable pull down on SYNC1 signal
|
||||
gpio_pulldown_en(GPIO_SYNC2_IN); //Enable pull down on SYNC2 signal
|
||||
gpio_pulldown_en(GPIO_FAULT0_IN); //Enable pull down on FAULT0 signal
|
||||
gpio_pulldown_en(GPIO_FAULT1_IN); //Enable pull down on FAULT1 signal
|
||||
gpio_pulldown_en(GPIO_FAULT2_IN); //Enable pull down on FAULT2 signal
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set gpio 12 as our test signal that generates high-low waveform continuously, connect this gpio to capture pin.
|
||||
*/
|
||||
static void gpio_test_signal(void *arg)
|
||||
{
|
||||
printf("intializing test signal...\n");
|
||||
gpio_config_t gp;
|
||||
gp.intr_type = GPIO_INTR_DISABLE;
|
||||
gp.mode = GPIO_MODE_OUTPUT;
|
||||
gp.pin_bit_mask = GPIO_SEL_12;
|
||||
gpio_config(&gp);
|
||||
while (1) {
|
||||
//here the period of test signal is 20ms
|
||||
gpio_set_level(GPIO_NUM_12, 1); //Set high
|
||||
vTaskDelay(10); //delay of 10ms
|
||||
gpio_set_level(GPIO_NUM_12, 0); //Set low
|
||||
vTaskDelay(10); //delay of 10ms
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief When interrupt occurs, we receive the counter value and display the time between two rising edge
|
||||
*/
|
||||
static void disp_captured_signal(void *arg)
|
||||
{
|
||||
uint32_t *current_cap_value = (uint32_t *)malloc(CAP_SIG_NUM*sizeof(uint32_t));
|
||||
uint32_t *previous_cap_value = (uint32_t *)malloc(CAP_SIG_NUM*sizeof(uint32_t));
|
||||
capture evt;
|
||||
while (1) {
|
||||
xQueueReceive(cap_queue, &evt, portMAX_DELAY);
|
||||
if (evt.sel_cap_signal == MCPWM_SELECT_CAP0) {
|
||||
current_cap_value[0] = evt.capture_signal - previous_cap_value[0];
|
||||
previous_cap_value[0] = evt.capture_signal;
|
||||
current_cap_value[0] = (current_cap_value[0] / 10000) * (10000000000 / rtc_clk_apb_freq_get());
|
||||
printf("CAP0 : %d us\n", current_cap_value[0]);
|
||||
}
|
||||
if (evt.sel_cap_signal == MCPWM_SELECT_CAP1) {
|
||||
current_cap_value[1] = evt.capture_signal - previous_cap_value[1];
|
||||
previous_cap_value[1] = evt.capture_signal;
|
||||
current_cap_value[1] = (current_cap_value[1] / 10000) * (10000000000 / rtc_clk_apb_freq_get());
|
||||
printf("CAP1 : %d us\n", current_cap_value[1]);
|
||||
}
|
||||
if (evt.sel_cap_signal == MCPWM_SELECT_CAP2) {
|
||||
current_cap_value[2] = evt.capture_signal - previous_cap_value[2];
|
||||
previous_cap_value[2] = evt.capture_signal;
|
||||
current_cap_value[2] = (current_cap_value[2] / 10000) * (10000000000 / rtc_clk_apb_freq_get());
|
||||
printf("CAP2 : %d us\n", current_cap_value[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if MCPWM_EN_CAPTURE
|
||||
/**
|
||||
* @brief this is ISR handler function, here we check for interrupt that triggers rising edge on CAP0 signal and according take action
|
||||
*/
|
||||
static void IRAM_ATTR isr_handler(void)
|
||||
{
|
||||
uint32_t mcpwm_intr_status;
|
||||
capture evt;
|
||||
mcpwm_intr_status = MCPWM[MCPWM_UNIT_0]->int_st.val; //Read interrupt status
|
||||
if (mcpwm_intr_status & CAP0_INT_EN) { //Check for interrupt on rising edge on CAP0 signal
|
||||
evt.capture_signal = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP0); //get capture signal counter value
|
||||
evt.sel_cap_signal = MCPWM_SELECT_CAP0;
|
||||
xQueueSendFromISR(cap_queue, &evt, NULL);
|
||||
}
|
||||
if (mcpwm_intr_status & CAP1_INT_EN) { //Check for interrupt on rising edge on CAP0 signal
|
||||
evt.capture_signal = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP1); //get capture signal counter value
|
||||
evt.sel_cap_signal = MCPWM_SELECT_CAP1;
|
||||
xQueueSendFromISR(cap_queue, &evt, NULL);
|
||||
}
|
||||
if (mcpwm_intr_status & CAP2_INT_EN) { //Check for interrupt on rising edge on CAP0 signal
|
||||
evt.capture_signal = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP2); //get capture signal counter value
|
||||
evt.sel_cap_signal = MCPWM_SELECT_CAP2;
|
||||
xQueueSendFromISR(cap_queue, &evt, NULL);
|
||||
}
|
||||
MCPWM[MCPWM_UNIT_0]->int_clr.val = mcpwm_intr_status;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configure whole MCPWM module
|
||||
*/
|
||||
static void mcpwm_example_config(void *arg)
|
||||
{
|
||||
//1. mcpwm gpio initialization
|
||||
mcpwm_example_gpio_initialize();
|
||||
|
||||
//2. initialize mcpwm configuration
|
||||
printf("Configuring Initial Parameters of mcpwm...\n");
|
||||
mcpwm_config_t pwm_config;
|
||||
pwm_config.frequency = 1000; //frequency = 1000Hz
|
||||
pwm_config.cmpr_a = 60.0; //duty cycle of PWMxA = 60.0%
|
||||
pwm_config.cmpr_b = 50.0; //duty cycle of PWMxb = 50.0%
|
||||
pwm_config.counter_mode = MCPWM_UP_COUNTER;
|
||||
pwm_config.duty_mode = MCPWM_DUTY_MODE_0;
|
||||
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); //Configure PWM0A & PWM0B with above settings
|
||||
pwm_config.frequency = 500; //frequency = 500Hz
|
||||
pwm_config.cmpr_a = 45.9; //duty cycle of PWMxA = 45.9%
|
||||
pwm_config.cmpr_b = 7.0; //duty cycle of PWMxb = 07.0%
|
||||
pwm_config.counter_mode = MCPWM_UP_COUNTER;
|
||||
pwm_config.duty_mode = MCPWM_DUTY_MODE_0;
|
||||
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_1, &pwm_config); //Configure PWM1A & PWM1B with above settings
|
||||
pwm_config.frequency = 400; //frequency = 400Hz
|
||||
pwm_config.cmpr_a = 23.2; //duty cycle of PWMxA = 23.2%
|
||||
pwm_config.cmpr_b = 97.0; //duty cycle of PWMxb = 97.0%
|
||||
pwm_config.counter_mode = MCPWM_UP_DOWN_COUNTER; //frequency is half when up down count mode is set i.e. SYMMETRIC PWM
|
||||
pwm_config.duty_mode = MCPWM_DUTY_MODE_1;
|
||||
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_2, &pwm_config); //Configure PWM2A & PWM2B with above settings
|
||||
|
||||
#if MCPWM_EN_CARRIER
|
||||
//3. carrier configuration
|
||||
//comment if you don't want to use carrier mode
|
||||
//in carrier mode very high frequency carrier signal is generated at mcpwm high level signal
|
||||
mcpwm_carrier_config_t chop_config;
|
||||
chop_config.carrier_period = 6; //carrier period = (6 + 1)*800ns
|
||||
chop_config.carrier_duty = 3; //carrier duty = (3)*12.5%
|
||||
chop_config.carrier_os_mode = MCPWM_ONESHOT_MODE_EN; //If one shot mode is enabled then set pulse width, if disabled no need to set pulse width
|
||||
chop_config.pulse_width_in_os = 3; //first pulse width = (3 + 1)*carrier_period
|
||||
chop_config.carrier_ivt_mode = MCPWM_CARRIER_OUT_IVT_EN; //output signal inversion enable
|
||||
mcpwm_carrier_init(MCPWM_UNIT_0, MCPWM_TIMER_2, &chop_config); //Enable carrier on PWM2A and PWM2B with above settings
|
||||
//use mcpwm_carrier_disable function to disable carrier on mcpwm timer on which it was enabled
|
||||
#endif
|
||||
|
||||
#if MCPWM_EN_DEADTIME
|
||||
//4. deadtime configuration
|
||||
//comment if you don't want to use deadtime submodule
|
||||
//add rising edge delay or falling edge delay. There are 8 different types, each explained in mcpwm_deadtime_type_t in mcpwm.h
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_BYPASS_FED, 1000, 1000); //Enable deadtime on PWM2A and PWM2B with red = (1000)*100ns on PWM2A
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_BYPASS_RED, 300, 2000); //Enable deadtime on PWM1A and PWM1B with fed = (2000)*100ns on PWM1B
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_ACTIVE_RED_FED_FROM_PWMXA, 656, 67); //Enable deadtime on PWM0A and PWM0B with red = (656)*100ns & fed = (67)*100ns on PWM0A and PWM0B generated from PWM0A
|
||||
//use mcpwm_deadtime_disable function to disable deadtime on mcpwm timer on which it was enabled
|
||||
#endif
|
||||
|
||||
#if MCPWM_EN_FAULT
|
||||
//5. enable fault condition
|
||||
//comment if you don't want to use fault submodule, also u can comment the fault gpio signals
|
||||
//whenever fault occurs you can configure mcpwm signal to either force low, force high or toggle.
|
||||
//in cycmode, as soon as fault condition is over, the mcpwm signal is resumed, whereas in oneshot mode you need to reset.
|
||||
mcpwm_fault_init(MCPWM_UNIT_0, MCPWM_HIGH_LEVEL_TGR, MCPWM_SELECT_F0); //Enable FAULT, when high level occurs on FAULT0 signal
|
||||
mcpwm_fault_init(MCPWM_UNIT_0, MCPWM_HIGH_LEVEL_TGR, MCPWM_SELECT_F1); //Enable FAULT, when high level occurs on FAULT1 signal
|
||||
mcpwm_fault_init(MCPWM_UNIT_0, MCPWM_HIGH_LEVEL_TGR, MCPWM_SELECT_F2); //Enable FAULT, when high level occurs on FAULT2 signal
|
||||
mcpwm_fault_set_oneshot_mode(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_SELECT_F0, MCPWM_FORCE_MCPWMXA_HIGH, MCPWM_FORCE_MCPWMXB_LOW); //Action taken on PWM1A and PWM1B, when FAULT0 occurs
|
||||
mcpwm_fault_set_oneshot_mode(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_SELECT_F1, MCPWM_FORCE_MCPWMXA_LOW, MCPWM_FORCE_MCPWMXB_HIGH); //Action taken on PWM1A and PWM1B, when FAULT1 occurs
|
||||
mcpwm_fault_set_oneshot_mode(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_SELECT_F2, MCPWM_FORCE_MCPWMXA_HIGH, MCPWM_FORCE_MCPWMXB_LOW); //Action taken on PWM0A and PWM0B, when FAULT2 occurs
|
||||
mcpwm_fault_set_oneshot_mode(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_SELECT_F1, MCPWM_FORCE_MCPWMXA_LOW, MCPWM_FORCE_MCPWMXB_HIGH); //Action taken on PWM0A and PWM0B, when FAULT1 occurs
|
||||
#endif
|
||||
|
||||
#if MCPWM_EN_SYNC
|
||||
//6. Syncronization configuration
|
||||
//comment if you don't want to use sync submodule, also u can comment the sync gpio signals
|
||||
//here synchronization occurs on PWM1A and PWM1B
|
||||
mcpwm_sync_enable(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_SELECT_SYNC0, 200); //Load counter value with 20% of period counter of mcpwm timer 1 when sync 0 occurs
|
||||
#endif
|
||||
|
||||
#if MCPWM_EN_CAPTURE
|
||||
//7. Capture configuration
|
||||
//comment if you don't want to use capture submodule, also u can comment the capture gpio signals
|
||||
//configure CAP0, CAP1 and CAP2 signal to start capture counter on rising edge
|
||||
//we generate a gpio_test_signal of 20ms on GPIO 12 and connect it to one of the capture signal, the disp_captured_function displays the time between rising edge
|
||||
//In general practice you can connect Capture to external signal, measure time between rising edge or falling edge and take action accordingly
|
||||
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, MCPWM_POS_EDGE, 0); //capture signal on rising edge, prescale = 0 i.e. 800,000,000 counts is equal to one second
|
||||
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP2, MCPWM_POS_EDGE, 0); //capture signal on rising edge, prescale = 0 i.e. 800,000,000 counts is equal to one second
|
||||
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP1, MCPWM_POS_EDGE, 0); //capture signal on rising edge, prescale = 0 i.e. 800,000,000 counts is equal to one second
|
||||
//enable interrupt, so each this a rising edge occurs interrupt is triggered
|
||||
MCPWM[MCPWM_UNIT_0]->int_ena.val = CAP0_INT_EN | CAP1_INT_EN | CAP2_INT_EN; //Enable interrupt on CAP0, CAP1 and CAP2 signal
|
||||
mcpwm_isr_register(MCPWM_UNIT_0, isr_handler, NULL, ESP_INTR_FLAG_IRAM, NULL); //Set ISR Handler
|
||||
#endif
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
printf("Testing MCPWM...\n");
|
||||
cap_queue = xQueueCreate(1, sizeof(capture)); //comment if you don't want to use capture module
|
||||
xTaskCreate(disp_captured_signal, "mcpwm_config", 4096, NULL, 5, NULL); //comment if you don't want to use capture module
|
||||
xTaskCreate(gpio_test_signal, "gpio_test_signal", 4096, NULL, 5, NULL); //comment if you don't want to use capture module
|
||||
xTaskCreate(mcpwm_example_config, "mcpwm_example_config", 4096, NULL, 5, NULL);
|
||||
}
|
||||
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(mcpwm_bldc_control_hall_sensor)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := mcpwm_bldc_control_hall_sensor
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# MCPWM BLDC motor control(hall sensor feedback) Example
|
||||
|
||||
This example will show you how to use MCPWM module to control bldc motor with hall sensor feedback
|
||||
|
||||
The following examples uses MCPWM module to control bldc motor and vary its speed continuously
|
||||
|
||||
The bldc motor used for testing this code had hall sensor capture sequence of 6-->4-->5-->1-->3-->2-->6-->4--> and so on
|
||||
|
||||
IR2136 3-ph bridge driver is used for testing this example code
|
||||
|
||||
User needs to make changes according to the motor and gate driver ic used
|
||||
|
||||
|
||||
## Step 1: Pin assignment
|
||||
* The gpio init function initializes:
|
||||
* GPIO15 is assigned as the MCPWM signal for 1H(UH)
|
||||
* GPIO02 is assigned as the MCPWM signal for 1L(UL)
|
||||
* GPIO00 is assigned as the MCPWM signal for 2H(VH)
|
||||
* GPIO04 is assigned as the MCPWM signal for 2L(VL)
|
||||
* GPIO16 is assigned as the MCPWM signal for 3H(WH)
|
||||
* GPIO17 is assigned as the MCPWM signal for 3L(WL)
|
||||
* GPIO25 is assigned as the MCPWM capture signal for Hall A
|
||||
* GPIO26 is assigned as the MCPWM capture signal for HALL B
|
||||
* GPIO27 is assigned as the MCPWM capture signal for HALL C
|
||||
|
||||
|
||||
## Step 2: Connection
|
||||
* Connect GPIO15 with 1H/UH of BLDC motor driver
|
||||
* Connect GPIO02 with 1L/UL of BLDC motor driver
|
||||
* Connect GPIO00 with 2H/VH of BLDC motor driver
|
||||
* Connect GPIO04 with 2L/VL of BLDC motor driver
|
||||
* Connect GPIO16 with 3H/WH of BLDC motor driver
|
||||
* Connect GPIO17 with 3L/WL of BLDC motor driver
|
||||
* Connect GPIO25 to hall sensor A output
|
||||
* Connect GPIO26 to hall sensor B output
|
||||
* Connect GPIO27 to hall sensor C output
|
||||
|
||||
|
||||
## Step 3: Initialize MCPWM
|
||||
* You need to set the frequency and duty cycle of MCPWM timer
|
||||
* You need to set the MCPWM channel you want to use, and bind the channel with one of the timers
|
||||
* You need to set the capture unit, for POS/NEG edge capture
|
||||
* Also reversing the hall sensor BIT weight, will make bldc motor rotate CW or CCW
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "mcpwm_bldc_control_hall_sensor_example.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
@@ -0,0 +1,323 @@
|
||||
|
||||
/* MCPWM BLDC control Test code
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The following examples uses mcpwm module to control bldc motor vary its speed continiously
|
||||
* The BLDC motor used for testing this code had sequence of 6-->4-->5-->1-->3-->2-->6-->4--> and so on
|
||||
* IR2136 3-ph bridge driver is used for testing this example code
|
||||
* User needs to make changes according to the motor and gate driver ic used
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_attr.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "driver/mcpwm.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
|
||||
#define INITIAL_DUTY 10.0 //initial duty cycle is 10.0%
|
||||
#define MCPWM_GPIO_INIT 0 //select which function to use to initialize gpio signals
|
||||
|
||||
#define GPIO_HALL_TEST_SIGNAL 0 //Make this 1 to enable generation of hall sensors test signal on GPIO13, 12, 14
|
||||
#define CHANGE_DUTY_CONTINUOUSLY 0 //Make this 1 to change duty continuously
|
||||
|
||||
#define CAP_SIG_NUM 3 //three capture signals from HALL-A, HALL-B, HALL-C
|
||||
#define CAP0_INT_EN BIT(27) //Capture 0 interrupt bit
|
||||
#define CAP1_INT_EN BIT(28) //Capture 1 interrupt bit
|
||||
#define CAP2_INT_EN BIT(29) //Capture 2 interrupt bit
|
||||
|
||||
#define GPIO_PWM0A_OUT 15 //Set GPIO 15 as PWM0A
|
||||
#define GPIO_PWM0B_OUT 02 //Set GPIO 02 as PWM0B
|
||||
#define GPIO_PWM1A_OUT 00 //Set GPIO 00 as PWM1A
|
||||
#define GPIO_PWM1B_OUT 04 //Set GPIO 04 as PWM1B
|
||||
#define GPIO_PWM2A_OUT 16 //Set GPIO 16 as PWM2A
|
||||
#define GPIO_PWM2B_OUT 17 //Set GPIO 17 as PWM2B
|
||||
#define GPIO_CAP0_IN 25 //Set GPIO 25 as CAP0
|
||||
#define GPIO_CAP1_IN 26 //Set GPIO 26 as CAP1
|
||||
#define GPIO_CAP2_IN 27 //Set GPIO 27 as CAP2
|
||||
|
||||
typedef struct {
|
||||
uint32_t capture_signal;
|
||||
mcpwm_capture_signal_t sel_cap_signal;
|
||||
} capture;
|
||||
|
||||
static uint32_t hall_sensor_value = 0;
|
||||
static uint32_t hall_sensor_previous = 0;
|
||||
|
||||
xQueueHandle cap_queue;
|
||||
|
||||
static mcpwm_dev_t *MCPWM[2] = {&MCPWM0, &MCPWM1};
|
||||
|
||||
static void mcpwm_example_gpio_initialize(void)
|
||||
{
|
||||
printf("initializing mcpwm bldc control gpio...\n");
|
||||
#if MCPWM_GPIO_INIT
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, GPIO_PWM0A_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0B, GPIO_PWM0B_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1A, GPIO_PWM1A_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1B, GPIO_PWM1B_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM2A, GPIO_PWM2A_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM2B, GPIO_PWM2B_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_0, GPIO_CAP0_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_1, GPIO_CAP1_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_2, GPIO_CAP2_IN);
|
||||
#else
|
||||
mcpwm_pin_config_t pin_config = {
|
||||
.mcpwm0a_out_num = GPIO_PWM0A_OUT,
|
||||
.mcpwm0b_out_num = GPIO_PWM0B_OUT,
|
||||
.mcpwm1a_out_num = GPIO_PWM1A_OUT,
|
||||
.mcpwm1b_out_num = GPIO_PWM1B_OUT,
|
||||
.mcpwm2a_out_num = GPIO_PWM2A_OUT,
|
||||
.mcpwm2b_out_num = GPIO_PWM2B_OUT,
|
||||
.mcpwm_cap0_in_num = GPIO_CAP0_IN,
|
||||
.mcpwm_cap1_in_num = GPIO_CAP1_IN,
|
||||
.mcpwm_cap2_in_num = GPIO_CAP2_IN,
|
||||
.mcpwm_sync0_in_num = -1, //Not used
|
||||
.mcpwm_sync1_in_num = -1, //Not used
|
||||
.mcpwm_sync2_in_num = -1, //Not used
|
||||
.mcpwm_fault0_in_num = -1, //Not used
|
||||
.mcpwm_fault1_in_num = -1, //Not used
|
||||
.mcpwm_fault2_in_num = -1 //Not used
|
||||
};
|
||||
mcpwm_set_pin(MCPWM_UNIT_0, &pin_config);
|
||||
#endif
|
||||
gpio_pulldown_en(GPIO_CAP0_IN); //Enable pull down on CAP0 signal
|
||||
gpio_pulldown_en(GPIO_CAP1_IN); //Enable pull down on CAP1 signal
|
||||
gpio_pulldown_en(GPIO_CAP2_IN); //Enable pull down on CAP2 signal
|
||||
}
|
||||
|
||||
#if GPIO_HALL_TEST_SIGNAL
|
||||
/**
|
||||
* @brief Set gpio 13, 12, 14 as our test signal of hall sensors, that generates high-low waveform continuously
|
||||
* Attach this pins to GPIO 27, 26, 25 respectively for capture unit
|
||||
*/
|
||||
static void gpio_test_signal(void *arg)
|
||||
{
|
||||
printf("intializing test signal...\n");
|
||||
gpio_config_t gp;
|
||||
gp.intr_type = GPIO_INTR_DISABLE;
|
||||
gp.mode = GPIO_MODE_OUTPUT;
|
||||
gp.pin_bit_mask = GPIO_SEL_13 | GPIO_SEL_12 | GPIO_SEL_14;
|
||||
gpio_config(&gp);
|
||||
while (1) {
|
||||
gpio_set_level(GPIO_NUM_13, 1); //Set H1 high
|
||||
gpio_set_level(GPIO_NUM_12, 0); //Set H2 low
|
||||
gpio_set_level(GPIO_NUM_14, 1); //Set H3 high
|
||||
vTaskDelay(1);
|
||||
gpio_set_level(GPIO_NUM_14, 0); //Set H3 low
|
||||
vTaskDelay(1);
|
||||
gpio_set_level(GPIO_NUM_12, 1); //Set H2 high
|
||||
vTaskDelay(1);
|
||||
gpio_set_level(GPIO_NUM_13, 0); //Set H1 low
|
||||
vTaskDelay(1);
|
||||
gpio_set_level(GPIO_NUM_14, 1); //Set H3 high
|
||||
vTaskDelay(1);
|
||||
gpio_set_level(GPIO_NUM_12, 0); //Set H2 high
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief When interrupt occurs, we receive the counter value and display the time between two rising edge
|
||||
*/
|
||||
static void disp_captured_signal(void *arg)
|
||||
{
|
||||
uint32_t *current_cap_value = (uint32_t *)malloc(sizeof(CAP_SIG_NUM));
|
||||
uint32_t *previous_cap_value = (uint32_t *)malloc(sizeof(CAP_SIG_NUM));
|
||||
capture evt;
|
||||
while (1) {
|
||||
xQueueReceive(cap_queue, &evt, portMAX_DELAY);
|
||||
if (evt.sel_cap_signal == MCPWM_SELECT_CAP0) {
|
||||
current_cap_value[0] = evt.capture_signal - previous_cap_value[0];
|
||||
previous_cap_value[0] = evt.capture_signal;
|
||||
current_cap_value[0] = (current_cap_value[0] / 10000) * (10000000000 / rtc_clk_apb_freq_get());
|
||||
//printf("CAP0 : %d us\n", current_cap_value[0]);
|
||||
}
|
||||
if (evt.sel_cap_signal == MCPWM_SELECT_CAP1) {
|
||||
current_cap_value[1] = evt.capture_signal - previous_cap_value[1];
|
||||
previous_cap_value[1] = evt.capture_signal;
|
||||
current_cap_value[1] = (current_cap_value[1] / 10000) * (10000000000 / rtc_clk_apb_freq_get());
|
||||
//printf("CAP1 : %d us\n", current_cap_value[1]);
|
||||
}
|
||||
if (evt.sel_cap_signal == MCPWM_SELECT_CAP2) {
|
||||
current_cap_value[2] = evt.capture_signal - previous_cap_value[2];
|
||||
previous_cap_value[2] = evt.capture_signal;
|
||||
current_cap_value[2] = (current_cap_value[2] / 10000) * (10000000000 / rtc_clk_apb_freq_get());
|
||||
//printf("CAP2 : %d us\n", current_cap_value[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief this is ISR handler function, here we check for interrupt that triggers rising edge on CAP0 signal and according take action
|
||||
*/
|
||||
static void IRAM_ATTR isr_handler(void *arg)
|
||||
{
|
||||
uint32_t mcpwm_intr_status;
|
||||
capture evt;
|
||||
mcpwm_intr_status = MCPWM[MCPWM_UNIT_0]->int_st.val; //Read interrupt status
|
||||
if (mcpwm_intr_status & CAP0_INT_EN) { //Check for interrupt on rising edge on CAP0 signal
|
||||
evt.capture_signal = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP0); //get capture signal counter value
|
||||
evt.sel_cap_signal = MCPWM_SELECT_CAP0;
|
||||
xQueueSendFromISR(cap_queue, &evt, NULL);
|
||||
}
|
||||
if (mcpwm_intr_status & CAP1_INT_EN) { //Check for interrupt on rising edge on CAP1 signal
|
||||
evt.capture_signal = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP1); //get capture signal counter value
|
||||
evt.sel_cap_signal = MCPWM_SELECT_CAP1;
|
||||
xQueueSendFromISR(cap_queue, &evt, NULL);
|
||||
}
|
||||
if (mcpwm_intr_status & CAP2_INT_EN) { //Check for interrupt on rising edge on CAP2 signal
|
||||
evt.capture_signal = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP2); //get capture signal counter value
|
||||
evt.sel_cap_signal = MCPWM_SELECT_CAP2;
|
||||
xQueueSendFromISR(cap_queue, &evt, NULL);
|
||||
}
|
||||
MCPWM[MCPWM_UNIT_0]->int_clr.val = mcpwm_intr_status;
|
||||
}
|
||||
|
||||
#if CHANGE_DUTY_CONTINUOUSLY
|
||||
static void change_duty(void *arg)
|
||||
{
|
||||
int j;
|
||||
while (1) {
|
||||
for (j = 0; j < 18; j++) {
|
||||
//printf("duty cycle: %d\n", (0 +j*50));
|
||||
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, (INITIAL_DUTY + j * 5.0));
|
||||
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B, (INITIAL_DUTY + j * 5.0));
|
||||
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_A, (INITIAL_DUTY + j * 5.0));
|
||||
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_B, (INITIAL_DUTY + j * 5.0));
|
||||
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_A, (INITIAL_DUTY + j * 5.0));
|
||||
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_B, (INITIAL_DUTY + j * 5.0));
|
||||
vTaskDelay(500 / portTICK_RATE_MS);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configure whole MCPWM module for bldc motor control
|
||||
*/
|
||||
static void mcpwm_example_bldc_control(void *arg)
|
||||
{
|
||||
//1. mcpwm gpio initialization
|
||||
mcpwm_example_gpio_initialize();
|
||||
|
||||
//2. initial mcpwm configuration
|
||||
printf("Configuring Initial Parameters of mcpwm bldc control...\n");
|
||||
mcpwm_config_t pwm_config;
|
||||
pwm_config.frequency = 1000; //frequency = 1000Hz
|
||||
pwm_config.cmpr_a = 50.0; //duty cycle of PWMxA = 50.0%
|
||||
pwm_config.cmpr_b = 50.0; //duty cycle of PWMxb = 50.0%
|
||||
pwm_config.counter_mode = MCPWM_UP_COUNTER;
|
||||
pwm_config.duty_mode = MCPWM_DUTY_MODE_1;
|
||||
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); //Configure PWM0A & PWM0B with above settings
|
||||
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_1, &pwm_config); //Configure PWM1A & PWM1B with above settings
|
||||
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_2, &pwm_config); //Configure PWM2A & PWM2B with above settings
|
||||
|
||||
//3. Capture configuration
|
||||
//configure CAP0, CAP1 and CAP2 signal to start capture counter on rising edge
|
||||
//we generate a gpio_test_signal of 20ms on GPIO 12 and connect it to one of the capture signal, the disp_captured_function displays the time between rising edge
|
||||
//In general practice you can connect Capture to external signal, measure time between rising edge or falling edge and take action accordingly
|
||||
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, MCPWM_POS_EDGE, 0); //capture signal on rising edge, pulse num = 0 i.e. 800,000,000 counts is equal to one second
|
||||
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP1, MCPWM_POS_EDGE, 0); //capture signal on rising edge, pulse num = 0 i.e. 800,000,000 counts is equal to one second
|
||||
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP2, MCPWM_POS_EDGE, 0); //capture signal on rising edge, pulse num = 0 i.e. 800,000,000 counts is equal to one second
|
||||
//enable interrupt, so each this a rising edge occurs interrupt is triggered
|
||||
MCPWM[MCPWM_UNIT_0]->int_ena.val = (CAP0_INT_EN | CAP1_INT_EN | CAP2_INT_EN); //Enable interrupt on CAP0, CAP1 and CAP2 signal
|
||||
mcpwm_isr_register(MCPWM_UNIT_0, isr_handler, NULL, ESP_INTR_FLAG_IRAM, NULL); //Set ISR Handler
|
||||
//According to the hall sensor input value take action on PWM0A/0B/1A/1B/2A/2B
|
||||
while (1) {
|
||||
hall_sensor_value = (gpio_get_level(GPIO_NUM_27) * 1) + (gpio_get_level(GPIO_NUM_26) * 2) + (gpio_get_level(GPIO_NUM_25) * 4);
|
||||
if (hall_sensor_value != hall_sensor_previous) {
|
||||
//printf("hall_sen val: %d\n", hall_sensor_value);
|
||||
if (hall_sensor_value == 2) {
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_B);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_high(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_B);
|
||||
//MCPWMXA to duty mode 1 and MCPWMXB to duty mode 0 or vice versa will generate MCPWM compliment signal of each other, there are also other ways to do it
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, MCPWM_DUTY_MODE_1); //Set PWM0A to duty mode one
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B, MCPWM_DUTY_MODE_0); //Set PWM0B back to duty mode zero
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_BYPASS_FED, 100, 100); //Deadtime of 10us
|
||||
}
|
||||
if (hall_sensor_value == 6) {
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B);
|
||||
mcpwm_deadtime_disable(MCPWM_UNIT_0, MCPWM_TIMER_0);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_high(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_B);
|
||||
//MCPWMXA to duty mode 1 and MCPWMXB to duty mode 0 or vice versa will generate MCPWM compliment signal of each other, there are also other ways to do it
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_A, MCPWM_DUTY_MODE_1); //Set PWM2A to duty mode one
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_B, MCPWM_DUTY_MODE_0); //Set PWM2B back to duty mode zero
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_BYPASS_FED, 100, 100); //Deadtime of 10us
|
||||
}
|
||||
if (hall_sensor_value == 4) {
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_B);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_high(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B);
|
||||
//MCPWMXA to duty mode 1 and MCPWMXB to duty mode 0 or vice versa will generate MCPWM compliment signal of each other, there are also other ways to do it
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_A, MCPWM_DUTY_MODE_1); //Set PWM2A to duty mode one
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_B, MCPWM_DUTY_MODE_0); //Set PWM2B back to duty mode zero
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_BYPASS_FED, 100, 100); //Deadtime of 10us
|
||||
}
|
||||
if (hall_sensor_value == 5) {
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_B);
|
||||
mcpwm_deadtime_disable(MCPWM_UNIT_0, MCPWM_TIMER_2);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_high(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B);
|
||||
//MCPWMXA to duty mode 1 and MCPWMXB to duty mode 0 or vice versa will generate MCPWM compliment signal of each other, there are also other ways to do it
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_A, MCPWM_DUTY_MODE_1); //Set PWM1A to duty mode one
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_B, MCPWM_DUTY_MODE_0); //Set PWM1B back to duty mode zero
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_BYPASS_FED, 100, 100); //Deadtime of 10uss
|
||||
}
|
||||
if (hall_sensor_value == 1) {
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_high(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_B);
|
||||
//MCPWMXA to duty mode 1 and MCPWMXB to duty mode 0 or vice versa will generate MCPWM compliment signal of each other, there are also other ways to do it
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_A, MCPWM_DUTY_MODE_1); //Set PWM1A to duty mode one
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_B, MCPWM_DUTY_MODE_0); //Set PWM1B back to duty mode zero
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_BYPASS_FED, 100, 100); //Deadtime of 10uss
|
||||
}
|
||||
if (hall_sensor_value == 3) {
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_B);
|
||||
mcpwm_deadtime_disable(MCPWM_UNIT_0, MCPWM_TIMER_1);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_high(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_B);
|
||||
//MCPWMXA to duty mode 1 and MCPWMXB to duty mode 0 or vice versa will generate MCPWM compliment signal of each other, there are also other ways to do it
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, MCPWM_DUTY_MODE_1); //Set PWM0A to duty mode one
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B, MCPWM_DUTY_MODE_0); //Set PWM0B back to duty mode zero
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_BYPASS_FED, 100, 100); //Deadtime of 10us
|
||||
}
|
||||
hall_sensor_previous = hall_sensor_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
printf("Testing MCPWM BLDC Control...\n");
|
||||
#if CHANGE_DUTY_CONTINUOUSLY
|
||||
xTaskCreate(change_duty, "change_duty", 2048, NULL, 2, NULL);
|
||||
#endif
|
||||
cap_queue = xQueueCreate(1, sizeof(capture)); //comment if you don't want to use capture module
|
||||
#if GPIO_HALL_TEST_SIGNAL
|
||||
xTaskCreate(gpio_test_signal, "gpio_test_signal", 2048, NULL, 2, NULL);
|
||||
#endif
|
||||
xTaskCreate(disp_captured_signal, "mcpwm_config", 4096, NULL, 2, NULL); //comment if you don't want to use capture module
|
||||
xTaskCreate(mcpwm_example_bldc_control, "mcpwm_example_bldc_control", 4096, NULL, 2, NULL);
|
||||
}
|
||||
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(mcpwm_brushed_dc_control)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := mcpwm_brushed_dc_control
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# MCPWM brushed dc motor control Example
|
||||
|
||||
This example will show you how to use MCPWM module to control brushed dc motor, you need to make connection between ESP32 and motor driver
|
||||
|
||||
This code is tested with L298 motor driver, user needs to make changes according to the driver they use
|
||||
|
||||
Motor first moves forward, then backward and then stops for 2 Secs each countinuously
|
||||
|
||||
|
||||
## Step 1: Pin assignment
|
||||
* GPIO15 is assigned as the enable/input 1 for motor driver
|
||||
* GPIO16 is assigned as the enable/input 2 for motor driver
|
||||
|
||||
|
||||
## Step 2: Connection
|
||||
* connect GPIO15 with input 1 of motor driver
|
||||
* connect GPIO16 with input 2 of motor driver
|
||||
|
||||
|
||||
## Step 3: Initialize MCPWM
|
||||
* You need to set the frequency and duty cycle of MCPWM timer
|
||||
* You need to set the MCPWM unit you want to use, and bind the unit with one of the timers
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "mcpwm_brushed_dc_control_example.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
@@ -0,0 +1,95 @@
|
||||
/* brushed dc motor control 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 example will show you how to use MCPWM module to control brushed dc motor.
|
||||
* This code is tested with L298 motor driver.
|
||||
* User may need to make changes according to the motor driver they use.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_attr.h"
|
||||
|
||||
#include "driver/mcpwm.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
|
||||
#define GPIO_PWM0A_OUT 15 //Set GPIO 15 as PWM0A
|
||||
#define GPIO_PWM0B_OUT 16 //Set GPIO 16 as PWM0B
|
||||
|
||||
static void mcpwm_example_gpio_initialize(void)
|
||||
{
|
||||
printf("initializing mcpwm gpio...\n");
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, GPIO_PWM0A_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0B, GPIO_PWM0B_OUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief motor moves in forward direction, with duty cycle = duty %
|
||||
*/
|
||||
static void brushed_motor_forward(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num , float duty_cycle)
|
||||
{
|
||||
mcpwm_set_signal_low(mcpwm_num, timer_num, MCPWM_OPR_B);
|
||||
mcpwm_set_duty(mcpwm_num, timer_num, MCPWM_OPR_A, duty_cycle);
|
||||
mcpwm_set_duty_type(mcpwm_num, timer_num, MCPWM_OPR_A, MCPWM_DUTY_MODE_0); //call this each time, if operator was previously in low/high state
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief motor moves in backward direction, with duty cycle = duty %
|
||||
*/
|
||||
static void brushed_motor_backward(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num , float duty_cycle)
|
||||
{
|
||||
mcpwm_set_signal_low(mcpwm_num, timer_num, MCPWM_OPR_A);
|
||||
mcpwm_set_duty(mcpwm_num, timer_num, MCPWM_OPR_B, duty_cycle);
|
||||
mcpwm_set_duty_type(mcpwm_num, timer_num, MCPWM_OPR_B, MCPWM_DUTY_MODE_0); //call this each time, if operator was previously in low/high state
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief motor stop
|
||||
*/
|
||||
static void brushed_motor_stop(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num)
|
||||
{
|
||||
mcpwm_set_signal_low(mcpwm_num, timer_num, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_low(mcpwm_num, timer_num, MCPWM_OPR_B);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configure MCPWM module for brushed dc motor
|
||||
*/
|
||||
static void mcpwm_example_brushed_motor_control(void *arg)
|
||||
{
|
||||
//1. mcpwm gpio initialization
|
||||
mcpwm_example_gpio_initialize();
|
||||
|
||||
//2. initial mcpwm configuration
|
||||
printf("Configuring Initial Parameters of mcpwm...\n");
|
||||
mcpwm_config_t pwm_config;
|
||||
pwm_config.frequency = 1000; //frequency = 500Hz,
|
||||
pwm_config.cmpr_a = 0; //duty cycle of PWMxA = 0
|
||||
pwm_config.cmpr_b = 0; //duty cycle of PWMxb = 0
|
||||
pwm_config.counter_mode = MCPWM_UP_COUNTER;
|
||||
pwm_config.duty_mode = MCPWM_DUTY_MODE_0;
|
||||
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); //Configure PWM0A & PWM0B with above settings
|
||||
while (1) {
|
||||
brushed_motor_forward(MCPWM_UNIT_0, MCPWM_TIMER_0, 50.0);
|
||||
vTaskDelay(2000 / portTICK_RATE_MS);
|
||||
brushed_motor_backward(MCPWM_UNIT_0, MCPWM_TIMER_0, 30.0);
|
||||
vTaskDelay(2000 / portTICK_RATE_MS);
|
||||
brushed_motor_stop(MCPWM_UNIT_0, MCPWM_TIMER_0);
|
||||
vTaskDelay(2000 / portTICK_RATE_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
printf("Testing brushed motor...\n");
|
||||
xTaskCreate(mcpwm_example_brushed_motor_control, "mcpwm_examlpe_brushed_motor_control", 4096, NULL, 5, NULL);
|
||||
}
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(mcpwm_servo_control)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := mcpwm_servo_control
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# MCPWM servo motor control Example
|
||||
|
||||
This example will show you how to use MCPWM module to control servo motor
|
||||
|
||||
Assign pulse width range and the maximum degree, accordingly the servo will move from 0 to maximum degree continuously
|
||||
|
||||
|
||||
## Step 1: Pin assignment
|
||||
* GPIO18 is assigned as the MCPWM signal for servo motor
|
||||
|
||||
|
||||
## Step 2: Connection
|
||||
* connect GPIO18 with servo pwm signal
|
||||
* other two wires of servo motor are VCC and GND
|
||||
|
||||
|
||||
## Step 3: Initialize MCPWM
|
||||
* You need to set the frequency(generally 50 Hz) and duty cycle of MCPWM timer
|
||||
* You need to set the MCPWM channel you want to use, and bind the channel with one of the timers
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "mcpwm_servo_control_example.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
@@ -0,0 +1,77 @@
|
||||
/* servo motor control 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 "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_attr.h"
|
||||
|
||||
#include "driver/mcpwm.h"
|
||||
#include "soc/mcpwm_periph.h"
|
||||
|
||||
//You can get these value from the datasheet of servo you use, in general pulse width varies between 1000 to 2000 mocrosecond
|
||||
#define SERVO_MIN_PULSEWIDTH 1000 //Minimum pulse width in microsecond
|
||||
#define SERVO_MAX_PULSEWIDTH 2000 //Maximum pulse width in microsecond
|
||||
#define SERVO_MAX_DEGREE 90 //Maximum angle in degree upto which servo can rotate
|
||||
|
||||
static void mcpwm_example_gpio_initialize(void)
|
||||
{
|
||||
printf("initializing mcpwm servo control gpio......\n");
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, 18); //Set GPIO 18 as PWM0A, to which servo is connected
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Use this function to calcute pulse width for per degree rotation
|
||||
*
|
||||
* @param degree_of_rotation the angle in degree to which servo has to rotate
|
||||
*
|
||||
* @return
|
||||
* - calculated pulse width
|
||||
*/
|
||||
static uint32_t servo_per_degree_init(uint32_t degree_of_rotation)
|
||||
{
|
||||
uint32_t cal_pulsewidth = 0;
|
||||
cal_pulsewidth = (SERVO_MIN_PULSEWIDTH + (((SERVO_MAX_PULSEWIDTH - SERVO_MIN_PULSEWIDTH) * (degree_of_rotation)) / (SERVO_MAX_DEGREE)));
|
||||
return cal_pulsewidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configure MCPWM module
|
||||
*/
|
||||
void mcpwm_example_servo_control(void *arg)
|
||||
{
|
||||
uint32_t angle, count;
|
||||
//1. mcpwm gpio initialization
|
||||
mcpwm_example_gpio_initialize();
|
||||
|
||||
//2. initial mcpwm configuration
|
||||
printf("Configuring Initial Parameters of mcpwm......\n");
|
||||
mcpwm_config_t pwm_config;
|
||||
pwm_config.frequency = 50; //frequency = 50Hz, i.e. for every servo motor time period should be 20ms
|
||||
pwm_config.cmpr_a = 0; //duty cycle of PWMxA = 0
|
||||
pwm_config.cmpr_b = 0; //duty cycle of PWMxb = 0
|
||||
pwm_config.counter_mode = MCPWM_UP_COUNTER;
|
||||
pwm_config.duty_mode = MCPWM_DUTY_MODE_0;
|
||||
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); //Configure PWM0A & PWM0B with above settings
|
||||
while (1) {
|
||||
for (count = 0; count < SERVO_MAX_DEGREE; count++) {
|
||||
printf("Angle of rotation: %d\n", count);
|
||||
angle = servo_per_degree_init(count);
|
||||
printf("pulse width: %dus\n", angle);
|
||||
mcpwm_set_duty_in_us(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, angle);
|
||||
vTaskDelay(10); //Add delay, since it takes time for servo to rotate, generally 100ms/60degree rotation at 5V
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
printf("Testing servo motor.......\n");
|
||||
xTaskCreate(mcpwm_example_servo_control, "mcpwm_example_servo_control", 4096, NULL, 5, NULL);
|
||||
}
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(pcnt)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := pcnt
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# PCNT Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example uses the pulse counter module (PCNT) to count the rising edges of the PWM pulses generated by the LED Controller module (LEDC).
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
Pin connection:
|
||||
|
||||
* GPIO4 is the default output GPIO of the 1 Hz pulse generator.
|
||||
* GPIO18 is the default pulse input GPIO. We need to short GPIO4 and GPIO18.
|
||||
* GPIO5 is the default control signal, which can be left floating with internal pull up, or connected to Ground (If GPIO5 is left floating, the value of counter increases with the rising edges of the PWM pulses. If GPIO15 is connected to Ground, the value decreases).
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
|
||||
Run this example, and you can see the following output log on the serial monitor:
|
||||
(Here, GPIO5 is connected to Ground)
|
||||
|
||||
```
|
||||
Current counter value :-1
|
||||
Current counter value :-2
|
||||
Current counter value :-3
|
||||
Current counter value :-4
|
||||
Event PCNT unit[0]; cnt: -5
|
||||
THRES0 EVT
|
||||
Current counter value :-5
|
||||
Current counter value :-6
|
||||
Current counter value :-7
|
||||
Current counter value :-8
|
||||
Current counter value :-9
|
||||
Event PCNT unit[0]; cnt: 0
|
||||
L_LIM EVT
|
||||
ZERO EVT
|
||||
Current counter value :0
|
||||
Current counter value :-1
|
||||
...
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
* program upload failure
|
||||
|
||||
* Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
|
||||
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
|
||||
|
||||
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "pcnt_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
@@ -0,0 +1,216 @@
|
||||
/* Pulse counter module - Example
|
||||
|
||||
For other examples please check:
|
||||
https://github.com/espressif/esp-idf/tree/master/examples
|
||||
|
||||
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 "freertos/FreeRTOS.h"
|
||||
#include "freertos/portmacro.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "driver/ledc.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/pcnt.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
/**
|
||||
* TEST CODE BRIEF
|
||||
*
|
||||
* Use PCNT module to count rising edges generated by LEDC module.
|
||||
*
|
||||
* Functionality of GPIOs used in this example:
|
||||
* - GPIO18 - output pin of a sample 1 Hz pulse generator,
|
||||
* - GPIO4 - pulse input pin,
|
||||
* - GPIO5 - control input pin.
|
||||
*
|
||||
* Load example, open a serial port to view the message printed on your screen.
|
||||
*
|
||||
* To do this test, you should connect GPIO18 with GPIO4.
|
||||
* GPIO5 is the control signal, you can leave it floating with internal pull up,
|
||||
* or connect it to ground. If left floating, the count value will be increasing.
|
||||
* If you connect GPIO5 to GND, the count value will be decreasing.
|
||||
*
|
||||
* An interrupt will be triggered when the counter value:
|
||||
* - reaches 'thresh1' or 'thresh0' value,
|
||||
* - reaches 'l_lim' value or 'h_lim' value,
|
||||
* - will be reset to zero.
|
||||
*/
|
||||
#define PCNT_TEST_UNIT PCNT_UNIT_0
|
||||
#define PCNT_H_LIM_VAL 10
|
||||
#define PCNT_L_LIM_VAL -10
|
||||
#define PCNT_THRESH1_VAL 5
|
||||
#define PCNT_THRESH0_VAL -5
|
||||
#define PCNT_INPUT_SIG_IO 4 // Pulse Input GPIO
|
||||
#define PCNT_INPUT_CTRL_IO 5 // Control GPIO HIGH=count up, LOW=count down
|
||||
#define LEDC_OUTPUT_IO 18 // Output GPIO of a sample 1 Hz pulse generator
|
||||
|
||||
xQueueHandle pcnt_evt_queue; // A queue to handle pulse counter events
|
||||
pcnt_isr_handle_t user_isr_handle = NULL; //user's ISR service handle
|
||||
|
||||
/* A sample structure to pass events from the PCNT
|
||||
* interrupt handler to the main program.
|
||||
*/
|
||||
typedef struct {
|
||||
int unit; // the PCNT unit that originated an interrupt
|
||||
uint32_t status; // information on the event type that caused the interrupt
|
||||
} pcnt_evt_t;
|
||||
|
||||
/* Decode what PCNT's unit originated an interrupt
|
||||
* and pass this information together with the event type
|
||||
* the main program using a queue.
|
||||
*/
|
||||
static void IRAM_ATTR pcnt_example_intr_handler(void *arg)
|
||||
{
|
||||
uint32_t intr_status = PCNT.int_st.val;
|
||||
int i;
|
||||
pcnt_evt_t evt;
|
||||
portBASE_TYPE HPTaskAwoken = pdFALSE;
|
||||
|
||||
for (i = 0; i < PCNT_UNIT_MAX; i++) {
|
||||
if (intr_status & (BIT(i))) {
|
||||
evt.unit = i;
|
||||
/* Save the PCNT event type that caused an interrupt
|
||||
to pass it to the main program */
|
||||
evt.status = PCNT.status_unit[i].val;
|
||||
PCNT.int_clr.val = BIT(i);
|
||||
xQueueSendFromISR(pcnt_evt_queue, &evt, &HPTaskAwoken);
|
||||
if (HPTaskAwoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Configure LED PWM Controller
|
||||
* to output sample pulses at 1 Hz with duty of about 10%
|
||||
*/
|
||||
static void ledc_init(void)
|
||||
{
|
||||
// Prepare and then apply the LEDC PWM timer configuration
|
||||
ledc_timer_config_t ledc_timer;
|
||||
ledc_timer.speed_mode = LEDC_LOW_SPEED_MODE;
|
||||
ledc_timer.timer_num = LEDC_TIMER_1;
|
||||
ledc_timer.duty_resolution = LEDC_TIMER_10_BIT;
|
||||
ledc_timer.freq_hz = 1; // set output frequency at 1 Hz
|
||||
ledc_timer.clk_cfg = LEDC_AUTO_CLK;
|
||||
ledc_timer_config(&ledc_timer);
|
||||
|
||||
// Prepare and then apply the LEDC PWM channel configuration
|
||||
ledc_channel_config_t ledc_channel;
|
||||
ledc_channel.speed_mode = LEDC_LOW_SPEED_MODE;
|
||||
ledc_channel.channel = LEDC_CHANNEL_1;
|
||||
ledc_channel.timer_sel = LEDC_TIMER_1;
|
||||
ledc_channel.intr_type = LEDC_INTR_DISABLE;
|
||||
ledc_channel.gpio_num = LEDC_OUTPUT_IO;
|
||||
ledc_channel.duty = 100; // set duty at about 10%
|
||||
ledc_channel.hpoint = 0;
|
||||
ledc_channel_config(&ledc_channel);
|
||||
}
|
||||
|
||||
/* Initialize PCNT functions:
|
||||
* - configure and initialize PCNT
|
||||
* - set up the input filter
|
||||
* - set up the counter events to watch
|
||||
*/
|
||||
static void pcnt_example_init(void)
|
||||
{
|
||||
/* Prepare configuration for the PCNT unit */
|
||||
pcnt_config_t pcnt_config = {
|
||||
// Set PCNT input signal and control GPIOs
|
||||
.pulse_gpio_num = PCNT_INPUT_SIG_IO,
|
||||
.ctrl_gpio_num = PCNT_INPUT_CTRL_IO,
|
||||
.channel = PCNT_CHANNEL_0,
|
||||
.unit = PCNT_TEST_UNIT,
|
||||
// What to do on the positive / negative edge of pulse input?
|
||||
.pos_mode = PCNT_COUNT_INC, // Count up on the positive edge
|
||||
.neg_mode = PCNT_COUNT_DIS, // Keep the counter value on the negative edge
|
||||
// What to do when control input is low or high?
|
||||
.lctrl_mode = PCNT_MODE_REVERSE, // Reverse counting direction if low
|
||||
.hctrl_mode = PCNT_MODE_KEEP, // Keep the primary counter mode if high
|
||||
// Set the maximum and minimum limit values to watch
|
||||
.counter_h_lim = PCNT_H_LIM_VAL,
|
||||
.counter_l_lim = PCNT_L_LIM_VAL,
|
||||
};
|
||||
/* Initialize PCNT unit */
|
||||
pcnt_unit_config(&pcnt_config);
|
||||
|
||||
/* Configure and enable the input filter */
|
||||
pcnt_set_filter_value(PCNT_TEST_UNIT, 100);
|
||||
pcnt_filter_enable(PCNT_TEST_UNIT);
|
||||
|
||||
/* Set threshold 0 and 1 values and enable events to watch */
|
||||
pcnt_set_event_value(PCNT_TEST_UNIT, PCNT_EVT_THRES_1, PCNT_THRESH1_VAL);
|
||||
pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_THRES_1);
|
||||
pcnt_set_event_value(PCNT_TEST_UNIT, PCNT_EVT_THRES_0, PCNT_THRESH0_VAL);
|
||||
pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_THRES_0);
|
||||
/* Enable events on zero, maximum and minimum limit values */
|
||||
pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_ZERO);
|
||||
pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_H_LIM);
|
||||
pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_L_LIM);
|
||||
|
||||
/* Initialize PCNT's counter */
|
||||
pcnt_counter_pause(PCNT_TEST_UNIT);
|
||||
pcnt_counter_clear(PCNT_TEST_UNIT);
|
||||
|
||||
/* Register ISR handler and enable interrupts for PCNT unit */
|
||||
pcnt_isr_register(pcnt_example_intr_handler, NULL, 0, &user_isr_handle);
|
||||
pcnt_intr_enable(PCNT_TEST_UNIT);
|
||||
|
||||
/* Everything is set up, now go to counting */
|
||||
pcnt_counter_resume(PCNT_TEST_UNIT);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/* Initialize LEDC to generate sample pulse signal */
|
||||
ledc_init();
|
||||
|
||||
/* Initialize PCNT event queue and PCNT functions */
|
||||
pcnt_evt_queue = xQueueCreate(10, sizeof(pcnt_evt_t));
|
||||
pcnt_example_init();
|
||||
|
||||
int16_t count = 0;
|
||||
pcnt_evt_t evt;
|
||||
portBASE_TYPE res;
|
||||
while (1) {
|
||||
/* Wait for the event information passed from PCNT's interrupt handler.
|
||||
* Once received, decode the event type and print it on the serial monitor.
|
||||
*/
|
||||
res = xQueueReceive(pcnt_evt_queue, &evt, 1000 / portTICK_PERIOD_MS);
|
||||
if (res == pdTRUE) {
|
||||
pcnt_get_counter_value(PCNT_TEST_UNIT, &count);
|
||||
printf("Event PCNT unit[%d]; cnt: %d\n", evt.unit, count);
|
||||
if (evt.status & PCNT_EVT_THRES_1) {
|
||||
printf("THRES1 EVT\n");
|
||||
}
|
||||
if (evt.status & PCNT_EVT_THRES_0) {
|
||||
printf("THRES0 EVT\n");
|
||||
}
|
||||
if (evt.status & PCNT_EVT_L_LIM) {
|
||||
printf("L_LIM EVT\n");
|
||||
}
|
||||
if (evt.status & PCNT_EVT_H_LIM) {
|
||||
printf("H_LIM EVT\n");
|
||||
}
|
||||
if (evt.status & PCNT_EVT_ZERO) {
|
||||
printf("ZERO EVT\n");
|
||||
}
|
||||
} else {
|
||||
pcnt_get_counter_value(PCNT_TEST_UNIT, &count);
|
||||
printf("Current counter value :%d\n", count);
|
||||
}
|
||||
}
|
||||
if(user_isr_handle) {
|
||||
//Free the ISR service handle.
|
||||
esp_intr_free(user_isr_handle);
|
||||
user_isr_handle = NULL;
|
||||
}
|
||||
}
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(ir_protocols)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := ir_protocols
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# IR Protocol Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example illustrates how to encode and decode RMT signals with/to common IR protocols (e.g. NEC and RC5).
|
||||
|
||||
[NEC](https://www.sbprojects.net/knowledge/ir/nec.php) and [RC5](https://www.sbprojects.net/knowledge/ir/rc5.php) have different encoding rules, but both can be compatible to RMT data format.
|
||||
|
||||
The example supports building and parsing both normal and extended NEC/RC5 protocol. And also supports `repeat code` which would be sent out if one remote key got pressed for a specific long time.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32 SoC (e.g. ESP32-DevKitC or ESP-WROVER-KIT)
|
||||
* An USB cable for power supply and programming
|
||||
* A 5mm infrared LED (e.g. IR333C) used to transmit encoded IR signals
|
||||
* An infrared receiver module (e.g. IRM-3638T), which integrates a demodulator and AGC circuit.
|
||||
|
||||
Example connection :
|
||||
|
||||
| ESP32 | IR333C | IRM-3638T |
|
||||
| -------- | ------ | --------- |
|
||||
| GPIO18 | Tx | × |
|
||||
| GPIO19 | × | Rx |
|
||||
| VCC 5V | √ | × |
|
||||
| VCC 3.3V | × | √ |
|
||||
| GND | GND | GND |
|
||||
|
||||
|
||||
### Configure the Project
|
||||
|
||||
Open the project configuration menu (`idf.py menuconfig`).
|
||||
|
||||
In the `Example Connection Configuration` menu:
|
||||
|
||||
* Select the infrared protocol used in the example under `Infrared Protocol` option.
|
||||
* Set the GPIO number used for transmitting the IR signal under `RMT TX GPIO` option.
|
||||
* Set the GPIO number used for receiving the demodulated IR signal under `RMT RX GPIO` option.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `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
|
||||
|
||||
Run this example, you will see the following output log (for NEC protocol):
|
||||
```
|
||||
I (2000) example: Send command 0x20 to address 0x10
|
||||
I (2070) example: Scan Code --- addr: 0x0010 cmd: 0x0020
|
||||
I (2220) example: Scan Code (repeat) --- addr: 0x0010 cmd: 0x0020
|
||||
I (4240) example: Send command 0x21 to address 0x10
|
||||
I (4310) example: Scan Code --- addr: 0x0010 cmd: 0x0021
|
||||
I (4460) example: Scan Code (repeat) --- addr: 0x0010 cmd: 0x0021
|
||||
I (6480) example: Send command 0x22 to address 0x10
|
||||
I (6550) example: Scan Code --- addr: 0x0010 cmd: 0x0022
|
||||
I (6700) example: Scan Code (repeat) --- addr: 0x0010 cmd: 0x0022
|
||||
I (8720) example: Send command 0x23 to address 0x10
|
||||
I (8790) example: Scan Code --- addr: 0x0010 cmd: 0x0023
|
||||
I (8940) example: Scan Code (repeat) --- addr: 0x0010 cmd: 0x0023
|
||||
I (10960) example: Send command 0x24 to address 0x10
|
||||
I (11030) example: Scan Code --- addr: 0x0010 cmd: 0x0024
|
||||
I (11180) example: Scan Code (repeat) --- addr: 0x0010 cmd: 0x0024
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For any technical queries, please open an [issue] (https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
||||
@@ -0,0 +1,11 @@
|
||||
set(component_srcs "src/ir_builder_rmt_nec.c"
|
||||
"src/ir_builder_rmt_rc5.c"
|
||||
"src/ir_parser_rmt_nec.c"
|
||||
"src/ir_parser_rmt_rc5.c")
|
||||
|
||||
idf_component_register(SRCS "${component_srcs}"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS ""
|
||||
PRIV_REQUIRES "driver"
|
||||
REQUIRES "")
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := include
|
||||
|
||||
COMPONENT_SRCDIRS := src
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Timings for NEC protocol
|
||||
*
|
||||
*/
|
||||
#define NEC_LEADING_CODE_HIGH_US (9000)
|
||||
#define NEC_LEADING_CODE_LOW_US (4500)
|
||||
#define NEC_PAYLOAD_ONE_HIGH_US (560)
|
||||
#define NEC_PAYLOAD_ONE_LOW_US (1690)
|
||||
#define NEC_PAYLOAD_ZERO_HIGH_US (560)
|
||||
#define NEC_PAYLOAD_ZERO_LOW_US (560)
|
||||
#define NEC_REPEAT_CODE_HIGH_US (9000)
|
||||
#define NEC_REPEAT_CODE_LOW_US (2250)
|
||||
#define NEC_ENDING_CODE_HIGH_US (560)
|
||||
|
||||
/**
|
||||
* @brief Timings for RC5 protocol
|
||||
*
|
||||
*/
|
||||
#define RC5_PULSE_DURATION_US (889)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,272 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
#define IR_TOOLS_FLAGS_PROTO_EXT (1 << 0) /*!< Enable Extended IR protocol */
|
||||
#define IR_TOOLS_FLAGS_INVERSE (1 << 1) /*!< Inverse the IR signal, i.e. take high level as low, and vice versa */
|
||||
|
||||
/**
|
||||
* @brief IR device type
|
||||
*
|
||||
*/
|
||||
typedef void *ir_dev_t;
|
||||
|
||||
/**
|
||||
* @brief IR builder type
|
||||
*
|
||||
*/
|
||||
typedef struct ir_builder_s ir_builder_t;
|
||||
|
||||
/**
|
||||
* @brief IR parser type
|
||||
*
|
||||
*/
|
||||
typedef struct ir_parser_s ir_parser_t;
|
||||
|
||||
/**
|
||||
* @brief Type definition of IR builder
|
||||
*
|
||||
*/
|
||||
struct ir_builder_s {
|
||||
/**
|
||||
* @brief Period time of sending repeat code
|
||||
*
|
||||
*/
|
||||
uint32_t repeat_period_ms;
|
||||
|
||||
/**
|
||||
* @brief Build frame header
|
||||
*
|
||||
* @param[in] builder: Handle of IR builder
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Build frame header successfully
|
||||
* - ESP_FAIL: Build frame header failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*make_head)(ir_builder_t *builder);
|
||||
|
||||
/**
|
||||
* @brief Build logic bit zero
|
||||
*
|
||||
* @param[in] builder: Handle of IR builder
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Build logic bit zero successfully
|
||||
* - ESP_FAIL: Build logic bit zero failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*make_logic0)(ir_builder_t *builder);
|
||||
|
||||
/**
|
||||
* @brief Build logic bit one
|
||||
*
|
||||
* @param[in] builder: Handle of IR builder
|
||||
*
|
||||
* @return
|
||||
* ESP_OK: Build logic bit one successfully
|
||||
* ESP_FAIL: Build logic bit one failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*make_logic1)(ir_builder_t *builder);
|
||||
|
||||
/**
|
||||
* @brief Build frame tail
|
||||
*
|
||||
* @param[in] builder: Handle of IR builder
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Build frame tail successfully
|
||||
* - ESP_FAIL: Build frame tail failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*make_end)(ir_builder_t *builder);
|
||||
|
||||
/**
|
||||
* @brief Build a complete frame
|
||||
*
|
||||
* @param[in] builder: Handle of IR builder
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Build a complete frame successfully
|
||||
* - ESP_FAIL: Build a complete frame failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*build_frame)(ir_builder_t *builder, uint32_t address, uint32_t command);
|
||||
|
||||
/**
|
||||
* @brief Build a repeat frame
|
||||
*
|
||||
* @param[in] builder: Handle of IR builder
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Build a repeat frame successfully
|
||||
* - ESP_FAIL: Build a repeat frame failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*build_repeat_frame)(ir_builder_t *builder);
|
||||
|
||||
/**
|
||||
* @brief Get the result frame after a series of building steps
|
||||
*
|
||||
* @param[in] builder: Handle of IR builder
|
||||
* @param[out] result: Result of frame building, which contains all of the raw data that could be send directly
|
||||
* @param[out] length: Length of result data
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Get result data successfully
|
||||
* - ESP_ERR_INVALID_ARG: Get result data failed because of invalid arguments
|
||||
* - ESP_FAIL: Get result data failed because some other errors occurred
|
||||
*/
|
||||
esp_err_t (*get_result)(ir_builder_t *builder, void *result, uint32_t *length);
|
||||
|
||||
/**
|
||||
* @brief Free resources used by IR builder
|
||||
*
|
||||
* @param[in] builder: Handle of IR builder
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Free resources successfully
|
||||
* - ESP_FAIL: Free resources failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*del)(ir_builder_t *builder);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type definition of IR parser
|
||||
*
|
||||
*/
|
||||
struct ir_parser_s {
|
||||
/**
|
||||
* @brief Input raw data to IR parser
|
||||
*
|
||||
* @param[in] parser: Handle of IR parser
|
||||
* @param[in] raw_data: Raw data which need decoding by IR parser
|
||||
* @param[in] length: Length of raw data
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Input raw data successfully
|
||||
* - ESP_ERR_INVALID_ARG: Input raw data failed because of invalid argument
|
||||
* - ESP_FAIL: Input raw data failed because some other error occurred
|
||||
*/
|
||||
esp_err_t (*input)(ir_parser_t *parser, void *raw_data, uint32_t length);
|
||||
|
||||
/**
|
||||
* @brief Get the scan code after decoding of raw data
|
||||
*
|
||||
* @param[in] parser: Handle of IR parser
|
||||
* @param[out] address: Address of the scan code
|
||||
* @param[out] command: Command of the scan code
|
||||
* @param[out] repeat: Indicate if it's a repeat code
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Get scan code successfully
|
||||
* - ESP_ERR_INVALID_ARG: Get scan code failed because of invalid arguments
|
||||
* - ESP_FAIL: Get scan code failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*get_scan_code)(ir_parser_t *parser, uint32_t *address, uint32_t *command, bool *repeat);
|
||||
|
||||
/**
|
||||
* @brief Free resources used by IR parser
|
||||
*
|
||||
* @param[in] parser: Handle of IR parser
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Free resource successfully
|
||||
* - ESP_FAIL: Free resources fail failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*del)(ir_parser_t *parser);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Configuration type of IR builder
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t buffer_size; /*!< Size of the internal buffer used by IR builder */
|
||||
ir_dev_t dev_hdl; /*!< IR device handle */
|
||||
uint32_t flags; /*!< Flags for IR builder, different flags will enable different features */
|
||||
} ir_builder_config_t;
|
||||
|
||||
/**
|
||||
* @brief Configuration type of IR parser
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
ir_dev_t dev_hdl; /*!< IR device handle */
|
||||
uint32_t flags; /*!< Flags for IR parser, different flags will enable different features */
|
||||
uint32_t margin_us; /*!< Timing parameter, indicating the tolerance to environment noise */
|
||||
} ir_parser_config_t;
|
||||
|
||||
/**
|
||||
* @brief Default configuration for IR builder
|
||||
*
|
||||
*/
|
||||
#define IR_BUILDER_DEFAULT_CONFIG(dev) \
|
||||
{ \
|
||||
.buffer_size = 64, \
|
||||
.dev_hdl = dev, \
|
||||
.flags = 0, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default configuration for IR parser
|
||||
*
|
||||
*/
|
||||
#define IR_PARSER_DEFAULT_CONFIG(dev) \
|
||||
{ \
|
||||
.dev_hdl = dev, \
|
||||
.flags = 0, \
|
||||
.margin_us = 200, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creat a NEC protocol builder
|
||||
*
|
||||
* @param config: configuration of NEC builder
|
||||
* @return
|
||||
* Handle of NEC builder or NULL
|
||||
*/
|
||||
ir_builder_t *ir_builder_rmt_new_nec(const ir_builder_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Creat a RC5 protocol builder
|
||||
*
|
||||
* @param config: configuration of RC5 builder
|
||||
* @return
|
||||
* Handle of RC5 builder or NULL
|
||||
*/
|
||||
ir_builder_t *ir_builder_rmt_new_rc5(const ir_builder_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Creat a NEC protocol parser
|
||||
*
|
||||
* @param config: configuration of NEC parser
|
||||
* @return
|
||||
* Handle of NEC parser or NULL
|
||||
*/
|
||||
ir_parser_t *ir_parser_rmt_new_nec(const ir_parser_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Creat a RC5 protocol parser
|
||||
*
|
||||
* @param config: configuration of RC5 parser
|
||||
* @return
|
||||
* Handle of RC5 parser or NULL
|
||||
*/
|
||||
ir_parser_t *ir_parser_rmt_new_rc5(const ir_parser_config_t *config);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user