Смотрите, может поможет (или запросите у меня полный исходник: fk0@fk0.name):
void smb_init(void)
{
AT91S_PIO *pio = AT91C_BASE_PIOA;
pio->PIO_SODR = AT91C_PA10_TWD | AT91C_PA11_TWCK;
pio->PIO_PER = AT91C_PA10_TWD | AT91C_PA11_TWCK;
pio->PIO_IFER = AT91C_PA10_TWD | AT91C_PA11_TWCK;
pio->PIO_MDER = AT91C_PA10_TWD | AT91C_PA11_TWCK;
pio->PIO_PPUDR = AT91C_PA10_TWD | AT91C_PA11_TWCK;
pio->PIO_ASR = AT91C_PA10_TWD | AT91C_PA11_TWCK;
AT91C_BASE_AIC->AIC_SVR[AT91C_ID_TWI] = (uint32_t)smb_intr;
smb_ei();
}
bool smb_hwreset(void)
{
AT91S_TWI *twi = AT91C_BASE_TWI;
uint32_t sr;
twi->TWI_IDR = -1;
twi->TWI_CR = AT91C_TWI_SWRST;
/* compute C[LHK]DIV */
uint8_t CLDIV, CHDIV;
uint8_t CKDIV;
{
uint8_t v;
unsigned n;
v = (MCK / 1000 / config.bus.freq - 3) / 256;
CKDIV = 0;
while (v > 0)
v /= 2, CKDIV++;
n = (MCK / 1000 / config.bus.freq - 3) / (1 << CKDIV);
CHDIV = config.bus.ratio * n / 100;
CLDIV = n - CHDIV;
}
twi->TWI_CWGR = (CKDIV << 16) | (CHDIV << 8) | CLDIV;
twi->TWI_CR = AT91C_TWI_MSDIS;
twi->TWI_CR = AT91C_TWI_MSEN;
sr = twi->TWI_SR;
if ((sr & AT91C_TWI_TXCOMP) && (sr & AT91C_TWI_TXRDY))
return 1;
else
return 0;
}
#define smb_txbyte(byte) { AT91C_BASE_TWI->TWI_THR=(byte); }
#define smb_rxbyte() (AT91C_BASE_TWI->TWI_RHR)
static inline void smb_wait_tx(void)
{
AT91S_TWI *twi = AT91C_BASE_TWI;
twi->TWI_IDR = -1;
twi->TWI_IER =
AT91C_TWI_NACK | AT91C_TWI_TXRDY /*|AT91C_TWI_TXCOMP */ ;
}
void smb_start_tx(uint_least8_t addr, uint_least8_t byte)
{
AT91S_TWI *twi = AT91C_BASE_TWI;
AT91S_PIO *pio = AT91C_BASE_PIOA;
pio->PIO_PDR = AT91C_PA10_TWD | AT91C_PA11_TWCK;
twi->TWI_MMR = AT91C_TWI_DADR & (addr << 16); /* MREAD=0 */
smb_wait_tx();
smb_txbyte(byte);
//twi->TWI_CR=AT91C_TWI_START;
}
static inline void smb_wait_rx(void)
{
AT91S_TWI *twi = AT91C_BASE_TWI;
twi->TWI_IDR = -1;
twi->TWI_IER =
AT91C_TWI_RXRDY | AT91C_TWI_NACK /*| AT91C_TWI_TXCOMP */ ;
}
static inline void smb_rx_last(void)
{
AT91C_BASE_TWI->TWI_CR = AT91C_TWI_STOP;
}
void smb_start_rx(uint_least8_t addr)
{
AT91S_TWI *twi = AT91C_BASE_TWI;
AT91S_PIO *pio = AT91C_BASE_PIOA;
pio->PIO_PDR = AT91C_PA10_TWD | AT91C_PA11_TWCK;
twi->TWI_MMR = (AT91C_TWI_DADR & (addr << 16)) | AT91C_TWI_MREAD;
smb_rxbyte(); // XXX I don't know why, but it helps...
smb_wait_rx();
twi->TWI_CR = AT91C_TWI_START;
}
inline static void smb_stop(void)
{
AT91S_TWI *twi = AT91C_BASE_TWI;
twi->TWI_CR = AT91C_TWI_STOP;
twi->TWI_IDR = -1;
twi->TWI_IER = AT91C_TWI_TXCOMP;
}
void smb_off(void)
{
AT91S_PIO *pio = AT91C_BASE_PIOA;
pio->PIO_OER = AT91C_PA11_TWCK | AT91C_PA10_TWD;
pio->PIO_PER = AT91C_PA10_TWD | AT91C_PA11_TWCK;
AT91C_BASE_TWI->TWI_IDR = -1;
unsigned n;
for (n = 0; n <= 12; n++) {
delay2us();
pio->PIO_CODR = AT91C_PA11_TWCK;
delay2us();
pio->PIO_CODR = AT91C_PA10_TWD;
delay2us();
pio->PIO_SODR = AT91C_PA11_TWCK;
delay2us();
pio->PIO_SODR = AT91C_PA10_TWD;
}
delay2us();
pio->PIO_ODR = AT91C_PA11_TWCK | AT91C_PA10_TWD;
}
#define RXRDY() (SR & AT91C_TWI_RXRDY)
#define TXRDY() (SR & AT91C_TWI_TXRDY)
#define ISACK() (!(SR & AT91C_TWI_NACK))
#define ISSTOP() (SR & AT91C_TWI_TXCOMP)
[ZX]