Всем привет! Работаю с EEPROM ST m24512 по i2c интерфейсу мк msp430f5510. Запись происходит нормально , но чтении происходит только один раз , при последующих чтениях зависает i2c. Чтение работает с использованием DMA согласно эраташиту, I2C в режиме сингл мастер. Исходный код написан в IAR v5. Вот пример:
---------------------- i2c_dma.asm--------------------------------------------------------------------
#define I2C_SPEED 100000
#define SPEED_COEFF SYS_CLOCK/I2C_SPEED // коэф равен 240 24Мгц/100Кгц
PUBLIC I2C_Init // ф-я инициализации I2c
PUBLIC Get_I2C_ptr // функция возвращает указатель на буффер i2c
PUBLIC I2C_StartTrans // функция осуществляет транзакцию на i2c шине
PUBLIC i2c_state
RSEG DATA
i2c_ptr: DS16 1 // // указатель на текущий считываемый / записываемый байт
i2c_buffer: DS8 EEPROM_PAGE_SIZE+2 // буффер I2C размером 128+2 = 129 байт
i2c_length: DS8 1 // количество байт для приема/передачи
i2c_state: DS8 1 // байт состояния i2c
i2c_last_error: DS8 1 // байт последней ошибки i2c
RSEG CODE
/**********************************/
// инициализация i2c extern void I2C_Init(void);
/***********************************/
I2C_Init:
bis.b #UCSWRST,&UCB1CTL1
mov.b #UCSWRST+UCSSEL_2+UCTR,&UCB1CTL1
mov.b #UCMST+UCMODE_3,&UCB1CTL0
bis.b #BIT1+BIT2,&P4OUT
bis.b #BIT1+BIT2,&P4REN
bic.b #BIT1+BIT2,&P4DIR
bis.b #BIT1+BIT2,&P4SEL
mov.b #LOW(SPEED_COEFF),&UCB1BR0
mov.b #HIGH(SPEED_COEFF),&UCB1BR1
bic.b #UCSWRST,&UCB1CTL1
clr.b &UCB1IFG
bis.b #UCNACKIE+UCALIE+UCTXIE,&UCB1IE
clr.b &i2c_last_error
mov.b #I2C_IDLE,&i2c_state
mov #DMA0TSEL_22,&DMACTL0 // 22 тригер на DMA 0
mov #DMARMWDIS,&DMACTL4
mov #DMADT_0+DMADSTINCR_3+DMASRCINCR_0+DMADSTBYTE+DMASRCBYTE+DMAIE,&DMA0CTL // режим сингл трансфер
movx.a #UCB1RXBUF,&DMA0SA // исходный адресс это UCB1RXBUF
movx.a #i2c_buffer,&DMA0DA // адресс назначения - буффер
reta
/********************************************************************************
****/
// функция обработки транзакции на i2c ; extern signed char I2C_StartTrans(char addr,char count,char state);
/********************************************************************************
*****/
;in R12- адресс устройства на i2c шине
;in R13 - число байт для транзакции
;in R14- состояние (нужен рестарт после транзакции или нет)
;out R12 - выход , код ошибки
I2C_StartTrans:
mov.b #S_OK,&i2c_last_error
mov.b R13,&i2c_length
mov #i2c_buffer,&i2c_ptr
clr.b &UCB1IFG
bit.b #BIT0,R12 // проверяем младший бит в адрессе если 0 - то осуществляем запись , если 1 то чтение
jz __i2cstart0
/********************************************************************/
// receive
/*********************************************************************/
mov.b #I2C_RECEIVE,&i2c_state
bic.b #UCTR,&UCB1CTL1
mov R13,&DMA0SZ // запихиваем количество байт для чтения в DMA0SZ
bis #DMAEN,&DMA0CTL // разрешаем DMA
jmp __i2cstart03
/********************************************************************/
// transmit
/******************************************************************/
__i2cstart0:
mov.b #I2C_TRANSMIT,&i2c_state
bis.b #UCTR,&UCB1CTL1
__i2cstart03:
bis.b R14,&i2c_state
rrc R12
and.b #0x7F,R12
mov R12,&UCB1I2CSA // UCB1I2CSA = R12 >> 1
bis.b #UCTXSTT,&UCB1CTL1 // начинаем транзакцию - посылаем старт
__i2cstart02:
_WDR_a // сброс ватчдога
cmp.b #I2C_IDLE,&i2c_state // ждем пока i2c_state == I2C_IDLE
jne __i2cstart02
__uuip:
bit.b #UCTXNACK+UCTXSTP+UCTXSTT,&UCB1CTL1
jnz __uuip
mov.b &i2c_last_error,R12
reta
/*****************************************************************/
// возвращает указатель на буффер i2c ; extern char* Get_I2C_ptr(void);
/*****************************************************************/
; R12 - указатель на буффер i2c
Get_I2C_ptr:
cmp.b #I2C_IDLE,&i2c_state
jne Get_I2C_ptr
mov #i2c_buffer,R12
reta
/*****************************************************************/
// обработчик прерывания i2c
/***************************************************************/
I2C_INT:
add &UCB1IV,PC
reti
jmp ALIFG_ISR ; artbitration lost
jmp NACKIFF_ISR ; nack return
jmp STTIFG_ISR ; start received
jmp STPIFG_ISR ; stop received
jmp RXIFG_ISR ; data received
TXIFG_ISR: ; transmit buffer empty
tst.b &i2c_length // пока i2c_length != 0 то шлем байты из буффера
jnz __i2ctxifg0
bic.b #UCTXIFG,&UCB1IFG // если i2c_length == 0 то i2c_state = I2C_IDLE и проверяем нужен ли рестарт, если не нужен просто выходим из
// интерапта
bit.b #I2C_ENABLE_RESTART,&i2c_state
mov.b #I2C_IDLE,&i2c_state
jnz __i2ctxifg_ex
bis.b #UCTXSTP,&UCB1CTL1
reti
__i2ctxifg0:
push.w r12
mov &i2c_ptr,r12
mov.b @R12+,&UCB1TXBUF
mov R12,&i2c_ptr
dec.b &i2c_length
pop.w r12
__i2ctxifg_ex:
reti
// если арбитраж потерян (не нужно в синглмастере)
ALIFG_ISR:
mov.b #I2C_ARBITRATION_LOST,&i2c_last_error
jmp __inti2c0
// если принят NACK возвращаем код ошибки
NACKIFF_ISR:
mov.b #I2C_NACK_RETURN,&i2c_last_error
__inti2c0:
clr.b &UCB1IFG
mov.b #I2C_IDLE,&i2c_state
bis.b #UCTXSTP,&UCB1CTL1
reti
STTIFG_ISR:
STPIFG_ISR:
reti
RXIFG_ISR: // не используем так как есть DMA
reti
COMMON INTVEC
ORG USCI_B1_VECTOR
DW I2C_INT
END
---------------------- dma.asm--------------------------------------------------------------------
EXTERN i2c_state
EXTERN I2C_Init
RSEG CODE
/***************************************************************************/
// обработчик прерывания от DMA вызывается когда прочитано нужно число байт
/***************************************************************************/
DMA_HANDLER:
add &DMAIV,PC
reti
jmp DMA0_HND
jmp DMA1_HND
jmp DMA2_HND
reti
reti
reti
reti
reti
DMA0_HND:
bic.b #UCRXIFG,&UCB1IFG // UCB1IFG &= ~UCRXIFG
mov.b #I2C_IDLE,&i2c_state // i2c_state = I2C_IDLE
bis.b #UCTXNACK+UCTXSTP,&UCB1CTL1 // формируем NACK и стоп
__uuuu:
bit.b #UCBBUSY,&UCB1STAT // ждем освобождения i2c
jnz __uuuu
// calla #I2C_Init // !!!!! костыль , если его воткнуть то прием будет работать иначе будет работать только один раз
DMA1_HND:
DMA2_HND:
reti
COMMON INTVEC
ORG DMA_VECTOR
DW DMA_HANDLER
END
----------------------------------------------------------eeprom.c-----------------------------------------------------------------------------------
extern signed char I2C_StartTrans(uchar addr, uchar count, char state);
extern char* Get_I2C_ptr(void);
// addr - адресс считывания
// ptr - указатель куда скопировать прочитанное
// count - количество байт нужное считать
char eeprom_read_bytes(ushort addr,uchar* ptr, uchar count)
{
char hr;
uchar* ptr2;
if(count > EEPROM_PAGE_SIZE) // проверяем количество байт - должно быть меньше чем буффер i2c
return EEPROM_IVALID_SIZE;
ptr2 = Get_I2C_ptr(); // получаем указатель на буффер i2c
*ptr2 = (BYTE)(addr >> 8); // запихиваем в буффер адресс (16 бит)
*(ptr2+1) = (BYTE)(addr&0x00FF);
hr = I2C_StartTrans(EERPOM_I2C_ADDR&WRITE_OP,2,RESTART_AFTER_TRANSACTION); // отправляем в eeprom адресс и
//генерируем рестарт
hr = I2C_StartTrans(EERPOM_I2C_ADDR|READ_OP,count,STOP_AFTER_TRANSACTION); // считываем в буффер count байт из
//eeprom и генерирует стоп
memcpy_m(ptr,ptr2,count) ; // копируем из буффера i2c в ptr
return hr; // возвращаем код ошибки
}
Уже 2 неделю никак не могу толком настроить i2c. Хотя софтварная эмуляция функций (i2c на портах) I2C_StartTrans и I2c_init работает без проблем. ТАкже работает без проблем если после каждого чтения переинициализировать i2c.