Created
December 19, 2010 06:24
-
-
Save PhirePhly/747140 to your computer and use it in GitHub Desktop.
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, 2010 | |
// kennethfinnegan.blogspot.com | |
// | |
// 4 digit 7 segment clock | |
// Runs on a ATTiny 2313, with time keeping handled by a DS1307 | |
#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 // AVR clock frequency in Hz | |
#define TIMER_FREQ 300 // multiplex frequency in Hz | |
#define DS1307 0x68 // I2C address | |
// GLOBALS | |
static volatile uint8_t currdig=0; | |
static volatile uint8_t newdigit; | |
uint16_t count, bounce[2]; // tracks rough passage of time | |
uint8_t time[3]; | |
uint16_t digits[9]; | |
// These are functions instead of macros to save program space | |
uint8_t BCD2DEC(uint8_t bcd) { | |
return (bcd & 0xF) + ((bcd >> 4) * 10); | |
} | |
uint8_t DEC2BCD(uint8_t dec) { | |
return (dec % 10) | ((dec / 10) << 4); | |
} | |
// Download the current time from DS1307 RTC | |
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]; | |
} | |
// Upload updated time to DS1307 RTC | |
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); | |
} | |
// Update the cached display bitfields, so they don't need to be | |
// calculated when needed | |
void updateDisplay(void) { | |
uint8_t table[] = {0x3F,0x18,0x76,0x7C,0x59,0x6D, | |
0x6F,0x38,0x7F,0x79,0x7B,0x73}; | |
uint8_t hour = BCD2DEC(time[2]), ampm=0; | |
if (hour > 11) ampm = 1; | |
hour = (hour % 12) ? (hour % 12) : 12; | |
digits[0] = 0; | |
digits[1] = (hour/10) ? table[hour/10] : 0; | |
digits[2] = table[hour%10] | (1 << 8); | |
digits[3] = table[time[1] >> 4]; | |
digits[4] = table[time[1] & 0x0F] | (1 << 8); | |
digits[5] = table[time[0] >> 4]; | |
digits[6] = table[time[0] & 0x0F]; | |
digits[7] = table[10 + ampm]; | |
digits[8] = 0; | |
} | |
// Shift out a variable number of bits shift-register style | |
void shiftOut(uint16_t bits, uint8_t cnt) { | |
for ( ; cnt; cnt--) { | |
PORTB = (PORTB & 0xFC)| ((bits>>(cnt-1))<<1); | |
PINB = 1; | |
PINB = 1; | |
} | |
} | |
// Output Compare 1 overflow interrupt | |
// Flags for new digit for display multiplexing | |
SIGNAL (SIG_OUTPUT_COMPARE1A) { | |
newdigit = 1; | |
} | |
int main(void) | |
{ | |
DDRA = 0x04; | |
PORTA = 0x07; | |
DDRB = 0xFF; | |
PORTB = 0x00; | |
DDRD = 0x20; | |
PORTD = 0x03; | |
I2C_init(); | |
// Timer 0 Fast PWM for charge pump | |
TCCR0A = 0x23; | |
TCCR0B = 0x01; | |
OCR0B = 210; | |
// Timer 1 for multiplex flag | |
// use CLK/1024 prescale value, clear timer/counter on compareA match | |
TCCR1B = (0<<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) { | |
// Flag set by timer int | |
if (newdigit) { | |
newdigit = 0; | |
currdig = (currdig + 1) % 9; | |
if (currdig == 8) currdig = 1; | |
shiftOut(1<<currdig, 9); | |
shiftOut(digits[currdig], 11); | |
// Update time from DS1307 every 1/3 second | |
count++; | |
if (count > 100) { | |
count = 0; | |
downloadTime(); | |
updateDisplay(); | |
} | |
// Decrement debounce timers | |
if (bounce[0]) bounce[0] = bounce[0] - 1; | |
if (bounce[1]) bounce[1] = bounce[1] - 1; | |
} | |
// Check for button press, which is disabled if bounce[n] isn't | |
// back down to zero yet. Buttons go to ground with internal | |
// pull-up resistors | |
if (!(PIND & (1<<0)) && bounce[0] == 0) { | |
time[2] = DEC2BCD((BCD2DEC(time[2])+1)%24); | |
uploadTime(); | |
bounce[0] = 0x1FF; | |
} | |
if (!(PIND & (1<<1)) && bounce[1] == 0) { | |
time[0] = 0x00; | |
time[1] = DEC2BCD((BCD2DEC(time[1])+1)%60); | |
uploadTime(); | |
bounce[1] = 0x1FF; | |
} | |
// Brightness adjust by changing PWM for night-mode | |
OCR0B = 100 + 110 * (PINA & 1); | |
} | |
} |
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