Created
April 15, 2011 06:32
-
-
Save PhirePhly/921255 to your computer and use it in GitHub Desktop.
Nixie Tube based thermometer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Kenneth Finnegan, 2011 | |
// kennethfinnegan.blogspot.com | |
// | |
// Nixie Thermometer | |
// Reads DS1631 I2C thermometer and displays on | |
// three 74141 decoded Nixie tubes connected | |
// directly to the ATTiny2313 I/O ports | |
// | |
// Pinout on ATTiny2313 | |
// Digit 1: PB[0-3] | |
// Digit 2: PD[2-5] | |
// Decimal point: PD1 | |
// Digit 3: PA[0-1],PD6,PB4 | |
// Mode select jumper: PD0 | |
// I2C to DS1361: PB[5,7] | |
#include <inttypes.h> | |
#include <avr/io.h> | |
#include <avr/interrupt.h> | |
#include <util/delay.h> | |
#ifndef __ATtiny2313__ | |
#define __ATtiny2313__ | |
#endif | |
#include "USI_I2C.c" | |
// CONSTANTS | |
#define DS1631 0x48 | |
// GLOBALS | |
uint8_t temp[2]; | |
uint8_t convertC2F; | |
// Sends op code to DS1631 to start continuous temp conversions | |
void startConvert(void) { | |
uint8_t buf[2]; | |
buf[0] = (DS1631 << 1) | 0; | |
buf[1] = 0x51; | |
I2C_xfer(buf, 2); | |
} | |
// Download most recent 12 bit temperature conversion from DS1631 | |
// First 8 bits whole degrees, second byte 4 bits of fractional degrees | |
// Frame bits: 8.4 | |
void downloadTemp(void) { | |
uint8_t buf[3]; | |
buf[0] = (DS1631 << 1) | 0; | |
buf[1] = 0xAA; | |
I2C_xfer(buf, 2); | |
buf[0] |= 1; | |
I2C_xfer(buf, 3); | |
temp[0] = buf[1]; | |
temp[1] = buf[2]; | |
} | |
// Convert 8.4 in celcius to appropriate BCD for 74141 decoders | |
void updateDisplay(void) { | |
uint8_t digits[3]; | |
if (convertC2F == 0) { // Display C temps | |
digits[0] = temp[0] / 10; | |
digits[1] = temp[0] % 10; | |
digits[2] = temp[1] / 25; | |
PORTD |= 0x02; // Set decimal point | |
} else { | |
// Handle as single 32 bit int for temperature conversion | |
// 24.8 Due to overflow and carry issues with 8/16 bit words | |
uint32_t i = (temp[0] << 8) | temp[1]; | |
// Convert from C to F | |
i = (9 * i) / 5 + (32 << 8); | |
if ((i >> 8) > 100) { | |
digits[0] = (i >> 8) / 100; | |
digits[1] = ((i >> 8) / 10) % 10; | |
digits[2] = (i >> 8) % 10; | |
PORTD &= ~(0x02); // Clear decimal point | |
} else { | |
digits[0] = (i >> 8) / 10; | |
digits[1] = (i >> 8) % 10; | |
digits[2] = (i & 0xFF) / 25; | |
// Check for overflow for byte to decimal conversion (250-255) | |
digits[2] = digits[2] > 9 ? 9 : digits[2]; | |
PORTD |= 0x02; // Set decimal point | |
} | |
} | |
// Update output PORTs | |
// Digit 0: PB[0-3] | |
// Digit 1: PD[2-5] | |
// Decimal point: PD1 | |
// Digit 2: PA[0-1],PD6,PB4 | |
PORTA = (PORTA & 0xFC) | (digits[2] & 0x03); | |
PORTB = (PORTB & 0xE0) | digits[0] | ((digits[2] & 0x08) << 1); | |
PORTD = (PORTD & 0x83) | (digits[1] << 2) | ((digits[2] & 0x4) << 4); | |
} | |
int main(void) | |
{ | |
DDRA = 0x03; | |
PORTA = 0x07; | |
DDRB = 0xFF; | |
PORTB = 0x00; | |
DDRD = 0xFE; | |
PORTD = 0x01; | |
I2C_init(); | |
sei(); | |
startConvert(); | |
uint8_t i=0; | |
while (1) { | |
// Check the C/F config jumper | |
convertC2F = PIND & 0x01; | |
// Update displayed temperature | |
downloadTemp(); | |
updateDisplay(); | |
// Slow it all down to make the display seem less nervous | |
_delay_ms(1000); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// I2C library using USI hardware support | |
// Kenneth Finnegan, 2010 | |
// kennethfinnegan.blogspot.com | |
// Heavily based on Atmel application note AVR310 | |
#include <util/delay.h> | |
#include <avr/io.h> | |
#include <inttypes.h> | |
//#define PARAM_VERIFY | |
//#define SIGNAL_VERIFY | |
//#define NOISE_TESTING | |
#ifdef I2C_FAST_MODE | |
#define I2C_T1 2 // >1.3us | |
#define I2C_T2 1 // >0.6us | |
#else | |
#define I2C_T1 5 // >4.7us | |
#define I2C_T2 5 // >4.0us | |
#endif | |
// Bit & byte definitions | |
#define I2C_READ_BIT 0 // R/W bit position in device address byte | |
#define I2C_ADDR_BITS 1 // LSB position of device address | |
#define I2C_NACK_BIT 0 // Bit position of (N)ACK bit | |
#define TRUE 1 | |
#define FALSE 0 | |
#define I2C_READ 1 | |
#define I2C_WRITE 0 | |
// ERRORS | |
#define I2C_ERR_NO_DATA 0x01 | |
#define I2C_ERR_DATA_OUT_OF_BND 0x02 | |
#define I2C_ERR_UE_START 0x03 | |
#define I2C_ERR_UE_STOP 0x04 | |
#define I2C_ERR_UE_DATA_COL 0x05 | |
#define I2C_ERR_NO_ACK_ON_DATA 0x06 | |
#define I2C_ERR_NO_ACK_ON_ADDR 0x07 | |
#define I2C_ERR_MISSING_START_CON 0x08 | |
#define I2C_ERR_MISSING_STOP_CON 0x09 | |
// Device port definitions | |
#if defined(__ATtiny2313__) | |
#define I2C_DDR DDRB | |
#define I2C_PORT PORTB | |
#define I2C_PIN PINB | |
#define I2C_SDA PB5 | |
#define I2C_SCL PB7 | |
#endif | |
// Globals | |
union I2C_state { | |
uint8_t errorState; | |
struct { | |
uint8_t addressByte :1; | |
uint8_t dataDirection :1; | |
uint8_t unused :6; | |
}; | |
} I2C_state; | |
// Function Definitions | |
void I2C_init(void); | |
uint8_t I2C_xfer(uint8_t *buffer, uint8_t length); | |
// Private function definitions | |
uint8_t I2C_byte_xfer(uint8_t reg); | |
uint8_t I2C_gen_start(void); | |
uint8_t I2C_gen_stop(void); | |
void I2C_delay(uint8_t length) { | |
do { | |
_delay_us(1); | |
} while (--length); | |
} | |
void I2C_init(void) { | |
// Set IO pins as output with pullup resistors | |
I2C_PORT |= (1<<I2C_SDA) | (1<<I2C_SCL); | |
I2C_DDR |= (1<<I2C_SDA) | (1<<I2C_SCL); | |
// Preload release of SDA | |
USIDR = 0xFF; | |
// Set USI in two wire mode, clock source USITC software strobe | |
USICR = (1<<USIWM1) | (1<<USICS1) | (1<<USICLK); | |
// Clear flags and counter | |
USISR = (1<<USISIF) | (1<<USIOIF) | (1<<USIPF) | (1<<USIDC) | | |
(0x0<<USICNT0); | |
} | |
// Main I2C transfer function. First byte of the buffer should be: | |
// (I2C_DEV_ADDR << I2C_ADDR_BITS) | READ/WRITE | |
uint8_t I2C_xfer(uint8_t *buffer, uint8_t buflen) { | |
// USISR for 8 bit xfer | |
uint8_t SR_8bit = (1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)| | |
(0x0<<USICNT0); | |
// USISR for 1 bit xfer | |
uint8_t SR_1bit = (1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)| | |
(0xE<<USICNT0); | |
I2C_state.errorState = 0; | |
I2C_state.addressByte = TRUE; | |
#ifdef PARAM_VERIFY | |
if (buffer > (uint8_t*)RAMEND) { | |
I2C_state.errorState = I2C_ERR_DATA_OUT_OF_BND; | |
return FALSE; | |
} | |
if (buflen <= 1) { | |
I2C_state.errorState = I2C_ERR_NO_DATA; | |
return FALSE; | |
} | |
#endif | |
// TODO NOISE_TESTING | |
if ((buffer[0] & (1<<I2C_READ_BIT)) == I2C_READ) { | |
I2C_state.dataDirection = I2C_READ; | |
} else { | |
I2C_state.dataDirection = I2C_WRITE; | |
} | |
I2C_gen_start(); | |
do { | |
if (I2C_state.addressByte || | |
(I2C_state.dataDirection == I2C_WRITE)) { | |
// Transmit a byte on the I2C bus | |
// SCL low | |
I2C_PORT &= ~(1<<I2C_SCL); | |
// Load data register | |
USIDR = *(buffer++); | |
I2C_byte_xfer(SR_8bit); | |
// Receive N/ACK from slave | |
I2C_DDR &= ~(1<<I2C_SDA); | |
if (I2C_byte_xfer(SR_1bit) & (1<<I2C_NACK_BIT)) { | |
I2C_state.errorState = I2C_state.addressByte ? | |
I2C_ERR_NO_ACK_ON_ADDR : | |
I2C_ERR_NO_ACK_ON_DATA; | |
return FALSE; | |
} | |
// Only one address byte | |
I2C_state.addressByte = FALSE; | |
} else { | |
// Receive a byte on the I2C bus | |
// Set SDA as input | |
I2C_DDR &= ~(1<<I2C_SDA); | |
// Pull byte off the bus | |
*(buffer++) = I2C_byte_xfer(SR_8bit); | |
// Transmit ACK or NACK data | |
USIDR = (buflen == 1) ? 0xFF : 0x00; | |
I2C_byte_xfer(SR_1bit); | |
} | |
} while (--buflen); | |
I2C_gen_stop(); | |
return TRUE; | |
} | |
// Run the shift register until the counter overflows | |
// Function resets SDA line to output on return | |
uint8_t I2C_byte_xfer(uint8_t reg) { | |
// Preset counter | |
USISR = reg; | |
// Setup control register for clock strobe | |
reg = (1<<USIWM1) | (1<<USICS1) | (1<<USICLK) | (1<<USITC); | |
do { | |
I2C_delay(I2C_T1); | |
// Positive edge | |
USICR = reg; | |
// Wait for clock stretching | |
while (!(I2C_PIN & (1<<I2C_SCL))) ; | |
I2C_delay(I2C_T2); | |
// Negative edge | |
USICR = reg; | |
} while (!(USISR & (1<<USIOIF))); | |
I2C_delay(I2C_T1); | |
// Save shift register | |
reg = USIDR; | |
// Release SDA | |
USIDR = 0xFF; | |
// Set SDA as output | |
I2C_DDR |= (1<<I2C_SDA); | |
return reg; | |
} | |
uint8_t I2C_gen_start(void) { | |
// Release SCL | |
I2C_PORT |= (1<<I2C_SCL); | |
while(!(I2C_PORT & (1<<I2C_SCL))) ; | |
#ifdef I2C_FAST_MODE | |
I2C_delay(I2C_T2); | |
#else | |
I2C_delay(I2C_T1); | |
#endif | |
// Generate start condition | |
I2C_PORT &= ~(1<<I2C_SDA); | |
I2C_delay(I2C_T2); | |
I2C_PORT &= ~(1<<I2C_SCL); | |
I2C_PORT |= (1<<I2C_SDA); | |
#ifdef SIGNAL_VERIFY | |
// Check that the start condition was detected | |
if (!(USISR & (1<<USISIF))) { | |
I2C_state.errorState = I2C_ERR_MISSING_START_CON; | |
return FALSE; | |
} | |
#endif | |
return TRUE; | |
} | |
uint8_t I2C_gen_stop(void) { | |
// Pull SDA low | |
I2C_PORT &= ~(1<<I2C_SDA); | |
// Release SCL | |
I2C_PORT |= (1<<I2C_SCL); | |
// Wait for SCL to release | |
while(!(I2C_PIN & (1<<I2C_SCL))) ; | |
I2C_delay(I2C_T2); | |
I2C_PORT |= (1<<I2C_SDA); | |
I2C_delay(I2C_T1); | |
#ifdef SIGNAL_VERIFY | |
if (!(USISR & (1<<USIPF))) { | |
I2C_state.errorState = I2C_ERR_MISSING_STOP_CON; | |
return FALSE; | |
} | |
#endif | |
return TRUE; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment