diff --git a/.gitignore b/.gitignore index 65225d8..496ee2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1 @@ -.DS_Store -.vscode -build -sdkconfig -sdkconfig.old \ No newline at end of file +.DS_Store \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ab5c51..d1900c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1 +1 @@ -idf_component_register(SRCS "main.c" INCLUDE_DIRS "include") \ No newline at end of file +idf_component_register(SRCS "zh_cd74hc4067.c" INCLUDE_DIRS "include" REQUIRES driver) \ No newline at end of file diff --git a/README.md b/README.md index 3df3e4d..f011010 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,62 @@ -# esp_component_template +# ESP32 ESP-IDF and ESP8266 RTOS SDK component for CD74HC4067 16-channel analog multiplexer/demultiplexer -esp_component_template \ No newline at end of file +## Tested on + +1. [ESP8266 RTOS_SDK v3.4](https://docs.espressif.com/projects/esp8266-rtos-sdk/en/latest/index.html#) +2. [ESP32 ESP-IDF v5.4](https://docs.espressif.com/projects/esp-idf/en/release-v5.4/esp32/index.html) + +## Features + +1. Just an electronic galette switch between one of the 16 multiplexer GPIO and one ESP GPIO. +2. Support of all standard operations (digital read/write, interrupt, ADC, PWM, etc). + +## Attention + +1. EN GPIO on CD74HC4067 must be connected to ESP. + +## Using + +In an existing project, run the following command to install the components: + +```text +cd ../your_project/components +git clone http://git.zh.com.ru/alexey.zholtikov/zh_cd74hc4067 +``` + +In the application, add the component: + +```c +#include "zh_cd74hc4067.h" +``` + +## Examples + +Digital read and write: + +```c +#include "zh_cd74hc4067.h" + +void app_main(void) +{ + esp_log_level_set("zh_cd74hc4067", ESP_LOG_NONE); // For ESP8266 first enable "Component config -> Log output -> Enable log set level" via menuconfig. + gpio_config_t pin_config = { + .pin_bit_mask = (1ULL << GPIO_NUM_4)}; // Set control GPIO on ESP. + gpio_config(&pin_config); + zh_cd74hc4067_init_config_t init_config = { + .control_gpio_number = GPIO_NUM_4, + .en_gpio_number = GPIO_NUM_18, + .s0_gpio_number = GPIO_NUM_19, + .s1_gpio_number = GPIO_NUM_20, + .s2_gpio_number = GPIO_NUM_21, + .s3_gpio_number = GPIO_NUM_22, + }; + zh_cd74hc4067_init(&init_config); + gpio_set_direction(zh_cd74hc4067_pin(), GPIO_MODE_OUTPUT); // Set control GPIO on ESP to output. + gpio_set_level(zh_cd74hc4067_set(10), 1); // Connect 10 GPIO on CD74HC4067 and set it to HIGH. + zh_cd74hc4067_set(12); // Connect 12 GPIO on CD74HC4067. + gpio_set_direction(zh_cd74hc4067_pin(), GPIO_MODE_INPUT); // Set control GPIO on ESP to input. + bool level = gpio_get_level(zh_cd74hc4067_set(11)); // Connect 11 GPIO on CD74HC4067 and get it status. + level = gpio_get_level(zh_cd74hc4067_pin()); // Get status of currently connected GPIO on CD74HC4067. + uint8_t gpio = zh_cd74hc4067_get(); // Get currently connected GPIO number on CD74HC4067. +} +``` diff --git a/ds/CD74HC4067 16-Channel Analog Multiplexer:Demultiplexer.pdf b/ds/CD74HC4067 16-Channel Analog Multiplexer:Demultiplexer.pdf new file mode 100644 index 0000000..2bc2366 Binary files /dev/null and b/ds/CD74HC4067 16-Channel Analog Multiplexer:Demultiplexer.pdf differ diff --git a/include/main.h b/include/main.h deleted file mode 100644 index e69de29..0000000 diff --git a/include/zh_cd74hc4067.h b/include/zh_cd74hc4067.h new file mode 100644 index 0000000..0b2481f --- /dev/null +++ b/include/zh_cd74hc4067.h @@ -0,0 +1,55 @@ +#pragma once + +#include "esp_log.h" +#include "driver/gpio.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct // Structure for initial initialization of CD74HC4067 multiplexer. + { + uint8_t s0_gpio_number; // GPIO connected to S0 of CD74HC4067 multiplexer. + uint8_t s1_gpio_number; // GPIO connected to S1 of CD74HC4067 multiplexer. + uint8_t s2_gpio_number; // GPIO connected to S2 of CD74HC4067 multiplexer. + uint8_t s3_gpio_number; // GPIO connected to S3 of CD74HC4067 multiplexer. + uint8_t control_gpio_number; // GPIO connected to SIG of CD74HC4067 multiplexer. + uint8_t en_gpio_number; // GPIO connected to EN of CD74HC4067 multiplexer. + } zh_cd74hc4067_init_config_t; + + /** + * @brief Initialize CD74HC4067 multiplexer. + * + * @param[in] config Pointer to CD74HC4067 initialized configuration structure. Can point to a temporary variable. + * + * @return ESP_OK if success or an error code otherwise. + */ + esp_err_t zh_cd74hc4067_init(const zh_cd74hc4067_init_config_t *config); + + /** + * @brief Set CD74HC4067 GPIO for connect. + * + * @param[in] gpio GPIO for connect. + * + * @return Control GPIO number or -1 otherwise. + */ + gpio_num_t zh_cd74hc4067_set(uint8_t gpio); + + /** + * @brief Get CD74HC4067 connected GPIO. + * + * @return GPIO number. + */ + uint8_t zh_cd74hc4067_get(void); + + /** + * @brief Get CD74HC4067 control GPIO number. + * + * @return Control GPIO number. + */ + gpio_num_t zh_cd74hc4067_pin(void); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/main.c b/main.c deleted file mode 100644 index e69de29..0000000 diff --git a/version.txt b/version.txt index e69de29..afaf360 100644 --- a/version.txt +++ b/version.txt @@ -0,0 +1 @@ +1.0.0 \ No newline at end of file diff --git a/zh_cd74hc4067.c b/zh_cd74hc4067.c new file mode 100644 index 0000000..805b239 --- /dev/null +++ b/zh_cd74hc4067.c @@ -0,0 +1,115 @@ +#include "zh_cd74hc4067.h" + +static const char *TAG = "zh_cd74hc4067"; + +#define ZH_CD74HC4067_LOGI(msg, ...) ESP_LOGI(TAG, msg, ##__VA_ARGS__) +#define ZH_CD74HC4067_LOGW(msg, ...) ESP_LOGW(TAG, msg, ##__VA_ARGS__) +#define ZH_CD74HC4067_LOGE(msg, ...) ESP_LOGE(TAG, msg, ##__VA_ARGS__) +#define ZH_CD74HC4067_LOGE_ERR(msg, err, ...) ESP_LOGE(TAG, "[%s:%d:%s] " msg, __FILE__, __LINE__, esp_err_to_name(err), ##__VA_ARGS__) + +#define ZH_CD74HC4067_CHECK(cond, err, msg, ...) \ + if (!(cond)) \ + { \ + ZH_CD74HC4067_LOGE_ERR(msg, err); \ + return err; \ + } + +static const uint8_t _cd74hc4067_matrix[16][4] = { + {0, 0, 0, 0}, + {1, 0, 0, 0}, + {0, 1, 0, 0}, + {1, 1, 0, 0}, + {0, 0, 1, 0}, + {1, 0, 1, 0}, + {0, 1, 1, 0}, + {1, 1, 1, 0}, + {0, 0, 0, 1}, + {1, 0, 0, 1}, + {0, 1, 0, 1}, + {1, 1, 0, 1}, + {0, 0, 1, 1}, + {1, 0, 1, 1}, + {0, 1, 1, 1}, + {1, 1, 1, 1}}; + +static zh_cd74hc4067_init_config_t _init_config = {0}; +static uint8_t _connected_gpio = 0; +static bool _is_initialized = false; + +static bool _zh_cd74hc4067_gpio_check(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f); + +esp_err_t zh_cd74hc4067_init(const zh_cd74hc4067_init_config_t *config) +{ + ZH_CD74HC4067_LOGI("CD74HC4067 initialization started."); + ZH_CD74HC4067_CHECK(config != NULL, ESP_ERR_INVALID_ARG, "CD74HC4067 initialization failed. Invalid argument."); + if (_is_initialized == true) + { + ZH_CD74HC4067_LOGW("CD74HC4067 initialization failed. CD74HC4067 is already initialized."); + return ESP_OK; + } + ZH_CD74HC4067_CHECK((config->s0_gpio_number < GPIO_NUM_MAX) && (config->s1_gpio_number < GPIO_NUM_MAX) && + (config->s2_gpio_number < GPIO_NUM_MAX) && (config->s3_gpio_number < GPIO_NUM_MAX) && + (config->en_gpio_number < GPIO_NUM_MAX) && (config->control_gpio_number < GPIO_NUM_MAX), + ESP_FAIL, "CD74HC4067 initialization failed. Invalid GPIO number."); + bool gpio_check = _zh_cd74hc4067_gpio_check(config->s0_gpio_number, config->s1_gpio_number, config->s2_gpio_number, config->s3_gpio_number, config->en_gpio_number, config->control_gpio_number); + ZH_CD74HC4067_CHECK(gpio_check == true, ESP_FAIL, "CD74HC4067 initialization failed. Invalid GPIO number."); + gpio_config_t pin_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = (1ULL << config->s0_gpio_number) | (1ULL << config->s1_gpio_number) | (1ULL << config->s2_gpio_number) | + (1ULL << config->s3_gpio_number) | (1ULL << config->en_gpio_number), + }; + esp_err_t err = gpio_config(&pin_config); + ZH_CD74HC4067_CHECK(err == ESP_OK, err, "CD74HC4067 initialization failed. GPIO initialization failed.") + _init_config = *config; + _is_initialized = true; + ZH_CD74HC4067_LOGI("CD74HC4067 initialization completed successfully."); + return ESP_OK; +} + +gpio_num_t zh_cd74hc4067_set(uint8_t gpio) +{ + ZH_CD74HC4067_LOGI("CD74HC4067 connect GPIO started."); + ZH_CD74HC4067_CHECK(_is_initialized == true, ESP_FAIL, "CD74HC4067 connect GPIO failed. CD74HC4067 not initialized."); + ZH_CD74HC4067_CHECK(gpio <= 15, ESP_FAIL, "CD74HC4067 initialization failed. Invalid argument."); + esp_err_t err = gpio_set_level(_init_config.en_gpio_number, 1); + ZH_CD74HC4067_CHECK(err == ESP_OK, ESP_FAIL, "CD74HC4067 connect GPIO failed. GPIO driver internal error."); + err = gpio_set_level(_init_config.s0_gpio_number, _cd74hc4067_matrix[gpio][0]); + ZH_CD74HC4067_CHECK(err == ESP_OK, ESP_FAIL, "CD74HC4067 connect GPIO failed. GPIO driver internal error."); + err = gpio_set_level(_init_config.s1_gpio_number, _cd74hc4067_matrix[gpio][1]); + ZH_CD74HC4067_CHECK(err == ESP_OK, ESP_FAIL, "CD74HC4067 connect GPIO failed. GPIO driver internal error."); + err = gpio_set_level(_init_config.s2_gpio_number, _cd74hc4067_matrix[gpio][2]); + ZH_CD74HC4067_CHECK(err == ESP_OK, ESP_FAIL, "CD74HC4067 connect GPIO failed. GPIO driver internal error."); + err = gpio_set_level(_init_config.s3_gpio_number, _cd74hc4067_matrix[gpio][3]); + ZH_CD74HC4067_CHECK(err == ESP_OK, ESP_FAIL, "CD74HC4067 connect GPIO failed. GPIO driver internal error."); + err = gpio_set_level(_init_config.en_gpio_number, 0); + ZH_CD74HC4067_CHECK(err == ESP_OK, ESP_FAIL, "CD74HC4067 connect GPIO failed. GPIO driver internal error."); + ZH_CD74HC4067_LOGI("CD74HC4067 connect GPIO completed successfully."); + _connected_gpio = gpio; + return (gpio_num_t)_init_config.control_gpio_number; +} + +uint8_t zh_cd74hc4067_get(void) +{ + return _connected_gpio; +} + +gpio_num_t zh_cd74hc4067_pin(void) +{ + return (gpio_num_t)_init_config.control_gpio_number; +} + +static bool _zh_cd74hc4067_gpio_check(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f) +{ + uint8_t matrix[] = {a, b, c, d, e, f}; + for (uint8_t i = 0; i <= sizeof(matrix); ++i) + { + for (uint8_t j = i + 1; j <= sizeof(matrix); ++j) + { + if (matrix[i] == matrix[j]) + { + return false; + } + } + } + return true; +} \ No newline at end of file