Добавлен i2c и распиновка МК

This commit is contained in:
2026-03-13 15:47:13 +03:00
parent 12922152bb
commit d03d26cef1
7 changed files with 1226 additions and 32 deletions

971
MDK-ARM/Core/App/i2c.c Normal file
View File

@@ -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*)&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;
}