The PIO program is fairly general in that you could use it to talk to almost any 1-Wire device, but we are interested in the DS18B20 and it is easy enough to implement a function to read the temperature along the lines of the previous function:
float getTemperature(PIO pio, uint sm) {
First we send an initialization pulse followed by a Skip ROM and a Convert command. The first item sets a write operation with an initialization pulse:
Next we should wait until the conversion is complete, but for simplicity we wait for 1s – after which time the conversion is either complete or the device is broken. Then we send another initialization pulse, a Skip ROM and a Read ScratchPad command:
Using these two functions the interaction with the 1-Wire device starts to look a lot like using the SPI or I2C bus.
This is about as complex a PIO program as you can implement. This raises the question of what to do if you need something more complex? You might think that using additional state machines might help, but you are still limited to 32 instructions in total. There are two possible practical solutions. You can use immediately executed instructions put directly into the state machine from the C program. This works for small tasks such as initialization. The second solution is more generally useful. If you can split your program into small, less than 32 instructions, modules then you can load one into one PIO and another into the second PIO. This, of course, occupies both PIOs and you can’t use a PIO for another task. For example, you could use one PIO to send data to a 1-Wire bus device and the other to receive data. This approach is limited to just two modules, so a total of 64 instructions. You can extend this idea to any number of modules if they can be reloaded into the same PIO. For example, by splitting the 1‑Wire code into a receive and a send module you could easily afford the time to load each module and set it up into the same PIO.
The current program can only work with a single device on the 1-Wire bus. If you want to support more, you could implement the search algorithm outlined in Chapter 15 of Raspberry Pi IOT in C, Second Edition, though this is complex. A simpler solution is to use one state machine and one GPIO line per device. This can support up to eight different 1-Wire devices.
The Complete Program
The C program using the read and write functions is:
The 1-Wire bus is a proprietary, but widely-used, bus. It is simple and very capable.
As its name suggests it makes use of a single data wire and usually a power supply and ground.
It is possible to dispense with the power line and the connected device will draw power from the data line.
Implementing the 1-Wire protocol is mostly a matter of getting the timing right.
There are three types of interaction: presence pulse, read and write.
The presence pulse simply asks any connected devices to reply and make themselves known.
The 1-Wire protocol is easier to implement than you might think because each bit is sent as a “slot” and while timing is critical within the slot, how fast slots are sent isn’t and the master is in control of when this happens.
The DS18B20 temperature sensor is one of the most commonly encountered 1‑Wire bus devices. It is small, low-cost and you can use multiple devices on a single bus.
After a convert command is sent to the device, it can take 750ms before a reading is ready.
To test for data ready you have to poll on a single bit. Reading a zero means data not ready and reading a one means data ready.
When the data is ready you can read the scratchpad memory where the data is stored.
The DS18B20 has other commands that can be used to set temperature alarms etc, but these are rarely used.
With a shift in viewpoint it is just possible to squeeze a 1-Wire bus protocol into a 32 instruction PIO program.
JetBrains has launched a non-commercial license for its JavaScript and TypeScript IDE, WebStorm, and for Rider, its cross-platform .NET and game development IDE.