¿Cómo evitarías todos estos Delays?

Ya que tanto hablamos de evitar el uso de los indeseables delays, aunque particularmente creo que en ciertas ocasiones solucionan el problema fácilmente si el proyecto lo permite, les traigo un problema.

Resumiendo: el amigo locodelafonola me envío un mensaje pidiendo ayuda de como podría evitar el uso de delays en una cierta librería usando un timer para evitar que su proyecto no se vea afectado (en su caso, los delays si importan).

La librería, pensada para manejar un displays 7 segmentos a partir de un I2C usando el integrado TM1637 con un Atmega 328p, es la siguiente:

- tm1637.h:

Código:
/**
* Copyright (c) 2017, Åukasz Marcin Podkalicki <lpodkalicki@gmail.com>
*
* This is ATtiny13/25/45/85 library for 4-Digit LED Display based on TM1637 chip.
*
* Features:
* - display digits
* - display colon
* - display raw segments
* - display on/off
* - brightness control
*
* References:
* - library: https://github.com/lpodkalicki/attiny-tm1637-library
* - documentation: https://github.com/lpodkalicki/attiny-tm1637-library/README.md
* - TM1637 datasheet: https://github.com/lpodkalicki/attiny-tm1637-library/blob/master/docs/TM1637_V2.4_EN.pdf
*/

#ifndef    _TM1637_H_
#define    _TM1637_H_

#include <avr/pgmspace.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <avr/io.h>

// Main Settings
#define    TM1637_DIO_PIN                PB1
#define    TM1637_CLK_PIN                PB2
#define    TM1637_DELAY_US                (50)
#define    TM1637_DEFAULT_BRIGHTNESS    (7)

// TM1637 commands
#define    TM1637_CMD_SET_DATA            0x40
#define    TM1637_CMD_SET_ADDR            0xC0
#define    TM1637_CMD_SET_DSIPLAY        0x80

// TM1637 data settings (use bitwise OR to contruct complete command)
#define    TM1637_SET_DATA_WRITE        0x00 // write data to the display register
#define    TM1637_SET_DATA_READ        0x02 // read the key scan data
#define    TM1637_SET_DATA_A_ADDR        0x00 // automatic address increment
#define    TM1637_SET_DATA_F_ADDR        0x04 // fixed address
#define    TM1637_SET_DATA_M_NORM        0x00 // normal mode
#define    TM1637_SET_DATA_M_TEST        0x10 // test mode

// TM1637 address settings (use bitwise OR to contruct complete command)
#define    TM1637_SET_ADR_00H            0x00 // address 00
#define    TM1637_SET_ADR_01H            0x01 // address 01
#define    TM1637_SET_ADR_02H            0x02 // address 02
#define    TM1637_SET_ADR_03H            0x03 // address 03
#define    TM1637_SET_ADR_04H            0x04 // address 04
// TM1637 display control command set (use bitwise OR to consruct complete command)
#define    TM1637_SET_DISPLAY_OFF        0x00 // off
#define    TM1637_SET_DISPLAY_ON        0x08 // on


/**
* Initialize TM1637 display driver.
* Clock pin (TM1637_CLK_PIN) and data pin (TM1637_DIO_PIN)
* are defined at the top of this file.
*/
void TM1637_init(void);

/**
* Display digits ('0'..'9') at positions (0x00..0x03)
*/
void TM1637_display_digit(const uint8_t addr, const uint8_t digit);

/**
* Display raw segments at positions (0x00..0x03)
*
*      bits:                 hex:
*        -- 0 --               -- 01 --
*       |       |             |        |
*       5       1            20        02
*       |       |             |        |
*        -- 6 --               -- 40 --
*       |       |             |        |
*       4       2            10        04
*       |       |             |        |
*        -- 3 --               -- 08 --
*
* Example segment configurations:
* - for character 'H', segments=0b01110110
* - for character '-', segments=0b01000000
* - etc.
*/
void TM1637_display_segments(const uint8_t addr, const uint8_t segments);

/**
* Display colon on/off.
*/
void TM1637_display_colon(bool value);

/**
* Clear all display segments (including colon).
*/
void TM1637_clear(void);

/**
* Set display brightness.
* Min brightness: 0
* Max brightness: 7
*/
void TM1637_set_brightness(const uint8_t brightness);

/**
* Turn display on/off.
*/
void TM1637_enable(const bool value);

#endif    /* !_ATTINY_TM1637_H_ */

- tm1637.c:

C:
/**
* Marcin Podkalicki <lpodkalicki@gmail.com>
*
* This is ATtiny13/25/45/85 library for 4-Digit LED Display based on TM1637 chip.
*
* Features:
* - display digits
* - display colon
* - display raw segments
* - display on/off
* - brightness control
*
* References:
* - library: https://github.com/lpodkalicki/attiny-tm1637-library
* - documentation: https://github.com/lpodkalicki/attiny-tm1637-library/README.md
* - TM1637 datasheet: https://github.com/lpodkalicki/attiny-tm1637-library/blob/master/docs/TM1637_V2.4_EN.pdf
*/
#include <stdlib.h>
#include <avr/pgmspace.h>
#include <avr/io.h>
#include <util/delay.h>
#include "tm1637.h"




