DCC++Arduino: Bare-bones DCC++ generator

Travis Farmer Mar 8, 2018

  1. Travis Farmer

    Travis Farmer TrainBoard Member

    249
    249
    7
    i finally ordered some(10) ATtiny85 from Digikey. this should really speed up development when they arrive. in the mean-time, i have to build myself a programmer. I am pretty sure i have a ZIF socket for DIP chips kicking around, and some perfboard. fairly easy to make, i just have to find my workbench again. :ROFLMAO:
    ~Travis
     
    Atani likes this.
  2. Travis Farmer

    Travis Farmer TrainBoard Member

    249
    249
    7
    I received my shipment of ATtiny85 (10) yesterday. i haven't found my workbench yet (quite a plethora of unfinished projects), but i hope to at some point this weekend. :oops:
    I have drawn up a rough schematic of proposed circuit connections, if i am able to attach a PDF. I haven't done much on the code yet due to work leaving me exhausted, but i hope to get some more work done on it this weekend, also.
    On the schematic, i have included an on-board transistor to provide the inverted complement to the DCC++ signal needed by some motor drivers.
    for those with motor driver shields, it may be worthwhile to build the circuit onto a UNO prototyping shield, and make the connections to whatever pins your motor driver shield requires.

    ~Travis
     

    Attached Files:

  3. WillemT

    WillemT TrainBoard Member

    15
    10
    2
    Glad you are getting somewhere. I had my order in for almost a week now, delivery is usually next day.

    I would use a 10K for R3 and probably try that for R2 as well, I like low currents when you can get away with it.

    I assume you will program the chip elsewhere? You could add a few pins for an ISP programmer to plug onto it, makes it a lot easier when you find you need to alter any fuse setting or upload new versions of the sketch.

    Looking forward to your findings. As soon as I get something, hopefully early next week, I will join in.

    Willem.
     
    Travis Farmer likes this.
  4. Travis Farmer

    Travis Farmer TrainBoard Member

    249
    249
    7
    I updated the original attachment. thanks for the tip.

    i opted to remove the ATtiny85, and use an IC socket. that way, i can use two chips. one for a stable prototype, and one for development, using a separate programming board (with a ZIF socket).

    fortunately, i have a digital oscilloscope, as well as a logic analyzer, so i can directly monitor the output when checking for proper signal. that way i won't have to worry about "killing" my only DCC loco. ;)
    ~Travis
     
  5. Travis Farmer

    Travis Farmer TrainBoard Member

    249
    249
    7
    https://github.com/travisfarmer/tinyDCCpp
    ok, i will try and explain what i have done. the above link is for the Main track standalone ATtiny85 DCC++ generator. i haven't hardware tested it yet, as i have not yet had time to excavate room on my work bench.
    protocol wise, the ATtiny85 DCC++ generator received data as follows.
    Code:
    0xff 0x00 0x01 .......DATA
    1     2       3              4
    assuming it formats on your screen as i have typed it. ;)

    byte 1 and 2 command the start of the packet (the combination of a 0xff followed by a 0x00 should not occur at any other time).
    byte 3 is a single byte (0x00 to 0xff) that describes the length of the data (4). so if data is 4 bytes long, byte 3 would be 0x03.

    Structure of DATA bytes (follows above bytes)
    Set Throttle
    Code:
    0x01 0x00 0x00 0x00 0x00 0x00
    1       2        3       4        5        6
    byte 1 is equal to 0x01. this is the command identifier.
    byte 2 is the register byte.
    byte 3 and 4 is the loco ID, or Cab (high byte first, then lowbyte. if only lowbyte, highbyte is 0x00)
    byte 4 is the speed.
    byte 5 is the direction.

    Engine Decoder Functions
    (6 bytes used)
    byte 1 is equal to 0x02.
    byte 2 is the register byte.
    byte 3 and 4 is the loco ID.
    byte 5 is BYTE1.
    byte 6 is BYTE2.

    Accessory Decoder Functions
    (5 bytes used)
    byte 1 is equal to 0x03.
    byte 2 and 3 is the accessory address.
    byte 4 is the accessory number.
    byte 4 is enable (0x00 or 0x01).

    Track Power
    (2 bytes used)
    Byte 1 is equal to 0xfd.
    byte 2 is equal to 0x01 (on), or 0x00 (off).

    Read Track Current
    (1 byte used)
    Byte 1 is equal to 0xfe.

    hopefully i will find some time to hardware test it. it is embarrassing to provide untested code. :oops:

    ~Travis
     
    WillemT likes this.
  6. WillemT

    WillemT TrainBoard Member

    15
    10
    2
    I received my ATtiny85s, 2 x 8pin DIP and 4 x SMD.

    I installed one on a bread board along with an UNO as SPI programmer (connected LEDs to pins 7,8 and 9 - I like some blinking light feedback). Also added a LED to the Tiny's pin0 (physical pin 5). I then proceeded as follows:

    1. Using the factory fuse setting, which should be 1MHz, I uploaded a Blink type sketch. I set on/off to 500/500ms giving 1Hz at 50% duty. All worked as expected.

    2. Next I added code to set Timer1 to about 1Hz. 1MHz divided by 4096 (pre-scale values are binary) which gives 244.1406... Hz to the Timer. With a 244 Top and 122 Compare we get almost 1Hz at 50% duty. Connected a LED to pin1 (physical pin 6) and it worked by blinking at about 1Hz. (Slowly drifting compared to the pin0 LED).

    3. Decided to up the clock frequency. The max pre-scaler for Timer1 is 16K - that is 4x the 4096 value. So, seeing a 4MHz option I chose it and burned the fuses. This resulted in one dead ATtiny. Further investigation showed the 4MHz option is for an external crystal. Fortunately I do have 16MHz crystals. (The ATtine85s I have are 20DU and 20SU which allows up to 20MHz external clocks). With crystal and caps connected (the advantage of using a breadboard) I burned the fuses to 1MHz again. All back on line.

    4. Next I upped the internal clock to 8MHz - that is an internal option (I checked it this time). So, with the pre-scaler set to 16K and the clock at 8MHz the Timer1 LED pulsed at close to 2Hz. All seems to work.

    5. Added an interrupt routine for MatchA which clocks a further LED (pin3 - physical 2). I set the routine to clock the LED between on/off every second interrupt. The LED pulsed at 1Hz. All working.

    6. I then burned fuses for 16MHz, using the internal 8MHz clock and PLL. Changed the Timer1 pre-scaler to 16 and set Top to 200 and MatchA to 100. This is for a DCC++ zero as per post #24 above. That in theory should give us a 5KHz signal at 50% duty. I counted 2500 interrupts between clocking the LED between on and off. The result was a LED pulsing at 1Hz. All working as it should.

    I messed a bit using MatchB which outputs on pin4 (physical 3) to free pin1 to try and use serial comms. The extension I have for the ATTiny claims it uses pins 0 and 1. Enabling serial comms immediately interfered with the frequency on Timer1. It appears that most soft serial drivers use that timer. That makes it a non-option. What I did, however, find is that my ATtinys seem to still have the bug when using MatchB (at least the DIP ones do). I could not find anything on the web as to whether that was ever fixed. Fortunately there is an easy workaround and it worked fine.

    I did not get to trying I2C communications since I do not have any I2C chips other than a 4 digit 7-segment display. I did not feel like implementing that for a test at this stage. I will pick up some 23017 and 23008 chips tomorrow morning and then try that. I do have some ISP chips which I will also try.

    Sorry for the long post. Thought it might save you some trouble.

    Willem.
     
    Last edited: Apr 12, 2018
    Travis Farmer likes this.
  7. Travis Farmer

    Travis Farmer TrainBoard Member

    249
    249
    7
    i planned on the DCC++ ATtiny85 being I2C slaves actualy. that way they can be controlled by most any choice of MCU, or even a Rpi. this leaves more headroom for the master MCU to handle I/O, and other tasks, without the risk of blocking code disrupting the DCC++ signal.

    good to know the tests worked. at least now i know that i am not writing code for a doomed project. ;)

    ~Travis
     
  8. WillemT

    WillemT TrainBoard Member

    15
    10
    2
    Yes. I realise that. However, it is much easier to initially check I2C working as a master. To test as slave I need to get a master going. I do not want to switch the UNO between programmer and master, too much work:D (only have one available). The biggest problem for me is checking if things are working - I do not have a scope ... yet. I need to switch LEDs to check things working.

    In any case, I got and connected a MCP23008. Set it up for 4 Input and 4 Output. Connected LEDs to GP4 and 6. Added a small toggle routine which switch pin status on the 23008 and called it from the blink loop as well as the Interrupt routine. All works.

    Hint: If you have problems getting the I2C working make sure you have pull-up resistors on the SCL SDA lines:barefoot:.

    Next I will test it as a slave. I will add a second ATtiny85 and run that as a slave driving it from with this one.

    How are you doing?

    Willem.
     
    Travis Farmer likes this.
  9. Travis Farmer

    Travis Farmer TrainBoard Member

    249
    249
    7
    amidst a variety of weekend "ToDo list" items, i regularly think longingly of this project. as i suspect i will be getting rain later this afternoon, i hope to sneak away to my "Man Cave", and solder up a prototype. I also plan to integrate in-circuit programming to save a little effort., though as the pins for programming, are also for other tasks, i will have to think of that when i work with it.

    what i plan on doing for testing, is to program an UNO to send some basic "messages" via I2C, in a debugging sequence, and i will monitor the output. i may even unpack my circular test track, and try the prototype live to visualize the results. as the debug controller will be programmed very simply, i should also consider supplying the code if it works, so others can debug the generator. key phrase, "if it works". ;)

    progress has been slow due to after-work exhaustion, but i hope to really get something done this weekend. providing the "ToDo list" doesn't spontaneously manifest new items. ;)

    i am aware. ;) i plan on using 4.7K for each.

    ~Travis
     
  10. WillemT

    WillemT TrainBoard Member

    15
    10
    2
    That is what I used (eventually).

    I also connected the UNO's SPI to the ATtiny's SPI as well as the I2C to the 23008 simultaneously. Both worked just fine. So I can upload a sketch and it responds immediately - no need to connect/disconnect.

    When I had the LEDs connected they also responded without disconnection the SPI from the programmer. Once the Wire functions are enabled the pins are reconfigured and are no longer available as digital I/O. Uploading a new sketch however resets the ATtiny so that SPI takes over again. Quite neat actually.

    Willem.
     
  11. Travis Farmer

    Travis Farmer TrainBoard Member

    249
    249
    7
    ok, not sure what i am doing wrong... allow me to state what i have done.
    after double checking the schematic and my "spaghetti wire, with bubble-gum solder" prototype, i connected my logic analyzer to the DCC++ pins (logic level, not the HV AC output), and proceeded to program the ATtiny85. i could see the programming data fly by on the DCC++ output pins, and the upload stated successful (via my USBasp programmer). after removing the programmer, and applying power, i watched the analyzer output, and the only thing that happened was the inverter output of the DCC++ signal was opposite value of the non-inverted output, as it should, but no signal.

    i suspect two possible causes. i forgot the pull-up on the reset, and possibly the 16Mhz internal clock was not activated.
    I seem to remember something about an internal reset pull-up, but that may be just the Arduino boards... i will check the datasheet.

    so, clearly an ATtiny85 isn't as easy to work with as an Arduino, for me anyway. :oops:
    perhaps a cup of coffee is in order.

    ~Travis
     
  12. Travis Farmer

    Travis Farmer TrainBoard Member

    249
    249
    7
    After re-reading the above, it occurred to me i never made those changes i don't think. so i changed:
    Code:
    #define DCC_ZERO_BIT_TOTAL_DURATION_TIMER1 3199
    #define DCC_ZERO_BIT_PULSE_DURATION_TIMER1 1599
    
    #define DCC_ONE_BIT_TOTAL_DURATION_TIMER1 1855
    #define DCC_ONE_BIT_PULSE_DURATION_TIMER1 927
    to now be:
    Code:
    #define DCC_ZERO_BIT_TOTAL_DURATION_TIMER1 200
    #define DCC_ZERO_BIT_PULSE_DURATION_TIMER1 100
    
    #define DCC_ONE_BIT_TOTAL_DURATION_TIMER1 116
    #define DCC_ONE_BIT_PULSE_DURATION_TIMER1 58
    at least that is how i interpreted it. in any case, i get square-wave output now on the DCC++ and DCC++ inverted pins, as seen from my logic analyzer output. (attached)

    I haven't sent any I2C data to it yet, but at least i have made progress since yesterday. it seems to be outputting a DCC idle packet anyway. i still have to code up the debugging controller (UNO/MEGA2560), then i will be able to function test the generator.
    I will update my GitHub copy of the code, as soon as i finish this post.
    EDIT: Updated -> https://github.com/travisfarmer/tinyDCCpp

    ~Travis
     

    Attached Files:

    Last edited: Apr 15, 2018
  13. WillemT

    WillemT TrainBoard Member

    15
    10
    2
    I can see what happened.

    Loading values greater than 255 into the 8-bit registers get their high bytes truncated. So, loading a zero using the values 3199:1599 end up as 127:63 when the high byte is truncated. This sets a period of 127us flipped at 63us (50%) and should have just given you a higher frequency. However loading a one with values 1855:927 ends up as 63:159. This is a problem since the Match is now later than the Top and, if I interpret the docs correctly, that will simple lock the output high. Having set the correct values will get you going.

    On my side I experimented with I2C comms. Normal Master seems to work fine to the 23008. I next added the second ATtiny and set it up as a slave. After a lot of finger trouble I got the Master to write to the Slave and the Slave accepting it. The read on the Slave, however, was a problem - I just did not seem to get the correct values. Not being able to see what you get and just using flashing LEDs for checking makes debugging hard.

    To try and see what is happening I decided to connect a 16x2 LCD panel to the Slave. That turned out to be a no-go, the LCD library does not work when Wire is initialised as a Slave. I then moved the LCD to the Master and checked what I am sending, that showed correct. The only way I could think of checking what is received by the Slave was to try and return the value to the Master. I added code to request a value from the Slave by the Master and then tried to get the Slave to return it. After quite a lot of frustration trying to get that going I made some changes as to how variables are handled by the Slave, and BANG - everything works.

    So I can send from the Master, receive by the Slave and return values from the Slave to the Master - two way comms all working. I can now proceed to the next stage.

    Let me explain my hang-up with the comms. I kind of accepted basic DC++ train control can be managed on the ATtiny after your initial work. That is pretty useless if we cannot control the Base Station. Also using more powerful, and expensive, MCUs to try and control the ATtiny is kind of counter productive - the thread title says is all. My idea, and hence joining in, is to get an inexpensive minimal DCC++ setup going with, say, maybe only two ATtinys.

    At this stage it seems we can use a Master ATtiny to read switches and a potentiometer on the ADC input, not enough pins to use an encoder, to send control instructions to the Slave ATtiny acting as a Base Station. We can read back from the Slave and probably display status and also maybe read CV's etc. This could be a small, relatively inexpensive selfcontained DCC++ controller. That is what I will actually build for simple connection and testing. When going further to Wi-Fi and NMRA control I feel we rather go to larger MCU's or move to the newer designs like the great work done using the ESP32 by Atani and others.

    I will now start looking at controlling the DCC++ part.

    Willem.
     

Share This Page