VFD modular clock firmware

Dependencies:   DipCortex-EEprom RTC flw mbed

VFDDisplay.cpp

Committer:
Backstr?m
Date:
2015-02-24
Revision:
12:dfb422107412
Parent:
0:f6e68b4ce169

File content as of revision 12:dfb422107412:

/*
 * VFD Modular Clock - mbed
 * (C) 2011-14 Akafugu Corporation
 *
 * This program is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 *
 */

#include "VFDDisplay.h"
#include <stdarg.h>

#define sbi(a, b) (a) |= (1 << (b))
#define cbi(a, b) (a) &= ~(1 << (b))

uint8_t calculate_segments_7(uint8_t character);

VFDDisplay::VFDDisplay(PinName data, PinName clock, PinName latch, PinName blank, uint8_t digits)
        : m_mode(Long)
                , m_blink_mode(Full)
        , m_data(data)
        , m_clock(clock)
        , m_latch(latch)
        , m_blank(blank)
        , m_digits(digits)
        , m_multiplex_counter(0)
        , m_position(0)
        , m_scroll_offset(0)
        , m_message_length(0)
        , m_blink(false)
        , m_display_on(true)
        , m_brightness(10)
{
    //m_blank.period_ms(1);
    //m_blank = 0.0;
    m_blank = 0;
}

void VFDDisplay::cls()
{
    memset(m_buffer, ' ', MESSAGE_LIMIT);
    memset(m_char_buffer, ' ', MESSAGE_LIMIT);
    memset(m_dot_buffer, ' ', MESSAGE_LIMIT);
    m_position = 0;
    m_scroll_offset = 0;
    m_message_length = 0;
}

void VFDDisplay::setPosition(uint8_t pos)
{
    m_position = pos;
    if (m_position >= MESSAGE_LIMIT)
        m_position = MESSAGE_LIMIT-1;
}

// Write 8 bits to HV5812 driver
void VFDDisplay::writeHV5812(uint8_t data)
{
    // shift out MSB first
    for (uint8_t i = 0; i < 8; i++)  {
        if (!!(data & (1 << (7 - i))))
            m_data = 1;
        else
            m_data = 0;

        m_clock = 1;
        m_clock = 0;
    }
}

const uint32_t BLINK_OFF_TIMER = 200;
const uint32_t BLINK_ON_TIMER = 350;
volatile uint32_t blink_countdown = BLINK_ON_TIMER;

extern DigitalOut led;

void VFDDisplay::multiplexTick()
{
    char d;
    
    if (m_reverse_display)
        d = m_char_buffer[m_digits - m_multiplex_counter - 1 + m_scroll_offset];
    else
        d = m_char_buffer[m_multiplex_counter + m_scroll_offset];

    // fixme: does not work properly when scrolling
    if (m_dot_buffer[m_multiplex_counter + m_scroll_offset - 1] == '.')
        setDot(m_multiplex_counter, true);
    else
        setDot(m_multiplex_counter, false);

    if (m_blink) {
        blink_countdown--;
        
        if (blink_countdown == 0) {
            m_display_on = !m_display_on;

            if (m_display_on)
                blink_countdown = BLINK_ON_TIMER;
            else  
                blink_countdown = BLINK_OFF_TIMER;
        }
    }
        
        handleBlink(d);
            
    m_multiplex_counter++;  
    if (m_multiplex_counter > m_multiplex_limit)
        m_multiplex_counter = 0;
}

int VFDDisplay::printf(const char* format, ...)
{
    va_list ap;
    va_start(ap, format);
    int ret = vsnprintf(m_buffer, MESSAGE_LIMIT, format, ap);
    va_end(ap);
    
    // post-processing for seamless dot support
    // splits buffer into two: one that contains characters
    // and one that contains dots
    uint8_t pos = 0;
    
    char* temp = m_buffer;
    
    while (*temp) {
        if (*temp == '.') {
            if (pos > 0) m_dot_buffer[pos-1] = '.';
            temp++;   
        }
        else {
            m_dot_buffer[pos] = ' ';
            m_char_buffer[pos++] = *temp;
            temp++;    
        }
    }
      
    /*    
    for (uint8_t i = 0; i < strlen(m_buffer); i++) {
        char c = m_buffer[i];
        
        if (c == '.') {
            m_dot_buffer[pos-1] = '.';
            //m_char_buffer[pos++] = c;
            i++;
        }
        else {
            m_dot_buffer[pos] = ' ';
            m_char_buffer[pos++] = c;
        }    
    }
    */
    
    m_dot_buffer[pos] = 0;
    m_char_buffer[pos] = 0;
    
    m_position = ret;
    m_message_length = strlen(m_char_buffer);

    return ret;
}

int VFDDisplay::_putc(int c)
{
    // fixme: support dot buffer
    m_char_buffer[m_position] = c;
    m_buffer[m_position++] = c;
    m_message_length++; 
    return 1;
}
    
int VFDDisplay::_getc()
{
    return -1;
}

void VFDDisplay::scroll(int8_t spaces /*= 1*/)
{
    m_scroll_offset += spaces;
    //if (m_scroll_offset > MESSAGE_LIMIT - m_digits)
    //    m_scroll_offset = 0;
}

void VFDDisplay::resetScroll()
{
    m_scroll_offset = 0;
}

bool VFDDisplay::scrollFinished()
{
    if (m_scroll_offset > strlen(m_char_buffer))
        return true;
    return false;
}

void VFDDisplay::setDot(uint8_t pos, bool on)
{
    if (on) {
        sbi(m_dots, pos);
    }
    else {
        cbi(m_dots, pos);
    }
}

void VFDDisplay::toggleTimeMode()
{
    if (m_mode == Short) m_mode = Extra;
    else if (m_mode == Extra) m_mode = Long;
    else m_mode = Short;
}

void VFDDisplay::setBrightness(uint8_t brite)
{
    if (brite > 10) brite = 10;
    m_brightness = brite;
}

uint8_t VFDDisplay::incBrightness()
{
    float f = m_blank;
    if (f >= 0.9) f = 0.0;
    f+= 0.1;
    m_blank = f;
    return f*10;
}