#define    TM1637_DIO_HIGH()        (PORTB |= _BV(TM1637_DIO_PIN))
#define    TM1637_DIO_LOW()        (PORTB &= ~_BV(TM1637_DIO_PIN))
#define    TM1637_DIO_OUTPUT()        (DDRB |= _BV(TM1637_DIO_PIN))
#define    TM1637_DIO_INPUT()        (DDRB &= ~_BV(TM1637_DIO_PIN))
#define    TM1637_DIO_READ()         (((PINB & _BV(TM1637_DIO_PIN)) > 0) ? 1 : 0)
#define    TM1637_CLK_HIGH()        (PORTB |= _BV(TM1637_CLK_PIN))
#define    TM1637_CLK_LOW()        (PORTB &= ~_BV(TM1637_CLK_PIN))

#define    TM1637_FLAG_ENABLED        (1 << 0)
#define    TM1637_FLAG_SHOWCOLON    (1 << 1)


static void TM1637_configure(void);
static void TM1637_cmd(uint8_t value);
static void TM1637_start(void);
static void TM1637_stop(void);
static uint8_t TM1637_write_byte(uint8_t value);

static const uint8_t _digit2segments[] =
{

//    0x5E, // D
//    0x55, // M
//    0x76, // X
//    0x00, // blank
//    0x77, // a
//    0x5E, // d
//    0x50, // r
//    0x79, // e
//    0x6D, // s
//    0x6D, // s
//    0x00, // blank
    0x3F, // 0
    0x06, // 1
    0x5B, // 2
    0x4F, // 3
    0x66, // 4
    0x6D, // 5
    0x7D, // 6
    0x07, // 7
    0x7F, // 8
    0x6F  // 9
};

static uint8_t _brightness = TM1637_DEFAULT_BRIGHTNESS;
static uint8_t _digit = 0xff;
static uint8_t _flags = 0x00;

void
TM1637_init(void)
{

    DDRB |= (_BV(TM1637_DIO_PIN)|_BV(TM1637_CLK_PIN));
    PORTB &= ~(_BV(TM1637_DIO_PIN)|_BV(TM1637_CLK_PIN));
    _flags |= TM1637_FLAG_ENABLED;
    TM1637_clear();
}

void
TM1637_display_digit(const uint8_t addr, const uint8_t digit)
{
    uint8_t segments = digit < 10 ? _digit2segments[digit] : 0x00;

    if (addr == TM1637_SET_ADR_01H) {
        _digit = digit;
        if (_flags & TM1637_FLAG_SHOWCOLON) {
            segments |= 0x80;
        }
    }

    TM1637_display_segments(addr, segments);
}

void
TM1637_display_segments(const uint8_t addr, const uint8_t segments)
{

    TM1637_cmd(TM1637_CMD_SET_DATA | TM1637_SET_DATA_F_ADDR);
    TM1637_start();
    TM1637_write_byte(TM1637_CMD_SET_ADDR | addr);
    TM1637_write_byte(segments);
    TM1637_stop(); 
    TM1637_configure(); 
}

void
TM1637_display_colon(bool value)
{

    if (value) {
        _flags |= TM1637_FLAG_SHOWCOLON;
    } else {
        _flags &= ~TM1637_FLAG_SHOWCOLON;
    }
    TM1637_display_digit(TM1637_SET_ADR_01H, _digit);
}

void
TM1637_clear(void)
{ 

    TM1637_display_colon(false);
    TM1637_display_segments(TM1637_SET_ADR_00H, 0x00);
    TM1637_display_segments(TM1637_SET_ADR_01H, 0x00);
    TM1637_display_segments(TM1637_SET_ADR_02H, 0x00);
    TM1637_display_segments(TM1637_SET_ADR_03H, 0x00);
    TM1637_display_segments(TM1637_SET_ADR_04H, 0x00);
}
void
TM1637_set_brightness(const uint8_t brightness)
{

    _brightness = brightness & 0x07;
    TM1637_configure();
}

void
TM1637_enable(bool value)
{

    if (value) {
        _flags |= TM1637_FLAG_ENABLED;
    } else {
        _flags &= ~TM1637_FLAG_ENABLED;
    }
    TM1637_configure();
}

void
TM1637_configure(void)
{
    uint8_t cmd;

    cmd = TM1637_CMD_SET_DSIPLAY;
    cmd |= _brightness;
    if (_flags & TM1637_FLAG_ENABLED) {
        cmd |= TM1637_SET_DISPLAY_ON;
    }

    TM1637_cmd(cmd);
}

void
TM1637_cmd(uint8_t value)
{

    TM1637_start();
    TM1637_write_byte(value);
    TM1637_stop();
}

void
TM1637_start(void)
{

    TM1637_DIO_HIGH();
    TM1637_CLK_HIGH();
    _delay_us(TM1637_DELAY_US);
    TM1637_DIO_LOW();
}

void
TM1637_stop(void)
{

    TM1637_CLK_LOW();
    _delay_us(TM1637_DELAY_US);

    TM1637_DIO_LOW();
    _delay_us(TM1637_DELAY_US);

    TM1637_CLK_HIGH();
    _delay_us(TM1637_DELAY_US);

    TM1637_DIO_HIGH();
}

