Skip to content

Commit

Permalink
shift_reg: add shift_reg component
Browse files Browse the repository at this point in the history
The feature is a driver to interface the microcontroller with shift
registers just using 3 pins: Clock, Data and Latch.

For further information, please, take a look at the component
[README.md](components/shift_reg/README.md).

Feature requested by issue UncleRus#457.
  • Loading branch information
JaimeAlbq committed Oct 9, 2022
1 parent 26f4cf0 commit 2bd3b94
Show file tree
Hide file tree
Showing 14 changed files with 556 additions and 0 deletions.
24 changes: 24 additions & 0 deletions components/shift_reg/.eil.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
components:
- name: shift_reg
description: Driver for generic shift register to expend I/O by writing or reading from it in a serial communication.
group: misc
groups: []
code_owners: jaimealbq
depends:
# FIXME conditional depends
- name: driver
- name: log
- name: esp_idf_lib_helpers
thread_safe: yes
targets:
- name: esp32
- name: esp8266
- name: esp32s2
- name: esp32c3
licenses:
- name: ISC
copyrights:
- author:
name: jaimealbq
year: 2022
11 changes: 11 additions & 0 deletions components/shift_reg/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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}
)
13 changes: 13 additions & 0 deletions components/shift_reg/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Copyright (c) 2022 Jaime Albuquerque <[email protected]>

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.
56 changes: 56 additions & 0 deletions components/shift_reg/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# `Shift Register`
This driver can be used as an interface with a shift register in (still in progress) and out (such as [74HC595](https://www.ti.com/lit/ds/symlink/sn74hc595.pdf)).

## Usage
To use the library, it needs to be configured before initialize. To do so a `shift_reg_config_s` needs to be set.

### Configuration
The `shift_reg_config_s` 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
* `struct mode`
* `shift_reg_dir_t dir` - Direction mode of the shift register
* `shift_reg_bit_mode_t bit_mode` - Bit mode
* `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_s *shft)`, 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_s *shft)` 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_s *shft)` function, where:
* `data` - the address of the bening of the data
* `len` - the length of the data in bytes
* `shft` - 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_s *shft);` 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](https://github.com/jaimealbq)
* GitLab: [jaimealbq](https://gitlab.com/jaimealbq)
* LinkedIn: [Jaime Albuquerque](https://www.linkedin.com/in/jaimealbq)
7 changes: 7 additions & 0 deletions components/shift_reg/component.mk
Original file line number Diff line number Diff line change
@@ -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
209 changes: 209 additions & 0 deletions components/shift_reg/shift_reg.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/*
* Copyright (c) 2022 Jaime Albuquerque <[email protected]>
*
* 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_s *shft)
{
esp_err_t err = ESP_FAIL;

if (shft == NULL)
{
ESP_LOGE(tag, "%s: must have the configuration of the shift register", __func__);
err = ESP_ERR_INVALID_ARG;
return err;
}

shft->reg_value = (uint8_t *)malloc(shft->num_reg); // Create an array with all registers

if (shft->reg_value == NULL)
{
ESP_LOGE(tag, "%s: no heap memory to allocate reg_value", __func__);
err = ESP_ERR_NO_MEM;
return err;
}

memset(shft->reg_value, 0, shft->num_reg); // Start all registers as 0

gpio_config_t *io_conf = (gpio_config_t *)malloc(sizeof(gpio_config_t));

// disable interrupt
io_conf->intr_type = GPIO_INTR_DISABLE;

switch (shft->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 (shft->pin.clk >= 32)
buf32_1 |= 1 << (shft->pin.clk - 32);
else
buf32_0 |= 1 << shft->pin.clk;

if (shft->pin.latch >= 32)
buf32_1 |= 1 << (shft->pin.latch - 32);
else
buf32_0 |= 1 << shft->pin.latch;

if (shft->pin.data >= 32)
buf32_1 |= 1 << (shft->pin.data - 32);
else
buf32_0 |= 1 << shft->pin.data;

result = ((uint64_t)buf32_1 << 32) | ((uint64_t)buf32_0 << 0);

io_conf->pin_bit_mask = result;

break;

case SHIFT_DIR_INPUT:
ESP_LOGE(tag, "%s: Input shift register not done yet", __func__);
err = ESP_ERR_NOT_FOUND;
break;

case SHIFT_DIR_INPUT_OUTPUT:
ESP_LOGE(tag, "%s: Input output shift register not done yet", __func__);
err = ESP_ERR_NOT_FOUND;
break;

default:
ESP_LOGE(tag, "%s: Mode of shift register not found", __func__);
err = ESP_ERR_NOT_FOUND;
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);

free(io_conf);

return err;
}

esp_err_t shift_reg_deinit(shift_reg_config_s *shft)
{
free(shft->reg_value);
return ESP_OK;
}

esp_err_t shift_reg_send(uint8_t *data, uint8_t len, shift_reg_config_s *shft)
{
esp_err_t err = ESP_FAIL;

if (shft == NULL || len > shft->num_reg || data == NULL)
{
ESP_LOGE(tag, "%s: must have a valid argument;", __func__);
err = ESP_ERR_INVALID_ARG;
return err;
}

if (shft->mode.bit_mode == SHIFT_BIT_MODE_MSB)
{
for (uint8_t i = 0; i < len; i++)
{
shift_reg_send8bits(data[i], shft);
shft->reg_value[i] = data[i];
}
}
else
{
for (int8_t i = len - 1; i >= 0; i--)
{
shift_reg_send8bits(data[i], shft);
shft->reg_value[i] = data[i];
}
}

err = ESP_OK;

return err;
}

esp_err_t shift_reg_send8bits(uint8_t data, shift_reg_config_s *shft)
{
esp_err_t err = ESP_FAIL;

if (shft == NULL)
{
ESP_LOGE(tag, "%s: must have a valid argument;", __func__);
err = ESP_ERR_INVALID_ARG;
return err;
}

if (shft->mode.bit_mode == SHIFT_BIT_MODE_MSB)
{
// MSB Mode
for (int8_t i = 7; i >= 0; i--)
{
if ((data >> i) & 1)
{
SETPIN(shft->pin.data);
}
else
{
CLRPIN(shft->pin.data);
}

SETPIN(shft->pin.clk);
_DELAY_US(1);
CLRPIN(shft->pin.clk);
_DELAY_US(1);
}
}
else
{
// LSB Mode
for (int8_t i = 0; i < 8; i++)
{
if ((data >> i) & 1)
{
SETPIN(shft->pin.data);
}
else
{
CLRPIN(shft->pin.data);
}

SETPIN(shft->pin.clk);
_DELAY_US(1);
CLRPIN(shft->pin.clk);
_DELAY_US(1);
}
}

err = ESP_OK;

return err;
}

esp_err_t shift_reg_latch(shift_reg_config_s *shft)
{
SETPIN(shft->pin.latch);
_DELAY_US(1);
CLRPIN(shft->pin.latch);
_DELAY_US(1);

return ESP_OK;
}
Loading

0 comments on commit 2bd3b94

Please sign in to comment.