diff options
| author | Bdale Garbee <bdale@gag.com> | 2013-05-16 00:36:23 -0600 | 
|---|---|---|
| committer | Bdale Garbee <bdale@gag.com> | 2013-05-16 00:36:23 -0600 | 
| commit | 02d111b1b53ef01fc6e9ab6c4bc60b8af1be0067 (patch) | |
| tree | 8356f4a019969ee99a45e264c87d38555cf316cc /src/attiny/ao_i2c_attiny.c | |
| parent | 7a2e1f05adad990a6b161865267abf07ffec7a7e (diff) | |
| parent | 7699a55aed3a9a7daeb4c6a5a9a280f43edf455f (diff) | |
Merge branch 'branch-1.2' into debian
Diffstat (limited to 'src/attiny/ao_i2c_attiny.c')
| -rw-r--r-- | src/attiny/ao_i2c_attiny.c | 238 | 
1 files changed, 238 insertions, 0 deletions
| diff --git a/src/attiny/ao_i2c_attiny.c b/src/attiny/ao_i2c_attiny.c new file mode 100644 index 00000000..2ee44fd2 --- /dev/null +++ b/src/attiny/ao_i2c_attiny.c @@ -0,0 +1,238 @@ +/* + * Copyright © 2011 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include <ao.h> + +/* + * ATtiny USI as an I2C interface + */ + +#define I2C_USICR ((0 << USISIE) |	/* No start condition interrupt */ \ +		   (0 << USIOIE) |	/* No counter overflow interrupt */ \ +		   (1 << USIWM1) |	/* Two-wire mode */		\ +		   (0 << USIWM0) |	/*   ... */			\ +		   (1 << USICS1) |	/* Software clock strobe */	\ +		   (0 << USICS0) |	/*   ... */			\ +		   (1 << USICLK))	/*   ... */			\ + +#define I2C_USICR_TICK (I2C_USICR | (1 << USITC))	/* Toggle the clock on every write */ + +#define I2C_USISR_1BIT	((1<<USISIF)|	/* Clear start condition flag */ \ +			 (1<<USIOIF)|	/* Clear overflow flag */	\ +			 (1<<USIPF)|	/* Clear stop condition flag */	\ +			 (1<<USIDC)|	/* Clear data collision flag */	\ +			 (0xE<<USICNT0)) /* Set counter value to 0xe */ + +#define I2C_USISR_8BIT	((1<<USISIF)|	/* Clear start condition flag */ \ +			 (1<<USIOIF)|	/* Clear overflow flag */	\ +			 (1<<USIPF)|	/* Clear stop condition flag */	\ +			 (1<<USIDC)|	/* Clear data collision flag */	\ +			 (0x0<<USICNT0)) /* Set counter value to 0 */ + +#define T2_TWI    5 		/* >4.7μs */ +#define T4_TWI    4 		/* >4.0μs */ + +static inline void ao_i2c_transfer(uint8_t sr) +{ +	USISR = sr; +	for (;;) { +		ao_delay_us(T2_TWI); + +		/* Clock high */ +		USICR = I2C_USICR_TICK; + +		/* Wait for clock high (clock stretching) */ +		ao_delay_us(T4_TWI); +		while(!(I2C_PIN & (1<<I2C_PIN_SCL))) +			; + +		/* Clock low */ +		USICR = I2C_USICR_TICK; + +		/* Check for transfer complete */ +		if (USISR & (1 << USIOIF)) +			break; +	} +	ao_delay_us(T2_TWI); +} + +static inline uint8_t ao_i2c_get_byte(uint8_t sr) +{ +	uint8_t	ret; + +	/* Set SDA to input */ +	I2C_DIR &= ~(1<<I2C_PIN_SDA); + +	ao_i2c_transfer(sr); + +	ret = USIDR; +	USIDR = 0xff; + +	/* Set SDA to output */ +	I2C_DIR |= (1<<I2C_PIN_SDA); + +	return ret; +} + +static uint8_t +ao_i2c_write_byte(uint8_t byte) +{ +	/* Pull SCL low */ +	I2C_PORT &= ~(1<<I2C_PIN_SCL); + +	/* Write the byte */ +	USIDR = byte; +       +	/* Clock and verify (N)ACK from slave */ + +	ao_i2c_transfer(I2C_USISR_8BIT); + +	if (ao_i2c_get_byte(I2C_USISR_1BIT) & 0x80) +		return 0; + +	return 1; +} + +static uint8_t +ao_i2c_read_byte(uint8_t ack) +{ +	uint8_t	ret; + +	/* Read the data */ +	ret = ao_i2c_get_byte(I2C_USISR_8BIT); + +	/* Ack it */ +	USIDR = ack; +	ao_i2c_transfer(I2C_USISR_8BIT); + +	return ret; +} + +uint8_t +ao_i2c_start_bus(uint8_t address) +{ +	/* Release SCL to ensure that (repeated) Start can be performed */ + +	I2C_PORT |= (1<<I2C_PIN_SCL); + +	while( !(I2C_PORT & (1<<I2C_PIN_SCL)) ) +		; +	ao_delay_us(T2_TWI); + +	/* Generate Start Condition */ + +	/* Pull SDA low */ +	I2C_PORT &= ~(1<<I2C_PIN_SDA); +	ao_delay_us(T4_TWI);                          + +	/* Pull SCL low */ +	I2C_PORT &= ~(1<<I2C_PIN_SCL); + +	/* Raise SDA */ +	I2C_PORT |= (1<<I2C_PIN_SDA); + +	return ao_i2c_write_byte(address); +} + +static void +ao_i2c_stop_bus(void) +{ +	/* Pull SDA low. */ +	I2C_PORT &= ~(1<<I2C_PIN_SDA); + +	/* Release SCL. */ +	I2C_PORT |= (1<<I2C_PIN_SCL); + +	/* Wait for SCL to go high */ +	while( !(I2C_PIN & (1<<I2C_PIN_SCL)) ); +	ao_delay_us(T4_TWI); + +	/* Raise SDA */ +	I2C_PORT |= (1<<I2C_PIN_SDA); +	ao_delay_us(T2_TWI); +} + +/* Send bytes over SPI. + * + * This just polls; the SPI is set to go as fast as possible, + * so using interrupts would take way too long + */ +uint8_t +ao_i2c_send_bus(void __xdata *block, uint16_t len, uint8_t stop) +{ +	uint8_t	*d = block; + +	while (len--) +		if (!ao_i2c_write_byte (*d++)) +			return 0; +	if (stop) +		ao_i2c_stop_bus(); +	return 1; +} + +/* Send bytes over SPI. + * + * This just polls; the SPI is set to go as fast as possible, + * so using interrupts would take way too long + */ +uint8_t +ao_i2c_send_fixed_bus(uint8_t d, uint16_t len, uint8_t stop) +{ +	while (len--) +		if (!ao_i2c_write_byte (d)) +			return 0; +	if (stop) +		ao_i2c_stop_bus(); +	return 1; +} + +/* Receive bytes over SPI. + * + * Poll, sending zeros and reading data back + */ +uint8_t +ao_i2c_recv_bus(void __xdata *block, uint16_t len, uint8_t stop) +{ +	uint8_t	*d = block; + +	while (len--) +		*d++ = ao_i2c_read_byte (len ? 0x00 : 0xff); +	if (stop) +		ao_i2c_stop_bus(); +	return 1; +} + +/* + * Initialize USI + * + * Chip select is the responsibility of the caller + */ + +void +ao_i2c_init(void) +{ +	/* Pull-ups on SDA and SCL */ +	I2C_PORT |= (1<<I2C_PIN_SDA); +	I2C_PORT |= (1<<I2C_PIN_SCL); +   +	/* SCL and SDA are outputs */ +	I2C_DIR  |= (1<<I2C_PIN_SCL); +	I2C_DIR  |= (1<<I2C_PIN_SDA); +   +	USIDR =  0xFF; +	USICR =  I2C_USICR; +} | 
