Решил, что надо себя развлечь и вернулся к ковырянию кода.
Всё готово к тому, чтобы перейти на обработку прерываниями и затем добавить синхронизацию через РТОС.
Итак, как выглядит передача через порт I2C1 (жёстко прибит гвоздями, т.к. именно он используется в схеме Awesome Board, но при желании это всё можно "параметризировать").
- перед началом работы модуля I2C его вообще желательно встряхивать в начальное состояние (параноидальный метод!):
I2C1->CR1 &= ~(1 << I2C_CR1_PE_Pos); I2C1->CR1 |= (1 << I2C_CR1_PE_Pos);
- модуль DMA должен быть проинициализирован перед тем, как мы начнём передачу (хоть это на самом деле и не строго-обязательно, но так всё-таки разумнее):
DMA1_Stream6->CR = 0; DMA1_Stream6->CR = (0x01 << DMA_SxCR_CHSEL_Pos) | // канал (0x01 << DMA_SxCR_PL_Pos) | // приоритет (0x00 << DMA_SxCR_MSIZE_Pos) | // грануляция памяти 8 бит (0x00 << DMA_SxCR_PSIZE_Pos) | // грануляция периферии 8 бит (1 * DMA_SxCR_MINC) | // инкремент памяти 1 байт (0 * DMA_SxCR_PINC) | // инкремент периферии отсутствует (0x01 << DMA_SxCR_DIR_Pos ); // направление ПАМЯТЬ->ПЕРИФЕРИЯ DMA1_Stream6->M0AR = (uint32_t)txbuf; /* адрес в памяти */ DMA1_Stream6->PAR = (uint32_t)&I2C1->TXDR; /* адрес периферии */ DMA1_Stream6->NDTR = txcount; // сколько порций данных передать __DMB(); SCB_CleanDCache_by_Addr ((uint32_t*)txbuf, txcount); /* когерентность кэша */
- прописываем адрес подчинённого устройства и параметры передачи; врубаем передачу
I2C1->CR2 = (0 << I2C_CR2_ADD10_Pos) | /* адресация 7 бит */ ((addr & 0xFE) << I2C_CR2_SADD_Pos) | /* адрес устройства (внимание!) */ (0 << I2C_CR2_RD_WRN_Pos) | /* направление передачи 0 (ЗАПИСЬ) */ (txcount << I2C_CR2_NBYTES_Pos) | /* кол-во байт */ (0 << I2C_CR2_AUTOEND_Pos); // управление состоянием STOP/RESTART: 0 программное (см.RM), 1 автоматическая выдача STOP I2C1->CR1 |= I2C_CR1_TXDMAEN; // разрешаем работу с DMA TX I2C1->CR2 |= I2C_CR2_START; /* запускаем передачу состояния START и адреса */
- а вот тут можно уже сделать обработку по прерыванию, но пока что сделано тупо ожидание:
while (!(I2C1->ISR & (I2C_ISR_TXIS | I2C_ISR_NACKF))) {}; /* ждём либо флаг TXIS, либо получение NACK */
- ..за которым следует развилка и ещё одно ожидание, вопиющее "вынеси меня в прерывание!!!":
if ((I2C1->ISR & I2C_ISR_NACKF)) { /* нам никто не ответил? */ I2C1->ICR |= I2C_ICR_NACKCF; /* чистим флаг */ //DMA1_Stream6->CR &= ~DMA_SxCR_EN; I2C1->CR1 &= ~(1 << I2C_CR1_PE_Pos); /* выключаем I2C */ return -1; /* выходим с ошибкой */ }; DMA1_Stream6->CR |= DMA_SxCR_EN; /* НЕ сбрасывая флаг TXIS, включаем DMA */ while (!(DMA1->HISR & DMA_HISR_TCIF6)) {}; /* и дожидаемся окончания передачи */ DMA1->HIFCR |= DMA_HIFCR_CTCIF6; /* чистим флаг TCI */ I2C1->CR1 &= ~I2C_CR1_TXDMAEN; /* отключаем событие DMA TX * p.966; p.218 */ DMA1_Stream6->CR &= ~DMA_SxCR_EN; /* глушим поток DMA */
Если после передачи в I2C устройство не следует чтение, то надо сгенерить состояние STOP, дождаться высвобождения шины и выключить модуль.