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:
- tm1637.c:
Y un ejemplo de su uso sería el siguiente:
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.
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.