Dallas One Wire UART Conversion

Marathonman

Well-known member
After reading all I can about one wire it seems the MCU is taxed rather hard and I have read that the UART can off load most of that load allowing the MCU to do other things. I have many examples yet no real code to work with. All the examples are software serial and seems to not take advantage of the buffers associated with RX and TX. I have read a few on other forums but the answer was never found or of no interest to people that actually know coding better then us newbies.

What I would like to do is have this code below take advantage of the RX and TX buffers and I know Teensy 4 and 4.1 do not have open drain RX and TX pins. The two included pics are in fact from MAXIM and I am sure they work but I have no clue as to reflect that in the coding.

Any such help would be highly appreciated and this code could help a lot of people out there that want to use UART One Wire for sensors. I would also like to have these put into a struct to send over LoRa to be viewed in the house. Again I am sure a lot of people would benefit from this.

The two examples at the bottom is from Maxim in which i am implementing the first one which both turns push/pull into open drain.

Code:
// Include the libraries we need
#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 15
#define TEMPERATURE_PRECISION 9

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

// arrays to hold device addresses
DeviceAddress insideThermometer, outsideThermometer;

// Assign address manually. The addresses below will need to be changed
// to valid device addresses on your bus. Device address can be retrieved
// by using either oneWire.search(deviceAddress) or individually via
// sensors.getAddress(deviceAddress, index)
// DeviceAddress insideThermometer = { 0x28, 0x1D, 0x39, 0x31, 0x2, 0x0, 0x0, 0xF0 };
// DeviceAddress outsideThermometer   = { 0x28, 0x3F, 0x1C, 0x31, 0x2, 0x0, 0x0, 0x2 };

void setup(void)
{
  // start serial port
  Serial.begin(9600);
  Serial.println("Dallas Temperature IC Control Library Demo");

  // Start up the library
  sensors.begin();

  // locate devices on the bus
  Serial.print("Locating devices...");
  Serial.print("Found ");
  Serial.print(sensors.getDeviceCount(), DEC);
  Serial.println(" devices.");

  // report parasite power requirements
  Serial.print("Parasite power is: ");
  if (sensors.isParasitePowerMode()) Serial.println("ON");
  else Serial.println("OFF");

  // Search for devices on the bus and assign based on an index. Ideally,
  // you would do this to initially discover addresses on the bus and then
  // use those addresses and manually assign them (see above) once you know
  // the devices on your bus (and assuming they don't change).
  //
  // method 1: by index
  if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0");
  if (!sensors.getAddress(outsideThermometer, 1)) Serial.println("Unable to find address for Device 1");

  // method 2: search()
  // search() looks for the next device. Returns 1 if a new address has been
  // returned. A zero might mean that the bus is shorted, there are no devices,
  // or you have already retrieved all of them. It might be a good idea to
  // check the CRC to make sure you didn't get garbage. The order is
  // deterministic. You will always get the same devices in the same order
  //
  // Must be called before search()
  //oneWire.reset_search();
  // assigns the first address found to insideThermometer
  //if (!oneWire.search(insideThermometer)) Serial.println("Unable to find address for insideThermometer");
  // assigns the seconds address found to outsideThermometer
  //if (!oneWire.search(outsideThermometer)) Serial.println("Unable to find address for outsideThermometer");

  // show the addresses we found on the bus
  Serial.print("Device 0 Address: ");
  printAddress(insideThermometer);
  Serial.println();

  Serial.print("Device 1 Address: ");
  printAddress(outsideThermometer);
  Serial.println();

  // set the resolution to 9 bit per device
  sensors.setResolution(insideThermometer, TEMPERATURE_PRECISION);
  sensors.setResolution(outsideThermometer, TEMPERATURE_PRECISION);

  Serial.print("Device 0 Resolution: ");
  Serial.print(sensors.getResolution(insideThermometer), DEC);
  Serial.println();

  Serial.print("Device 1 Resolution: ");
  Serial.print(sensors.getResolution(outsideThermometer), DEC);
  Serial.println();
}

// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
  for (uint8_t i = 0; i < 8; i++)
  {
    // zero pad the address if necessary
    if (deviceAddress[i] < 16) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
  }
}

// function to print the temperature for a device
void printTemperature(DeviceAddress deviceAddress)
{
  float tempC = sensors.getTempC(deviceAddress);
  if (tempC == DEVICE_DISCONNECTED_C)
  {
    Serial.println("Error: Could not read temperature data");
    return;
  }
  Serial.print("Temp C: ");
  Serial.print(tempC);
  Serial.print(" Temp F: ");
  Serial.print(DallasTemperature::toFahrenheit(tempC));
}