uint8_t
TM1637_write_byte(uint8_t value)
{
    uint8_t i, ack;

    for (i = 0; i < 8; ++i, value >>= 1) {
        TM1637_CLK_LOW();
        _delay_us(TM1637_DELAY_US);

        if (value & 0x01) {
            TM1637_DIO_HIGH();
        } else {
            TM1637_DIO_LOW();
        }

        TM1637_CLK_HIGH();
        _delay_us(TM1637_DELAY_US);
    }

    TM1637_CLK_LOW();
    TM1637_DIO_INPUT();
    TM1637_DIO_HIGH();
    _delay_us(TM1637_DELAY_US);

    ack = TM1637_DIO_READ();
    if (ack) {
        TM1637_DIO_OUTPUT();
        TM1637_DIO_LOW();
    }
    _delay_us(TM1637_DELAY_US);

    TM1637_CLK_HIGH();
    _delay_us(TM1637_DELAY_US);

    TM1637_CLK_LOW();
    _delay_us(TM1637_DELAY_US);

    TM1637_DIO_OUTPUT();

    return ack;
}

Y un ejemplo de su uso sería el siguiente:

C:
/**
* Copyright (c) 2017, Åukasz Marcin Podkalicki <lpodkalicki@gmail.com>
*
* This is ATtiny13 "Running Digits" example using attiny-tm1637-library,
* https://github.com/lpodkalicki/attiny-tm1637-library .
*
*/


#include <stdlib.h>
#include <avr/pgmspace.h>
#include <stdint.h>
#include <avr/io.h>
#include <util/delay.h>
#include "tm1637.h"
#define F_CPU 16000000UL  // 16 MHz

int main(void)
{
    uint8_t i = 0;

    /* setup */
    TM1637_init();
    /* loop */
    while (1) {
        TM1637_display_digit(TM1637_SET_ADR_00H, i % 0x10);
        TM1637_display_digit(TM1637_SET_ADR_01H, (i + 1) % 0x10);
        TM1637_display_digit(TM1637_SET_ADR_02H, (i + 2) % 0x10);
        TM1637_display_digit(TM1637_SET_ADR_03H, (i + 3) % 0x10);
        TM1637_display_digit(TM1637_SET_ADR_04H, (i + 4) % 0x10);
    //    TM1637_display_digit(TM1637_SET_ADR_05H, (i + 5) % 0x10);
    //    TM1637_display_colon(true);
        _delay_ms(200);
        TM1637_display_colon(false);
        _delay_ms(200);
        i++;
    }
}

Se puede ver que la librería está plagada de delays, llegué a contar aproximadamente 90 y pico de delays por cada llamada a la función "TM1637_display_digit".

La solución sencilla sería usar un RTOS, pero no estoy seguro hasta donde le daría al uC agregar uno externo (ej. FreeRTOS) ni tampoco sería conveniente complicar demasiado el proyecto de locodelafonola (que ya es extenso de por si). ¿Cómo lo resolverían uds?

. Lo primero que se destaca es que la librería realiza la comunicación I2C por soft y no aprovecha el I2C por hard que tiene el uC.
 
Pues habría que dedicarle tiempo y mirarlo con calma. Ahora mismo estoy liadísimo.
Ese integrado es SPI, yo trataría de usar el spi hardware en lugar de cualquier pin y programarle la velocidad que se quiera.

Si esos delays, como parece, son para ajustar la velocidad del spi por software son complicados de quitar porque serán muy cortos.

Una opción es hacer una rutina spi software gobernada por interrupciones que básicamente sea:
Rota
Envia
Pero llamar a una interrupción cada pocos μs es más pérdida que ganancia ya que la interrupción añade unas cuantas instrucciones de guardar y recuperar registros.

Otra sería hacer una rutina de delay que dentro de ella haga cosas
Un
Mi_delay()
Pero esa función para delays de μs así como que no

Voto por rehacer la librería usando el spi hardware.
 
Es I2C, tenés la líneas:

- DIO
- CLK

Estoy de acuerdo, si usas el I2C del hardware, podrías evitarte todos los delays, pero algo más tenés que hacer, yo agregaría una máquina de estado.
 
Yo creo que es SPI, perdona que insista.
Esos bichos llevan una señal de cs para cada integrado.


Edito, si que parece I²C, pero yo diría que no es estándard, no veo el identificador de dispositivo, lo mismo es que es "exclusivo"

Yo usé uno parecido con más pines que hubiera jurado que era spi.

Pues entonces al bus I²C si se necesita más de uno habrá que ver si hay un CD o algo, el que yo usé se podía enlazar como en una cadena uno detrás de otro.
 
Última edición:
Es cierto, habría que ver si el uC podría establecer comunicación con el I2C que tiene:

- Byte-oriented 2-wire serial interface (Phillips I2 C compatible)

Para el uC que fue hecha la librería no había I2C (o similar disponible), entonces se entiende el uso de una comunicación vía software.
 

Dr. Zoidberg

