alix-usv/usi-i2c-slave.c

142 lines
4.6 KiB
C

/***************************************************************************
* Copyright (C) 01/2009 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* 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 <avr/io.h>
#include <avr/interrupt.h>
#include "alix-usv.h"
#include "usi-i2c-slave.h"
#define USI_MASK 0x0F
#define USI_MASK_ACKBIT 0x10 /* one (ack)bit / 8bits */
#define USI_MASK_OUTPUT 0x20 /* SDA is output / input */
#define USI_RESET 0x00 /* wait for Start Condition */
#define USI_START 0x01 /* Start detected */
#define USI_SLA 0x02 /* wait for Slave Address */
#define USI_SLAW_ACK 0x03 /* ACK Slave Address + Write (Master writes) */
#define USI_SLAR_ACK 0x04 /* ACK Slave Address + Read (Master reads) */
#define USI_DATW 0x05 /* receive Data */
#define USI_DATW_ACK 0x06 /* transmit ACK for received Data */
#define USI_DATR 0x07 /* transmit Data */
#define USI_DATR_ACK 0x08 /* receive ACK for transmitted Data */
static uint8_t usi_state;
void usi_statemachine(void)
{
static uint8_t bcnt;
uint8_t tmp = USIDR;
uint8_t state = usi_state & USI_MASK;
/* Start Condition detected */
if (state == USI_START) {
usi_state = USI_SLA;
/* Slave Address received => prepare ACK/NACK */
} else if (state == USI_SLA) {
bcnt = 0;
if (tmp == (I2C_ADDRESS | 0x00))
usi_state = USI_SLAW_ACK | USI_MASK_ACKBIT | USI_MASK_OUTPUT;
else if (tmp == (I2C_ADDRESS | 0x01))
usi_state = USI_SLAR_ACK | USI_MASK_ACKBIT | USI_MASK_OUTPUT;
else
usi_state = USI_RESET;
/* ACK after SLA+W / Data received => prepare data receive */
} else if (state == USI_SLAW_ACK || state == USI_DATW_ACK) {
usi_state = USI_DATW;
/* Data received => prepare ACK transmit */
} else if (state == USI_DATW) {
usi_write(tmp, bcnt++);
usi_state = USI_DATW_ACK | USI_MASK_ACKBIT | USI_MASK_OUTPUT;
/* ACK after SLA+R transmitted / ACK after Data received => prepare data transmit */
} else if (state == USI_SLAR_ACK || (state == USI_DATR_ACK && tmp == 0x00)) {
USIDR = usi_read(bcnt++);
usi_state = USI_DATR | USI_MASK_OUTPUT;
/* Data transmitted => prepare ACK receive */
} else if (state == USI_DATR) {
usi_state = USI_DATR_ACK | USI_MASK_ACKBIT;
/* NACK after Data received / default => go idle */
} else {
usi_state = USI_RESET;
}
state = usi_state & USI_MASK;
if (state == USI_RESET) {
/* input */
DDRA &= ~I2C_SDA;
/* Enable StartCondition Interrupt, TWI Mode, count both edges */
USICR = (1<<USISIE) | (1<<USIWM1) | (1<<USICS1);
/* Clear Interrupt Flags, Clear Stop Condition Flag, wait for 2 edges */
USISR = (1<<USISIF) | (1<<USIOIF) | (1<<USIPF) | ((16 -2)<<USICNT0);
} else if (state == USI_SLA) {
/* Enable Overflow Interrupt, TWI Mode, extend SCL on Overflow */
USICR = (1<<USISIE) | (1<<USIOIE) | (1<<USIWM1) | (1<<USIWM0) | (1<<USICS1);
/* Clear Interrupt Flags, Clear Stop Condition Flag, wait for 16 edges */
USISR = (1<<USISIF) | (1<<USIOIF) | (1<<USIPF) | ((16 -16)<<USICNT0);
} else {
if (usi_state & USI_MASK_OUTPUT) {
DDRA |= I2C_SDA;
} else {
DDRA &= ~I2C_SDA;
}
if (usi_state & USI_MASK_ACKBIT) {
USIDR = 0x00;
USISR = (1<<USIOIF) | ((16 -2)<<USICNT0);
} else {
USISR = (1<<USIOIF) | ((16 -16)<<USICNT0);
}
}
}
ISR(USI_START_vect)
{
usi_state = USI_START;
/* check for STOP Condition */
while (PINA & I2C_SCL) {
if (PINA & I2C_SDA) {
usi_state = USI_RESET;
break;
}
}
usi_statemachine();
}
ISR(USI_OVF_vect)
{
usi_statemachine();
}