Devices & Components
Arduino Uno Rev3
CD4017
VFD Display
BC547
PC817
470nF Capacitor
1N4007 – High Voltage, High Current Rated Diode
Electrolitic Capacitors
CD4094
BC557
Dupont Jumpers
Perfboard
1/4W Resistors
Hardware & Tools
Wire Clipper
Solder Wire
Wire
Soldering iron (generic)
Project description
Code
Arduino code with working routine
arduino
1/* 2 Versatile VFD Display Hrdware Interface Arduino Program 3 Copyright (C) 2019 Genny A. Carogna 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation, either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <https://www.gnu.org/licenses/>. 17*/ 18 19 20 21/* this code uses resources from the Arduino in a "behind the curtains" way, the timer1 and SPI modules are both used and busy 22 * , so you may have big problems using SPI, SPI pins, and some functionalities based upon timer1... the circuit proposed 23 * doesn't have an onboard memory, so the segment setup must be "scanned" continuosly, every grid, 100 times per second 24 * , from the Arduino segments array, this translates also on the so-called "overhaed" CPU work, that, for you to know, it's 25 * no more than regular use of a micro at the end, it may hit around 3% of CPU work in terms of occupied time, you can also try 26 * to use the regular SPI tools of Arduino but it needs an indepth analisys of the outtake and procedure 27 * 28 * this code works with whatever 8 bit Arduino, also 3.3V should work ok, maybe reduce the SCK frequency, considering the CD4094 are 29 * powered along it and may "undervolt" (yes, exactly the overclock principles to fry your beloved PC) 30 */ 31 32 33 34 35#define ledLed 13 // it's the led pin that leds a led on to led you know that a led on means some led related stuff (scientist mumbojumbo of course) 36 37#define strobePin 4 // the Arduino pin to dedicate to the "strobe" line 38#define strobeHold 10 // microseconds // holding time to switch the optocoupler... not megahertz-fast but acceptable 39 40 41 42 43 44 45// data for my displays (a video cassette recorder, an audiophoolery CD reader and a compact stereo)... dont mind, delete, and just define "gridAmount" and "anodeAmount" 46#define JVC 47 48#define mitsubishiGrids 10 49#define mitsubishiAnodes 9 50#define marantzGrids 9 51#define marantzAnodes 14 52#define JVCGrids 11 53#define JVCAnodes 19 54 55#if defined mitsubishi 56#define gridAmount mitsubishiGrids 57#define anodeAmount mitsubishiAnodes 58 59#elif defined marantz 60#define gridAmount marantzGrids 61#define anodeAmount marantzAnodes 62 63#elif defined JVC 64#define gridAmount JVCGrids 65#define anodeAmount JVCAnodes 66#endif 67 68 69 70 71 72// this array, segments[x], stores the segment bits 73// segments[0] means grid zero, and the least significative bit is segment zero in that grid 74// you can access and modify this array as you like, from wherever, without doing anything else, the effect is immediately visible on 75// the display, and stays on as long you don't change the bits, for more than 8 segments per grid displays, you need a 76// 16 or 32 bit container per grid, this is managed automatically from the #define(s) above 77#if anodeAmount > 16 78volatile uint32_t segments[gridAmount] = {0}; // more than 16 segments per grid (32 bit needed) 79#elif anodeAmount > 8 80volatile uint16_t segments[gridAmount] = {0}; // more than 8 segments per grid (16 bit, twin transfer) 81#else 82volatile uint8_t segments[gridAmount] = {0}; // 8 or less segments per grid (8 bit) 83#endif 84 85 86 87 88 89 90void setup(){ 91 // turns SPI pins and some functional ones as output in a fast\\direct way 92 *portModeRegister(digitalPinToPort(PIN_SPI_SS)) |= digitalPinToBitMask(PIN_SPI_SS); 93 *portModeRegister(digitalPinToPort(PIN_SPI_MOSI)) |= digitalPinToBitMask(PIN_SPI_MOSI); 94 *portModeRegister(digitalPinToPort(PIN_SPI_SCK)) |= digitalPinToBitMask(PIN_SPI_SCK); 95 *portModeRegister(digitalPinToPort(strobePin)) |= digitalPinToBitMask(strobePin); 96 *portOutputRegister(digitalPinToPort(strobePin)) &= ~digitalPinToBitMask(strobePin); // put it low 97 *portModeRegister(digitalPinToPort(ledLed)) |= digitalPinToBitMask(ledLed); 98 *portOutputRegister(digitalPinToPort(ledLed)) &= ~digitalPinToBitMask(ledLed); // put it low 99 100 delay(800); // some delay to wait for the CD4017 reset to complete (you need to run the code AFTER powering up the 4017s, this makes it automatic in case you power all in once) 101 102 cli(); // disables interrupts to let us tweak some stuff without cats and dogs escaping everywhere 103 104 // timer1 configuration, it makes an interrupt happen "grids times 100" per second... this gives a 100Hz total refresh that is ok 105 TCCR1A = 0; 106 TCCR1B = 0; 107 TCNT1 = 0; 108 OCR1A = 160000 / gridAmount; 109 TCCR1B |= (1 << WGM12); 110 TCCR1B |= (1 << CS10); 111 TIMSK1 |= (1 << OCIE1A); 112 113 // SPI configuration, we cannot use the Arduino SPI tools bcs they rely on interrupts AND we 114 // need the communication happen INSIDE an interrupt.. 115 // change SPR0 for SPR1 and uncomment the last line to have half SPI clock speed 116 SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0); // 1MHz stock SCK speed 117 // SPSR = (1 << SPI2X); // uncomment this and mod above for half clock speed 118 119 sei(); // interrupts are a go again 120 121 // it's better to put your addictional setup() code from here 122} 123 124 125 126 127 128 129// you can do whatever in loop(), delays, pollings, but disabling interrupts or making the micro halt completely will 130// freeze the display on a grid, and if the voltage is at top power you may also burn some segments (!!) 131void loop() { 132 show(); // kinda screensaver, you can delete 133} 134 135 136 137 138 139 140 141// interrupt service routine, this happens at every grid scan switch and times 100 per second 142ISR(TIMER1_COMPA_vect){ 143 /* 144 ATTENTION 145 ATTENTION: i dunno why i need to start "turn" from 1 for proper functionality (i power the Arduino after and it may flip a grid), in case it keeps missing the correct grid at statup, vary this 146 ATTENTION 147 */ 148 static uint8_t turn = 1; // indestructible variable to remind at what grid we were the next interrupt 149 150#if anodeAmount > 24 151 SPDR = ~uint8_t(segments[turn] >> 24); // we need negated bits on the CD4094 (cos of the transistors arrangement) 152 while (!(SPSR & (1 << SPIF))) ; // lets wait it completes the transfer 153#endif 154#if anodeAmount > 16 155 SPDR = ~uint8_t(segments[turn] >> 16); // we need negated bits on the CD4094 (cos of the transistors arrangement) 156 while (!(SPSR & (1 << SPIF))) ; // lets wait it completes the transfer 157#endif 158#if anodeAmount > 8 159 SPDR = ~uint8_t(segments[turn] >> 8); // we need negated bits on the CD4094 (cos of the transistors arrangement) 160 while (!(SPSR & (1 << SPIF))) ; // lets wait it completes the transfer 161#endif 162 SPDR = ~uint8_t(segments[turn]); // we need negated bits on the CD4094 (cos of the transistors arrangement) 163 while (!(SPSR & (1 << SPIF))) ; // lets wait it completes the transfer... yyyYYYAAAAWWWWNNNnnn........... 164 165 // strobing (finally!!) this also switches the grid on the CD4017s 166 *portOutputRegister(digitalPinToPort(strobePin)) |= digitalPinToBitMask(strobePin); // fast pin drive ON 167 delayMicroseconds(strobeHold); 168 *portOutputRegister(digitalPinToPort(strobePin)) &= ~digitalPinToBitMask(strobePin); // fast pin drive OFF 169 170 // grid turn reminder at rotation 171 turn ++; 172 if (turn == gridAmount) turn = 0; 173} 174 175 176 177 178 179 180 181 182 183//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 184///////////FROM NOW THE SKETCH IS REFERRED TO MY DISPLAYS AND VISUAL GAMES, YOU CAN DELETE FROM HERE ON///////////////////////// 185//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 186 187 188 189 190// this is just a demo, delete as you like 191void show() { 192 193 // numbers flip 194#ifdef mitsubishi 195 for (uint8_t i = 0; i < 9; i ++) for (uint8_t j = 0; j < 11; j ++) { 196 delay(200); 197 mitsubishiPrintNum(j, i); 198 } 199 for (uint8_t i = 0; i < 9; i ++) for (uint8_t j = 0; j < 11; j ++) { 200 delay(20); 201 mitsubishiPrintNum(j, i); 202 } 203#elif defined marantz 204 for (uint8_t i = 0; i < 6; i ++) for (uint8_t j = 0; j < 11; j ++) { 205 delay(200); 206 marantzPrintNum(j, i); 207 } 208 for (uint8_t i = 0; i < 6; i ++) for (uint8_t j = 0; j < 11; j ++) { 209 delay(20); 210 marantzPrintNum(j, i); 211 } 212#elif defined JVC /* 213 for (uint8_t i = 0; i < 9; i ++) for (uint8_t j = 0; j < 11; j ++) { 214 delay(200); 215 JVCPrintNum(j, i); 216 } 217 for (uint8_t i = 0; i < 9; i ++) for (uint8_t j = 0; j < 11; j ++) { 218 delay(20); 219 JVCPrintNum(j, i); 220 }*/ 221#endif 222 223 delay(500); 224 225 226 227 228 // segments flip 229 for (uint8_t i = 0; i < gridAmount; i ++) for (uint8_t j = 0; j < anodeAmount + 1; j ++) { 230 delay(100); 231 segments[i] = (uint32_t(1) << j); 232 } 233 234 delay(500); 235 236 237 238 239 // all the display blinkng 240 for (uint8_t i = 0; i < 5; i ++) { 241 delay(200); 242 for (uint8_t j = 0; j < gridAmount; j ++) { 243 segments[j] = 0xFFFFFFFF; 244 } 245 delay(200); 246 for (uint8_t j = 0; j < gridAmount; j ++) { 247 segments[j] = 0; 248 } 249 } 250 251 delay(500); 252} 253 254 255 256// easy peasy seven segment number visualization 257void mitsubishiPrintNum(uint8_t val, uint8_t pos) { 258 if (pos > 2) pos ++; 259 260 uint16_t mask = 0xFF80; 261 uint8_t numbers[] = {B00111111, B00000110, B01011011, B01001111, B01100110, B01101101, B01111101, B00100111, B01111111, B01101111, B00000000}; 262 263 switch (pos) { 264 case 0: case 1: case 4: case 5: case 6: case 7: case 8: segments[pos] &= mask; segments[pos] |= numbers[val]; break; 265 case 2: if (val == 1) segments[1] |= (1 << 8); else segments[1] &= ~(uint16_t(1) << 8); break; 266 case 9: segments[9] &= 0xFFF0; if (val == 1) segments[9] |= 3; else if (val == 2) segments[9] |= B1101; 267 } 268} 269 270 271 272 273// easy peasy seven segment number visualization 274void marantzPrintNum(uint8_t val, uint8_t pos) { 275 if (pos > 5) return; 276 pos ++; 277 pos ++; 278 279 uint16_t mask = 0xFF80; // bitmask to delete the digit 280 uint8_t numbers[] = {B00111111, B00000110, B01011011, B01001111, B01100110, B01101101, B01111101, B00100111, B01111111, B01101111, B00000000}; 281 282 segments[pos] &= mask; // delete whatever digit 283 segments[pos] |= numbers[val]; // set digit 284} 285 286 287 288 289// easy peasy seven segment number visualization 290void JVCPrintNum(uint8_t val, uint8_t pos) { 291 // to come (you really don't care) 292} 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312// 313
Downloadable files
General Schematic
I avoided drawing ALL the BC557 transistors because they are just there connected on all the CD4094 outputs, all the same. There's a jumper selector (running on the red line) you must configure on the 4017 chips, it resets the two 4017 at the completion of the grids scanning, you must connect (or solder) the flying jumper on the output pin AFTER the last grid, so if your display has 10 grids the jumper goes on the 11th output. The Arduino connections are outlined in red, you can use the USB power for it but it's strongly advised to use a regular old-style transformer for the remaining supplies if you already have a grounded power supply like your PC. These supplies must be all DC except maybe the filament supply, I added some diodes because it's likely you need a separate transformer with a low voltage secondary. The filament power usually ranges around 3V 150mA, a 5V AC transformer will suffice. The "ballast" thing will possibly be a wirevound power potentiometer of like 100ohms, or a fixed resistor, or also some 1N4007 diodes to reduce the voltage. The BC557 transistors pull the anodes up to anodic voltage and the 100kohm resistors will let the voltage drive fall when the segment is off, while the CD4094 remain at 5V normally. The CD4017s will be powered with grid voltage and don't require additional transistors. There's a "ghetto" reset circuit for the 4017 that lasts like one tenth of a second, you have to wait for it to settle before running the code. The "original" schematic for cascading some CD4017 required other logic gates chips, I used instead an NPN transistor and the optocoupler itself to replicate an AND gate, it's fast enough at closing so it's perfect for the task, at releasing it's instead a bit slower but we don't care, especially because the clock inputs on the 4017 are schmitt triggered and the required speed is not "scary" there. There are various grounds for the chips and stuff as you can see, take a good look.
General Schematic

