diff --git a/components/shift_reg/.eil.yml b/components/shift_reg/.eil.yml new file mode 100644 index 00000000..de897d31 --- /dev/null +++ b/components/shift_reg/.eil.yml @@ -0,0 +1,20 @@ +name: shift_reg +description: Driver for generic shift register interface. +version: 0.0.1 +groups: + - misc +code_owners: JaimeAlbq +depends: + - name: driver + - name: log + - name: esp_idf_lib_helpers +thread_safe: yes +targets: + - esp32 + - esp8266 + - esp32s2 + - esp32c3 +license: ISC +copyrights: + - name: JaimeAlbq + year: 2022 diff --git a/components/shift_reg/CMakeLists.txt b/components/shift_reg/CMakeLists.txt new file mode 100644 index 00000000..47c458a3 --- /dev/null +++ b/components/shift_reg/CMakeLists.txt @@ -0,0 +1,11 @@ +if(${IDF_TARGET} STREQUAL esp8266) + set(req esp8266 log esp_idf_lib_helpers) +else() + set(req driver log esp_idf_lib_helpers) +endif() + +idf_component_register( + SRCS shift_reg.c + INCLUDE_DIRS . + REQUIRES ${req} +) diff --git a/components/shift_reg/LICENSE b/components/shift_reg/LICENSE new file mode 100644 index 00000000..2f240711 --- /dev/null +++ b/components/shift_reg/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2022 Jaime Albuquerque + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/components/shift_reg/component.mk b/components/shift_reg/component.mk new file mode 100644 index 00000000..f966ad15 --- /dev/null +++ b/components/shift_reg/component.mk @@ -0,0 +1,7 @@ +COMPONENT_ADD_INCLUDEDIRS = . + +ifdef CONFIG_IDF_TARGET_ESP8266 +COMPONENT_DEPENDS = esp8266 log esp_idf_lib_helpers +else +COMPONENT_DEPENDS = driver log esp_idf_lib_helpers +endif diff --git a/components/shift_reg/shift_reg.c b/components/shift_reg/shift_reg.c new file mode 100644 index 00000000..0d1595bc --- /dev/null +++ b/components/shift_reg/shift_reg.c @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2022 Jaime Albuquerque + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "shift_reg.h" + +static char *tag = "shift_reg"; + +esp_err_t shift_reg_init(shift_reg_config_t *dev) +{ + esp_err_t err = ESP_FAIL; + + if (dev == NULL) + { + ESP_LOGE(tag, "%s: must have the configuration of the shift register", __func__); + err = ESP_ERR_INVALID_ARG; + return err; + } + + dev->reg_value = (uint8_t *)malloc(dev->num_reg); // Create an array with all registers + + if (dev->reg_value == NULL) + { + ESP_LOGE(tag, "%s: no heap memory to allocate reg_value", __func__); + err = ESP_ERR_NO_MEM; + return err; + } + + memset(dev->reg_value, 0, dev->num_reg); // Start all registers as 0 + + gpio_config_t io_conf; + + // disable interrupt + io_conf.intr_type = GPIO_INTR_DISABLE; + + switch (dev->mode.dir) + { + case SHIFT_DIR_OUTPUT: + // set as output mode + io_conf.mode = GPIO_MODE_OUTPUT; + uint32_t buf32_0 = 0; + uint32_t buf32_1 = 0; + uint64_t result = 0; + + if (dev->pin.clk >= 32) + buf32_1 |= 1 << (dev->pin.clk - 32); + else + buf32_0 |= 1 << dev->pin.clk; + + if (dev->pin.latch >= 32) + buf32_1 |= 1 << (dev->pin.latch - 32); + else + buf32_0 |= 1 << dev->pin.latch; + + if (dev->pin.data >= 32) + buf32_1 |= 1 << (dev->pin.data - 32); + else + buf32_0 |= 1 << dev->pin.data; + + result = ((uint64_t)buf32_1 << 32) | ((uint64_t)buf32_0 << 0); + + io_conf.pin_bit_mask = result; + + break; + + default: + ESP_LOGE(tag, "%s: Mode of shift register not found", __func__); + err = ESP_ERR_INVALID_ARG; + break; + } + // 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 + err = gpio_config(&io_conf); + + return err; +} + +esp_err_t shift_reg_deinit(shift_reg_config_t *dev) +{ + if (dev == NULL) + { + ESP_LOGE(tag, "%s: must have a valid argument;", __func__); + return ESP_ERR_INVALID_ARG; + } + + free(dev->reg_value); + return ESP_OK; +} + +esp_err_t shift_reg_send(shift_reg_config_t *dev, uint8_t *data, uint8_t len) +{ + esp_err_t err = ESP_FAIL; + + if (dev == NULL || len > dev->num_reg || data == NULL) + { + ESP_LOGE(tag, "%s: must have a valid argument;", __func__); + err = ESP_ERR_INVALID_ARG; + return err; + } + + if (dev->mode.bit_mode == SHIFT_BIT_MODE_MSB) + { + for (uint8_t i = 0; i < len; i++) + { + shift_reg_send8bits(dev, data[i]); + dev->reg_value[i] = data[i]; + } + } + else + { + for (int8_t i = len - 1; i >= 0; i--) + { + shift_reg_send8bits(dev, data[i]); + dev->reg_value[i] = data[i]; + } + } + + err = ESP_OK; + + return err; +} + +esp_err_t shift_reg_send8bits(shift_reg_config_t *dev, uint8_t data) +{ + esp_err_t err = ESP_FAIL; + + if (dev == NULL) + { + ESP_LOGE(tag, "%s: must have a valid argument;", __func__); + err = ESP_ERR_INVALID_ARG; + return err; + } + + if (dev->mode.bit_mode == SHIFT_BIT_MODE_MSB) + { + // MSB Mode + for (int8_t i = 7; i >= 0; i--) + { + gpio_set_level(dev->pin.data, (data >> i) & 1); + + gpio_set_level(dev->pin.clk, true); + ets_delay_us(1); + gpio_set_level(dev->pin.clk, false); + ets_delay_us(1); + } + } + else + { + // LSB Mode + for (int8_t i = 0; i < 8; i++) + { + gpio_set_level(dev->pin.data, (data >> i) & 1); + + gpio_set_level(dev->pin.clk, true); + ets_delay_us(1); + gpio_set_level(dev->pin.clk, false); + ets_delay_us(1); + } + } + + err = ESP_OK; + + return err; +} + +esp_err_t shift_reg_latch(shift_reg_config_t *dev) +{ + esp_err_t err = ESP_FAIL; + + if (dev == NULL) + { + ESP_LOGE(tag, "%s: must have a valid argument;", __func__); + err = ESP_ERR_INVALID_ARG; + return err; + } + + gpio_set_level(dev->pin.latch, true); + ets_delay_us(1); + gpio_set_level(dev->pin.latch, false); + ets_delay_us(1); + + err = ESP_OK; + + return err; +} diff --git a/components/shift_reg/shift_reg.h b/components/shift_reg/shift_reg.h new file mode 100644 index 00000000..2b9184f9 --- /dev/null +++ b/components/shift_reg/shift_reg.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2022 Jaime Albuquerque + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * @file shift_reg.h + * @defgroup shift_reg shift_reg + * @{ + * + * ESP-IDF driver for generic shift register using 3 pins: + * - data + * - clock + * - latch + * + */ + +#if !defined(__SHIFT_REG__H__) +#define __SHIFT_REG__H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include +#include +#include +#include + +/** + * @brief Direction of the data + * + * @todo Add Input mode + * + */ +typedef enum { + SHIFT_DIR_OUTPUT = 0, +} shift_reg_dir_t; + +/** + * @brief Register orientation mode + * + */ +typedef enum { + SHIFT_BIT_MODE_LSB = 0, + SHIFT_BIT_MODE_MSB, +} shift_reg_bit_mode_t; + +/** + * @brief This structure stores the mode of the shift register + * + */ +typedef struct { + shift_reg_dir_t dir : 2; // Direction mode of the shift register + shift_reg_bit_mode_t bit_mode : 1; // Bit mode +} shift_reg_mode_t; + +/** + * @brief This structure stores all used pins to interface with the shift register + * + */ +typedef struct { + gpio_num_t clk; // Clock pin + gpio_num_t data; // Data/Signal pin + gpio_num_t latch; // Latch pin +} shift_reg_pin_t; + +/** + * @brief This structure stores all needed configuration of the shift register component + * + */ +typedef struct { // Configuration of shift register + uint8_t num_reg; // Number of shift registers + uint8_t *reg_value; // Last value of all registers + shift_reg_mode_t mode; // Direction adn bit mode + shift_reg_pin_t pin; // Set of pins +} shift_reg_config_t; + +/** + * @brief Initialize the microcontroller to do the output + * + * @param dev shift register configuration + * @return `ESP_OK` on success + */ +esp_err_t shift_reg_init(shift_reg_config_t *dev); + +/** + * @brief Free used memory + * + * @param dev shift register configuration + * @return `ESP_OK` on success + */ +esp_err_t shift_reg_deinit(shift_reg_config_t *dev); + +/** + * @brief Send the whole data + * + * @param dev shift register configuration + * @param data data vector + * @param len length of the data + * @return `ESP_OK` on success + */ +esp_err_t shift_reg_send(shift_reg_config_t *dev, uint8_t *data, uint8_t len); + +/** + * @brief Send 1 byte of data + * + * @param dev shift register configuration + * @param data 1 byte data + * @return `ESP_OK` on success + */ +esp_err_t shift_reg_send8bits(shift_reg_config_t *dev, uint8_t data); + +/** + * @brief Latch the data inside of the shift register + * + * @param dev shift register configuration + * @return `ESP_OK` on success + */ +esp_err_t shift_reg_latch(shift_reg_config_t *dev); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif // __SHIFT_REG__H__ diff --git a/docs/source/groups/shift_reg.rst b/docs/source/groups/shift_reg.rst new file mode 100644 index 00000000..252b2de2 --- /dev/null +++ b/docs/source/groups/shift_reg.rst @@ -0,0 +1,111 @@ +``Shift Register`` +================== + +This driver can be used as an interface with a shift register in (still +in progress) and out (such as +`74HC595 `__). + +Usage +----- + +To use the library, it needs to be configured before initialize. To do +so a ``shift_reg_config_t`` needs to be set. + +Configuration +~~~~~~~~~~~~~ + +The ``shift_reg_config_t`` struct store all necessaries configurtions +and value of aech register. + +- ``uint8_t num_reg`` - Number of registers which will be used +- ``uint8_t *reg_value`` - Vector for the last value of all registers; + it can be used for know what is the actual value of the registers + +Mode configuration +~~~~~~~~~~~~~~~~~~ + +- ``struct mode`` + + - ``shift_reg_dir_t dir`` - Direction mode of the shift register + - ``shift_reg_bit_mode_t bit_mode`` - Bit mode + +Pin configurations +~~~~~~~~~~~~~~~~~~ + +- ``struct pin`` + + - ``gpio_num_t clk`` - Clock pin + - ``gpio_num_t data`` - Data/Signal pin + - ``gpio_num_t latch`` - Latch pin + +Direcition mode +^^^^^^^^^^^^^^^ + +The ``shift_reg_dir_t`` says the direction of the register. + +- ``SHIFT_DIR_OUTPUT`` - Set the register as output +- ``SHIFT_DIR_INPUT`` - Set the register as input +- ``SHIFT_DIR_INPUT_OUTPUT`` - Set the register as output and input + +First bit configuration +^^^^^^^^^^^^^^^^^^^^^^^ + +The ``shift_reg_bit_mode_t`` says the bit mode of the data. + +- ``SHIFT_BIT_MODE_LSB`` - Start send data from the lowest significant + bit +- ``SHIFT_BIT_MODE_MSB`` - Start send data from the most significandt + bit + +Initialization +~~~~~~~~~~~~~~ + +To initialize the shift register it is going to need to call the +``esp_err_t shift_reg_init(shift_reg_config_t *dev)``, by passing the +created shift register configurations. + +De-initialization +~~~~~~~~~~~~~~~~~ + +Since the ``uint8_t *reg_value`` is created accordingly of the number of +registers, the vector allocate the necessary size in the heap memory, so +``esp_err_t shift_reg_deinit(shift_reg_config_t *dev)`` can be used to +free this memory. + +Sending the data +~~~~~~~~~~~~~~~~ + +To send the intended data, call the +``esp_err_t shift_reg_send(uint8_t *data, uint8_t len, shift_reg_config_t *dev)`` +function, where: + +- ``data`` - the address of the bening of the data +- ``len`` - the length of the data in bytes +- ``dev`` - the shift register interface to be used + +**NOTE**: The data sent will be just in the internal memory +(register(s)) of the shift register(s), and not reflected in the pins. +See lataching pins to see how the pins can be set. + +Latching pins +~~~~~~~~~~~~~ + +To set the pins as it is in the internal memory of the register(s), the +function ``esp_err_t shift_reg_latch(shift_reg_config_t *dev);`` needs +to be called. It will be latched by the passed shifter interface. + +TODO +---- + +- ☐ Implement input shift register +- ☐ Interface with `hd44780 <../hd44780/>`__ + +Author +====== + +Jaime Albuquerque + +- GitHub: `jaimealbq `__ +- GitLab: `jaimealbq `__ +- LinkedIn: `Jaime + Albuquerque `__ diff --git a/examples/shift_reg/default/CMakeLists.txt b/examples/shift_reg/default/CMakeLists.txt new file mode 100644 index 00000000..4cf2c302 --- /dev/null +++ b/examples/shift_reg/default/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../../../components) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(example_example) diff --git a/examples/shift_reg/default/Makefile b/examples/shift_reg/default/Makefile new file mode 100644 index 00000000..4de3bb31 --- /dev/null +++ b/examples/shift_reg/default/Makefile @@ -0,0 +1,6 @@ +#V := 1 +PROJECT_NAME := example_example + +EXTRA_COMPONENT_DIRS := $(CURDIR)/../../../components + +include $(IDF_PATH)/make/project.mk diff --git a/examples/shift_reg/default/README.md b/examples/shift_reg/default/README.md new file mode 100644 index 00000000..67cc76ea --- /dev/null +++ b/examples/shift_reg/default/README.md @@ -0,0 +1,39 @@ +# Shift register out + +## What the example does + +The example shift out periodicly to 2 registers [75HC595](https://www.ti.com/lit/ds/symlink/sn74hc595.pdf) a 2 bytes long number, which gets increments. + +## Configuration + +The following configuration was used: + +| 74HC595 | GPIO | +|---------|------| +| Serial Clock | `GPIO_NUM_12` | +| Data/Signal | `GPIO_NUM_15` | +| Latch | `GPIO_NUM_13` | + +| Mode | enum | +|------|------| +| output | `SHIFT_DIR_OUTPUT` | +| Most Significant Bit (MSB) | `SHIFT_BIT_MODE_MSB` | + +``` +static shift_reg_config_t shifter = { + .num_reg = 2, + .mode = { + .dir = SHIFT_DIR_OUTPUT, + .bit_mode = SHIFT_BIT_MODE_MSB, + }, + .pin = { + .clk = GPIO_NUM_12, + .data = GPIO_NUM_13, + .latch = GPIO_NUM_15, + }, + }; +``` + +## Notes + +For more information, please vist the component directory [shift_reg](/components/shift_reg/). \ No newline at end of file diff --git a/examples/shift_reg/default/main/CMakeLists.txt b/examples/shift_reg/default/main/CMakeLists.txt new file mode 100644 index 00000000..cf2c455c --- /dev/null +++ b/examples/shift_reg/default/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/shift_reg/default/main/component.mk b/examples/shift_reg/default/main/component.mk new file mode 100644 index 00000000..004b18e6 --- /dev/null +++ b/examples/shift_reg/default/main/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_INCLUDEDIRS = . diff --git a/examples/shift_reg/default/main/main.c b/examples/shift_reg/default/main/main.c new file mode 100644 index 00000000..21cf1655 --- /dev/null +++ b/examples/shift_reg/default/main/main.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 Jaime Albuqueruqe + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include +#include +#include + +void app_main(void) +{ + static shift_reg_config_t shifter = { + .num_reg = 2, + .mode = { + .dir = SHIFT_DIR_OUTPUT, + .bit_mode = SHIFT_BIT_MODE_MSB, + }, + .pin = { + .clk = GPIO_NUM_12, + .data = GPIO_NUM_13, + .latch = GPIO_NUM_15, + }, + }; + + + ESP_ERROR_CHECK(shift_reg_init(&shifter)); + + while (true) { + uint8_t value [2]; + for (uint16_t i = 0; i < 0xFFFF; i++) { + value[0] = (uint8_t) (i & 0xFF); + value[1] = (uint8_t) ((i >> 8) & 0xFF); + ESP_ERROR_CHECK(shift_reg_send(&shifter, value, 2)); + ESP_ERROR_CHECK(shift_reg_latch(&shifter)); + ESP_LOGI(__func__, "reg_value: 0x%02X 0x%02X", shifter.reg_value[0], shifter.reg_value[1]); + vTaskDelay(2 / pdMS_TO_TICKS); + } + } +}