Чтобы не быть голословным, см. вложения. Работает на PIC24HJx dsPIC30F60x, на остальных не проверял, должно с минимальной доработкой. P.S. Совсем забыл. Есть блокирующий код - задержки в процедуре восстановления связи. Ещё используется таймер для определения зависания шины. Остальное очевидно и мне было очень удобно. Очередь на 4 сообщения (три ведомых на шине) и в неё в произвольном порядке лились команды и данные. К примеру, шилась EEPROM в то же время по шине шли команды на смену усиления PGA и подстройку ЦАПа смещения. И это дело было равнодушно к электромагнитной обстановке. Процесс к коротким отказам шины не критичен, в EEPROM информация писалась с CRC32 и сверкой чтением после записи.
P.P.S. Чтобы лучше понять логику работы с запросом транзакции и его блоками, вот пример чтения из I
2C EEPROM. Здесь в одну транзакцию "Transaction Request" объединены два блока "Transaction Request Block" (TRB), один запись, другой - чтение. При этом сами блоки и их данные определяются в месте вызова, так как использующий шину код лучше знает, чего и сколько нужно отправить и принять. Блоки одной транзакции должны быть размещены в общем буфере последовательно. Производится чтение или запись определяется выбором функции создания блока, I2C_MasterWriteTRBBuild() или I2C_MasterReadTRBBuild(). В каждом блоке описывается количество данных и передаётся указатель на данные.
Когда цепочка блоков (в примере - два) готова, указатель на неё с количеством блоков передаётся фунции создания транзкции, I2C_MasterTRinsert(). При этом сама транзация занимает место в очереди, которая определена в коде i2c.c и размер которой зависит от потребностей всех задач шины. Отмечу, что статус транзакции обновляется для каждой транзакции отдельно, так как у каждой транзакции может быть свой статус - передаётся указателем при создании транзакции.
В примере код блокирующий, но ничто не мешает опрашивать статус транзакции в цикле машины состояний или другим образом. Учитыывая меры против зависания шиниы (таймер), код, несмотря на наличие while(), блокирует процессор лишь на ограниченное время - на заданное количество попыток.
#include "i2c.h"
static I2CTRB EEPROM_I2CTRB[2]; //Read and/or write operations to be placed in I2C queue
static volatile RequestStatus_t I2CmsgStat; //I2C message status
int eeprom_read(uint8_t *Data, uint16_t DataAddress, uint8_t Length) {
uint8_t Address[2]; //The buffer for two bytes of EEPROM data address
Address[0] = DataAddress >> 8; //Setup EEPROM data address
Address[1] = DataAddress & 0xFF; //
//Create EEPROM address setup operation, an I2C write block, first
I2C_MasterWriteTRBBuild(&EEPROM_I2CTRB[0], Address, 2, EEPROM_I2C_Addr);
//Then create data reading operation, an I2C read block
I2C_MasterReadTRBBuild(&EEPROM_I2CTRB[1], Data, Length, EEPROM_I2C_Addr);
//Now I2C transaction request is ready for queueing
unsigned Trial = I2Ctrials5ms; //Maximum possible number of writes in 5ms
//The code will try to communicate the EEPROM until the success or the
//timeout by trials number if the trials were not successfull, for example,
//if EEPROM was busy by write operation.
do { //Trial repeat loop
if (!Trial--) { //Check for EEPROM access timeout
//All tries are out without of a success. The EEPROM was not
//answered in a time more than 5ms or there was some other problems
//on I2C bus.
return (1);
}
//Put data address writing and data reading into I2C queue
SET_AND_SAVE_CPU_IPL(i2c_eeprom_old_ipl, I2Cpriority);
I2C_MasterTRinsert(2, //The number of transactions in a block
&EEPROM_I2CTRB[0], //The transaction block itself with two transcactions
&I2CmsgStat); //The status of transaction to test
RESTORE_CPU_IPL(i2c_eeprom_old_ipl); //Restore CPU priority level
//Wait for transaction block completeness
while (I2CmsgStat == I2C_PENDING); //Wait for transaction
//pending and execution
} while (I2CmsgStat != I2C_COMPLETE); //Repeat until the success or a timeout
//EEPROM data reading is complete
return (0); //Return OK
}