Skip to content

Instantly share code, notes, and snippets.

@martinmroz
Created June 11, 2020 01:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save martinmroz/d872a9562aa10bbf86b9385d8de6ffe9 to your computer and use it in GitHub Desktop.
Save martinmroz/d872a9562aa10bbf86b9385d8de6ffe9 to your computer and use it in GitHub Desktop.
/*
* tube_display.c
* Project TubeClock/Display
*
* Created Friday, September 3, 2010 by Martin Mroz.
* Copyright (C) 2010 Martin Mroz, All Rights Reserved.
*/
#include <avr/pgmspace.h>
#include "hardware/tube_display.h"
#include "tasks/debug_log.h"
/* ------------------------------------------------------------------------- */
#define TUBE_DISPLAY_DIGITS 4
static volatile uint8_t _anodeMask = 0;
static volatile uint8_t _contents[TUBE_DISPLAY_DIGITS];
static volatile uint8_t _currentDigit = 0;
static volatile BOOL _first = NO;
/* ------------------------------------------------------------------------- */
static void _display_reset_masks(void)
{
/* Reset all of the port values */
TUBE_GROUP_B_PORT &= ~TUBE_GROUP_B_MASK;
TUBE_GROUP_C_PORT &= ~TUBE_GROUP_C_MASK;
TUBE_GROUP_D_PORT &= ~TUBE_GROUP_D_MASK;
TUBE_ANODE_PIN_PORT &= ~TUBE_ANODE_PIN_MASK;
}
static uint8_t _tube_group_b_port_mask(BOOL hasDecimal)
{
/* This simply returns the decimal */
if (hasDecimal == YES) {
return (1 << TUBE_GROUP_B_KDP);
} else {
return 0x00;
}
}
static uint8_t _tube_group_c_port_mask(uint8_t contents)
{
/* Make sure contents are reasonable */
assert(contents <= 9 || contents == TUBE_NO_CONTENTS);
/* Group C controls K0 through K3 */
switch (contents) {
case 0: return (1 << TUBE_GROUP_C_K0); break;
case 1: return (1 << TUBE_GROUP_C_K1); break;
case 2: return (1 << TUBE_GROUP_C_K2); break;
case 3: return (1 << TUBE_GROUP_C_K3); break;
default: return 0x00; break;
}
}
static uint8_t _tube_group_d_port_mask(uint8_t contents)
{
/* Make sure contents are reasonable */
assert(contents <= 9 || contents == TUBE_NO_CONTENTS);
/* Group D controls K4 through K9 */
switch (contents) {
case 4: return (1 << TUBE_GROUP_D_K4); break;
case 5: return (1 << TUBE_GROUP_D_K5); break;
case 6: return (1 << TUBE_GROUP_D_K6); break;
case 7: return (1 << TUBE_GROUP_D_K7); break;
case 8: return (1 << TUBE_GROUP_D_K8); break;
case 9: return (1 << TUBE_GROUP_D_K9); break;
default: return 0x00; break;
}
}
static inline uint8_t _tube_anode_pin_port_mask(uint8_t anodeNumber)
{
/* Make sure we have a sane anode number */
assert(anodeNumber < TUBE_DISPLAY_DIGITS);
/* This map converts the number into its associated pin */
static uint8_t maskValueMap[] PROGMEM = {
(1 << TUBE_ANODE_PIN_AN0),
(1 << TUBE_ANODE_PIN_AN1),
(1 << TUBE_ANODE_PIN_AN2),
(1 << TUBE_ANODE_PIN_AN3)
};
return pgm_read_byte(&maskValueMap[anodeNumber]);
}
/* ------------------------------------------------------------------------- */
void tube_display_init(void)
{
/* Halt the clock while we set up */
TCCR2B = (0 << CS22) | (0 << CS21) | (0 << CS20);
/* Re-initialize the statics */
_anodeMask = 0;
_currentDigit = 0;
memset((void *)_contents, '\0', sizeof(_contents));
/* Configure the data direction registers */
TUBE_GROUP_B_DDR |= TUBE_GROUP_B_MASK;
TUBE_GROUP_C_DDR |= TUBE_GROUP_C_MASK;
TUBE_GROUP_D_DDR |= TUBE_GROUP_D_MASK;
TUBE_ANODE_PIN_DDR |= TUBE_ANODE_PIN_MASK;
/* Reset the port values */
_display_reset_masks();
/* Configure Timer/Counter 2 for 120Hz Refresh */
/* CTC Mode - OCF2A when TCNT2 Reached */
TCCR2A = (1 << WGM21);
TCCR2B = (1 << CS22) | (1 << CS21) | (1 << CS20);
TCNT2 = 0;
OCR2A = 35;
TIMSK2 = (1 << OCIE2A);
}
void tube_display_set_anode_mask(uint8_t mask)
{
/* Strip off any invalid bits just to make sure */
_anodeMask = (mask & TUBE_ANODE_MASK);
}
BOOL tube_display_set_contents(uint8_t* contents, uint8_t count)
{
/* Track errors setting content */
BOOL invalidContents = NO;
/* Limit the contents */
if (count > TUBE_DISPLAY_DIGITS) {
count = TUBE_DISPLAY_DIGITS;
}
/* Copy over all the valid elements */
for (int i = 0; i < count; ++i) {
if ((contents[i] & ~TUBE_DECIMAL_FLAG) > 9) {
_contents[i] = 0;
invalidContents = YES;
} else {
_contents[i] = contents[i];
}
}
return invalidContents;
}
/* ------------------------------------------------------------------------- */
ISR(TIMER2_COMPA_vect)
{
/* First, turn everything off */
_display_reset_masks();
/* Compute the mask value */
uint8_t anode = (1 << _currentDigit);
/* If this anode is enabled, proceed */
if ((_anodeMask & anode) != 0) {
BOOL hasDecimal = NO;
uint8_t contentIndex = TUBE_DISPLAY_DIGITS - _currentDigit - 1;
uint8_t contents = _contents[contentIndex];
/* If there is a decimal, strip it out and store the flag */
if ((contents & TUBE_DECIMAL_FLAG) != 0) {
hasDecimal = YES;
contents &= ~TUBE_DECIMAL_FLAG;
}
/* Write the various groups since we're enabled */
TUBE_GROUP_B_PORT |= _tube_group_b_port_mask(hasDecimal);
TUBE_GROUP_C_PORT |= _tube_group_c_port_mask(contents);
TUBE_GROUP_D_PORT |= _tube_group_d_port_mask(contents);
TUBE_ANODE_PIN_PORT |= _tube_anode_pin_port_mask(_currentDigit);
}
/* Roll over the digit counter */
if (++_currentDigit == TUBE_DISPLAY_DIGITS) {
_currentDigit = 0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment