Created
June 11, 2020 01:26
-
-
Save martinmroz/d872a9562aa10bbf86b9385d8de6ffe9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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