/** * \file saa1064.c * \brief I2C-connection to the SAA1064 LED-driver * \author Ronald Schaten * \version $Id: saa1064.c,v 1.2 2007/01/03 12:38:55 rschaten Exp $ * * License: See documentation. */ /* based on http://www.mulder.franken.de/ntpdcfledclock/ */ #include #include #include "saa1064.h" #define LEDPORT PORTC /**< the Port used for the connection */ #define LEDPIN PINC /**< the Port used for the connection */ #define LEDDDR DDRC /**< the Port used for the connection */ #define SDAPIN PC4 /**< which pins of the port */ #define SCLPIN PC5 /**< which pins of the port */ #define SAA_ADR 0x70 /**< the I2C addresses of the SAA 1064 LED drivers */ #define I2C_READ 0x01 /**< command used to read from I2C */ #define I2C_WRITE 0x00 /**< command used to write to I2C */ #define DELAYVAL 3 /**< pause between certain actions on the bus. Should be at least (10 * freq) / 3, so we set 3 at 1 MHz */ 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 parameter 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. * \param what the byte to transmit */ 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_ADR | 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_ADR | 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(); }