I’ve written an implementation of the OneWire bus which uses the USART to offload all of the bus timing requirements on the hardware and is fully interrupt driven for read, write and search.
The implementation follows Maxim’s App 214 - Using a UART to Implement a 1-Wire Bus Master. A compile time option for table based or bit-by-bit OneWire CRC provides a tradeoff between speed and code space utilization.
An open drain drive circuit is required to adapt the push-pull TX output to the open drain + 4.7k pullup of the onewire bus. You can either build the external circuit in App 214 which uses n-channel mosfets, or if you have common 2N3904 (or another NPN transistor) handy, the following circuit can be built on the breadboard:
ONEWIRE_TX and ONEWIRE_RX are connected to the core’s TX and RX pins respectively. ONEWIRE_RX_TX is connected to the onewire bus pin of your onewire devices. R1 and R2 provide biasing and current control of the NPN transistors. R3 is the required external pullup of 4.7K. R4 provides input current limiting to the RX pin of the core which allows the bus voltage to exceed the 3.3V IO voltage limit. The +5V can either be connected to VIN (5V from usb) or the 3.3V digital power rail, depending on how you wish to power your onewire devices. D1 and D2 provide optional excessive voltage protection.
Here is how I’ve implemented this circuit using the included breadboard, two 2N3904, and a ds18b20 temperature sensor on a pigtail (sorry for the lousy cellphone quality photo):
If there is interest I can submit a pull request to merge this into the baseline Spark core software or when external modules are more defined, build a module for this work. For the time being, interested parties can pull my github branches, mattande/core-firmware:onewirehw and mattande/core-common-lib:onewirehw. The onewirehw class is fully documented in the onewirehw.h header file. There is also included a (very simplified) ds18b20 interface class, onewire_ds18b20.cpp and sample application.cpp which uses the cloud API.
Future work, which I intend to take on once the core software settles down a bit includes:
- If the RX pin is unconnected or there is a problem with the open drain output circuit, the bus will always report as “busy” after the first doWriteRead(). This would require an API to tie into the SysTick interrupt to perform some receive timeout processing.
- There is no built in support for “parasite power” devices. The onwirehw class could be extended to optionally take an IO pin as the driver for a parasite power circuit which would be energized after doWriteRead() for a specified number of milliseconds. The bus would remain in the “busy” state while the parasite power devices are powered. This requires external hardware and a tie in to the SysTick interrupt.
- During search, a CRC calculation is performed in the interrupt context. While the default table based CRC is quite fast, it would be best to break this out to occur in a user or system task.
- This software uses USART2 and the USART2 interrupt. There is also a Spark Wiring class (Serial1) which uses USART2 and the USART2 interrupt. I’ve put in some code to allow this interrupt to be claimed by one class or the other. If the Spark guys like this approach then we’re cool… otherwise some changes need to be made.
Feedback and comments are welcome.