972 lines
32 KiB
C
972 lines
32 KiB
C
/**
|
||
******************************************************************************
|
||
* @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;
|
||
}
|