Files
VK035_Template/MDK-ARM/Core/App/i2c.c

972 lines
32 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
******************************************************************************
* @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*)&reg, 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;
}