Binary_DCF77_Clock/firmware/saa1064.c
2007-01-02 21:30:40 +00:00

131 lines
3.7 KiB
C

/**
* \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 <avr/io.h>
#include <util/delay.h>
#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();
}