Логика работы такая:
1. Мастер открывает соединение со слейвом на запись, и посылает пакетом, например, пять байт.
2. Слейв их получает. Вопрос - куда их слейв пишет? Да куда угодно. Например, первый байт - это адрес слова, потом четыре байта пишутся в адреса: (адрес_слова), (адрес_слова+1)...(адрес_слова+3). Или, первые байт - адрес слова, потом значение, потом адрес другого слова, потом значение и т.д. Вся логика того, как работать с принятым пакетом - исключительно на стороне слейва отрабатывает! Но для того, чтобы правильно записать соответствующие байты в слейва, мастер должен знать логику работы слейва и правильно формировать посылку.
1б. Теперь мастер открывает соединение на чтение. Слейв будет отсылать байты мастеру, пока тот не скажет, что ему достаточно. Вопрос - какие байты будет отсылать слейв? Опять же, вся логика на стороне слейва, мастер только должен знать, что именно отсылает слейв.
Простейший случай - у нас есть контрольный регистр у слейва из трех байт. Соответственно надо реализовать две функции - записи и чтения контрольного регистра.
Первая имеет вид: [s][addr/w][byte1][byte2][byte3][p]. При получении такого запроса слейв пишет первый байт в control_reg[1], второй в control_reg[2] и т.д.
На чтение: [s][addr/r][byte1/ack][byte2/ack][byte3/nack][p]. При получении такого запроса слейв начинает пихать в канал байты control_reg[pointer_to_word++], пока не получит nack от мастера, после этого указатель на байт ресеттится.
Я рекомендую такой алгоритм отладки:
1. Пишем 16 байт в слейва. Слейв каждый полученный байт кидает в отладочную консоль. Сравниваем. убеждаемся, что на стороне слейва все правильно приняли. Можно еще отдельно потом вывести весь массив байт, чтобы убедиться, что все, что мы приняли, мы правильно сохранили в массив.
2. При запросе на чтение отдаем один и тот же байт, какой-нить красивый, типа AE, или 6B. Убеждаемся, что мастер получил 16-ть байт AE.
3. При запросе на чтение отдаем байт byte++, убеждаемся, что мастер получил массив увеличивающихся байт, типа {0, 1, 3, 4, ..., F}
4. При запросе на чтение отдаем значения байт, расположенных в памяти по увеличивающемуся указателю - проверям...
"как на еепроме" стоит реализовывать логику только в случае, если нужно писать/читать индивидуальные байты.
Вот на всякий случай мой код от ПИКа, в нем можно разобраться (первый байт при записи - адрес слова, остальное - значения):
- Код: Выделить всё • Развернуть
if (SSP1IF && SSP1IE)
{
unsigned char SSPSTAT_TEMP = SSP1STAT & 0b00101101;
unsigned char tempBuf;
if (SSPSTAT_TEMP == 0b00001001)
//STATE 1: MASTER WRITE, LAST BYTE WAS AN ADDRESS
{
tempBuf = SSPBUF;
i2c_set_register = 1;
}
else if (SSPSTAT_TEMP == 0b00101001)
//STATE 2: MASTER WRITE, LAST BYTE WAS DATA
{
if (i2c_set_register)
{
do i2c_register = SSPBUF; while (SSP1STATbits.BF);
if (i2c_register >= sizeof(CONTROL_REG))
i2c_register = 0x00;
i2c_set_register = 0;
} else
{
CONTROL_REG[i2c_register++] = SSPBUF;
}
}
else if (SSPSTAT_TEMP == 0b00001101)
//STATE 3: MASTER READ, LAST BYTE WAS AN ADDRESS
{
SSP1IF = 0;
tempBuf = SSPBUF;
while (SSP1STATbits.BF) continue;
do {
SSP1CON1bits.WCOL = 0;
SSPBUF = CONTROL_REG[i2c_register++];
} while (SSP1CON1bits.WCOL);
SSP1CON1bits.CKP = 1;
}
else if (SSPSTAT_TEMP == 0b00101100)
//STATE 4: MASTER READ, LAST BYTE WAS DATA
{
while (SSP1STATbits.BF) continue;
do {
SSP1CON1bits.WCOL = 0;
SSPBUF = CONTROL_REG[i2c_register++];
} while (SSP1CON1bits.WCOL);
SSP1CON1bits.CKP = 1;
}
else if ((SSPSTAT_TEMP == 00101000) && SSP1CON1bits.CKP)
//STATE 5: MASTER NACK
{
//SSP1CON1bits.CKP = 1;
i2c_register = 0x00;
}
else
{
//i2c error
SSP1CON1bits.SSPEN = 0;
SSP1CON1bits.SSPEN = 1;
}
SSP1IF = 0;
SSP1CON1bits.CKP = 1;
}