// function to print a device's resolution
void printResolution(DeviceAddress deviceAddress)
{
  Serial.print("Resolution: ");
  Serial.print(sensors.getResolution(deviceAddress));
  Serial.println();
}

// main function to print information about a device
void printData(DeviceAddress deviceAddress)
{
  Serial.print("Device Address: ");
  printAddress(deviceAddress);
  Serial.print(" ");
  printTemperature(deviceAddress);
  Serial.println();
}

/*
   Main function, calls the temperatures in a loop.
*/
void loop(void)
{
  // call sensors.requestTemperatures() to issue a global temperature
  // request to all devices on the bus
  Serial.print("Requesting temperatures...");
  sensors.requestTemperatures();
  Serial.println("DONE");

  // print the device information
  printData(insideThermometer);
  printData(outsideThermometer);
}


Open Drain Buffer One Wire.PNG

Open Drain Biffer.PNG
 
Note: Teensy Uart code does have support for Half duplex mode. It uses the TX pin of the UART.

You can use one of the defines on the begin method, like, what I use to talk to Dynamixel Servos:

DXLSerial.begin(1000000, SERIAL_8N1_HALF_DUPLEX);

There is also a method on HardwareSerial to set the TX pin as OPEN Drain:
void setTX(uint8_t pin, bool opendrain=false);

I don't remember if these two work with each other or not. But you can always look at the code.

You can use the appropriate registers to set most any pin as Open Drain:
PinMode also has the option to set an output Open Drain) OUTPUT_OPENDRAIN

You might take a look at some of these things and see if it meets your needs.
 
@ Paul
However, processor clock frequencies in many 32-bit systems routinely exceed 100MHz. Using a GPIO pin as the 1-Wire bus master consumes a large number of clock cycles per 1-Wire bit. Its edges may not be precisely controlled to meet the necessary timing requirements. Precious battery power is consumed in 32-bit portable systems while generating the 1-Wire read and write time slots. The burden of bit timing and byte framing operations from the main processor can be offloaded if a UART peripheral is available.

the processor can service the UART at its leisure in framing bit time slots into byte values. The communication with 1-Wire devices can be designated a low-priority task. The processor need not waste processing cycles or power, or neglect high-priority, time critical tasks.

Bit banging can be used to perform one-wire communication. This eliminates the need for external hardware and only requires one pin, at the cost of being highly CPU intensive.

This is as per Maxim or am I reading or interpreting this wrong. I have read it elsewhere but I am not sure where. I will find it and post it here.


@ KurtE I didn't know that, I thought Teensy 4 and 4.1 were push/pull. IF they can be configured as open drain then I can save a part as in NC7WZ07 as per the original post graph. IF so can the pins be linked internally or will they still have to be linked externally?

A byte must be simultaneously transmitted and received by the UART to generate 1-Wire timing. Therefore, the peripheral must support full-duplex
operation.
I am a little confused here, you say half duplex and Maxim says full duplex?? Either was using the RX and TX pins takes advantage of the buffer which would be better then a regular pin right or I again missing something here.

EDIT; Microchip AN2658 says it is a half duplex.? I understand now two way communication yet not at the same time.

Thank you both and I see a lot of people voicing concern about using UART as one wire so thanks again for your help.

Regards,
Marathonman
 
Last edited:
I see a lot of people voicing concern about using UART as one wire

Can you also give links to this?

When you mention relevant info exists somewhere else, please be specific with links or clear references to anyone to find that other info. You're basically asking other people to write an alternate version of the OneWire library. You can at least put the extra effort into locating the specific info rather than also asking anyone who might do this work to also put the time info finding the info you're mentioning.
 
I understand fully, I have been scoring the internet for over a week and have seen many forums where people are asking for UART one wire but none has been solved and thread closed. IF you want i can scour the internet again copying the link then and posting them here if that's what you want. I am not here to make you upset by no means, all I would like to see is the UART one wire come to Teensy or Arduino for that matter.

If in fact I find that the one wire library is to intensive on the MCU then I will be force to another alternative as I will have a lot of code to deal with and the last thing I need is more delays.

Maxim posted all kinds of literature on this very subject yet none of it was related to Arduino. I just thought it would off load some burden from the MCU but if this is not the case then I will drop it and go with one wire library which I know you spent many an hours on and I thank you so much. Your contributions to Arduino are endless with many thanking you as well as I.

Regards,
Marathonman
 
