Skip to content

File syn_seg7.c

File List > display > syn_seg7.c

Go to the documentation of this file

#include "syn_seg7.h"

#include "../util/syn_assert.h"

#include <stdio.h>
#include <string.h>

/* Standard 7-Segment Font Map: Bit 0=A, 1=B, 2=C, 3=D, 4=E, 5=F, 6=G, 7=DP */
static uint8_t char_to_seg7(char c)
{
    switch (c) {
    case '0':
        return 0x3F;
    case '1':
        return 0x06;
    case '2':
        return 0x5B;
    case '3':
        return 0x4F;
    case '4':
        return 0x66;
    case '5':
        return 0x6D;
    case '6':
        return 0x7D;
    case '7':
        return 0x07;
    case '8':
        return 0x7F;
    case '9':
        return 0x6F;
    case 'A':
    case 'a':
        return 0x77;
    case 'B':
    case 'b':
        return 0x7C;
    case 'C':
    case 'c':
        return 0x39;
    case 'D':
    case 'd':
        return 0x5E;
    case 'E':
    case 'e':
        return 0x79;
    case 'F':
    case 'f':
        return 0x71;
    case 'H':
    case 'h':
        return 0x76;
    case 'L':
    case 'l':
        return 0x38;
    case 'P':
    case 'p':
        return 0x73;
    case 'U':
    case 'u':
        return 0x3E;
    case '-':
        return 0x40;
    case '_':
        return 0x08;
    case ' ':
    default:
        return 0x00;
    }
}

SYN_Status syn_seg7_init(SYN_Seg7 *seg, const SYN_GPIO_Pin segments[8], const SYN_GPIO_Pin *digits,
                         uint8_t num_digits, SYN_Seg7Type type)
{
    SYN_ASSERT(seg != NULL);
    SYN_ASSERT(segments != NULL);
    SYN_ASSERT(digits != NULL);
    SYN_ASSERT(num_digits > 0 && num_digits <= SYN_SEG7_MAX_DIGITS);

    memset(seg, 0, sizeof(*seg));
    seg->num_digits = num_digits;
    seg->type = type;
    seg->active_digit = 0;
    seg->leading_zeros = false;

    memcpy(seg->segment_pins, segments, sizeof(SYN_GPIO_Pin) * 8);
    memcpy(seg->digit_pins, digits, sizeof(SYN_GPIO_Pin) * num_digits);

    /* Initialize segment output pins */
    SYN_GPIO_State idle_seg_state =
        (type == SYN_SEG7_COMMON_CATHODE) ? SYN_GPIO_LOW : SYN_GPIO_HIGH;
    for (uint8_t i = 0; i < 8; i++) {
        syn_port_gpio_init(seg->segment_pins[i], SYN_GPIO_OUTPUT);
        syn_port_gpio_write(seg->segment_pins[i], idle_seg_state);
    }

    /* Initialize digit select output pins (idle OFF) */
    SYN_GPIO_State idle_digit_state =
        (type == SYN_SEG7_COMMON_CATHODE) ? SYN_GPIO_HIGH : SYN_GPIO_LOW;
    for (uint8_t d = 0; d < num_digits; d++) {
        syn_port_gpio_init(seg->digit_pins[d], SYN_GPIO_OUTPUT);
        syn_port_gpio_write(seg->digit_pins[d], idle_digit_state);
    }

    return SYN_OK;
}

