STM32CubeMx предлагает две библиотеки для упрощения работы разработчикам:
- HAL
- LL
Чтобы понять разницу между двумя подходами, необходимо взглянуть н пример отправки сообщений по интерфейсу I2C
HAL
//глобальные переменные
I2C_HandleTypeDef I2cHandle;
uint8_t aTxBuffer[] = "что-то очень важное"
//функция передачи данных
HAL_I2C_Master_Transmit(&I2cHandle, (uint16_t)I2C_ADDRESS, (uint8_t*)aTxBuffer, TXBUFFERSIZE, 10000);
Короткая функция, которая скрывает от разработчика все подробности. Можно также выбрать версии, использующие прерывания и DMA.
LL
LL_I2C_AcknowledgeNextData(I2C1, LL_I2C_ACK);
LL_I2C_GenerateStartCondition(I2C1);
while(!LL_I2C_IsActiveFlag_SB(I2C1));
LL_I2C_TransmitData8(I2C1, SLAVE_OWN_ADDRESS | I2C_REQUEST_WRITE);
while(!LL_I2C_IsActiveFlag_ADDR(I2C1));
while(LL_I2C_IsActiveFlag_TXE(I2C1));
LL_I2C_TransmitData8(I2C1, data);
LL_I2C_GenerateStopCondition(I2C1);
Предоставлен полный контроль над интерфейсом, скрыта только непосредственная работа с регистрами.
Вывод
Библиотека HAL в этом отношении берет на себя основную работу по защите пользователя от каких бы то ни было внезапных происшествий, но плата за это -- скорость. Впрочем для быстрого прототипирования то, что нужно.
В то время как библиотека LL даёт куда больший контроль над МК, что не всегда будет плюсом, особенно в прототипировании. В обучении она показывает себя с наилучшей стороны.
Приложения (в которых приведен код функций):
HAL подробно
Hardware Abstract Layer был создан, чтобы упростить жизнь пользователю, одновременно дав ему максимально широкие возможности. Приведу для наглядности код одной из функций HAL.
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
Переменная начала тиков для будущих операций
uint32_t tickstart = HAL_GetTick();
Если интерфейс находится в состоянии готовности, то начинаем основные операции. В противном случае, заканчиваем функцию в состоянии "HAL_BUSY".
if (hi2c->State == HAL_I2C_STATE_READY)
{
/* Wait until BUSY flag is reset */
Оживаем сброса флага интерфейса I2C_FLAG_BUSY, здесь нам впервые понадобится зарезервированная переменная, которая используется для выхода из бесконечного цикла -- иначе говоря, проверять не больше определенного количества раз. Если не дождались, то заканчиваем функцию с ошибкой.
if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BUSY, SET, I2C_TIMEOUT_BUSY_FLAG, tickstart) != HAL_OK)
{
return HAL_BUSY;
}
Блокируем интерфейс от нежелательных воздействий в процессе нашей работы с ним.
__HAL_LOCK(hi2c);
Проверяем включен ли интерфейс и включаем его, в противном случае.
if ((hi2c->Instance->CR1 & I2C_CR1_PE) != I2C_CR1_PE)
{
__HAL_I2C_ENABLE(hi2c);
}
/* Disable Pos */
Отключаем флаг Pos, используемый для принятия данных.
CLEAR_BIT(hi2c->Instance->CR1, I2C_CR1_POS);
Устанавливаем стартовые параметры интерфейса.
hi2c->State = HAL_I2C_STATE_BUSY_TX;
hi2c->Mode = HAL_I2C_MODE_MASTER;
hi2c->ErrorCode = HAL_I2C_ERROR_NONE;
Нам надо передать данные из буфера pData размером Size.
/* Prepare transfer parameters */
hi2c->pBuffPtr = pData;
hi2c->XferCount = Size;
hi2c->XferSize = hi2c->XferCount;
hi2c->XferOptions = I2C_NO_OPTION_FRAME;
Отправляем адрес получателя Slave Address. Если не дождались, то заканчиваем функцию с ошибкой.
if (I2C_MasterRequestWrite(hi2c, DevAddress, Timeout, tickstart) != HAL_OK)
{
return HAL_ERROR;
}
Сбрасываем флаг адреса.
__HAL_I2C_CLEAR_ADDRFLAG(hi2c);
Отправляем данные в заданном количестве
while (hi2c->XferSize > 0U)
{
Ждем флаг освобождения регистра отправки данных
if (I2C_WaitOnTXEFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK)
{
if (hi2c->ErrorCode == HAL_I2C_ERROR_AF)
{
/* Generate Stop */
Если получили код ошибки, то останавливаем процесс передачи данных.
SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP);
}
return HAL_ERROR;
}
В противном случае, передаем последовательно данные.
hi2c->Instance->DR = *hi2c->pBuffPtr;
hi2c->pBuffPtr++;
Обновляем счетчики.
hi2c->XferCount--;
hi2c->XferSize--;
Если установлен флаг окончания передачи данных, то осуществить снова передачу байта.
if ((__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BTF) == SET) && (hi2c->XferSize != 0U))
{
hi2c->Instance->DR = *hi2c->pBuffPtr;
hi2c->pBuffPtr++;
hi2c->XferCount--;
hi2c->XferSize--;
}
Проверяем, что флаг окончания не возвращает ошибку.
/* Wait until BTF flag is set */
if (I2C_WaitOnBTFFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK)
{
if (hi2c->ErrorCode == HAL_I2C_ERROR_AF)
{
Останавливаем процедуру, в случае ошибки.
SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP);
}
return HAL_ERROR;
}
}
В противном случае, корректно завершаем функцию.
SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP);
hi2c->State = HAL_I2C_STATE_READY;
hi2c->Mode = HAL_I2C_MODE_NONE;
__HAL_UNLOCK(hi2c);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}