dsp0401.c 7.29 KB
/*
 * Copyright (C) 2017 Inria
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License v2.1. See the file LICENSE in the top level
 * directory for more details.
 */

/**
 * @ingroup     drivers_dsp0401
 * @{
 *
 * @file
 * @brief       Device driver implementation for dsp0401 alphanumeric display
 *
 * @author      Alexandre Abadie <alexandre.abadie@inria.fr>
 *
 * @}
 */

#include <inttypes.h>
#include <string.h>

#include "dsp0401.h"
#include "xtimer.h"
#include "periph/gpio.h"
#include "periph/pwm.h"

#define ENABLE_DEBUG        (0)
#include "debug.h"

#define LATCH_DELAY         (50U) /* 50 us */
#define SCK                 (dev->params.clk)
#define SDI                 (dev->params.sdi)
#define LAT                 (dev->params.lat)
#define PWM_PERIPH          (dev->params.pwm)
#define PWM_CHAN            (dev->params.pwm_channel)
#define BRIGHTNESS          (dev->params.brightness)
#define PWM_FREQ            (1000U)
#define PWM_STEPS           (256U)
#define MOD_COUNT           (dev->params.module_count)

/* Internal Ascii char map */
#define CHAR_MAP_MIN         (30U)
#define CHAR_MAP_MAX         (127U)

static const uint8_t dsp0401_charmap[][2] = {
                     /* dec: char */
    { 0x00, 0x00 },  /*  30:      */
    { 0x00, 0x00 },  /*  31:      */
    { 0x00, 0x00 },  /*  32:  ' ' */
    { 0x00, 0x00 },  /*  33:   !  */
    { 0x00, 0x00 },  /*  34:   "  */
    { 0x00, 0x00 },  /*  35:   #  */
    { 0xB5, 0xB5 },  /*  36:   $  */
    { 0xB3, 0xB3 },  /*  37:   %  */
    { 0x00, 0x00 },  /*  38:   &  */
    { 0x01, 0x00 },  /*  39:   `  */
    { 0x81, 0x00 },  /*  40:   (  */
    { 0x00, 0x42 },  /*  41:   )  */
    { 0x53, 0x53 },  /*  42:   *  */
    { 0x11, 0x11 },  /*  43:   +  */
    { 0x00, 0x00 },  /*  44:   ,  */
    { 0x10, 0x10 },  /*  45:   -  */
    { 0x00, 0x04 },  /*  46:   .  */
    { 0x02, 0x02 },  /*  47:   /  */
    { 0xAC, 0xAC },  /*  48:   0  */
    { 0x01, 0x01 },  /*  49:   1  */
    { 0x9C, 0x9C },  /*  50:   2  */
    { 0xBC, 0x84 },  /*  51:   3  */
    { 0x38, 0x30 },  /*  52:   4  */
    { 0xB4, 0xB4 },  /*  53:   5  */
    { 0xB4, 0xBC },  /*  54:   6  */
    { 0x16, 0x92 },  /*  55:   7  */
    { 0xBC, 0xBC },  /*  56:   8  */
    { 0xBC, 0xB4 },  /*  57:   9  */
    { 0x04, 0x04 },  /*  58:   :  */
    { 0x04, 0x02 },  /*  59:   ;  */
    { 0x00, 0x00 },  /*  60:   <  */
    { 0x90, 0x14 },  /*  61:   =  */
    { 0x00, 0x00 },  /*  62:   >  */
    { 0x00, 0x00 },  /*  63:   ?  */
    { 0xAC, 0x9D },  /*  64:   @  */
    { 0x3C, 0xB8 },  /*  65:   A  */
    { 0xB1, 0xBC },  /*  66:   B  */
    { 0x84, 0xAC },  /*  67:   C  */
    { 0xAD, 0x85 },  /*  68:   D  */
    { 0x84, 0xBC },  /*  69:   E  */
    { 0x04, 0xB8 },  /*  70:   F  */
    { 0xB4, 0xAC },  /*  71:   G  */
    { 0x38, 0x38 },  /*  72:   H  */
    { 0x85, 0x85 },  /*  73:   I  */
    { 0xA8, 0x0C },  /*  74:   J  */
    { 0x42, 0x38 },  /*  75:   K  */
    { 0x80, 0x2C },  /*  76:   L  */
    { 0x2A, 0x68 },  /*  77:   M  */
    { 0x68, 0x68 },  /*  78:   N  */
    { 0xAC, 0xAC },  /*  79:   0  */
    { 0x1C, 0xB8 },  /*  80:   P  */
    { 0xEC, 0xAC },  /*  81:   Q  */
    { 0x5C, 0xB8 },  /*  82:   R  */
    { 0xB4, 0xB4 },  /*  83:   S  */
    { 0x05, 0x81 },  /*  84:   T  */
    { 0xA8, 0x2C },  /*  85:   U  */
    { 0x02, 0x2A },  /*  86:   V  */
    { 0x68, 0x2A },  /*  87:   W  */
    { 0x42, 0x42 },  /*  88:   X  */
    { 0x02, 0x42 },  /*  89:   Y  */
    { 0x86, 0x86 },  /*  90:   Z  */
    { 0x00, 0xAC },  /*  91:   [  */
    { 0x40, 0x40 },  /*  92:   \  */
    { 0xAC, 0x00 },  /*  93:   ]  */
    { 0x00, 0x00 },  /*  94:   ^  */
    { 0x80, 0x04 },  /*  95:   _  */
    { 0x00, 0x40 },  /*  96:   `  */
    { 0x00, 0x00 },  /*  97:   a  */
    { 0x00, 0x00 },  /*  98:   b  */
    { 0x00, 0x00 },  /*  99:   c  */
    { 0x00, 0x00 },  /* 100:   d  */
    { 0x00, 0x00 },  /* 101:   e  */
    { 0x00, 0x00 },  /* 102:   f  */
    { 0x00, 0x00 },  /* 103:   g  */
    { 0x00, 0x00 },  /* 104:   h  */
    { 0x00, 0x00 },  /* 105:   i  */
    { 0x00, 0x00 },  /* 106:   j  */
    { 0x00, 0x00 },  /* 107:   k  */
    { 0x00, 0x00 },  /* 108:   l  */
    { 0x00, 0x00 },  /* 109:   m  */
    { 0x00, 0x00 },  /* 110:   n  */
    { 0x00, 0x00 },  /* 111:   o  */
    { 0x00, 0x00 },  /* 112:   p  */
    { 0x00, 0x00 },  /* 113:   q  */
    { 0x00, 0x00 },  /* 114:   r  */
    { 0x00, 0x00 },  /* 115:   s  */
    { 0x00, 0x00 },  /* 116:   t  */
    { 0x00, 0x00 },  /* 117:   u  */
    { 0x00, 0x00 },  /* 118:   v  */
    { 0x00, 0x00 },  /* 119:   w  */
    { 0x00, 0x00 },  /* 120:   x  */
    { 0x00, 0x00 },  /* 121:   y  */
    { 0x00, 0x00 },  /* 122:   z  */
    { 0x85, 0x11 },  /* 123:   {  */
    { 0x01, 0x01 },  /* 124:   |  */
    { 0x11, 0x85 },  /* 125:   }  */
    { 0x00, 0x00 },  /* 126:   ~  */
    { 0x00, 0x00 },  /* 127:      */
};

