diff --git a/MDK-ARM/Core/App/i2c.c b/MDK-ARM/Core/App/i2c.c new file mode 100644 index 0000000..57fadd8 --- /dev/null +++ b/MDK-ARM/Core/App/i2c.c @@ -0,0 +1,971 @@ +/** + ****************************************************************************** + * @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; +} diff --git a/MDK-ARM/Core/App/i2c.h b/MDK-ARM/Core/App/i2c.h new file mode 100644 index 0000000..aa90e8b --- /dev/null +++ b/MDK-ARM/Core/App/i2c.h @@ -0,0 +1,183 @@ +/** + ****************************************************************************** + * @file i2c.h + * @author Разваляев Алексей + * @brief Драйвер I2C на основе PLIB035. + * Данный файл содержит определения типов, структур и прототипы функций + * для работы с I2C, включая: + * + Структуры и typedef для I2C + * + Прототипы функций для инициализации и API драйвера + * + ****************************************************************************** + */ + +#ifndef __I2C_H +#define __I2C_H + +//-- Includes ------------------------------------------------------------------ +#include "plib035.h" +#include "plib035_i2c.h" +#include "retarget_conf.h" + +//-- Defines ------------------------------------------------------------------- +// Дефайны для пинов I2C +#define I2C_SCL_Pin GPIO_Pin_0 /**< PA0 — I2C SCL */ +#define I2C_SDA_Pin GPIO_Pin_1 /**< PA1 — I2C SDA */ +#define I2C_GPIO_Port GPIOA /**< GPIO порт I2C */ + +// Стандартные частоты I2C +#define I2C_STANDARD_MODE 100000 /**< 100 kHz */ +#define I2C_FAST_MODE 400000 /**< 400 kHz */ +#define I2C_FAST_MODE_PLUS 1000000 /**< 1 MHz */ +#define I2C_HIGH_SPEED_MODE 3400000 /**< 3.4 MHz */ + +// Максимальное время ожидания по умолчанию (мс) +#define I2C_DEFAULT_TIMEOUT 1000 + +// Коды ошибок I2C +#define I2C_ERROR_NONE 0x00 +#define I2C_ERROR_BUS_BUSY 0x01 +#define I2C_ERROR_TIMEOUT 0x02 +#define I2C_ERROR_NACK 0x03 +#define I2C_ERROR_ARBITRATION_LOST 0x04 +#define I2C_ERROR_BUS_ERROR 0x05 +#define I2C_ERROR_PEC_FAIL 0x06 + +//-- Types --------------------------------------------------------------------- + +/** + * @brief Типы callback-функций I2C + */ +typedef enum +{ + I2C_Callback_Tx, /*!< Передача данных завершена */ + I2C_Callback_Rx, /*!< Приём данных завершён */ + I2C_Callback_Addr, /*!< Ведомый получил свой адрес */ + I2C_Callback_Error, /*!< Ошибка I2C */ + +} I2C_CallbackTypeDef; + +/** + * @brief Режимы работы I2C + */ +typedef enum +{ + I2C_Mode_Master, /*!< Режим мастера */ + I2C_Mode_Slave, /*!< Режим ведомого */ +} I2C_ModeTypeDef; + +/** + * @brief Направление передачи I2C + */ +typedef enum +{ + I2C_Direction_Transmitter, /*!< Направление: передатчик */ + I2C_Direction_Receiver, /*!< Направление: приемник */ +} I2C_DirectionTypeDef; + +/** + * @brief Размер регистра устройства + */ +typedef enum +{ + I2C_RegSize_8bit, /*!< Размер регистра: 8 бит */ + I2C_RegSize_16bit, /*!< Размер регистра: 16 бит */ +} I2C_RegSizeTypeDef; + +/** + * @brief Структура инициализации I2C + */ +typedef struct +{ + I2C_ModeTypeDef Mode; /*!< Режим работы: I2C_Mode_Master или I2C_Mode_Slave */ + FunctionalState HSMode; /*!< Высокоскоростной режим */ + FunctionalState Addr10Bit; /*!< 10-битная адресация */ + I2C_RegSizeTypeDef RegSize; /*!< Размер регистра: I2C_RegSize_8bit или I2C_RegSize_16bit */ + uint16_t SlaveAddr; /*!< Адрес ведомого (для режима Slave) */ + + FunctionalState Timeout; /*!< Таймаут: */ + I2C_TimeoutClkDiv_TypeDef TimeoutClkDiv; /*!< Делитель тактирования таймаута */ + uint8_t TimeoutLoad; /*!< Значение загрузки счетчика таймаута */ + + FunctionalState AlertResponse; /*!< Ответ на тревогу */ + FunctionalState GlobalCall; /*!< Общий вызов */ + +} I2C_Init_TypeDef; + +/** + * @brief Расширенная конфигурация I2C + */ +typedef struct +{ + I2C_Init_TypeDef I2C_Init; /*!< Базовая конфигурация I2C */ + + uint32_t I2CFreq; /*!< Частота тактирования I2C в Гц */ + uint32_t FSFreq; /*!< Частота SCL в стандартном режиме в Гц */ + uint32_t HSFreq; /*!< Частота SCL в высокоскоростном режиме в Гц */ + + /* Callback функции */ + void (*TxCallback)(void); /*!< Вызывается при завершении передачи */ + void (*RxCallback)(void); /*!< Вызывается при завершении приема */ + void (*AddrCallback)(void); /*!< Вызывается когда ведомый получает свой адрес */ + void (*ErrCallback)(uint8_t error); /*!< Вызывается при ошибке I2C с кодом ошибки */ + +} I2C_ExtInit_TypeDef; + +/** + * @brief Хендл I2C + */ +typedef struct +{ + I2C_TypeDef *Instance; /*!< Регистры I2C */ + I2C_ExtInit_TypeDef *Config; /*!< Конфигурация I2C */ + + uint8_t LastError; /*!< Код последней ошибки */ + +} I2C_HandleTypeDef; + +//-- External handles ---------------------------------------------------------- + +extern I2C_HandleTypeDef hi2c; + +//-- Exported functions prototypes --------------------------------------------- + +/* Init functions */ + +/* Первичная инициализация I2C */ +void i2c_init_first(void); +/* Инициализация I2C */ +OperationStatus i2c_init(I2C_HandleTypeDef *hi2c, I2C_ExtInit_TypeDef *NewConfig); +/* Инициализация GPIO для I2C */ +void i2c_gpio_init(void); +/* Деинициализация GPIO для I2C */ +void i2c_gpio_deinit(void); + +/* API functions*/ + +/* Установка callback-функции I2C */ +OperationStatus I2C_Set_Callback(I2C_HandleTypeDef *hi2c, I2C_CallbackTypeDef CallbackType, void (*Callback)()); +/* Запуск I2C */ +OperationStatus I2C_Start(I2C_HandleTypeDef *hi2c); +/* Остановка I2C */ +OperationStatus I2C_Stop(I2C_HandleTypeDef *hi2c); + +/* Получение кода последней ошибки */ +uint8_t I2C_GetLastError(I2C_HandleTypeDef *hi2c); + +/* Передача данных в режиме мастера (блокирующий режим) */ +OperationStatus I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t addr, uint8_t *buf, uint16_t size, uint32_t timeout); +/* Прием данных в режиме мастера (блокирующий режим) */ +OperationStatus I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t addr, uint8_t *buf, uint16_t size, uint32_t timeout); +/* Запись регистра устройства (блокирующий режим) */ +OperationStatus I2C_Master_WriteReg(I2C_HandleTypeDef *hi2c, uint16_t addr, uint16_t reg, uint8_t value, uint32_t timeout); +/* Чтение регистра устройства (блокирующий режим) */ +OperationStatus I2C_Master_ReadReg(I2C_HandleTypeDef *hi2c, uint16_t addr, uint16_t reg, uint8_t *buf, uint16_t size, uint32_t timeout); +/* Запись нескольких регистров (блокирующий режим) */ +OperationStatus I2C_Master_WriteRegs(I2C_HandleTypeDef *hi2c, uint16_t addr, uint16_t reg, uint8_t *buf, uint16_t size, uint32_t timeout); +/* Чтение нескольких регистров (блокирующий режим) */ +OperationStatus I2C_Master_ReadRegs(I2C_HandleTypeDef *hi2c, uint16_t addr, uint16_t reg, uint8_t *buf, uint16_t size, uint32_t timeout); + +/* Проверка наличия устройства на шине */ +OperationStatus I2C_Master_IsDeviceReady(I2C_HandleTypeDef *hi2c, uint16_t addr, uint32_t timeout); + +#endif /*__I2C_H*/ diff --git a/MDK-ARM/Core/App/main.c b/MDK-ARM/Core/App/main.c index 3e1b17f..c939f39 100644 --- a/MDK-ARM/Core/App/main.c +++ b/MDK-ARM/Core/App/main.c @@ -17,8 +17,9 @@ void periph_init() { sysclk_init(); uart_init_first(); - adc_init_first(); tmr_init_first(); + adc_init_first(); + i2c_init_first(); gpio_init(); #ifdef RETARGET retarget_init(); diff --git a/MDK-ARM/Core/Config/periph_config.h b/MDK-ARM/Core/Config/periph_config.h index 734aee3..19811f3 100644 --- a/MDK-ARM/Core/Config/periph_config.h +++ b/MDK-ARM/Core/Config/periph_config.h @@ -33,6 +33,7 @@ #include "uart.h" #include "tmr.h" #include "adc.h" +#include "i2c.h" /* Обработчик ошибок */ void Error_Handler(void); @@ -59,6 +60,10 @@ void Error_Handler(void); #define USE_ADC_DC2 0 /*!< Использовать Компаратор 2 */ #define USE_ADC_DC3 0 /*!< Использовать Компаратор 3 */ +/* I2C */ +#define USE_I2C 0 /*!< Использовать I2C */ + + /** @note Для RETARGET надо объявить этот дефайн в проекте Options for Target -> C/C++ -> Defines */ @@ -306,6 +311,23 @@ static ADC_DC_ExtInit_TypeDef adc_dc3_config = { }; #endif + +//-- I2C Конфигурации --------------------------------------------------------- +#if USE_I2C==1 +static I2C_ExtInit_TypeDef i2c_config = { +//Mode, HSMode, Addr10Bit, RegSize, SlaveAddr + I2C_Mode_Master, DISABLE, DISABLE, I2C_RegSize_8bit, 0x00, +//Timeout, TimeoutClkDiv, TimeoutLoad + DISABLE, I2C_TimeoutClkDiv_Div4, 0x00, +//AlertResponse, GlobalCall + DISABLE, DISABLE, +//I2CFreq, FSFreq, HSFreq + SYSCLK_CORE_CLOCK_MHZ * __MHZ, I2C_STANDARD_MODE, I2C_HIGH_SPEED_MODE, +//TxCallback, RxCallback, AddrCallback, ErrCallback + NULL, NULL, NULL, NULL +}; +#endif + //-- NVIC Конфигурации -------------------------------------------------------- /** @brief Приоритеты прерываний, 0 - самый высокий приоритет*/ static uint8_t NCIV_Priorities[] = diff --git a/MDK-ARM/Template.uvoptx b/MDK-ARM/Template.uvoptx index cd80bec..5ef52ac 100644 --- a/MDK-ARM/Template.uvoptx +++ b/MDK-ARM/Template.uvoptx @@ -380,6 +380,18 @@ 0 0 0 + .\Core\App\i2c.c + i2c.c + 0 + 0 + + + 2 + 11 + 1 + 0 + 0 + 0 .\Core\App\sysclk.c sysclk.c 0 @@ -387,7 +399,7 @@ 2 - 11 + 12 1 0 0 @@ -407,7 +419,7 @@ 0 3 - 12 + 13 5 0 0 @@ -419,7 +431,7 @@ 3 - 13 + 14 5 0 0 @@ -431,7 +443,7 @@ 3 - 14 + 15 5 0 0 @@ -443,7 +455,7 @@ 3 - 15 + 16 5 0 0 @@ -455,7 +467,7 @@ 3 - 16 + 17 5 0 0 @@ -467,7 +479,7 @@ 3 - 17 + 18 5 0 0 @@ -479,7 +491,7 @@ 3 - 18 + 19 1 0 0 @@ -491,7 +503,7 @@ 3 - 19 + 20 1 0 0 @@ -503,7 +515,7 @@ 3 - 20 + 21 5 0 0 @@ -515,7 +527,7 @@ 3 - 21 + 22 1 0 0 @@ -535,7 +547,7 @@ 0 4 - 22 + 23 1 0 0 @@ -547,7 +559,7 @@ 4 - 23 + 24 2 0 0 @@ -567,7 +579,7 @@ 0 5 - 24 + 25 1 0 0 @@ -579,7 +591,7 @@ 5 - 25 + 26 1 0 0 @@ -591,7 +603,7 @@ 5 - 26 + 27 1 0 0 @@ -603,7 +615,7 @@ 5 - 27 + 28 1 0 0 @@ -615,7 +627,7 @@ 5 - 28 + 29 1 0 0 @@ -627,7 +639,7 @@ 5 - 29 + 30 1 0 0 @@ -639,7 +651,7 @@ 5 - 30 + 31 1 0 0 @@ -651,7 +663,7 @@ 5 - 31 + 32 1 0 0 @@ -663,7 +675,7 @@ 5 - 32 + 33 1 0 0 @@ -675,7 +687,7 @@ 5 - 33 + 34 1 0 0 @@ -687,7 +699,7 @@ 5 - 34 + 35 1 0 0 @@ -699,7 +711,7 @@ 5 - 35 + 36 1 0 0 @@ -711,7 +723,7 @@ 5 - 36 + 37 1 0 0 @@ -723,7 +735,7 @@ 5 - 37 + 38 1 0 0 @@ -735,7 +747,7 @@ 5 - 38 + 39 1 0 0 @@ -755,7 +767,7 @@ 0 6 - 39 + 40 1 0 0 @@ -767,7 +779,7 @@ 6 - 40 + 41 1 0 0 @@ -779,7 +791,7 @@ 6 - 41 + 42 5 0 0 diff --git a/MDK-ARM/Template.uvprojx b/MDK-ARM/Template.uvprojx index 2fbcc92..0cf3359 100644 --- a/MDK-ARM/Template.uvprojx +++ b/MDK-ARM/Template.uvprojx @@ -434,6 +434,11 @@ 1 .\Core\App\adc.c + + i2c.c + 1 + .\Core\App\i2c.c + sysclk.c 1 diff --git a/Pinout.xlsx b/Pinout.xlsx new file mode 100644 index 0000000..28e500f Binary files /dev/null and b/Pinout.xlsx differ