Created
March 28, 2010 20:28
-
-
Save PhirePhly/347018 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
// 2010 Kenneth Finnegan | |
// kennethfinnegan.blogspot.com | |
// | |
// 4 digit 7 segment clock | |
// Runs on a ATTiny 2313, with time keeping handled by a DS1307 | |
// Digits are output as BCD, which is decoded by an external 7447 | |
// PA0:1 - Time set buttons | |
// PB0:3 - Digit anodes | |
// PB5 - SDA | |
// PB7 - SCK | |
// PD2:5 - BCD output to 7447 | |
// PD6 - PM LED | |
#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 XTAL 8000000L // Crystal frequency in Hz | |
#define TIMER_FREQ 300 // multiplex frequency in Hz | |
#define DS1307 0x68 | |
// GLOBALS | |
static volatile uint8_t digits[4]; | |
static volatile uint8_t ampmflag; | |
static volatile uint8_t currdig=0; | |
int count; | |
uint8_t time[3]; | |
// Output Compare 1 overflow interrupt | |
// Handles display multiplexing | |
SIGNAL (SIG_OUTPUT_COMPARE1A) | |
{ | |
currdig = (currdig + 1) % 4; | |
// Turn off last digit | |
PORTB |= 0xF; | |
// Setup 7447 decoder with next digit | |
PORTD = 0x3 | (digits[currdig] << 2) | (ampmflag<<PD6); | |
// Turn on new digit | |
PORTB &= ~(1<<currdig); | |
} | |
uint8_t BCD2DEC(uint8_t bcd) { | |
return (bcd & 0xF) + ((bcd >> 4) * 10); | |
} | |
uint8_t DEC2BCD(uint8_t dec) { | |
return (dec % 10) | ((dec / 10) << 4); | |
} | |
void downloadTime(void) { | |
uint8_t buf[4]; | |
buf[0] = (DS1307 << 1) | 0; | |
buf[1] = 0x00; | |
I2C_xfer(buf, 2); | |
buf[0] |= 1; | |
I2C_xfer(buf, 4); | |
time[0] = buf[1]; | |
time[1] = buf[2]; | |
time[2] = buf[3]; | |
} | |
void uploadTime(void) { | |
uint8_t buf[5]; | |
buf[0] = (DS1307 << 1) | 0; | |
buf[1] = 0x00; | |
buf[2] = time[0]; | |
buf[3] = time[1]; | |
buf[4] = time[2]; | |
I2C_xfer(buf, 5); | |
} | |
void updateDisplay(void) { | |
if (PINA & 1) { | |
// Normal hour and minute display | |
digits[0] = time[1] & 0xF; | |
digits[1] = time[1] >> 4; | |
uint8_t hour = BCD2DEC(time[2]); | |
ampmflag = (hour < 12); | |
hour = hour%12; | |
if (hour == 0) | |
hour = 12; | |
digits[2] = hour%10; | |
digits[3] = (hour/10?1:0xF); // Blank on 0 | |
} else { | |
// Display seconds count | |
digits[0] = time[0] & 0xF; | |
digits[1] = time[0] >> 4; | |
digits[2] = 0xF; | |
digits[3] = 0xF; | |
ampmflag = time[0] & 1; | |
} | |
} | |
int main(void) | |
{ | |
DDRA = 0x04; | |
PORTA = 0x07; | |
DDRB = 0xFF | |
PORTB = 0x00; | |
DDRD = 0x7C; | |
PORTD = 0x03; | |
I2C_init(); | |
// use CLK/1024 prescale value, clear timer/counter on compareA match | |
TCCR1B = (1<<CS10) | (1<<CS12) | (1<<WGM12); | |
// preset timer1 high/low byte | |
OCR1A = ((XTAL/1024/TIMER_FREQ) - 1 ); | |
// enable Output Compare 1 overflow interrupt | |
TIMSK = (1<<OCIE1A); | |
// Enable interrupts | |
sei(); | |
while (1) { | |
downloadTime(); | |
updateDisplay(); | |
if (!(PIND & (1<<0))) { | |
time[2] = DEC2BCD((BCD2DEC(time[2])+1)%24); | |
uploadTime(); | |
} | |
if (!(PIND & (1<<1))) { | |
time[0] = 0x00; | |
time[1] = DEC2BCD((BCD2DEC(time[1])+1)%60); | |
uploadTime(); | |
} | |
_delay_ms(250); | |
} | |
} |
This file contains hidden or 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 | |
// 2010 Kenneth Finnegan | |
// 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