Well-known-Papá Pitufo
En base a lo publicado D@rkbytes hay dos soluciones:
1- Cambiá el chip de porquería ese por uno que use un protocolo estándard y soportado por hardware ==> adios delays (y)
2- Diseñar una API que emule los delays pero los haga vía interrupciones, con lo que vas a perder 5 us (ponele) por cada invocación, pero con la cantidad de delay que hay metidos dentro de la biblioteca junto con la activación de pines, dudo que ganes algo usando esta solución a menos que hagas un rediseño completo del soft que corre en el micro.(n)
3- Poner un micro solo para el control del display y que se comunique vía I2C con el 328. Ahí separo todo el despelote y el 328 puede correr con todo lo que quiera y mandar vía I2C (o SPI) la actualización al micro esclavo y seguir jugando con sus cosas...pero claro, es el doble de hardware.

Yo buscaría la solución 1...
Es cierto, habría que ver si el uC podría establecer comunicación con el I2C que tiene:

- Byte-oriented 2-wire serial interface (Phillips I2 C compatible)

Para el uC que fue hecha la librería no había I2C (o similar disponible), entonces se entiende el uso de una comunicación vía software.
Salvo que lo hagas transmitir como esclavo, pero hay que revisar todo lo que hace el hardware...
 
Última edición:
Cuando tenga un rato trolearé a los míos. Tengo varias malas ideas. A ver si alguna funciona.
 
Última edición:
Yo reemplazaría la dirección por el comando y lo usaría como maestro.

Supongo que el uC no se preocupa por el último bit de lectura/escritura de la dirección, sino que es el periférico el que está pendiente de eso.
 
ese chip arduinero Yo intenté hacerlo funcionar por hardware al i2c y no lo logre... lo que si pude hacer es portar libreria para el ccs compiler pic, tampoco allí la pude hacer funcionar por hardware.
Hay que leer bien la hoja de datos, porque una vez iniciado es como un i2c, tal vez se me paso algo por alto.... como el asunto de los flancos y demas yerbas...
 
Hola a todos y gracias por enseñar
Bueno ....vi el hilo pero no pude publicar ni siquiera un comentario
Tengo a mi vieja muy enferma ., tiene 93 años y por culpa del "BICHO" esta en casa para evitar males mayores
La verdad que con este problema del COVID-19 .,mi trabajo se reconvirtio ., entre el cuidado y las "changas casuales " ., no me queda tiempo para nada mas
La librerias que publico cosmefulanito ., al principio del hilo tiene un video (mala calidad..... perdonen) es este
Bueno ....... antes que nada la utilizacion del TM1637 en este caso ., no es presisamente convencional sino mas bienen es lo indicado en la hoja de datos
tm1637_schematic.png
Yo ya fabrique una placa para esto y no es el modulito barato para arduino
Que no es una aplicacion completa de las funciones de este CI
En cambio trate de seguir la hoja de datos para el uso mas deallado y completo
20200724_133528.jpg
que en realidad termino siendo algo como esto mas o menos
Tm1637 conexion agregado botones y leds.jpg
a diferencia tambien de lo que se consigue armado ., lo fabrique con display 0,56 y no con 0,36 que usan los modulos comerciales
Tm1637 conexion.jpg
Ahora muchos no imaginan las apliciones potenciales de este CI
en mi caso con un micro pequeño tipo ATtiny13 ., 45 ., 85 etc
Que tenga una USART con TX y RX que se colocaria en la misma placa mostrada ., y tendria un desarrollo completo y compacto
Porque aprte de manejar el display de cuatro digitos sobran 16 salidas para led (u optoacopladores ,etc)
Depende de la imaginacion de cada uno
Tambien la libreria ejemplo de aplicacion pemite manejar el "BILLO" de los led por Pwm
muchos piensan que es I2C pero no es ., tampoco es TWI del todo ., porque no tiene ni maneja direccion de esclavo
Pero no voy a escribir sobre cosas tecnicas que apenas entiendo del todo ., por aprender solo y no tener maestro para que indique mi error
Bueno amigo cosmefulanito ., mi otro amigo aleman de muhos años., me mando una nueva lib para el TM1637 y TM1651 , que es solo para cuatro dgitos y no para seis como el TM1637
La lib tiene cosas muy interesates como que aprovecha la memoria del TM1637 y carga los cambios mientra esta en el estado bajo o apagado usando la memoria del CI
Aca va el main demo ., probado en atmega 328p ., atmega162 -16pu ., y atmega128A
Código:
/* ------------------------------------------------ ----------
                      tm1651_demo.c

     Funciones básicas del TM16xx con 4 dígitos.
     Pantalla de 7 segmentos


     MCU: todo AVR
             (probado ATtiny85, ATtiny2313, ATtiny44, ATmega8, ATmega328p)

     F_CPU: 8 MHz interno


     Pinout

     Controlador TM16xx
     ---------------------------
        PB1 CLK
        PB0 DIO

     PB0 y PB1 son cada uno con resistencias emergentes de 2.2 kOhm
     debe proporcionarse después de + 5V

     2020-04-02 R. Seelig
   -------------------------------------------------- -------- */

#define F_CPU 16000000UL  // 16 MHz
#include <stdint.h>
#include <util/delay.h>
#include <avr/io.h>

#include "tm1637.h"

#define delay  _delay_ms




/* ----------------------------------------------------------
                            MAIN
   ---------------------------------------------------------- */
void main(void)
{

  int16_t i, i2;
  uint8_t b;

   bright= 11;
  tm16_init();

  while(1)
  {
    for (i= 10; i> -1; i--)  // Contador de cuenta regresiva
    {
    //  tm16_setdez(i,3);    // Punto decimal en digito 3 encendido
      _delay_ms(500);
      tm16_setdez(i,0);      // Punto decimal en digito 3 apagado
      _delay_ms(500);
    }

    for (i= 0; i< 6; i++)      // hacer que 0000 parpadee
    {
      tm16_clear();
      _delay_ms(500);
      tm16_setdez(0,0);
      _delay_ms(500);
    }

    for (i= 4; i> -1; i--)       // hacer que el dígito se mueva
    {
      tm16_clear();
      tm16_setzif(i,0);
      _delay_ms(400);
    }

    tm16_sethex(0x1b37);       //Mostrar valor hexadecimal
    _delay_ms(4000);

     tm16_clear();

    for (i2= 0; i2< 20; i2++)       // Segmento corriendo luz, 20 pases
    {
      for (i= 0; i< 6; i++)
      {
        tm16_setseg(0,i);
        tm16_setseg(1,i);
        tm16_setseg(2,i);
        tm16_setseg(3,i);
        tm16_setseg(4,i);
        _delay_ms(100);
      }
    }

    //Mostrar botones como código, el boton 5 repite la demostración
    // Boton 4 = "MENU"
    // Boton 7 = "UP"
    // Boton 6 = "DOWN"
    // Boton 5 = "ENTER"
    do
    {
      b= tm16_readkey();
      tm16_setdez(b,0);

      _delay_ms(50);
    }
    while (b != 5);
    _delay_ms(50);
    while (tm16_readkey() == 5); // al precionar se reinicia el demo
  }
}
aca la lib TM167.C
Código:
/* ------------------------------------------------ ------------------
                             tm1651.c

     Módulo de software para TM16xx, módulo de controlador para
     Pantallas de 4 dígitos y 7 segmentos con ánodo común y
     Interfaz de botón adicional.

     Adecuado para: TM1651, TM1637

     MCU: todo AVR
             (probado ATtiny85, ATtiny2313, ATtiny44, ATmega8, ATmega328p)

     F_CPU: 8 MHz interno


     Pinout

     Controlador TM16xx
     ---------------------------
        PB1 CLK
        PB0 DIO

     CLK y DIO son cada uno con resistencias emergentes de 2.2 kOhm
     debe proporcionarse después de + 5V

    2020-04-02 R. Seelig
   ------------------------------------------------------------------ */

/*

    Asignación de segmento de la pantalla:

        a
       ---
    f | g | b       segmento  | a | b | c | d | e | f | g | Colon (solo para POS1) |
       --- ----------------------------------------------- ----------------------------------
    e |   | c         Bit-Nr. | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
       ---
        d

    El bit 7 de la pantalla POS1 de 7 segmentos es el colon
*/
#include "tm1637.h"

/*----------------------------------------------------------
                     Globale Variable
   ---------------------------------------------------------- */

uint8_t    bright = 11;//contiene valor para el brillo (permitido: 0x00...0x0f);

uint8_t    led7sbmp[16] =  // Patrón de mapa de bits para dígitos de 0...F
                { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
                  0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71 };


/*------------------- comunicación -----------------------

    El módulo controlador tm16 se vuelve algo "extraño
    dirigido. Utiliza un I2C para la comunicación.
    Protocolo, pero SIN una asignación de dirección. El chip es
    por lo tanto SIEMPRE abordado. Por esta razón, el
    Comunicación realizada mediante bitbanging. 
     Por eso puede usar cualquier conexión de E / S libre del controlador
    (Ver define al principio).

    Además, en contraste con muchas otras construcciones I2C
    la salida en serie con el bit menos significativo
    (LSB primero)
   ---------------------------------------------------------- */
void tm16_start(void)              // I2C Bus-comienzo
{
  bb_scl_hi();
  bb_sda_hi();
  puls_len();
  bb_sda_lo();
}

void tm16_stop(void)               // I2C Bus-parar
{
  bb_scl_lo();
  puls_len();
  bb_sda_lo();
  puls_len();
  bb_scl_hi();
  puls_len();
  bb_sda_hi();
}

void tm16_write (uint8_t value)    // I2C Bus-Transferencia de datos
{
  uint8_t i;

  for (i = 0; i <8; i++)
  {
    bb_scl_lo();

    //salida serial de bitbanging, LSB primero
    if (value & 0x01) { bb_sda_hi();
    }
    else { bb_sda_lo();
    }
    puls_len();
    value = value >> 1;
    bb_scl_hi();
    puls_len();
  }
  bb_scl_lo();
  puls_len();  // ACK no es consultado por simplicidad
  bb_scl_hi();
  puls_len();
  bb_scl_lo();

}


/* -------------------------------------------------------
                    tm16_read (uint8_t ack)

   lee un byte del bus I2c.

   Entregando:
               1: después de leer, el esclavo se enciende
                   Acuse de recibo enviado
               0: no se envía confirmación

   Regreso:
               leer byte
   ------------------------------------------------------- */
 
uint8_t tm16_read(uint8_t ack)
{
  uint8_t data= 0x00;
  uint8_t i;

  bb_sda_hi();

  for(i= 0; i< 8; i++)
  {
    bb_scl_lo();
    puls_len();
    bb_scl_hi();

    puls_len();

    if(bb_is_sda()) data|= (1 << i);
  }

  bb_scl_lo();
  bb_sda_hi();

  puls_len();

  if (ack)
  {
    bb_sda_lo();
    puls_len();
  }

  bb_scl_hi();
  puls_len();

  bb_scl_lo();
  puls_len();

  bb_sda_hi();

  return data;
}



