2007-01-02 22:30:40 +01:00
|
|
|
/**
|
|
|
|
* \file saa1064.c
|
|
|
|
* \brief I2C-connection to the SAA1064 LED-driver
|
|
|
|
* \author Ronald Schaten
|
2007-01-03 13:38:55 +01:00
|
|
|
* \version $Id: saa1064.c,v 1.2 2007/01/03 12:38:55 rschaten Exp $
|
2007-01-02 22:30:40 +01:00
|
|
|
*
|
|
|
|
* License: See documentation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* based on http://www.mulder.franken.de/ntpdcfledclock/ */
|
|
|
|
|
|
|
|
#include <avr/io.h>
|
|
|
|
#include <util/delay.h>
|
|
|
|
#include "saa1064.h"
|
|
|
|
|
2007-01-03 13:38:55 +01:00
|
|
|
#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 */
|
2007-01-02 22:30:40 +01:00
|
|
|
|
2007-01-03 13:38:55 +01:00
|
|
|
#define SDAPIN PC4 /**< which pins of the port */
|
|
|
|
#define SCLPIN PC5 /**< which pins of the port */
|
2007-01-02 22:30:40 +01:00
|
|
|
|
2007-01-03 13:38:55 +01:00
|
|
|
#define SAA_ADR 0x70 /**< the I2C addresses of the SAA 1064 LED drivers */
|
2007-01-02 22:30:40 +01:00
|
|
|
|
2007-01-03 13:38:55 +01:00
|
|
|
#define I2C_READ 0x01 /**< command used to read from I2C */
|
|
|
|
#define I2C_WRITE 0x00 /**< command used to write to I2C */
|
2007-01-02 22:30:40 +01:00
|
|
|
|
2007-01-03 13:38:55 +01:00
|
|
|
#define DELAYVAL 3 /**< pause between certain actions on the bus. Should be at least (10 * freq) / 3, so we set 3 at 1 MHz */
|
2007-01-02 22:30:40 +01:00
|
|
|
|
|
|
|
void led_init(void) {
|
|
|
|
/* activate pullups */
|
|
|
|
LEDPORT |= (1 << SCLPIN) | (1 << SDAPIN);
|
|
|
|
}
|
|
|
|
|
2007-01-03 13:38:55 +01:00
|
|
|
/**
|
|
|
|
* Send START, defined as high-to-low SDA with SCL high.
|
2007-01-02 22:30:40 +01:00
|
|
|
* Expects SCL and SDA to be high already!
|
2007-01-03 13:38:55 +01:00
|
|
|
* Returns with SDA and SCL low.
|
|
|
|
*/
|
2007-01-02 22:30:40 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2007-01-03 13:38:55 +01:00
|
|
|
/**
|
|
|
|
* Send STOP, defined as low-to-high SDA with SCL high.
|
2007-01-02 22:30:40 +01:00
|
|
|
* Expects SCL and SDA to be low already!
|
2007-01-03 13:38:55 +01:00
|
|
|
* Returns with SDA and SCL high.
|
|
|
|
*/
|
2007-01-02 22:30:40 +01:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2007-01-03 13:38:55 +01:00
|
|
|
/**
|
|
|
|
* Transmits the byte in parameter what.
|
2007-01-02 22:30:40 +01:00
|
|
|
* Returns 1 if the byte was ACKed, 0 if not.
|
|
|
|
* Expects SCL and SDA to be low already!
|
2007-01-03 13:38:55 +01:00
|
|
|
* Returns with SDA and SCL low.
|
|
|
|
* \param what the byte to transmit
|
|
|
|
*/
|
2007-01-02 22:30:40 +01:00
|
|
|
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 */
|
2007-01-03 13:38:55 +01:00
|
|
|
I2C_transmit_byte(SAA_ADR | I2C_WRITE);
|
2007-01-02 22:30:40 +01:00
|
|
|
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();
|
2007-01-03 13:38:55 +01:00
|
|
|
I2C_transmit_byte(SAA_ADR | I2C_WRITE); /* Address first driver */
|
2007-01-02 22:30:40 +01:00
|
|
|
I2C_transmit_byte(0); /* Address Config Register on device */
|
|
|
|
I2C_transmit_byte(((led_brightness & 0x07) << 4) | 0x07); /* Send Settings */
|
|
|
|
I2C_stop();
|
|
|
|
}
|