/** ****************************************************************************** * @file i2c.c * @author Разваляев Алексей * @brief Драйвер I2C на основе PLIB035. * Этот файл содержит: * + Инициализацию I2C * + Функции для работы в режиме мастера (блокирующие): * - Блокирующая передача/прием * - Функции чтения/записи регистров устройств * - Проверка наличия устройства * ****************************************************************************** * @attention * * Использование этого драйвера предполагает наличие корректных настроек: * - Определена конфигурационная структура i2c_config в periph_config.h * - Определены пины SDA/SCL для I2C * ****************************************************************************** * @verbatim ============================================================================== ##### Как использовать этот драйвер ##### ============================================================================== 1. Настройка в periph_config.h: (+) Определите i2c_config для I2C (+) Настройте частоту SCL (FSFreq для стандартного режима, HSFreq для высокоскоростного) (+) Используйте enum для настройки: • I2C_Mode_Master - режим мастера • I2C_Mode_Slave - режим ведомого 2. Инициализация: (+) i2c_init_first() - настройка GPIO, тактирования (+) i2c_init(&hi2c, &config) - инициализация I2C 3. Запуск I2C: (+) I2C_Start(&hi2c) - включение I2C 4. Работа в режиме мастера (блокирующие функции): - Основные функции: (+) I2C_Master_Transmit(&hi2c, addr, buf, size, timeout) - передача данных (+) I2C_Master_Receive(&hi2c, addr, buf, size, timeout) - прием данных (+) I2C_Master_WriteReg(&hi2c, addr, reg, val, timeout) - запись регистра (+) I2C_Master_ReadReg(&hi2c, addr, reg, buf, size, timeout) - чтение регистра (+) I2C_Master_WriteRegs(&hi2c, addr, reg, buf, size, timeout) - запись нескольких регистров (+) I2C_Master_ReadRegs(&hi2c, addr, reg, buf, size, timeout) - чтение нескольких регистров (+) I2C_Master_IsDeviceReady(&hi2c, addr, timeout) - проверка наличия устройства 5. Обработка ошибок: (+) I2C_GetLastError(&hi2c) - получение кода последней ошибки (+) Callback-функция ошибки вызывается автоматически при ошибках 6. GPIO для I2C: (+) Пины настраиваются автоматически при вызове i2c_init_first() (+) При необходимости можно вызвать i2c_gpio_deinit() для восстановления ============================================================================== ##### Особенности работы ##### ============================================================================== - Режимы работы: - Master: инициирует передачу данных, управляет шиной - Slave: реагирует на запросы мастера - Скорости работы: - Standard Mode (до 100 кГц) - Fast Mode (до 400 кГц) - Fast Mode Plus (до 1 МГц) - High Speed Mode (до 3.4 МГц) - Адресация: - 7-битная адресация (стандартная) - 10-битная адресация (поддерживается) - Обработка ошибок: - Обрабатываются ошибки шины, арбитража, таймаута - При ошибке вызывается ErrCallback и сохраняется код ошибки - После ошибки требуется переинициализация I2C - Функции работы с регистрами: - Упрощают чтение/запись регистров устройств на шине I2C - Поддерживают различные размеры регистров (8/16 бит) - Проверка устройства: - Функция проверяет ответ устройства на свой адрес - Полезна для проверки подключения устройств @endverbatim ****************************************************************************** */ //-- Includes ------------------------------------------------------------------ #include "periph_config.h" I2C_HandleTypeDef hi2c; /*!< Хендл I2C */ //-- Private function prototypes ----------------------------------------------- static OperationStatus __i2c_master_wait_bus_free(I2C_HandleTypeDef *hi2c, uint32_t timeout); static OperationStatus __i2c_master_send_start(I2C_HandleTypeDef *hi2c, uint32_t timeout); static OperationStatus __i2c_master_send_address(I2C_HandleTypeDef *hi2c, uint16_t addr, uint8_t direction, uint32_t timeout); static OperationStatus __i2c_master_send_stop(I2C_HandleTypeDef *hi2c, uint32_t timeout); static OperationStatus __i2c_master_transmit_blocking(I2C_HandleTypeDef *hi2c, uint8_t *buf, uint16_t size, uint32_t timeout); static OperationStatus __i2c_master_receive_blocking(I2C_HandleTypeDef *hi2c, uint8_t *buf, uint16_t size, uint32_t timeout); static void __i2c_set_error(I2C_HandleTypeDef *hi2c, uint8_t error); //-- I2C Init functions ------------------------------------------------------- /** * @brief Первичная инициализация I2C * @details Настройка I2C и хендла: GPIO, тактирование */ void i2c_init_first(void) { #if (USE_I2C==1) // Настройка пинов для I2C i2c_gpio_init(); // Включение тактирования и сброс I2C RCU_APBClkCmd(RCU_APBClk_I2C, ENABLE); RCU_APBRstCmd(RCU_APBRst_I2C, ENABLE); // Инициализируем I2C hi2c.Instance = I2C; hi2c.LastError = I2C_ERROR_NONE; i2c_init(&hi2c, &i2c_config); #endif } /** * @brief Инициализация I2C * @param hi2c указатель на хендл I2C * @param NewConfig указатель на новую конфигурацию I2C, иначе используется та, что в структуре * @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка */ OperationStatus i2c_init(I2C_HandleTypeDef *hi2c, I2C_ExtInit_TypeDef *NewConfig) { if(!hi2c || !hi2c->Instance) return ERROR; if(NewConfig != NULL) { hi2c->Config = NewConfig; } if(hi2c->Config == NULL) { return ERROR; } // Сбрасываем ошибку hi2c->LastError = I2C_ERROR_NONE; // Настраиваем частоту SCL if(hi2c->Config->I2C_Init.HSMode == ENABLE) { // Высокоскоростной режим I2C_HSFreqConfig(hi2c->Config->HSFreq, hi2c->Config->I2CFreq); } else { // Стандартный/быстрый режим I2C_FSFreqConfig(hi2c->Config->FSFreq, hi2c->Config->I2CFreq); } // Настраиваем таймаут if(hi2c->Config->I2C_Init.Timeout != DISABLE) { I2C_TimeoutClkDivConfig(hi2c->Config->I2C_Init.TimeoutClkDiv); I2C_SetTimeoutCounterLoad(hi2c->Config->I2C_Init.TimeoutLoad); } // Настраиваем дополнительные функции I2C_AlertResponseMatchCmd(hi2c->Config->I2C_Init.AlertResponse); I2C_GlobalCallMatchCmd(hi2c->Config->I2C_Init.GlobalCall); return OK; } /** * @brief Установка коллбека I2C * @param hi2c указатель на хендл I2C * @param CallbackType Тип коллбека * @param Callback Функция коллбека * @retval OperationStatus OK - если успешно, ERROR - при ошибке */ OperationStatus I2C_Set_Callback(I2C_HandleTypeDef* hi2c, I2C_CallbackTypeDef CallbackType, void (*Callback)()) { if (!hi2c || !hi2c->Instance || !hi2c->Config) return ERROR; switch(CallbackType) { case I2C_Callback_Tx: hi2c->Config->TxCallback = Callback; break; case I2C_Callback_Rx: hi2c->Config->RxCallback = Callback; break; case I2C_Callback_Addr: hi2c->Config->AddrCallback = Callback; break; case I2C_Callback_Error: hi2c->Config->ErrCallback = Callback; break; default: return ERROR; } return OK; } //-- I2C API functions -------------------------------------------------------- /** * @brief Запуск I2C * @param hi2c указатель на хендл I2C * @retval OperationStatus OK - если успешно, ERROR - при ошибке */ OperationStatus I2C_Start(I2C_HandleTypeDef *hi2c) { if (!hi2c) return ERROR; // Сбрасываем ошибку hi2c->LastError = I2C_ERROR_NONE; // Включаем I2C I2C_Cmd(ENABLE); // Если режим ведомого, настраиваем адрес и включаем распознавание if(hi2c->Config->I2C_Init.Mode == I2C_Mode_Slave) { if(hi2c->Config->I2C_Init.Addr10Bit == ENABLE) { I2C_Slave10AddrCmd(ENABLE); I2C_SetSlave10Addr(hi2c->Config->I2C_Init.SlaveAddr >> 8); I2C_SetSlaveAddr(hi2c->Config->I2C_Init.SlaveAddr & 0xFF); } else { I2C_Slave10AddrCmd(DISABLE); I2C_SetSlaveAddr(hi2c->Config->I2C_Init.SlaveAddr); } I2C_SlaveCmd(ENABLE); } return OK; } /** * @brief Остановка I2C * @param hi2c указатель на хендл I2C * @retval OperationStatus OK - если успешно, ERROR - при ошибке */ OperationStatus I2C_Stop(I2C_HandleTypeDef *hi2c) { if (!hi2c) return ERROR; // Выключаем I2C I2C_Cmd(DISABLE); // Если режим ведомого, выключаем распознавание адреса if(hi2c->Config->I2C_Init.Mode == I2C_Mode_Slave) { I2C_SlaveCmd(DISABLE); } return OK; } /** * @brief Получение кода последней ошибки * @param hi2c указатель на хендл I2C * @retval Код последней ошибки I2C */ uint8_t I2C_GetLastError(I2C_HandleTypeDef *hi2c) { if (!hi2c) return I2C_ERROR_NONE; return hi2c->LastError; } //-- I2C Master functions ----------------------------------------------------- /** * @brief Передача данных в режиме мастера (блокирующий режим) * @param hi2c указатель на хендл I2C * @param addr адрес устройства на шине I2C * @param buf указатель на буфер данных * @param size размер данных в байтах * @param timeout таймаут ожидания (мс) * @retval OperationStatus OK - если успешно, ERROR - при ошибке или таймауте */ OperationStatus I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t addr, uint8_t *buf, uint16_t size, uint32_t timeout) { OperationStatus status; if (!hi2c || !buf || size == 0) { __i2c_set_error(hi2c, I2C_ERROR_NONE); return ERROR; } // Проверяем, свободна ли шина status = __i2c_master_wait_bus_free(hi2c, timeout); if (status != OK) return status; // Отправляем START status = __i2c_master_send_start(hi2c, timeout); if (status != OK) return status; // Отправляем адрес с направлением "запись" status = __i2c_master_send_address(hi2c, addr, I2C_Direction_Transmitter, timeout); if (status != OK) { // При NACK отправляем STOP и выходим if (hi2c->LastError == I2C_ERROR_NACK) { __i2c_master_send_stop(hi2c, timeout); } return status; } // Передаем данные status = __i2c_master_transmit_blocking(hi2c, buf, size, timeout); // Отправляем STOP (даже при ошибке в данных) __i2c_master_send_stop(hi2c, timeout); // Если была ошибка передачи данных, возвращаем ошибку if (status != OK) return status; // Вызываем callback успешной передачи if (hi2c->Config->TxCallback) hi2c->Config->TxCallback(); return OK; } /** * @brief Прием данных в режиме мастера (блокирующий режим) * @param hi2c указатель на хендл I2C * @param addr адрес устройства на шине I2C * @param buf указатель на буфер приема * @param size количество принимаемых байт * @param timeout таймаут ожидания (мс) * @retval OperationStatus OK - если успешно, ERROR - при ошибке или таймауте */ OperationStatus I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t addr, uint8_t *buf, uint16_t size, uint32_t timeout) { OperationStatus status; if (!hi2c || !buf || size == 0) { __i2c_set_error(hi2c, I2C_ERROR_NONE); return ERROR; } // Проверяем, свободна ли шина status = __i2c_master_wait_bus_free(hi2c, timeout); if (status != OK) return status; // Отправляем START status = __i2c_master_send_start(hi2c, timeout); if (status != OK) return status; // Отправляем адрес с направлением "чтение" status = __i2c_master_send_address(hi2c, addr, I2C_Direction_Receiver, timeout); if (status != OK) { // При NACK отправляем STOP и выходим if (hi2c->LastError == I2C_ERROR_NACK) { __i2c_master_send_stop(hi2c, timeout); } return status; } // Принимаем данные status = __i2c_master_receive_blocking(hi2c, buf, size, timeout); // Отправляем STOP (даже при ошибке приема) __i2c_master_send_stop(hi2c, timeout); // Если была ошибка приема данных, возвращаем ошибку if (status != OK) return status; // Вызываем callback успешного приема if (hi2c->Config->RxCallback) hi2c->Config->RxCallback(); return OK; } /** * @brief Запись регистра устройства (блокирующий режим) * @param hi2c указатель на хендл I2C * @param addr адрес устройства на шине I2C * @param reg адрес регистра * @param value значение для записи * @param timeout таймаут ожидания (мс) * @retval OperationStatus OK - если успешно, ERROR - при ошибке или таймауте */ OperationStatus I2C_Master_WriteReg(I2C_HandleTypeDef *hi2c, uint16_t addr, uint16_t reg, uint8_t value, uint32_t timeout) { uint8_t data[3]; uint16_t reg_size = (hi2c->Config->I2C_Init.RegSize == I2C_RegSize_8bit) ? 1 : 2; if (reg_size == 1) { data[0] = (uint8_t)reg; data[1] = value; return I2C_Master_Transmit(hi2c, addr, data, 2, timeout); } else { data[0] = (uint8_t)(reg >> 8); data[1] = (uint8_t)reg; data[2] = value; return I2C_Master_Transmit(hi2c, addr, data, 3, timeout); } } /** * @brief Чтение регистра устройства (блокирующий режим) * @param hi2c указатель на хендл I2C * @param addr адрес устройства на шине I2C * @param reg адрес регистра * @param buf указатель на буфер для данных * @param size размер данных для чтения * @param timeout таймаут ожидания (мс) * @retval OperationStatus OK - если успешно, ERROR - при ошибке или таймауте */ OperationStatus I2C_Master_ReadReg(I2C_HandleTypeDef *hi2c, uint16_t addr, uint16_t reg, uint8_t *buf, uint16_t size, uint32_t timeout) { OperationStatus status; uint8_t reg_data[2]; uint16_t reg_size = (hi2c->Config->I2C_Init.RegSize == I2C_RegSize_8bit) ? 1 : 2; // Сначала отправляем адрес регистра if (reg_size == 1) { reg_data[0] = (uint8_t)reg; status = I2C_Master_Transmit(hi2c, addr, reg_data, 1, timeout); } else { reg_data[0] = (uint8_t)(reg >> 8); reg_data[1] = (uint8_t)reg; status = I2C_Master_Transmit(hi2c, addr, reg_data, 2, timeout); } if (status != OK) return status; // Затем читаем данные return I2C_Master_Receive(hi2c, addr, buf, size, timeout); } /** * @brief Запись нескольких регистров (блокирующий режим) * @param hi2c указатель на хендл I2C * @param addr адрес устройства на шине I2C * @param reg начальный адрес регистра * @param buf указатель на буфер данных * @param size количество регистров для записи * @param timeout таймаут ожидания (мс) * @retval OperationStatus OK - если успешно, ERROR - при ошибке или таймауте */ OperationStatus I2C_Master_WriteRegs(I2C_HandleTypeDef *hi2c, uint16_t addr, uint16_t reg, uint8_t *buf, uint16_t size, uint32_t timeout) { OperationStatus status; uint16_t reg_size = (hi2c->Config->I2C_Init.RegSize == I2C_RegSize_8bit) ? 1 : 2; // Проверяем, свободна ли шина status = __i2c_master_wait_bus_free(hi2c, timeout); if (status != OK) return status; // Отправляем START status = __i2c_master_send_start(hi2c, timeout); if (status != OK) return status; // Отправляем адрес с направлением "запись" status = __i2c_master_send_address(hi2c, addr, I2C_Direction_Transmitter, timeout); if (status != OK) { // При NACK отправляем STOP и выходим if (hi2c->LastError == I2C_ERROR_NACK) { __i2c_master_send_stop(hi2c, timeout); } return status; } // Отправляем адрес регистра (первый байт) if (reg_size == 1) { status = __i2c_master_transmit_blocking(hi2c, (uint8_t*)®, 1, timeout); } else { // Для 16-битного адреса отправляем старший, затем младший байт uint8_t reg_bytes[2] = {(uint8_t)(reg >> 8), (uint8_t)reg}; status = __i2c_master_transmit_blocking(hi2c, reg_bytes, 2, timeout); } if (status != OK) { __i2c_master_send_stop(hi2c, timeout); return status; } // Отправляем данные status = __i2c_master_transmit_blocking(hi2c, buf, size, timeout); // Отправляем STOP __i2c_master_send_stop(hi2c, timeout); // Если была ошибка передачи данных, возвращаем ошибку if (status != OK) return status; // Вызываем callback успешной передачи if (hi2c->Config->TxCallback) hi2c->Config->TxCallback(); return OK; } /** * @brief Чтение нескольких регистров (блокирующий режим) * @param hi2c указатель на хендл I2C * @param addr адрес устройства на шине I2C * @param reg начальный адрес регистра * @param buf указатель на буфер для данных * @param size количество регистров для чтения * @param timeout таймаут ожидания (мс) * @retval OperationStatus OK - если успешно, ERROR - при ошибке или таймауте */ OperationStatus I2C_Master_ReadRegs(I2C_HandleTypeDef *hi2c, uint16_t addr, uint16_t reg, uint8_t *buf, uint16_t size, uint32_t timeout) { // Для чтения нескольких регистров используется та же функция, что и для одного // т.к. после отправки адреса регистра можно читать любое количество байт return I2C_Master_ReadReg(hi2c, addr, reg, buf, size, timeout); } /** * @brief Проверка наличия устройства на шине * @param hi2c указатель на хендл I2C * @param addr адрес устройства на шине I2C * @param timeout таймаут ожидания (мс) * @retval OperationStatus OK - если устройство отвечает, ERROR - если нет ответа */ OperationStatus I2C_Master_IsDeviceReady(I2C_HandleTypeDef *hi2c, uint16_t addr, uint32_t timeout) { OperationStatus status; if (!hi2c) return ERROR; // Проверяем, свободна ли шина status = __i2c_master_wait_bus_free(hi2c, timeout); if (status != OK) return status; // Отправляем START status = __i2c_master_send_start(hi2c, timeout); if (status != OK) return status; // Отправляем адрес с направлением "запись" (любое направление подойдет для проверки) status = __i2c_master_send_address(hi2c, addr, I2C_Direction_Transmitter, timeout); // Отправляем STOP независимо от результата __i2c_master_send_stop(hi2c, timeout); // Если устройство ответило ACK, значит оно готово if (status == OK && hi2c->LastError == I2C_ERROR_NONE) return OK; return ERROR; } //-- I2C GPIO functions ------------------------------------------------------- /** * @brief Инициализация GPIO для I2C */ void i2c_gpio_init(void) { #if USE_I2C==1 // Получаем структуры GPIO_Init_TypeDef *sda_config = gpio_get_init(I2C_GPIO_Port, I2C_SDA_Pin); GPIO_Init_TypeDef *scl_config = gpio_get_init(I2C_GPIO_Port, I2C_SCL_Pin); // SDA пин if (sda_config != NULL) { GPIO_StructInit(sda_config); sda_config->Pin = I2C_SDA_Pin; sda_config->AltFunc = ENABLE; sda_config->Digital = ENABLE; sda_config->OutMode = GPIO_OutMode_OD; // Открытый сток для I2C sda_config->PullMode = GPIO_PullMode_PU; // Подтяжка к питанию для I2C GPIO_Init(I2C_GPIO_Port, sda_config); } // SCL пин if (scl_config != NULL) { GPIO_StructInit(scl_config); scl_config->Pin = I2C_SCL_Pin; scl_config->AltFunc = ENABLE; scl_config->Digital = ENABLE; scl_config->OutMode = GPIO_OutMode_OD; // Открытый сток для I2C scl_config->PullMode = GPIO_PullMode_PU; // Подтяжка к питанию для I2C GPIO_Init(I2C_GPIO_Port, scl_config); } #endif } /** * @brief Деинициализация GPIO для I2C */ void i2c_gpio_deinit(void) { #if USE_I2C==1 // Получаем структуры GPIO_Init_TypeDef *sda_config = gpio_get_init(I2C_GPIO_Port, I2C_SDA_Pin); GPIO_Init_TypeDef *scl_config = gpio_get_init(I2C_GPIO_Port, I2C_SCL_Pin); // Восстанавливаем оригинальные настройки из таблицы if (sda_config != NULL) { GPIO_StructInit(sda_config); sda_config->Pin = I2C_SDA_Pin; GPIO_Init(I2C_GPIO_Port, sda_config); } if (scl_config != NULL) { GPIO_StructInit(scl_config); scl_config->Pin = I2C_SCL_Pin; GPIO_Init(I2C_GPIO_Port, scl_config); } #endif } //-- I2C private functions ---------------------------------------------------- /** * @brief Установка ошибки I2C */ static void __i2c_set_error(I2C_HandleTypeDef *hi2c, uint8_t error) { if (!hi2c) return; hi2c->LastError = error; // Вызываем callback ошибки, если он установлен if (hi2c->Config->ErrCallback) hi2c->Config->ErrCallback(error); } /** * @brief Ожидание освобождения шины I2C */ static OperationStatus __i2c_master_wait_bus_free(I2C_HandleTypeDef *hi2c, uint32_t timeout) { uint32_t starttick = millis(); while(I2C_BusBusyStatus() == SET) { if(millis() - starttick > timeout) { __i2c_set_error(hi2c, I2C_ERROR_BUS_BUSY); return ERROR; } } return OK; } /** * @brief Отправка условия START */ static OperationStatus __i2c_master_send_start(I2C_HandleTypeDef *hi2c, uint32_t timeout) { uint32_t starttick = millis(); I2C_State_TypeDef state; // Формируем START I2C_StartCmd(); // Ожидаем подтверждения формирования START while(1) { state = I2C_GetState(); if (state == I2C_State_STDONE || state == I2C_State_HMTMCOK) { // START успешно сформирован return OK; } else if (state == I2C_State_BERROR) { __i2c_set_error(hi2c, I2C_ERROR_BUS_ERROR); return ERROR; } else if (state == I2C_State_IDLARL || state == I2C_State_HIDLARL) { __i2c_set_error(hi2c, I2C_ERROR_ARBITRATION_LOST); return ERROR; } if(millis() - starttick > timeout) { __i2c_set_error(hi2c, I2C_ERROR_TIMEOUT); return ERROR; } } } /** * @brief Отправка адреса устройства */ static OperationStatus __i2c_master_send_address(I2C_HandleTypeDef *hi2c, uint16_t addr, uint8_t direction, uint32_t timeout) { uint8_t address; uint32_t starttick = millis(); I2C_State_TypeDef state; // Формируем адрес if (addr > 0x7F) { // 10-битный адрес - первая часть всегда на запись address = 0xF0 | ((addr >> 7) & 0x06); I2C_SetData(address | I2C_Direction_Transmitter); } else { // 7-битный адрес address = (addr << 1); I2C_SetData(address | direction); } // Сбрасываем флаг прерывания I2C_ITStatusClear(); // Ожидаем подтверждения адреса while(1) { state = I2C_GetState(); // ACK получен для 7-битного адреса if ((direction == I2C_Direction_Transmitter && (state == I2C_State_MTADPA || state == I2C_State_HMTADPA)) || (direction == I2C_Direction_Receiver && (state == I2C_State_MRADPA || state == I2C_State_HMRADPA))) { return OK; } // NACK получен else if ((direction == I2C_Direction_Transmitter && (state == I2C_State_MTADNA || state == I2C_State_HMTADNA)) || (direction == I2C_Direction_Receiver && (state == I2C_State_MRADNA || state == I2C_State_HMRADNA))) { __i2c_set_error(hi2c, I2C_ERROR_NACK); return ERROR; } // Ошибка шины else if (state == I2C_State_BERROR) { __i2c_set_error(hi2c, I2C_ERROR_BUS_ERROR); return ERROR; } // Потеря арбитража else if (state == I2C_State_IDLARL || state == I2C_State_HIDLARL) { __i2c_set_error(hi2c, I2C_ERROR_ARBITRATION_LOST); return ERROR; } if(millis() - starttick > timeout) { __i2c_set_error(hi2c, I2C_ERROR_TIMEOUT); return ERROR; } } } /** * @brief Отправка условия STOP */ static OperationStatus __i2c_master_send_stop(I2C_HandleTypeDef *hi2c, uint32_t timeout) { uint32_t starttick = millis(); // Формируем STOP I2C_StopCmd(); // Сбрасываем флаг прерывания I2C_ITStatusClear(); // Ожидаем освобождения шины (опционально, не обязательно) // while(I2C_BusBusyStatus() == SET) // { // if(millis() - starttick > timeout) // { // __i2c_set_error(hi2c, I2C_ERROR_TIMEOUT); // return ERROR; // } // } return OK; } /** * @brief Блокирующая передача данных */ static OperationStatus __i2c_master_transmit_blocking(I2C_HandleTypeDef *hi2c, uint8_t *buf, uint16_t size, uint32_t timeout) { uint32_t starttick; I2C_State_TypeDef state; for(uint16_t i = 0; i < size; i++) { // Отправляем байт данных I2C_SetData(buf[i]); // Сбрасываем флаг прерывания I2C_ITStatusClear(); starttick = millis(); // Ожидаем подтверждения байта while(1) { state = I2C_GetState(); // ACK получен if (state == I2C_State_MTDAPA || state == I2C_State_HMTDAPA) { break; } // NACK получен else if (state == I2C_State_MTDANA || state == I2C_State_HMTDANA) { __i2c_set_error(hi2c, I2C_ERROR_NACK); return ERROR; } // Ошибка шины else if (state == I2C_State_BERROR) { __i2c_set_error(hi2c, I2C_ERROR_BUS_ERROR); return ERROR; } // Потеря арбитража else if (state == I2C_State_IDLARL || state == I2C_State_HIDLARL) { __i2c_set_error(hi2c, I2C_ERROR_ARBITRATION_LOST); return ERROR; } if(millis() - starttick > timeout) { __i2c_set_error(hi2c, I2C_ERROR_TIMEOUT); return ERROR; } } } return OK; } /** * @brief Блокирующий прием данных */ static OperationStatus __i2c_master_receive_blocking(I2C_HandleTypeDef *hi2c, uint8_t *buf, uint16_t size, uint32_t timeout) { uint32_t starttick; I2C_State_TypeDef state; for(uint16_t i = 0; i < size; i++) { starttick = millis(); // Ожидаем приема байта while(1) { state = I2C_GetState(); // Байт получен с ACK или NACK if (state == I2C_State_MRDAPA || state == I2C_State_MRDANA || state == I2C_State_HMRDAPA || state == I2C_State_HMRDANA) { // Читаем данные buf[i] = I2C_GetData(); // Для последнего байта отправляем NACK if (i == size - 1) { I2C_NACKCmd(); } break; } // Ошибка шины else if (state == I2C_State_BERROR) { __i2c_set_error(hi2c, I2C_ERROR_BUS_ERROR); return ERROR; } // Потеря арбитража else if (state == I2C_State_IDLARL || state == I2C_State_HIDLARL) { __i2c_set_error(hi2c, I2C_ERROR_ARBITRATION_LOST); return ERROR; } if(millis() - starttick > timeout) { __i2c_set_error(hi2c, I2C_ERROR_TIMEOUT); return ERROR; } } } return OK; }