/* ------------------------------------------------ ----------
                      Funciones de usuario
    -------------------------------------------------- -------- */


 /* ------------------- SELECTPOS ---------------------------

        selecciona la posición de visualización que se describirá
     --------------------------------------------------------- */

void tm16_selectpos(char nr)
{
  tm16_start();
  tm16_write(0x40);// Selección de registros LED
  tm16_stop();

  tm16_start();
  tm16_write(0xc0 | nr); // Selección de la pantalla de 7 segmentos.
}

/* ----------------------- SETBRIGHT ------------------------

       establece el brillo de la pantalla
       los valores permitidos para el valor son 0 .. 15
    ---------------------------------------------------------- */
void tm16_setbright(uint8_t value)
{
  tm16_start();
  tm16_write(0x80 | value);  //registro inferior contiene valor de brillo
  tm16_stop();
}

/* -------------------------  CLEAR -------------------------

       borra la pantalla del módulo
    --------------------------------------------------------- */
void tm16_clear(void)
{
  uint8_t i;

  tm16_selectpos(0);
  for(i=0; i<6; i++) { tm16_write(0x00); }
  tm16_stop();

  tm16_setbright(bright);

}

/* ---------------------- SETBMP --------------------------
       genera un patrón de mapa de bits en una posición
    --------------------------------------------------------- */
void tm16_setbmp(uint8_t pos, uint8_t value)
{
  tm16_selectpos(pos);             //Seleccione la pantalla que se describirá

  tm16_write(value);               // Valor de patrón de bits de salida en pantalla de 7 segmentos
  tm16_stop();

}

/* ---------------------- SETZIF ------------------------- -
       genera un dígito en una posición
       Nota: el patrón de bits de los dígitos está en
                  led7sbmp definido
    --------------------------------------------------------- */
void tm16_setzif(uint8_t pos, uint8_t zif)
{
  tm16_selectpos(pos);  //Seleccione la pantalla que se describirá

  zif= led7sbmp[zif];
  tm16_write(zif);     // Valor de patrón de bits de salida en pantalla de 7 segmentos
  tm16_stop();

}

/* ----------------------- SETSEG -------------------------
       establece un solo segmento de un anuncio

       pos: posición de visualización (0..3)
       seg: el segmento único (0..7 ver arriba)
    --------------------------------------------------------- */
void tm16_setseg(uint8_t pos, uint8_t seg)
{

  tm16_selectpos(pos);             // Seleccione la pantalla que se describirá
  tm16_write(1 << seg);
  tm16_stop();

}

/* ----------------------- SETDEZ -------------------------
       da un valor decimal de 4 dígitos en el
       pantalla apagada

       Entregando:

         valor: valor a emitir
         dpanz: posición del punto decimal en pantalla, 1
                 la pantalla habla a la derecha (y, por lo tanto, ninguno
                 Decimal)
                 0 => no hay visualización
    --------------------------------------------------------- */

void tm16_setdez(int value, uint8_t dpanz)
{
  uint8_t i,v, bmp;

  for (i= 4; i> 0; i--)
  {
    v= value % 10;
    bmp= led7sbmp[v];
    if (dpanz== 5-i) bmp= bmp | 0x80;     // Establecer punto decimal
    tm16_setbmp(i-1, bmp);
    value= value / 10;
  }
}

#if (enable_6digit == 1)

 /* -------------------- SETDEZ6DIGIT -----------------------
         da un valor decimal de 6 dígitos en el
         pantalla apagada

         Entregando:

           valor: valor a emitir
           dpanz: posición del punto decimal en pantalla, 1
                   la pantalla habla a la derecha (y, por lo tanto, ninguno
                   Decimal)
                   0 => no hay visualización
      --------------------------------------------------------- */

  void tm16_setdez6digit(uint32_t value, uint8_t dpanz)
  {
    uint8_t i,v, bmp;

    for (i= 6; i> 0; i--)
    {
      v= value % 10;
      bmp= led7sbmp[v];
      if (dpanz== 7-i) bmp= bmp | 0x80;     // Establecer punto decimal
      tm16_setbmp(i-1, bmp);
      value= value / 10;
    }
  }
#endif

/* ----------------------- SETHEX -------------------------
       devuelve un valor hexadecimal de 4 dígitos en el
       pantalla apagada
    --------------------------------------------------------- */

void tm16_sethex(uint16_t value)
{
  uint8_t i,v;

  for (i= 4; i> 0; i--)
  {
    v= value % 0x10;
    tm16_setbmp(i-1, led7sbmp[v]);
    value= value / 0x10;
  }
}

#if (enable_6digit == 1)

/* -------------------- SETHEX6DIGIT -----------------------
         devuelve un valor hexadecimal de 6 dígitos en el
         pantalla apagada
      --------------------------------------------------------- */

  void tm16_sethex6digit(uint32_t value)
  {
    uint8_t i,v;

    for (i= 6; i> 0; i--)
    {
      v= value % 0x10;
      tm16_setbmp(i-1, led7sbmp[v]);
      value= value / 0x10;
    }
  }

