ВходНаше всё Теги codebook 无线电组件 Поиск Опросы Закон Понедельник
25 ноября
385319 Топик полностью
fk0, легенда (04.02.2013 20:13, просмотров: 175) ответил fk0 на Слейв, если обмен начался и не завершился за определённое время -- сбрасывает свой I2C модуль и освобождает шину (если держал SCL в нуле и т.п.) Мастер свой сброс выполняет так в общем случае: через SFR регистры сбрасывает свой I2C модуль (если
Выдержки из слейва на 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]