Last edited:
I believe you should use OneWire and only revisit this if it causes a real problem.

And if you believe it's causing a problem, I hope you understand the way this forum generally works. We expect you to show a complete program which demonstrates the issue.
 
Funnily enough, I looked at my Microchip 16F690 project of many years ago that uses a one-wire temperature sensor and I used the UART.

Code:
char reset_1wire(){

  /* set up uart for 9600 baud, send 0xf0 */

 /*  207 16 */
   BAUDCTL = 8;   /* 16 bit generator */
   TXSTA = 0x24;  /* enable tx, hi rate */
   SPBRGH = 0;
   SPBRG = 207;
   
   RCSTA= 0x90;
   flush_rx();
   TXREG= 0xf0;

  /* wait for RX done */
   while( (PIR1 & 0x20) == 0 );
   wire_read= RCREG;

   check_overrun();
  
  /* set up for 1 wire normal operation */
   SPBRG= 16;   /* 115k baud */
   crc= 0;

   if( wire_read == 0xf0 ) return 1;  /* bad return */
   return 0;                         /* ok found a device */

}


check_overrun(){

   if( RCSTA & 2 ){  /* overrun error condition */
     RCSTA= 0x80;
     wait_ms(1);
     RCSTA= 0x90;
     flush_rx();
     }

}


flush_rx(){     /* flush uart rx fifo */

   wait_ms(1);
   while( PIR1 & 0x20 ){
      dv= RCREG;
      wait_ms(1);
      }
}


write_1wire( char val ){


   count= 8;
   do{
      while( (TXSTA & 2) == 0);   /* wait trmt */
      TXREG= ( val & 1 ) ? 0xff : 0x00;
      /*val >>= 1;   compiler issue with function argument */
      val = val >> 1;
      while ( (PIR1 & 0x20) == 0 );
      dv= RCREG;                  /* discard rx character */
      check_overrun();
      }while( --count );
   
}


char read_1wire(){

static char val;
static char tmp;

   count= 8;
   val= 0;
   do{
      while( (TXSTA & 2) == 0);   /* wait trmt */
      TXREG = 0xff;
      while ( (PIR1 & 0x20) == 0 );
      val >>= 1;
      tmp= 0;
      if( RCREG == 0xff ){
         val |= 0x80;
         ++tmp;  /* tmp= 1*/
         }
    /*  else val &= 0x7f; */
      check_overrun();
      tmp ^= crc;
      crc >>= 1;
      if( tmp & 1 ) crc ^= 0x8c;
      } while( --count );     

   return val;
}
 
Actually no I wasn't asking for the entire code to be written or rewritten for me, It was obviously taken this way. I was nearly expressing desire for some help to point me in the right direction. As I dive in deeper it seems that I am in way over my head on this taking much more coding prowess the first impression. Again according to Microchip AN2658 using a UART one wire has minimal need for CPU cycles as apposed to bit banging which is known to have high overhead. I can't do this as all the other code i have running will not allow that or any such delays. Just do this attitude is like someone telling you to stick your hand in a fire knowing it will burn you.

Rcarr; Yes I am aware of the Maxim tutorial 214 and all such literature from them on UART One Wire on my computer along with over a week scoring the internet reading many dead threads that were never solved or finished and just closed. To me bit banging is not an option and I thank you for the code. I will check it out and let you know if there are any such issues.
Again thank you Rcarr and KurtE for your contributions.

Regards,
Marathonan
 
Rcarr; Your code has so many issues I can even begin to figure out how to fix it. I just used your name to identify the code.

Code:
sketch_Rcarr__UART_One_Wire:34: error: expected constructor, destructor, or type conversion before ';' token