void syn_seg7_scan(SYN_Seg7 *seg)
{
    if (seg == NULL || seg->num_digits == 0)
        return;

    /* Turn OFF previous active digit selector */
    SYN_GPIO_State off_digit_state =
        (seg->type == SYN_SEG7_COMMON_CATHODE) ? SYN_GPIO_HIGH : SYN_GPIO_LOW;
    syn_port_gpio_write(seg->digit_pins[seg->active_digit], off_digit_state);

    /* Advance to next digit */
    seg->active_digit = (uint8_t)((seg->active_digit + 1) % seg->num_digits);
    uint8_t mask = seg->digit_buffers[seg->active_digit];

    /* Set segment output levels according to mask & wiring type */
    for (uint8_t s = 0; s < 8; s++) {
        bool bit = (mask & (1u << s)) != 0;
        SYN_GPIO_State state = (seg->type == SYN_SEG7_COMMON_CATHODE)
                                   ? (bit ? SYN_GPIO_HIGH : SYN_GPIO_LOW)
                                   : (bit ? SYN_GPIO_LOW : SYN_GPIO_HIGH);
        syn_port_gpio_write(seg->segment_pins[s], state);
    }

    /* Turn ON active digit selector */
    SYN_GPIO_State on_digit_state =
        (seg->type == SYN_SEG7_COMMON_CATHODE) ? SYN_GPIO_LOW : SYN_GPIO_HIGH;
    syn_port_gpio_write(seg->digit_pins[seg->active_digit], on_digit_state);
}

void syn_seg7_clear(SYN_Seg7 *seg)
{
    if (seg == NULL)
        return;
    memset(seg->digit_buffers, 0, sizeof(seg->digit_buffers));
}

void syn_seg7_set_digit_raw(SYN_Seg7 *seg, uint8_t digit_idx, uint8_t seg_mask)
{
    if (seg == NULL || digit_idx >= seg->num_digits)
        return;
    seg->digit_buffers[digit_idx] = seg_mask;
}

void syn_seg7_print_int(SYN_Seg7 *seg, int32_t val)
{
    if (seg == NULL)
        return;

    syn_seg7_clear(seg);

    char buf[16];
    snprintf(buf, sizeof(buf), "%d", (int)val);
    size_t len = strlen(buf);

    if (len > seg->num_digits) {
        /* Overflow: display dashes ---- */
        for (uint8_t i = 0; i < seg->num_digits; i++) {
            seg->digit_buffers[i] = 0x40; /* '-' */
        }
        return;
    }

    /* Right-align string onto digit buffers */
    int start_digit = (int)seg->num_digits - (int)len;
    for (size_t i = 0; i < len; i++) {
        seg->digit_buffers[start_digit + (int)i] = char_to_seg7(buf[i]);
    }
}

void syn_seg7_print_float(SYN_Seg7 *seg, float val, uint8_t decimals)
{
    if (seg == NULL)
        return;

    if (decimals >= seg->num_digits) {
        decimals = (uint8_t)(seg->num_digits > 0 ? seg->num_digits - 1 : 0);
    }

    syn_seg7_clear(seg);

    char buf[16];
    snprintf(buf, sizeof(buf), "%.*f", (int)decimals, (double)val);

    uint8_t dig_idx = 0;
    for (size_t i = 0; buf[i] != '\0' && dig_idx < seg->num_digits; i++) {
        if (buf[i] == '.') {
            if (dig_idx > 0) {
                seg->digit_buffers[dig_idx - 1] |= 0x80; /* Set DP bit */
            }
        } else {
            seg->digit_buffers[dig_idx++] = char_to_seg7(buf[i]);
        }
    }
}

void syn_seg7_print_hex(SYN_Seg7 *seg, uint32_t val)
{
    if (seg == NULL)
        return;

    syn_seg7_clear(seg);

    char buf[16];
    snprintf(buf, sizeof(buf), "%X", (unsigned int)val);
    size_t len = strlen(buf);

    if (len > seg->num_digits) {
        len = seg->num_digits;
    }

    int start_digit = (int)seg->num_digits - (int)len;
    for (size_t i = 0; i < len; i++) {
        seg->digit_buffers[start_digit + (int)i] = char_to_seg7(buf[i]);
    }
}

void syn_seg7_print_str(SYN_Seg7 *seg, const char *str)
{
    if (seg == NULL || str == NULL)
        return;

    syn_seg7_clear(seg);

    uint8_t dig_idx = 0;
    for (size_t i = 0; str[i] != '\0' && dig_idx < seg->num_digits; i++) {
        if (str[i] == '.') {
            if (dig_idx > 0) {
                seg->digit_buffers[dig_idx - 1] |= 0x80;
            }
        } else {
            seg->digit_buffers[dig_idx++] = char_to_seg7(str[i]);
        }
    }
}