添加智能灯固件代码

This commit is contained in:
kerwincui
2021-07-13 17:14:51 +08:00
parent 332f74dd17
commit ecc0b91b8b
2568 changed files with 229441 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
# Peripherals Examples
This section provides examples how to configure and use ESP32s 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.

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(adc)

View File

@@ -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

View File

@@ -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.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "adc1_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -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));
}
}

View File

@@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(adc2)

View File

@@ -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

View File

@@ -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.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "adc2_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -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

View File

@@ -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 );
}
}

View File

@@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(gpio)

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "gpio_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@@ -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);
}
}

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(i2c-example)

View File

@@ -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

View File

@@ -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 ESP32s 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: ** Theres 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.)

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "i2c_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -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

View File

@@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@@ -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);
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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: ** Theres 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 dont 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 cant 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.)

View File

@@ -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()

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "i2ctools_example_main.c"
"cmd_i2ctools.c"
INCLUDE_DIRS ".")

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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

View File

@@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@@ -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));
}

View File

@@ -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,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 storage, data, fat, , 1M,

View File

@@ -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

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp32-i2s-driver-example)

View File

@@ -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

View File

@@ -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.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "i2s_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -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 := .

View File

@@ -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;
}
}

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(i2s-adc-dac)

View File

@@ -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

View File

@@ -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.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "app_main.c"
INCLUDE_DIRS ".")

View File

@@ -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

View File

@@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -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,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 storage, data, fat, , 2M,

View File

@@ -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")

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ledc)

View File

@@ -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

View File

@@ -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.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "ledc_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@@ -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);
}
}

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mcpwm_basic_config)

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "mcpwm_basic_config_example.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@@ -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);
}

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mcpwm_bldc_control_hall_sensor)

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "mcpwm_bldc_control_hall_sensor_example.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@@ -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);
}

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mcpwm_brushed_dc_control)

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "mcpwm_brushed_dc_control_example.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@@ -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);
}

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mcpwm_servo_control)

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "mcpwm_servo_control_example.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@@ -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);
}

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(pcnt)

View File

@@ -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

View File

@@ -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.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "pcnt_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@@ -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;
}
}

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ir_protocols)

View File

@@ -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

View File

@@ -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.

View File

@@ -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 "")

View File

@@ -0,0 +1,3 @@
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_SRCDIRS := src

View File

@@ -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

View File

@@ -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