sketch_Rcarr__UART_One_Wire:46: error: expected constructor, destructor, or type conversion before ';' token

 flush_rx(){     /* flush uart rx fifo */

            ^

sketch_Rcarr__UART_One_Wire:56: error: expected constructor, destructor, or type conversion before ';' token

sketch_Rcarr__UART_One_Wire: In function 'char reset_1wire()':

sketch_Rcarr__UART_One_Wire:9: error: 'BAUDCTL' was not declared in this scope

    BAUDCTL = 8;   /* 16 bit generator */

    ^

sketch_Rcarr__UART_One_Wire:10: error: 'TXSTA' was not declared in this scope

    TXSTA = 0x24;  /* enable tx, hi rate */

    ^

sketch_Rcarr__UART_One_Wire:11: error: 'SPBRGH' was not declared in this scope

    SPBRGH = 0;

    ^

sketch_Rcarr__UART_One_Wire:12: error: 'SPBRG' was not declared in this scope

    SPBRG = 207;

    ^

sketch_Rcarr__UART_One_Wire:14: error: 'RCSTA' was not declared in this scope

    RCSTA= 0x90;

    ^

sketch_Rcarr__UART_One_Wire:15: error: 'flush_rx' was not declared in this scope

    flush_rx();

             ^

sketch_Rcarr__UART_One_Wire:16: error: 'TXREG' was not declared in this scope

    TXREG= 0xf0;

    ^

sketch_Rcarr__UART_One_Wire:19: error: 'PIR1' was not declared in this scope

    while( (PIR1 & 0x20) == 0 );

            ^

sketch_Rcarr__UART_One_Wire:20: error: 'wire_read' was not declared in this scope

    wire_read= RCREG;

    ^

sketch_Rcarr__UART_One_Wire:20: error: 'RCREG' was not declared in this scope

    wire_read= RCREG;

               ^

sketch_Rcarr__UART_One_Wire:22: error: 'check_overrun' was not declared in this scope

    check_overrun();

                  ^

sketch_Rcarr__UART_One_Wire:26: error: 'crc' was not declared in this scope

    crc= 0;

    ^

C:\Users\Donald\Documents\Arduino\sketch_Rcarr__UART_One_Wire\sketch_Rcarr__UART_One_Wire.ino: At global scope:

C:\Users\Donald\Documents\Arduino\sketch_Rcarr__UART_One_Wire\sketch_Rcarr__UART_One_Wire.ino:34:15: warning: ISO C++ forbids declaration of 'check_overrun' with no type [-fpermissive]

 check_overrun(){

               ^

sketch_Rcarr__UART_One_Wire: In function 'int check_overrun()':

sketch_Rcarr__UART_One_Wire:36: error: 'RCSTA' was not declared in this scope

    if( RCSTA & 2 ){  /* overrun error condition */

        ^

sketch_Rcarr__UART_One_Wire:38: error: 'wait_ms' was not declared in this scope

      wait_ms(1);

               ^

Multiple libraries were found for "OneWire.h"

sketch_Rcarr__UART_One_Wire:40: error: 'flush_rx' was not declared in this scope

 Used: C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\OneWire

      flush_rx();

 Not used: C:\Users\Donald\Documents\Arduino\libraries\OneWire-master

               ^

sketch_Rcarr__UART_One_Wire:43: warning: no return statement in function returning non-void 

 }

 ^

C:\Users\Donald\Documents\Arduino\sketch_Rcarr__UART_One_Wire\sketch_Rcarr__UART_One_Wire.ino: At global scope:

C:\Users\Donald\Documents\Arduino\sketch_Rcarr__UART_One_Wire\sketch_Rcarr__UART_One_Wire.ino:46:10: warning: ISO C++ forbids declaration of 'flush_rx' with no type [-fpermissive]

 flush_rx(){     /* flush uart rx fifo */

          ^

sketch_Rcarr__UART_One_Wire: In function 'int flush_rx()':

sketch_Rcarr__UART_One_Wire:48: error: 'wait_ms' was not declared in this scope

    wait_ms(1);

             ^

sketch_Rcarr__UART_One_Wire:49: error: 'PIR1' was not declared in this scope

    while( PIR1 & 0x20 ){

           ^

sketch_Rcarr__UART_One_Wire:50: error: 'dv' was not declared in this scope

       dv= RCREG;

       ^

sketch_Rcarr__UART_One_Wire:50: error: 'RCREG' was not declared in this scope

       dv= RCREG;

           ^

sketch_Rcarr__UART_One_Wire:53: warning: no return statement in function returning non-void 

 }

 ^

C:\Users\Donald\Documents\Arduino\sketch_Rcarr__UART_One_Wire\sketch_Rcarr__UART_One_Wire.ino: At global scope:

C:\Users\Donald\Documents\Arduino\sketch_Rcarr__UART_One_Wire\sketch_Rcarr__UART_One_Wire.ino:56:23: warning: ISO C++ forbids declaration of 'write_1wire' with no type [-fpermissive]

 write_1wire( char val ){

                       ^

sketch_Rcarr__UART_One_Wire: In function 'int write_1wire(char)':

sketch_Rcarr__UART_One_Wire:59: error: 'count' was not declared in this scope

    count= 8;

    ^

sketch_Rcarr__UART_One_Wire:61: error: 'TXSTA' was not declared in this scope

       while( (TXSTA & 2) == 0);   /* wait trmt */

               ^

sketch_Rcarr__UART_One_Wire:62: error: 'TXREG' was not declared in this scope

       TXREG= ( val & 1 ) ? 0xff : 0x00;

       ^

sketch_Rcarr__UART_One_Wire:65: error: 'PIR1' was not declared in this scope

       while ( (PIR1 & 0x20) == 0 );

                ^

sketch_Rcarr__UART_One_Wire:66: error: 'dv' was not declared in this scope

       dv= RCREG;                  /* discard rx character */

       ^

sketch_Rcarr__UART_One_Wire:66: error: 'RCREG' was not declared in this scope

       dv= RCREG;                  /* discard rx character */

           ^

sketch_Rcarr__UART_One_Wire:70: warning: no return statement in function returning non-void 

 }

 ^

sketch_Rcarr__UART_One_Wire: In function 'char read_1wire()':

sketch_Rcarr__UART_One_Wire:78: error: 'count' was not declared in this scope

    count= 8;

    ^

sketch_Rcarr__UART_One_Wire:81: error: 'TXSTA' was not declared in this scope

       while( (TXSTA & 2) == 0);   /* wait trmt */

               ^

sketch_Rcarr__UART_One_Wire:82: error: 'TXREG' was not declared in this scope

       TXREG = 0xff;

       ^

sketch_Rcarr__UART_One_Wire:83: error: 'PIR1' was not declared in this scope

       while ( (PIR1 & 0x20) == 0 );

                ^

sketch_Rcarr__UART_One_Wire:86: error: 'RCREG' was not declared in this scope

       if( RCREG == 0xff ){

           ^

sketch_Rcarr__UART_One_Wire:92: error: 'crc' was not declared in this scope

       tmp ^= crc;

              ^

sketch_Rcarr__UART_One_Wire:97: error: expected '}' at end of input

    return val;

              ^

Using library OneWire at version 2.3.7 in folder: C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\OneWire 

Using library Arduino-Temperature-Control-Library-master at version 3.11.0 in folder: C:\Users\Donald\Documents\Arduino\libraries\Arduino-Temperature-Control-Library-master 

expected constructor, destructor, or type conversion before ';' token
 
Again according to Microchip AN2658 using a UART one wire has minimal need for CPU cycles as apposed to bit banging which is known to have high overhead. I can't do this as all the other code i have running will not allow that or any such delays.
....
To me bit banging is not an option...

Can you be more specific? What other code are you using that is so sensitive that it can not tolerate even the (usually insignificant) overhead of OneWire?


Actually no I wasn't asking for the entire code to be written or rewritten for me, It was obviously taken this way.

That's how I read it, and how I believe most reasonable people would read it.


Just do this attitude is like someone telling you to stick your hand in a fire knowing it will burn you.

I believe you're overthinking this far too much. OneWire has been very widely used in the 12 years since I fixed picked up its maintenance. In practice, the bitbanging time periods are so short that problems are very rare. So again, I'm asking you to be more specific. Can you do that?
 
My code is written for a Pic 16F690, is not a complete program, and has nothing to do with Teensy or Arduino. I offered it as an example of how the algorithms work in using a UART with a One-Wire device.

What you can glean from the code example:
To initialize the one wire device, set baud at 9600, write a value of 0xf0. If 0xf0 is received the one wire device was not found.
For data transfers, a baud rate of 115k is used.
For each bit in the data to be sent, a byte of 0xff is sent for 1's and 0x00 is sent for 0's.
For each bit you wish to receive, you send a 0xff. If 0xff is received, then a 1 bit was received. If something other than 0xff is received, a 0 bit was received.
The code also shows how the CRC is calculated.


I looked at the hardware of my project and I used a 4.7k resistor between TX and RX pins of the UART. Nothing fancy was required. For the algorithm to work, the receiver needs to receive what the transmitter sends.
 
@ RCarr; Yes, I do appreciate the trail on the code and yes I am studying it.

@Paul; I will use the code on your advice then when I am able to test all of it together I will share the results. As of now the only two sites I know that have UART One Wire code are Maxim and MicroE.

@KurtE; I read the manual last night on the 1060 series and it said exactly as you had said that the TX and the Rx lines can be configured as open drain. It also said the ports can be configured to be connected internally in a one wire fashion. It is my understanding that the Tx line can be connected to the RX or vice verse. Also concerning FiFO it also said that the DMA channels can be used in conjunction with the FiFo buffers to catch any of the overflow as part of the buffer. Not that I would need that but still interesting as that would substantially cut back on processor involvement.

Thank you,
Marathonman
 
Last edited:
Back
Top