#endif

/* ----------------------- LEER BOTONES (READKEY) ------------------------ -
      lee las claves conectadas y da esto como
      Argumento de vuelta.

      Anotación:
        No se devuelve ninguna matriz de claves. Es
        se activa más de un botón, solo el más alto
        Llave entregada. Entonces no es posible
        presione varias teclas al mismo tiempo.

    --------------------------------------------------------- */

uint8_t tm16_readkey(void)
{
  uint8_t key;

  key= 0;
  tm16_start();
  tm16_write(0x42);
  key= ~tm16_read(1);
  tm16_stop();
  if (key) key -= 16;
  return key;
}


/* ----------------------- INIT ----------------------------
       inicializa las conexiones del controlador
       Comunicación como salidas y borra la pantalla
    ---------------------------------------------------------- */
void tm16_init(void)
{
  scl_init();
  sda_init();
  tm16_clear();
}
Y aca la lib TM1637.H
Código:
/* ------------------------------------------------ ------------------
                              tm16xx.h

     Encabezado para direccionar un TM16xx, bloque de controlador para
     Pantallas de 4 dígitos y 7 segmentos con ánodo común y
     Interfaz de botón adicional.

     Adecuado para: TM1651, TM1637

     MCU: todo AVR
             (probado ATtiny85, ATtiny2313, ATtiny44, ATmega8, ATmega328p)

     F_CPU: 8 MHz interno


     Pinout

     Controlador TM16xx
     ---------------------------
        PB1 CLK
        PB0 DIO

     CLK y DIO son cada uno con resistencias emergentes de 2.2 kOhm
     debe proporcionarse después de + 5V

     2020-04-02 R. Seelig
   ------------------------------------------------------------------ */
/*
            DIG3  A   F   DIG2  DIG1  B
             _    _    _    _    _    _
          __|_|__|_|__|_|__|_|__|_|__|_|__
         |                                                |
         |     DIG3  DIG2   DIG1  DIG0   |
         |              __    __     __    __         |
         |             |__|  |__|   |__|  |__|        |
         |             |__|. |__|.  |__|. |__|.        |       
         |                                                |
         |           KYX-3461BS                 |
         |________________________________|
            |_|  |_|  |_|  |_|  |_|  |_|

             E    D   dp    C    G   DIG0



    Asignación de segmento de la pantalla:

         a
       ---
    f | g | b    segmento  | a | b | c | d | e | f | g | Colon (solo para POS1) |
       --- ----------------------------------------------- ----------------------------------
    e |   | c     Bit-Nr   | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
       ---
        d

    Nota básica:

    La pantalla más importante está conectada a la conexión GRID1 del TM16xx.
    Con una pantalla de 6 dígitos, DIG5 es su max.,que pueden conectar
    Con una pantalla de 4 dígitos, DIG3.es su max. que pueden conectar
    Sólo se puede conectar un máximo de 4 dígitos a un TM1651
    -------------------------------------------

    Ejemplo de conexiones para TM1651

    TM1651 (pin) Pantalla de 7 segmentos (pin)
    --------------------------------------------

    GRID4    9   ---------------  DIG0       6
    GRID3   10   ---------------  DIG1       8
    GRID2   11   ---------------  DIG2       9
    GRID1   12   ---------------  DIG3      12

    SEG1     2   ---------------    A       11
    SEG2     3   ---------------    B        7
    SEG3     4   ---------------    C        4
    SEG4     5   ---------------    D        2
    SEG5     6   ---------------    E        1
    SEG6     7   ---------------    F       10
    SEG7     8   ---------------    G        5

    VDD     13
    GND      1
    K1      16 (Keyscan 1)
    CLK     15
    DIO     14


-------------------------------------------

    Por ejemplo, conexiones para TM1637 (4 dígitos)

    TM1637 (pin) Pantalla de 7 segmentos (pin)
    --------------------------------------------


    GRID4   12   ---------------  DIG0       6
    GRID3   13   ---------------  DIG1       8
    GRID2   14   ---------------  DIG2       9
    GRID1   15   ---------------  DIG3      12

    SEG1     2   ---------------    A       11
    SEG2     3   ---------------    B        7
    SEG3     4   ---------------    C        4
    SEG4     5   ---------------    D        2
    SEG5     6   ---------------    E        1
    SEG6     7   ---------------    F       10
    SEG7     8   ---------------    G        5
    SEG8     9   ---------------   dp        3

    VDD     16
    GND      1
    K2      20 (Keyscan 2)
    K1      19 (Keyscan 1)
    CLK     18
    DIO     17

*/

  #ifndef in_tm1637
  #define in_tm1637

  #include <util/delay.h>
  #include <avr/io.h>

  #define  F_OSC    (16000)//oscillator freq. in kHz (typical 8MHz or 16MHz)
  #define enable_6digit  1  // Código para pantalla de 6 dígitos
                            // incluir

  /* ----------------------------------------------------------
            Conexión de CLK y DIO al controlador
     ---------------------------------------------------------- */
  // DIO según PB0
  #define bb_datport     B
  #define bb_datbitnr    1

  // CLK según PB1
  #define bb_clkport     B
  #define bb_clkbitnr    2

  // ----------------------------------------------------------------
  // macros de preprocesador por 2 textos de cadena para uso posterior
  // enlace dentro del preprocesador
  //
  // P.ej.:
  // #define ionr A
  // #define ioport conc2 (PORT, ionr)
  //
  // ioport ahora se trata como "PORTA"
  #define CONC2EXP(a,b)     a ## b
  #define conc2(a,b)        CONC2EXP(a, b)
  // ----------------------------------------------------------------

  // ------------------------------------------------ ----------------
  // Macros para inicializar los pines utilizados como salidas
  // y para configurar / eliminar estos pines (bitbanging)
  // ------------------------------------------------ ----------------

  #define datport           conc2(PORT,bb_datport)
  #define datddr            conc2(DDR,bb_datport)
  #define datpin            conc2(PIN,bb_datport)
  #define clkport           conc2(PORT,bb_clkport)
  #define clkddr            conc2(DDR,bb_clkport)

  // ------------------------------------------------ ----------------
  // Nota sobre la configuración 1 y 0 en los pines
  //
  // Los pines se cambian como entrada en la configuración básica.
  // Si se establece un 1, solo la configuración del pin se guarda como
  // se requiere entrada, ya que esto es de alta impedancia y la línea
  // se establece en 1 a través de la resistencia pull-up.
  // Si sale un 0, el pin se configura como una salida
  // y este pin está escrito con un 0
  // ------------------------------------------------ ----------------

  #define sda_init()  datddr &= ~(1<<bb_datbitnr)
  #define bb_sda_hi() sda_init()
  #define bb_sda_lo() {datddr |= (1 << bb_datbitnr);datport&=(~(1 << bb_datbitnr));}
  #define bb_is_sda()  ((datpin & (1<<bb_datbitnr))>>bb_datbitnr)

  #define scl_init()   datddr &= ~(1<<bb_clkbitnr)
  #define bb_scl_hi()  scl_init()
  #define bb_scl_lo()  {clkddr |= (1<<bb_clkbitnr);clkport&=(~(1<<bb_clkbitnr));}

  #define puls_us      5
  #define puls_len()   _delay_us(puls_us)


  /* ----------------------------------------------------------
                       Variable global
     ---------------------------------------------------------- */

  extern uint8_t bright;  //contiene valor para el brillo (permitido: 0x00 .. 0x0f);

  extern uint8_t led7sbmp[16];//Patrón de mapa de bits para dígitos de 0 .. F

  /* ----------------------------------------------------------
                           Prototipos
     ---------------------------------------------------------- */

  void tm16_start(void);
  void tm16_stop(void);
  void tm16_write (uint8_t value);
  uint8_t tm16_read(uint8_t ack);

  void tm16_init(void);
  void tm16_clear(void);
  void tm16_selectDIG(char nr);
  void tm16_setbright(uint8_t value);
  void tm16_setbmp(uint8_t DIG, uint8_t value);
  void tm16_setzif(uint8_t DIG, uint8_t zif);
  void tm16_setseg(uint8_t DIG, uint8_t seg);
  void tm16_setdez(int value, uint8_t dpanz);
  void tm16_sethex(uint16_t value);
  uint8_t tm16_readkey(void);

  #if (enable_6digit == 1)
  void tm16_setdez6digit(uint32_t value, uint8_t dpanz);
  void tm16_sethex6digit(uint32_t value);
  #endif

  #endif
