New to BBB... question on driving GPIO pins

Hi everyone! I am new to embedded linux systems and the beaglebone black. I have had some interest lately in creating a DCC model railroad controller for a while now, and while I have seen examples out there for Arduino and the RPi, not much is out there in the way of handling something like this on the beaglebone. The DCC signal is sent to the locomotives through the track via square-waveform AC in the voltage range of ±8v to ±22 v. A binary 0 is represented by a 100 microsecond +v, and a 100 microsecond -v, and a 1 is 58 microseconds +, -. My plan was to use an H-bridge setup to provide the +15v to the tracks, and use a GPIO pin on the board to control the h-bridge. One example I saw in Python for the RPi used a for loop to write the binary information in a similar fashion to this (c-type code below):

`
string bit_pattern = “111111111100100101”; // example string

for (int i = 0; i < bit_pattern.length(); i++) {

if (bit_pattern[i] == ‘1’) {

writePin (WHICHEVER_GPIO, PIN_HIGH);
usleep (binary_one_delay); // delay for ~58 microseconds
writePin (WHICHEVER_GPIO, PIN_LOW); // turn the pin back off
usleep (binary_one_delay); // and delay again to represent 1
}

else if (bit_pattern[i] == ‘0’) {

// same as above, using different delay pattern
}
}

// the DCC spec says there must be a delay here, I think it was 20 microseconds minimum?
usleep(packet_space_delay);
`

I’m not sure that the usleep function (or nanosleep) can provide the microsecond resolution I need here. The DCC spec also says that the packet should be resent several times to ensure packet delivery, so would it be possible to get this to work using the usleep() or nanosleep() timer and GPIO pins with this kind of accuracy if I am resending the packets, say, 10 times? I am currently reading through Derek Molloy’s book on the beaglebone so if I have to do something like use the PRUs I will do so, but I am really new to all of this and the amount of information out there is just overwhelming! Advice on how to proceed is of course greatly appreciated. Thanks!

I’m not sure that the usleep function (or nanosleep) can provide the microsecond resolution I need here.

It can, but can never be guaranteed. The problem here is kernel latency. Which can be mitigated some by using an RT kernel.

The DCC spec also says that the packet should be resent several times to ensure packet delivery, so would it be possible to get this to work using the usleep() or nanosleep() timer and GPIO pins with this kind of accuracy if I am resending the packets, say, 10 times? I am currently reading through Derek Molloy’s book on the beaglebone so if I have to do something like use the PRUs I will do so, but I am really new to all of this and the amount of information out there is just overwhelming! Advice on how to proceed is of course greatly appreciated. Thanks!

No idea of this DCC spec, but resending packets “just in case” is terribly inefficient. usleep() / nanosleep() are Linux API functions, which could be replaced by using a hardware timer from the Beaglebone. Here, I personally have zero experience, so I’m not sure how to implement such, or how well it would truely work out. I can say after reading some of DR Molloy’s companion web page for his book. One can create a high resolution / highly accurate timer using a PRU.

How I would recommend proceeding . . . is to learn more about the Linux programming interface ( API ), and experiment. Only you truly know what you want, or need. If using a PRU is required for timing, that is fine, but do know that communications between Linux, and the PRU can still present some latency issues. Whether these latency issues present a problem for your project now, is something only you can really answer. After all, there is nothing that is truly real time, only degrees of “real time enough”.

Well really from what I can tell from the spec, the packets continue to be resent to the locomotive that is active at the time for several reasons. One is because you must continue to apply power to the rails or none of the locomotives will move, and since locomotives ignore packets not bound for them, this supplies that need by basically continuing to send the packet to the active locomotive (decoder function updates, speed / direction updates, etc.) The power from the rails coming in (square-wave ac) is then full-wave rectified at the decoder to provide a constant power source to the decoder, locomotive, and accessories. The second reason is because model railroad track can be a woefully inefficient conductor (dirty track, dips in track, poor locomotive wheel contact) so a packet may be missed. The decoder has no specified way to allow the controller to know that it received the packet, so updates are continually sent to the active locomotive. It actually works really well in practice; DCC control is pretty great for modelers. I think once I get the rest of my components I will try to implement it and see if it fits my needs. I knew that the issue would be that the kernel would serve other needs as they arose (I have a pretty decent software design background and I do hobby operating system development as a side project) and that no timing precision was guaranteed. I will look into the possibility of on-board timers that would give me at least a decent level of resolution to fit this need before trying to delve into the many wonders of the onboard PRUs.