static void _shift_char(const dsp0401_t *dev, uint8_t c)
{
    /* Unsupported chars not displayed */
    if ((c < CHAR_MAP_MIN) || (c > CHAR_MAP_MAX)) {
        c = ' ';
    }

    for (unsigned p = 0 ; p < 2 ; ++p) {
        for (unsigned i = 0; i < 8; ++i)  {
            gpio_write(SDI, !!(dsp0401_charmap[c - CHAR_MAP_MIN][p] & (1 << (7 - i))));
            gpio_set(SCK);
            gpio_clear(SCK);
        }
    }
}

static void _latch(const dsp0401_t *dev)
{
    xtimer_usleep(LATCH_DELAY);
    gpio_set(LAT);
    xtimer_usleep(LATCH_DELAY);
    gpio_clear(LAT);
    xtimer_usleep(LATCH_DELAY);
}

int dsp0401_init(dsp0401_t *dev, const dsp0401_params_t *params)
{
    dev->params = *params;

    /* Initialize clock pin and set it to low */
    if (gpio_init(SCK, GPIO_OUT) < 0) {
        DEBUG("[ERROR] CLK GPIO initialization failed.");
        return -DSP0401_ERR_CLK_GPIO;
    }
    gpio_clear(SCK);

    /* Initialize data pin */
    if (gpio_init(SDI, GPIO_OUT) < 0) {
        DEBUG("[ERROR] SDI GPIO initialization failed.");
        return -DSP0401_ERR_SDI_GPIO;
    }
    gpio_clear(SDI);

    /* Initialize latch pin and set it to low */
    if (gpio_init(LAT, GPIO_OUT) < 0) {
        DEBUG("[ERROR] LAT GPIO initialization failed.");
        return -DSP0401_ERR_LAT_GPIO;
    }
    gpio_clear(LAT);

    /* Initialize PWM used for LED brightness */
    if (!pwm_init(PWM_PERIPH, PWM_LEFT, PWM_FREQ, PWM_STEPS)) {
        DEBUG("[ERROR] PWM initialization failed.");
        return -DSP0401_ERR_PWM;
    }
    pwm_set(PWM_PERIPH, PWM_CHAN, BRIGHTNESS);

    dsp0401_clear_text(dev);

    return DSP0401_OK;
}

void dsp0401_display_text(const dsp0401_t *dev, char *text)
{
    unsigned text_len = strlen(text);
    for (unsigned i = 0 ; i < MOD_COUNT * 4; ++i) {
        if (i < text_len) {
           _shift_char(dev, text[i]);
        } else {
            _shift_char(dev, ' ');
        }
    }

    _latch(dev);
}

void dsp0401_clear_text(const dsp0401_t *dev)
{
    /* each module has 4 alphanumeric displays */
    for (unsigned i = 0; i < MOD_COUNT * 4; ++i) {
        _shift_char(dev, ' ');
    }
    _latch(dev);
}

void dsp0401_scroll_text(const dsp0401_t *dev, char *text, uint16_t delay)
{
    dsp0401_clear_text(dev);

    for (unsigned i = 0; i < strlen(text); ++i) {
        _shift_char(dev, text[i]);
        _latch(dev);
        xtimer_usleep((uint32_t)(delay * US_PER_MS));
    }

    for (unsigned i = 0; i < MOD_COUNT * 4; ++i) {
        _shift_char(dev, ' ');
        _latch(dev);
        xtimer_usleep((uint32_t)(delay * US_PER_MS));
    }
}