Hice un video de estas lib ., a las apuradas ., y de mala calidad (disculpen )
Donde al final del video ., se ve como trabaja el escaner de lo botones
Y con el demo muestra que numero de boton es el precionado ., de los 16 que puede manejar
en mi caso elegi el numero 5 que manejaria "ENTER" ., para reinicar el demo
En este caso de los botones el pin de envio de datos del micrro al CI TM1637 se convierte de salida a entrada del micro
Asi recibe el micro el dato de que boton se uso ., no permite el uso de mas de un boton al mismo tiempo
En ese caso siempre enviara el dato de boton mas alto en numero
Volviendo al tema del hilo ., en el caso de los "Delays" ., estan configurados como la ejecucion de una funcion
Usando
Código:
 puls_len();
y definiendo la funcion como asi tambien el el valor dado de esta manera
[CODE
#define puls_us 5
#define puls_len() _delay_us(puls_us)
[/CODE]
Ahora comparaddo con el "DELAYS " de la primer lib mostrada ., en el hilo ., esta es muchisimo mas rapida y sin tanto "no hacer nada"
Tambien es mas "liviana" ocupa casi nada de memoria y espacio
Código:
AVR Memory Usage
----------------
Device: atmega328p

Program:    1488 bytes (4.5% Full)
(.text + .data + .bootloader)

Data:         18 bytes (0.9% Full)
(.data + .bss + .noinit)
una opcion que podria usarse ( pieso yo ) ., por lo tanto si estoy equiocado ., hagamelo saber (asi aprendo)
seria la opcion de usar el " avr_watchdog" ., que se usan reset de las usart
aparte de ser configurado en los FUSE tambien ., uso las configuracion de
Código:
wdt_enable(WDTO_500MS); //con steepers
wdt_enable(WDTO_120MS); //con led y rele solamente
Bueno espero haber contribuido a claificar un poco
Y aportar esta lib que le puede servir a muchos
 
Arriba