Sounds as though the packets are sent electrically through the tracks, which is . . . well pretty ingenius, but not totally unexpected I guess. I had not considered that based on your first post. Because it seemed as though you were implying switching a GPIO on, and off several times, to “send” 15vdc out of a DC/DC converter of some type. Which again, seemed like a really “cheap” way of attempting PWM.

Anyway, I have a pretty decent idea of implementing what you’re talking about, but am busy at the moment, and the post might be fairly lengthy . . . i’ll make a new post once I’m able.

Well I was going to use said conversion technique because each decoder soaks ~500mA and needs at minimum 8 volts to work, prefereably somewhere in the ±15 to ±18v range. So if I wanted to run 4 -5 locomotives at a time, thats 6 amps minimum ability to the track (just to err on the side of caution.)There is no way to support this kind of power drain from the board itself (neither high enough voltage nor amperage), so either way I will have to use a booster of some kind. I chose the method above because there are already working solutions that use it. It sounds too like it works pretty well, but I really am not too familiar with other techniques for doing this kind of thing so I would be very grateful to see an example of a different method. I would assume that you would use PWM through an h-bridge rather than sending a digital high / low? I look forward to hearing your plan. Thank you!

Well, I had assumed you’re an EE of some sort, or at least have a decent electronics background. But from memory, each GPIO pin on the board can only sink 3-5ma current at most. Depending on which pin it is. So this means you’re going to need an external power source no matter what. Which means, maybe a buffer, maybe a transistor, plus an external power source this buffer or transistor “switches”. Anyway, I’m no EE, so excuse the rough explanation here.

So, PWM I think is out of the question. If I understand what is going on: Which seems to be signaling over the tracks ? You tell me. But in this case, there are only really two options.

The first option, would be to use SPI for the signaling. Typically though, to use SPI in this manner is usually for “high speed” stuff. Which it does not seem you really need high speed for this. Also, I’ve never used SPI in this manner personally, and thinking of all the different things needing be done . . . it seems overly complex. But certainly possible.

The second option would be to bitbang GPIO. First, in this case I personally would opt to use one of the PRU’s. Second, I would probably at least initially hack one of the DDR3 to PRU examples to do this. By this, I mean you write a bit pattern to memory, the PRU grabs it, and “spits” it out over a GPIO pin. I think here is where it would start to get really interesting for me - Since there would be a lot to consider, and learn. Because this value you write to memory could also be a bit field containing the value, GPIO timing, and a repeat value( as in how many times to repeat ).

Whats more in the context of the PRU, reading / writing to DDR3 memory can be comparatively slow. Since I believe this would be done over the L3 interconnect. But it very well could e more than enough “speed” for your application. However, if it is not. Then it should be possible to mmap() the PRU’s shared memory. Where I believe the PRU’s have single cycle reads / writes.

Anyway, thats a really high level way of looking at how to solve the problem. Also since I have not read about the spec, and have not experimenting with my idea. There could be more to consider.

Bitbanging GPIO was actually I think the solution in the RPi code. I didn’t look at the methods involved in depth, but that’s what it seems was going on. So that was my plan, to use the PRUs to generate the bitstream to the tracks. So here is what I’m thinking: use the PRU to send the bitstream. Have a command buffer that the PRU reads from and that the software copies new packets into. In the event that there are no commands in the buffer, the PRU continues to send out the same packet it has been sending or some default packet that will maintain power to the track at all times. I would guess that the memory transactions between the PRU and the DDR3 onboard memory would be plenty fast to support the timing accuracy I need.

As far as the other hardware, I’m planning to build that circuit as I learn more. I do know the basics of electrical engineering, but in total honesty I am still very much trying to cut my teeth on the more advanced stuff so I am still trying to determine what to do. The idea of using the h-bridge (a TI LMD18200) came from an arduino-based solution, but in truth I hadn’t analyzed the circuit to see if it would fit this need. So off to the drawing board there.