Выдержки из слейва на atmega, может поможет:
#define smb_di() do { TWCR&=~_BV(TWIE); } while(0)
#define smb_ei() do { TWCR|=_BV(TWIE); } while(0)
void smbus_init(void)
{
smb_di();
TWCR = _BV(TWSTO) | _BV(TWINT);
TWCR = _BV(TWEA) | _BV(TWEN);
TWAR = (SMBUS_DEFAULT_ADDR << 1) | _BV(TWGCE);
TWAMR = 0;
smbus_addr = SMBUS_DEFAULT_ADDR;
smb_read = smb_read_default;
smb_rs = 0;
smb_ei();
}
void smbus_off(void)
{
smb_di();
TWCR = _BV(TWSTO) | _BV(TWINT);
return;
}
static inline void twi_reset(void)
{
TWCR = _BV(TWSTO) | _BV(TWINT) | _BV(TWEN);
TWAMR = 0;
smb_rs = 0;
smb_read = smb_read_default;
smbdebug("twi_reset\n");
TWCR = _BV(TWEA) | _BV(TWEN) | _BV(TWIE);
}
#define twi_last() do { \
TWCR&=~(_BV(TWEA)|_BV(TWINT)); /* send last byte */ \
}
static bool smb_assign_addr(void)
{
if (smb_buff[2] != sizeof(smbus_udid.b)) {
smbdebug("-\n");
return 0;
}
if (memcmp(smbus_udid.w, &smb_buff[3], sizeof(smbus_udid.w))) {
smbdebug("!\n");
return 0;
}
smbus_addr = smb_buff[19] >> 1;
TWAR = (smbus_addr << 1) | _BV(TWGCE);
smbdebug("new smbus addr=%d\n", (unsigned) smbus_addr);
return 1;
}
static void smb_device_reset(void)
{
smbus_addr = SMBUS_DEFAULT_ADDR;
TWAR = (smbus_addr << 1) | _BV(TWGCE);;
// smbdebug("new smbus addr=%2.2x\n", SMBUS_DEFAULT_ADDR);
}
ISR(TWI_vect)
{
switch (TW_STATUS) {
/* XXX NOTE: General call is ONLY FOR WRITING */
case TW_ST_SLA_ACK:
case TW_ST_ARB_LOST_SLA_ACK:
V = '1';
N_R++;
#if 0
puts("SMB_READ_ACK");
#endif
crc = crc8_byte((smbus_addr << 1) | TW_WRITE, crc); // FIXME crc==0
smb_read();
TWCR |= _BV(TWINT); // TODO make macro
break;
case TW_ST_DATA_ACK:
V = '2';
#if 0
puts("SMB_DATA_ACK");
#endif
#if 1
if (last_td != TWDR) {
//TWCR=_BV(TWSTO)|_BV(TWINT);
//TWCR|=_BV(TWSTO) | _BV(TWINT);
TWDR = 0xff;
TWCR &= ~(_BV(TWEA) | _BV(TWINT));
TWCR |= _BV(TWINT);
break;
}
#endif
smb_read();
TWCR |= _BV(TWINT);
break;
case TW_ST_LAST_DATA:
V = '3';
#if 0
puts("SMB_LAST_DATA");
#endif
TWCR |= _BV(TWEA) | _BV(TWINT);
smb_read = smb_read_default;
crc = 0;
break;
case TW_ST_DATA_NACK: /* NACK was sent */
V = '4';
#if 0
puts("SMB_READ_NACK");
#endif
TWCR |= _BV(TWEA) | _BV(TWINT);
smb_read = smb_read_default;
crc = 0;
break;
case TW_SR_SLA_ACK:
case TW_SR_ARB_LOST_SLA_ACK:
V = '5';
N_W++;
#if 0
puts("SMB_SLA_ACK");
#endif
crc = 0;
smb_buff[0] = (smbus_addr << 1) | TW_READ;
smb_rs = 0;
TWCR |= _BV(TWINT);
break;
case TW_SR_GCALL_ACK:
case TW_SR_ARB_LOST_GCALL_ACK:
V = '6';
N_W++;
#if 0
puts("@");
#endif
crc = 0;
smb_buff[0] = (SMBUS_GENERAL_CALL << 1) | TW_READ;
smb_rs = 0;
TWCR |= _BV(TWINT
case TW_NO_INFO:
V = '7';
TWCR |= _BV(TWINT);
break;
case TW_SR_DATA_ACK:
case TW_SR_GCALL_DATA_ACK:
V = '8';
if (smb_rs >= sizeof(smb_buff) - 1) {
crc = 0;
#if 0
puts("SMBRX OVF");
#endif
twi_reset();
break;
}
TWCR |= _BV(TWINT);
do { /* NOTE: last received byte is CRC */
uint_least8_t c = TWDR;
crc = crc8_byte(smb_buff[smb_rs], crc);
smb_buff[++smb_rs] = c;
} while (0);
break;
case TW_SR_DATA_NACK:
case TW_SR_GCALL_DATA_NACK:
V = '9';
#if 0
puts("SMB_NACK");
#endif
crc = 0; // XXX я могу послать NACK
TWCR |= _BV(TWINT);
break;
case TW_SR_STOP: /* STOP or repeated START */ ;
N_S++;
if (smb_buff[0] == ((SMBUS_GENERAL_CALL << 1) | TW_READ))
#if 1
if (smb_buff[1] == 0) {
/* global system STOP */
/* global system STOP */
// FIXME не работает с назначенным адресом???
#if 0
puts("SMBCMD: GENERAL STOP");
#endif
smbus_off(); // TODO call it in exit(); ?
//exit(0);
((void (*)(void)) 0) ();
}
#else
crc = 0;
smb_read = smb_read_default;
#endif
TWCR |= _BV(TWINT);
{
/* crc=smbus_crc(0, smb_buff, smb_rs); */
/* {{{ */
#if 0
fputs("SMBRX: ", stdout);
#endif
// FIXME проверка CRC при разборе команды write/only, а не до.
# if 0
if (crc != smb_buff[smb_rs]) {
TWCR |= _BV(TWINT);
// TWCR&=~_BV(TWEA);
smbdebug("%c ", V);
smbdebug("[CRC %2.2x!=%2.2x] ",
(unsigned) crc, (unsigned) smb_buff[smb_rs]);
smb_dump();
//putchar('z');
//twi_reset();
crc = 0;
return;
break; /* CRC not valid */
}
smb_dump();
#endif
/* }}} */
V = 'A';
switch (smb_buff[0] >> 1) {
#ifndef SMBTEST
case SMBUS_GENERAL_CALL:
case SMBUS_DEFAULT_ADDR:
switch (smb_buff[1]) {
case SMBUS_PREPARE_TO_ARP:
case SMBUS_RESET_DEVICE:
if (crc == smb_buff[smb_rs]) {
//smbdebug("generic reset/arp\n");
smb_device_reset();
}
crc = 0;
smb_read = smb_read_default;
break;
case SMBUS_GET_UDID:
//smbdebug("generic get udid\n");
crc = crc8_byte(smb_buff[smb_rs], crc);
smbus_udid.b[17] = 0xff; /* address not assigned */
smb_read = smb_get_udid;
break;
case SMBUS_ASSIGN_ADDRESS:
if (crc == smb_buff[smb_rs]) {
smb_assign_addr();
}
crc = 0;
smb_read = smb_read_default;
break;
default:
if (smbus_addr == 0) {
crc = 0;
smb_read = smb_read_default;
break;
}
/* if address already assigned */
if ((smb_buff[1] >> 1) != smbus_addr) {
crc = 0;
smb_read = smb_read_default;
break; /* not self address */
}
if (smb_buff[1] & 1) {
smbdebug("direct get udid\n");
smb_read = smb_get_udid;
} else if (crc == smb_buff[smb_rs]) {
smbdebug("direct reset\n");
smb_device_reset();
}
crc = 0;
smb_read = smb_read_default;
}
break;
#endif
default: /* non-SMBUS direct command */
smb_read = smb_command(smb_buff[1]);
}
} /* TW_SR_STOP */
break;
default:
case TW_BUS_ERROR:
V = 'B';
N_E++;
crc = 0;
#if 0
debug("SMB_BUS_ERR\n");
#endif
twi_reset();
break;
}
}
[ZX]
-
- у меня все на асе написано ) - whale(04.02.2013 20:26)
- атмега. ассемблер. конденсаторы в I2C. просто классика жанра. лучше бросай это... - fk0(04.02.2013 23:20)
- ниче, еще помучимся - whale(04.02.2013 23:28)
- атмега. ассемблер. конденсаторы в I2C. просто классика жанра. лучше бросай это... - fk0(04.02.2013 23:20)
- у меня все на асе написано ) - whale(04.02.2013 20:26)