General Schematic
I avoided drawing ALL the BC557 transistors because they are just there connected on all the CD4094 outputs, all the same. There's a jumper selector (running on the red line) you must configure on the 4017 chips, it resets the two 4017 at the completion of the grids scanning, you must connect (or solder) the flying jumper on the output pin AFTER the last grid, so if your display has 10 grids the jumper goes on the 11th output. The Arduino connections are outlined in red, you can use the USB power for it but it's strongly advised to use a regular old-style transformer for the remaining supplies if you already have a grounded power supply like your PC. These supplies must be all DC except maybe the filament supply, I added some diodes because it's likely you need a separate transformer with a low voltage secondary. The filament power usually ranges around 3V 150mA, a 5V AC transformer will suffice. The "ballast" thing will possibly be a wirevound power potentiometer of like 100ohms, or a fixed resistor, or also some 1N4007 diodes to reduce the voltage. The BC557 transistors pull the anodes up to anodic voltage and the 100kohm resistors will let the voltage drive fall when the segment is off, while the CD4094 remain at 5V normally. The CD4017s will be powered with grid voltage and don't require additional transistors. There's a "ghetto" reset circuit for the 4017 that lasts like one tenth of a second, you have to wait for it to settle before running the code. The "original" schematic for cascading some CD4017 required other logic gates chips, I used instead an NPN transistor and the optocoupler itself to replicate an AND gate, it's fast enough at closing so it's perfect for the task, at releasing it's instead a bit slower but we don't care, especially because the clock inputs on the 4017 are schmitt triggered and the required speed is not "scary" there. There are various grounds for the chips and stuff as you can see, take a good look.
General Schematic

Comments
Only logged in users can leave comments