NCS314 Custom Firmware

Tagged: ,

This topic contains 3 replies, has 2 voices, and was last updated by  Filt_r 1 year, 2 months ago.

Viewing 4 posts - 1 through 4 (of 4 total)
  • Author
    Posts
  • #41430

    lestinto
    Participant

    Hello,

    I’ve got an old NCS314 Nixie shield for the UNO that I’ve had for a number of years, still working great but I want to repurpose it for a new project.

    Was looking through the schematics and source code, looks like it’s controlled via SPI controller, but I’m still trying to get my bearings when it comes to how it works exactly (what to bit-bang where and in what order, what the encoding is etc.).

    If you guys had a piece of example code that just flashed the board such that it displayed a static 6 digit number with the two dots on or off, I’d very much appreciated it as that’d just about tell me everything I needed to know.

    Thanks,
    – Luca

    #41435

    Filt_r
    Participant

    I’ve been working on some custom firmware for my NCS314-6 with an Arduino Mega, perhaps I can help. I’m not an embedded software expert by any means and I’m not sure your level of experience so I’m going to bias towards over-explaining it.

    So, my clock has 6 digits/IN-14 tubes, each with 10 possible values plus the two colons for a total of 64 individual elements that can be switched on or off. On my board (HW rev 2.3) there are two 32-bit high-voltage shift registers daisy chained together (HV5122 on my board), so that any data that overflows from the first one is passed on to the second one. Both are clocked by the SPI clock SCK and fed data by SPI MOSI. Essentially, every time you want to update the display, you have to send 64-bits out over SPI – the first 32 bits correspond to the on/off state of the 32 outputs of the second shift register in the daisy chain (M:SS), and the next 32 bits you send control the outputs of the first shift register (HH:M)

    The encoding works like this: each nixie element has ten possible values (if we only turn one number on in a single tube at a time). So, we can break the 64-bit control word up into chunks of 10 bits per tube plus 2 bits for each colon (top dot, bottom dot). If you have 4 digits, then you’d have a 42-bit data structure that holds the state of the display. I’m going to use my 64-element display for the sake of example. On my board, the shift registers are mapped as follows:

    If my display looks like this:
    |H1| |H2| |C1| |M1| |M2| |C2| |S1| |S2|

    Then the control word maps as follows:
    [ C2[top,bot], S2[9-0], S1[9-0], M2[9-0], C1[top,bot], H2[9-0], H1[9-0], M1[9-0] ]
    =
    [00 0000000000 0000000000 0000000000 00 0000000000 0000000000 0000000000]

    So, if you wanted to turn on just the colons, you’d send the following 64 bits out over SPI. I added spaced to divide the bits into groups per-nixie tube + colons:
    [11 0000000000 0000000000 0000000000 11 0000000000 0000000000 0000000000]
    or if you wanted to illuminate all of the zeroes and no colons, you’d have this:
    [00 0000000001 0000000001 0000000001 00 0000000001 0000000001 0000000001]

    Here’s a table of the binary, hex, and decimal equivalents for each 10-bit chunk, per tube:

    +----------+------------+------+-----------+
    | Display  | Binary-10  | Hex  | Decimal   |
    +----------+------------+------+-----------+
    | 0        | 0000000001 | 001  | 1         |
    | 1        | 0000000010 | 002  | 2         |
    | 2        | 0000000100 | 004  | 4         |
    | 3        | 0000001000 | 008  | 8         |
    | 4        | 0000010000 | 010  | 16        |
    | 5        | 0000100000 | 020  | 32        |
    | 6        | 0001000000 | 040  | 64        |
    | 7        | 0010000000 | 080  | 128       |
    | 8        | 0100000000 | 100  | 256       |
    | 9        | 1000000000 | 200  | 512       |
    +----------+------------+------+-----------+

    The basic order of operations to update the display is the following:
    1. Initialize SPI and set up the shift register output-enable pin as a digital output. On my board, that is digital pin 10 and it’s named “LEpin” in the stock firmware.
    2. Initialize the data structure you’ll use to store the state of the display. This can be a 32-bit value that we change after sending once.
    3. Send the display data out over SPI. AVR are 8-bit microcontrollers so we must do this one byte (8 bits) at a time. Since I’m using a 32-bit int to store 1/2 of the display state at a time, I send it once for the first three digits and colon, change the value, then send it again for the last three digits and colon.
    4. Turn the output enable pin HIGH to illuminate the tubes in whatever state the data you just sent corresponds to. Then turn it off after some interval before you update the display again.

    So in code, if I wanted to show “12:34:56” on my display, I would do the following:

    const byte LEpin=10 // control pin 
    pinMode(LEpin, OUTPUT);
    
    SPI.begin();
    SPI.beginTransaction(SPISettings(2000000, LSBFIRST, SPI_MODE2));
    
    uint32_t display_state= 0; // the 32-bit data struct used to store half of the display state, fill with zeroes to start (= all elements turned off)
    
    digitalWrite(LEpin, LOW); // ensure the display is turned off
    
    // display data that corresponds to "4:56" (M:SS)
    display_state = 0b11000100000000001000000000010000;
    
    // Start transferring the first 32 bits to the shift registers
    SPI.transfer(display_state>>24);
    SPI.transfer(display_state>>16);
    SPI.transfer(display_state>>8);
    SPI.transfer(display_state);
    
    // Now the first shift reg is filled with data for the second shift reg - it will overflow and be pushed into the second shift reg after the next 32 bits are sent
    
    // update the display data for to "12:3" (HH:M) before sending
    display_state = 0b11000000100000000001000000000010;
    
    SPI.transfer(display_state>>24); // transfer the most significant byte first
    SPI.transfer(display_state>>16);
    SPI.transfer(display_state>>8);
    SPI.transfer(display_state); // ending with the least significant byte
    
    digitalWrite(LEpin, HIGH); // Power the nixie tubes and display the state that we just sent
    _delay_ms(2000); // Stay on for 2 seconds
    digitalWrite(LEpin, LOW); // turn tubes off

    Also note that the HV5222 uses LSB-first encoding so you have to send the 8 bit chunks in the opposite order if you have that part on your board.
    There’s a lot of code that already exists in the provided firmware to convert from intended display digits (i.e. 0-9) to the control words that get sent out over SPI. Since data types convert like in the table above, you don’t have to use expanded binary to represent this data, rather you can just shift (using << or >>) and concatenate the hex or decimal values to do the same thing. Hopefully this helps!

    • This reply was modified 1 year, 2 months ago by  Filt_r.
    • This reply was modified 1 year, 2 months ago by  Filt_r.
    #41438

    lestinto
    Participant

    Awesome, thank you very much this was just what I was looking for 🙂

    #41440

    Filt_r
    Participant

    Great! I am glad that helped.

Viewing 4 posts - 1 through 4 (of 4 total)

You must be logged in to reply to this topic.