/** * \file saa1064.c * \brief I2C-connection to the SAA1064 LED-driver * \author Ronald Schaten * \version $Id: saa1064.c,v 1.1 2007/01/02 21:30:40 rschaten Exp $ * * License: See documentation. */ /* based on http://www.mulder.franken.de/ntpdcfledclock/ */ #include #include #include "saa1064.h" /* The Port used for the connection */ #define LEDPORT PORTC #define LEDPIN PINC #define LEDDDR DDRC /* Which pins of the port */ #define SDAPIN PC4 #define SCLPIN PC5 /* the I2C addresses of the SAA 1064 LED drivers */ #define SAA_AD1 0x70 // or 0x76? #define I2C_READ 0x01 #define I2C_WRITE 0x00 /* Should be at least 27 (80 / 3) at 8 MHz */ /* This was the conservative value used for testing. However, half as much should work as well. */ #define DELAYVAL 3 void led_init(void) { /* activate pullups */ LEDPORT |= (1 << SCLPIN) | (1 << SDAPIN); } /* Send START, defined as high-to-low SDA with SCL high. * Expects SCL and SDA to be high already! * Returns with SDA and SCL low. */ static void I2C_start(void) { /* Change to output mode. */ LEDDDR |= (1 << SDAPIN) | (1 << SCLPIN); /* change SDA to low */ LEDPORT &= ~(1 << SDAPIN); _delay_loop_1(DELAYVAL); /* and SCL too */ LEDPORT &= ~(1 << SCLPIN); _delay_loop_1(DELAYVAL); } /* Send STOP, defined as low-to-high SDA with SCL high. * Expects SCL and SDA to be low already! * Returns with SDA and SCL high. */ static void I2C_stop(void) { /* Set SCL */ LEDPORT |= (1 << SCLPIN); _delay_loop_1(DELAYVAL); /* Set SDA */ LEDPORT |= (1 << SDAPIN); _delay_loop_1(DELAYVAL); /* Probably safer to tristate the bus */ LEDDDR &= ~((1 << SDAPIN) | (1 << SCLPIN)); } /* Transmits the byte in what. * Returns 1 if the byte was ACKed, 0 if not. * Expects SCL and SDA to be low already! * Returns with SDA and SCL low. */ static uint8_t I2C_transmit_byte(uint8_t what) { uint8_t i; for (i = 0; i < 8; i++) { /* First put data on the bus */ if (what & 0x80) { LEDPORT |= (1 << SDAPIN); } _delay_loop_1(DELAYVAL); /* Then set SCL high */ LEDPORT |= (1 << SCLPIN); _delay_loop_1(DELAYVAL); /* Take SCL back */ LEDPORT &= ~(1 << SCLPIN); _delay_loop_1(DELAYVAL); /* And SDA too */ LEDPORT &= ~(1 << SDAPIN); _delay_loop_1(DELAYVAL); what <<= 1; } /* OK that was the data, now we read back the ACK */ /* We need to tristate SDA for that */ LEDPORT |= (1 << SDAPIN); LEDDDR &= ~(1 << SDAPIN); /* Give the device some time */ _delay_loop_1(DELAYVAL); _delay_loop_1(DELAYVAL); _delay_loop_1(DELAYVAL); /* Then set SCL high */ LEDPORT |= (1 << SCLPIN); _delay_loop_1(DELAYVAL); _delay_loop_1(DELAYVAL); _delay_loop_1(DELAYVAL); i = LEDPIN & (1 << SDAPIN); /* Read ACK */ /* Take SCL back */ LEDPORT &= ~(1 << SCLPIN); _delay_loop_1(DELAYVAL); /* No more tristate, we pull SDA again */ LEDPORT &= ~(1 << SDAPIN); LEDDDR |= (1 << SDAPIN); _delay_loop_1(DELAYVAL); return (i == 0); } void set_led_digit(uint8_t digit, uint8_t val) { I2C_start(); /* Address device */ I2C_transmit_byte(SAA_AD1 | I2C_WRITE); I2C_transmit_byte((digit & 3) + 1); /* Address Digit Register on device */ I2C_transmit_byte(val); /* Send value for Digit */ I2C_stop(); } void set_led_brightness(uint8_t led_brightness) { I2C_start(); I2C_transmit_byte(SAA_AD1 | I2C_WRITE); /* Address first driver */ I2C_transmit_byte(0); /* Address Config Register on device */ I2C_transmit_byte(((led_brightness & 0x07) << 4) | 0x07); /* Send Settings */ I2C_stop(); }