All,
Below is my current code for handling a DS18B20 via an Arduino Uno. I admit that this is pretty low-level code; I really enjoy taking common chunks of code and distilling them down to their bare essentials and getting the sketch size uber compact so they can run on a much smaller uC. For some reason I can't get millis to return a non-zero value to me right now (been over a year since I coded Arduino so I'm probably screwing something simple up) but I know that a 12-bit temperature conversion takes WAY less than 750ms; it is more like 200ish ms. I plan on adding a bit of code so I can change the resolution of temperature measurements downward if less precision is needed.
Sketch size: 1,054 bytes || SRAM usage: 74 bytes
// Solder-free method of detecting the ROM of a DS18B20 - spread the 3 legs of the sensor wide enough to fit into GND, 13, and 12
// and place the sensor in these pins with the flat side facing the LED on the Uno and the round side facing away from the Uno.
// Then upload and open a serial monitor with a baud rate of 9600. No resistor is needed using this method.
#include <util/delay.h>
// ====================================================== Pre-Compiler Definitions ======================================================
// Direct port manipulation needed to conduct the OneWire bus
#define PowerPin PB4 // Pin 12 - we will be using this pin to supply Vcc to the DS18B20
#define POWER_TEMP_PROBE PORTB |= _BV(PowerPin) // Define method for powering the DB18B20
#define DEPOWER_TEMP_PROBE PORTB &= ~_BV(PowerPin) // Define method for depowering the DB18B20
#define Pin PB5 // Set up pin 13 as the data pin
#define DIRECT_MODE_OUTPUT DDRB |= _BV(Pin) // Much faster and smaller version of pinMode(Pin, OUTPUT)
#define DIRECT_MODE_INPUT DDRB &= ~_BV(Pin) // Much faster and smaller version of pinMode(Pin, INPUT)
#define DIRECT_WRITE_HIGH PORTB |= _BV(Pin) // Much faster and smaller version of digitalWrite(Pin, HIGH)
#define DIRECT_WRITE_LOW PORTB &= ~_BV(Pin) // Much faster and smaller version of digitalWrite(Pin, LOW)
#define DIRECT_READ PINB & _BV(Pin) ? 1 : 0 // One line if else statement using the format [test ? true return : false return]
// Delay values needed for conducting a OneWire bus
#define clk_div 1 // This code assumes a processor frequency of 16MHz but this can be lowered as long as clk_div is updated
#define DELAY_A 6/clk_div // Delay values obtained from http://www.maximintegrated.com/app-notes/index.mvp/id/126
#define DELAY_B 64/clk_div
#define DELAY_C 60/clk_div
#define DELAY_D 10/clk_div
#define DELAY_E 9/clk_div
#define DELAY_F 55/clk_div
#define DELAY_G 0/clk_div
#define DELAY_H 480/clk_div
#define DELAY_I 72/clk_div
#define DELAY_J 410/clk_div
// DS18B20 command codes
#define READROM 0x33 // Read the ROM of a OneWire device; there must only be one OneWire device on the bus!
#define STARTCONVO 0x44 // Tells device to take a temperature reading and put it on the scratchpad
#define READSCRATCH 0xBE // Read EEPROM
#define SKIPROM 0xCC // Tells all OneWire sensors on the bus that the next command applies to them
#define MATCHROM 0x55 // Tells all OneWire sensors on the bus to listen for a specific ROM next
#define myubbr (F_CPU/clk_div/16/9600-1) // Baud rate for UART
int main() {
uint8_t ROM[8] = { 0x28, 0xA, 0xA3, 0x83, 0x4, 0x0, 0x0, 0x63 };
// Initialize the UART
UBRR0H = (unsigned char)(myubbr>>8);
UBRR0L = (unsigned char)myubbr;
UCSR0A = 0;//Disable U2X mode
UCSR0B = (1<<TXEN0);//Enable transmitter
UCSR0C = (3<<UCSZ00);//N81
_delay_ms(100);
DDRB = B00010000; // Set Pin 12 as an output
for(;;) { // Loop forever
// Perform a OneWire reset pulse
POWER_TEMP_PROBE; // This isn't neccesary but I show it to demonstrate how to make the project conserve energy
simpletx("Presence pulse: ");
if(reset()) {
simpletx("Detected\t");
} else {
simpletx("Not Detected\t");
}
// Attempt to read the ROM, if nothing is present this will return 0x00 for each of the eight bytes
simpletx("ROM is: ");
write(READROM);
for(uint8_t i=0;i<8;i++) {
ROM[i]=read();
simpletx("0x");
txByteAsHex(ROM[i]);
if (i!=7) simpletx(",");
}
simpletx("\t\t");
// If we detected a Dallas family sensor, let's go ahead and take a temperature reading
if (ROM[0]=0x28) { // The first byte of all dallas sensors is always 0x28
reset();
write(SKIPROM);
write(STARTCONVO);
while(!read()); //_delay_ms(750); // Can either wait 750 ms for the conversion to be done or else read until we get a 1 back from the DS18B20 meaning it is signaling complete
reset();
write(MATCHROM);
for(uint8_t i = 0; i < 8; i++) write(ROM[i]);
write(READSCRATCH);
uint8_t tempLSB = read();
simpletx("Temperature: ");
txRawTempAsFloat( read()<<8 | tempLSB );
simpletx("F\n");
}
DEPOWER_TEMP_PROBE; // Rather than perform a reset to tell the probe to stop sending data, just cut the power and it will get the message!
_delay_ms(5000);
} // End for
} // End main
static inline uint8_t read() {
uint8_t r=0;
noInterrupts();
for (uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) {
DIRECT_MODE_OUTPUT;
DIRECT_WRITE_LOW;
_delay_us(DELAY_A);
DIRECT_MODE_INPUT;
DIRECT_WRITE_HIGH; // New line for no resistor modification / enable pull-up resistor
_delay_us(DELAY_E);
if (DIRECT_READ) r |= bitMask;
_delay_us(DELAY_F);
}
interrupts();
return r;
}
static inline void write(uint8_t v) {
noInterrupts();
for (uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) {
DIRECT_WRITE_LOW;
DIRECT_MODE_OUTPUT;
if (bitMask & v) {
_delay_us(DELAY_A);
DIRECT_WRITE_HIGH;
_delay_us(DELAY_B);
} else {
_delay_us(DELAY_C);
DIRECT_WRITE_HIGH;
_delay_us(DELAY_D);
}
}
DIRECT_MODE_INPUT;
interrupts();
}
static inline uint8_t reset(void) {
noInterrupts();
DIRECT_MODE_INPUT;
DIRECT_WRITE_LOW;
DIRECT_MODE_OUTPUT;
_delay_us(DELAY_H);
DIRECT_MODE_INPUT;
DIRECT_WRITE_HIGH; // New line for no resistor modification / enable pull-up resistor
_delay_us(DELAY_I);
uint8_t ret = !(DIRECT_READ);
interrupts();
_delay_us(DELAY_J);
return ret;
}
static inline void simpletx( char * string ) {
/*if (UCSR0B != (1<<TXEN0)) { //do we need to init the uart?
UBRR0H = (unsigned char)(myubbr>>8);
UBRR0L = (unsigned char)myubbr;
UCSR0A = 0;//Disable U2X mode
UCSR0B = (1<<TXEN0);//Enable transmitter
UCSR0C = (3<<UCSZ00);//N81
_delay_ms(30);
}*/
while (*string) {
while ( !( UCSR0A & (1<<UDRE0)) );
UDR0 = *string++; //send the data
}
}
static inline void txByteAsHex(uint8_t inp) {
char snd[3];
uint8_t tmp = inp>>4;
if (tmp<10) {
snd[0]=48+tmp;
} else {
snd[0]=55+tmp;
}
tmp=inp%16;
if (tmp<10) {
snd[1]=48+tmp;
} else {
snd[1]=55+tmp;
}
snd[2]='\0';
simpletx(snd);
}
// Converts the raw temperature from a DS18B20 directly to a string containing the temperature in °F with 1 decimal place
// avoids unnecessary floating point math, float variables, and casts, and 32-bit math
// TODO: May not work properly with temperatures below 32°F
static inline void txRawTempAsFloat(uint16_t raw) {
char buffer[6];
uint8_t decimalPos = 2; // default case of a temp between 0 and 99.9
uint8_t nullPos = 4; // default case of a temp between 0 and 99.9
uint16_t temp;
// Check to see if the temperature passed in is negative
if (raw>>11) {
// Can't get here unless one of the 5 most-significant bits are ones which means we have a negative number, convert it
raw = ~(raw-1); // Convert the two's compliment number back into one's compliment
if (raw > 284) { // This temperature is far enough negative in the celcius scale that it is also negative on the farhenheit scale
decimalPos += 1; // Account for the negative sign's place in the string
nullPos += 1; // Account for the negative sign's place in the string
buffer[0] = '-'; // Write the negative sign in the string
}
temp = (9*raw)/8-320; // Keeps only 1 decimal place but uses 16-bit math
} else {
temp = (9*raw)/8+320; // Keeps only 1 decimal place but uses 16-bit math
}
// Convert the raw temperature into the temperature in Fx10 so that one decimal place is kept
//uint32_t temp = (raw*1125ul+320000ul)/1000ul; // Keeps all 4 decimal places but uses 32-bit math
if(temp>=1000) { // We're looking at a positive number with three digits
decimalPos += 1;
nullPos += 1;
}
buffer[nullPos--] = '\0';
do {
if (nullPos==decimalPos) buffer[nullPos--] = '.';
buffer[nullPos--] = temp % 10 + '0';
temp /= 10;
} while (temp);
simpletx(buffer);
}
EDIT: Just recalled that to get millis() and micros() to work using my program structure I have to call the init() routine which is usually called by setup using the default arduino structure of setup() and loop().