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

777 lines
25 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 uart.c
* @author Разваляев Алексей
* @brief Драйвер UART на основе PLIB035.
* Этот файл содержит:
* + Инициализацию UART0/UART1
* + Управление FIFO
* + Передачу и приём данных в blocking и interrupt режимах
* + Общий обработчик прерываний UART
* + Функции настройки GPIO для UART
* + Очередь передачи для предотвращения потери данных
*
******************************************************************************
* @attention
*
* Использование этого драйвера предполагает наличие корректных настроек:
* - Определены конфигурационные структуры uartx_config в periph_config.h
* - Определены пины TX/RX для UART0/UART1
*
******************************************************************************
* @verbatim
==============================================================================
##### Как использовать этот драйвер #####
==============================================================================
1. Настройка в periph_config.h:
(+) Определите uart0_config, uart1_config для нужных UART
(+) Используйте макросы для направления:
• UART_Direction_None - прием и передача отключены
• UART_Direction_RxTx - полный дуплекс
• UART_Direction_Tx - только передача
• UART_Direction_Rx - только прием
2. Инициализация:
(+) uart_init_first() - настройка GPIO, тактирования и прерываний
(+) uart_init(&huart, &config) - инициализация конкретного UART
3. Callback-функции:
(+) UART_Set_Callback(&huart, UART_Callback_Rx, func) - при завершении приема
(+) UART_Set_Callback(&huart, UART_Callback_Tx, func) - при завершении передачи
(+) UART_Set_Callback(&huart, UART_Callback_Idle, func) - при обнаружении IDLE
(+) UART_Set_Callback(&huart, UART_Callback_Error, func) - при ошибках
4. Запуск UART:
(+) UART_Start(&huart, TxFifoLevel, RxFifoLevel) - включение UART и настройка FIFO
5. Передача и приём данных:
- Режим Polling:
(+) UART_Transmit(&huart, buf, size, timeout) - блокирующая передача
(+) UART_Receive(&huart, buf, size, timeout) - блокирующий прием
- Режим Interrupt:
(+) UART_Transmit_IT(&huart, buf, size) - неблокирующая передача
(+) UART_Receive_IT(&huart, buf, size) - неблокирующий прием
6. Обработка прерываний:
(+) Прерывания обрабатывают TX FIFO, RX FIFO, ошибки и состояние IDLE
7. GPIO для UART:
(+) Пины настраиваются автоматически при вызове uart_init_first()
(+) При необходимости можно вызвать uart0_gpio_deinit() для восстановления
==============================================================================
##### Особенности работы #####
==============================================================================
- Очередь передачи (USE_TX_QUEUE):
- Циклический буфер на 32 сообщения (TX_QUEUE_SIZE)
- Предотвращает потерю данных при частой отправке
- Включается автоматически при USE_TX_QUEUE=1 в uart.h
- Сообщения обрабатываются последовательно по завершении предыдущей передачи
- FIFO и прерывания:
- Аппаратный FIFO 16 байт для приема и передачи
- Уровни прерываний настраиваются через TxFifoLevel и RxFifoLevel
- Прерывание TX FIFO генерируется когда FIFO не полон
- Прерывание RX FIFO генерируется когда FIFO не пуст
- Состояние IDLE:
- Обнаруживается по таймауту приема (32 бит-времени)
- Генерирует прерывание RecieveTimeout
- Полезно для определения конца пакета переменной длины
- Обработка ошибок:
- Обрабатываются все типы ошибок UART: фрейм, паритет, переполнение, break
- При ошибке вызывается ErrCallback и сбрасываются флаги ошибок
- Ошибки не останавливают работу UART
- GPIO автоматическая настройка:
- Пины TX/RX настраиваются в AltFunc режим при инициализации
- Поддерживаются стандартные пины (PB10/PB11 для UART0, PB8/PB9 для UART1)
- Режимы работы:
- Polling: простой, но блокирующий, подходит для инициализации и отладки
- Interrupt: неблокирующий, требует прерываний и поддерживает callback-функций
- Queue: расширенный interrupt режим с буферизацией сообщений
@endverbatim
******************************************************************************
*/
//-- Includes ------------------------------------------------------------------
#include "periph_config.h"
UART_HandleTypeDef huart0; /*!< Хендл UART0 */
UART_HandleTypeDef huart1; /*!< Хендл UART1 */
//-- Private function prototypes -----------------------------------------------
static int __uart_fifo_receive(UART_HandleTypeDef *huart, uint8_t it_mode);
static int __uart_fifo_transmit(UART_HandleTypeDef *huart, uint8_t it_mode);
#if USE_TX_QUEUE==1
static void __uart_tx_queue_process(UART_HandleTypeDef *huart);
static OperationStatus __uart_tx_queue_push(UART_HandleTypeDef *huart, const uint8_t *buf, uint16_t size);
static OperationStatus __uart_tx_queue_pop(UART_HandleTypeDef *huart);
#endif
//-- Defines -------------------------------------------------------------------
//-- UART Init functions -------------------------------------------------------
/**
* @brief Первичная инициализация UART (UART0 / UART1)
* @details Настройка UART и хендлов: GPIO, тактирование и прерывания
*/
void uart_init_first(void)
{
#if (USE_UART0==1)
// Настройка пинов для UART0
uart0_gpio_init();
// Включаем тактирование UART0
RCU_UARTClkConfig(UART0_Num, RCU_PeriphClk_PLLClk, 0, DISABLE);
RCU_UARTClkCmd(UART0_Num, ENABLE);
UART_DeInit(UART0);
// Инициализируем UART0
huart0.Instance = UART0;
uart_init(&huart0, &uart0_config);
NVIC_EnableIRQ(UART0_TD_IRQn);
NVIC_EnableIRQ(UART0_RX_IRQn);
NVIC_EnableIRQ(UART0_TX_IRQn);
NVIC_EnableIRQ(UART0_E_RT_IRQn);
#endif
#if (USE_UART1==1)
// Настройка пинов для UART1
uart1_gpio_init();
// Включаем тактирование UART1
RCU_UARTClkConfig(UART1_Num, RCU_PeriphClk_PLLClk, 0, DISABLE);
RCU_UARTClkCmd(UART1_Num, ENABLE);
UART_DeInit(UART1);
// Инициализируем UART1
NVIC_EnableIRQ(UART1_TD_IRQn);
NVIC_EnableIRQ(UART1_RX_IRQn);
NVIC_EnableIRQ(UART1_TX_IRQn);
NVIC_EnableIRQ(UART1_E_RT_IRQn);
huart1.Instance = UART1;
uart_init(&huart1, &uart1_config);
#endif
}
/**
* @brief Инициализация UART
* @param htmr указатель на хендл UART
* @param NewConfig указатель на новую конфигурацию UART, иначе используется та, что в структуре
* @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
*/
OperationStatus uart_init(UART_HandleTypeDef *huart, UART_ExtInit_TypeDef *NewConfig)
{
if(!huart || !huart->Instance)
return ERROR;
if(NewConfig != NULL)
{
huart->Config = NewConfig;
}
if(huart->Config == NULL)
{
return ERROR;
}
UART_Init(huart->Instance, &huart->Config->UART_Init);
return OK;
}
/**
* @brief Установка коллбека UART
* @param htmr указатель на хендл UART
* @param CallbackType Тип коллбека
* @param Callback Функция коллбека
* @retval void
*/
OperationStatus UART_Set_Callback(UART_HandleTypeDef* huart, UART_CallbackTypeDef CallbackType, void (*Callback)(void))
{
if (!huart || !huart->Instance || !huart->Config)
return ERROR;
switch(CallbackType)
{
case UART_Callback_Rx:
huart->Config->RxCallback = Callback;
break;
case UART_Callback_Tx:
huart->Config->TxCallback = Callback;
break;
case UART_Callback_Idle:
huart->Config->IdleCallback = Callback;
break;
case UART_Callback_Error:
huart->Config->ErrCallback = Callback;
break;
default:
return ERROR;
}
return OK;
}
//-- UART API functions --------------------------------------------------------
/**
* @brief Запуск UART и инициализация FIFO
* @param huart указатель на хендл UART
* @param TxFifoLevel уровень прерывания TX FIFO
* @param RxFifoLevel уровень прерывания RX FIFO
* @retval OperationStatus OK - если успешно, ERROR - при ошибке
*/
OperationStatus UART_Start(UART_HandleTypeDef *huart, UART_FIFOLevel_TypeDef TxFifoLevel, UART_FIFOLevel_TypeDef RxFifoLevel)
{
if (!huart)
return ERROR;
UART_ITFIFOLevelRxConfig(huart->Instance, RxFifoLevel);
UART_ITFIFOLevelTxConfig(huart->Instance, TxFifoLevel);
huart->TxBufPtr = NULL;
huart->TxCount = 0;
huart->TxSize = 0;
huart->TxBusy = 0;
huart->RxBufPtr = NULL;
huart->RxCount = 0;
huart->RxSize = 0;
huart->RxBusy = 0;
#if USE_TX_QUEUE==1
huart->TxQueue.QueueHead = 0;
huart->TxQueue.QueueTail = 0;
huart->TxQueue.QueueCount = 0;
huart->TxQueue.QueueEmpty = 1;
huart->TxQueue.QueueFull = 0;
for (uint8_t i = 0; i < TX_QUEUE_SIZE; i++)
{
huart->TxQueue.Queue[i].Buf = NULL;
huart->TxQueue.Queue[i].Size = 0;
huart->TxQueue.Queue[i].InUse = 0;
}
#endif
UART_Cmd(huart->Instance, ENABLE);
return OK;
}
/**
* @brief Передача данных по UART (блокирующий режим)
* @param huart указатель на хендл UART
* @param buf указатель на буфер данных
* @param size размер данных в байтах
* @param timeout таймаут ожидания (мс)
* @retval OperationStatus OK - если успешно, ERROR - при ошибке или таймауте
*/
OperationStatus UART_Transmit(UART_HandleTypeDef *huart,
uint8_t *buf,
uint16_t size,
uint32_t timeout)
{
if (!huart || !buf || size == 0)
return ERROR;
// Если TX занят, возвращаем ERROR
if (huart->TxBusy)
return ERROR;
huart->TxBufPtr = buf;
huart->TxSize = size;
huart->TxCount = 0;
huart->TxBusy = 1;
uint32_t starttick = millis();
// Отправляем пока всё не отправится
while(__uart_fifo_transmit(huart, 0) == 0)
{
if(millis() - starttick > timeout)
return ERROR;
}
return OK;
}
/**
* @brief Приём данных по UART (блокирующий режим)
* @param huart указатель на хендл UART
* @param buf указатель на буфер приёма
* @param size количество принимаемых байт
* @param timeout таймаут ожидания (мс)
* @retval OperationStatus OK - если успешно, ERROR - при ошибке или таймауте
*/
OperationStatus UART_Receive(UART_HandleTypeDef *huart,
uint8_t *buf,
uint16_t size,
uint32_t timeout)
{
if (!huart || !buf || size == 0)
return ERROR;
// Если RX занят, возвращаем ERROR
if (huart->RxBusy)
return ERROR;
huart->RxBufPtr = buf;
huart->RxSize = size;
huart->RxCount = 0;
huart->RxBusy = 1;
uint32_t starttick = millis();
// Принимаем всё пока всё не примется
while(__uart_fifo_transmit(huart, 0) == 0)
{
if(millis() - starttick > timeout)
return ERROR;;
}
return OK;
}
/**
* @brief Передача данных по UART (прерывания)
* @param huart указатель на хендл UART
* @param buf указатель на буфер данных
* @param size размер данных в байтах
* @retval OperationStatus OK - если успешно, ERROR - если передача уже идёт
*/
OperationStatus UART_Transmit_IT(UART_HandleTypeDef *huart,
uint8_t *buf,
uint16_t size)
{
if (!huart || !buf || size == 0)
return ERROR;
#if USE_TX_QUEUE==1
// Автоматически используем очередь
if (huart->TxQueue.QueueFull)
return ERROR;
OperationStatus status = __uart_tx_queue_push(huart, buf, size);
if (status != OK)
return ERROR;
if (!huart->TxBusy)
{
__uart_tx_queue_process(huart);
}
return OK;
#else
// Без очереди
// Если TX занят, возвращаем ERROR
if (huart->TxBusy)
return ERROR;
huart->TxBufPtr = buf;
huart->TxSize = size;
huart->TxCount = 0;
huart->TxBusy = 1;
// Включаем прерывания по TX FIFO
UART_ITCmd(huart->Instance, UART_ITSource_TxFIFOLevel, ENABLE);
__uart_fifo_transmit(huart, 1);
return OK;
#endif
}
/**
* @brief Приём данных по UART (прерывания)
* @param huart указатель на хендл UART
* @param buf указатель на буфер приёма
* @param size количество принимаемых байт
* @retval OperationStatus OK - если успешно, ERROR - если приём уже идёт
*/
OperationStatus UART_Receive_IT(UART_HandleTypeDef *huart,
uint8_t *buf,
uint16_t size)
{
if (!huart || !buf || size == 0)
return ERROR;
// Если RX занят, возвращаем ERROR
if (huart->RxBusy)
return ERROR;
huart->RxBufPtr = buf;
huart->RxSize = size;
huart->RxCount = 0;
huart->RxBusy = 1;
// Включаем только RX прерывания
UART_ITCmd(huart->Instance,
UART_ITSource_RxFIFOLevel |
UART_ITSource_RecieveTimeout |
UART_ITSource_ErrorFrame |
UART_ITSource_ErrorParity |
UART_ITSource_ErrorOverflow |
UART_ITSource_ErrorBreak,
ENABLE);
return OK;
}
//-- UART Handler functions ---------------------------------------------------
/**
* @brief Общий обработчик прерываний UART
* @param huart указатель на хендл UART
* @details Обрабатывает RX/TX FIFO, ошибки и события Idle
* @retval void
*/
void uart_irq_handler(UART_HandleTypeDef *huart)
{
if (!huart || !huart->Instance || !huart->Config)
return;
// GPIO_SetBits(GPIOA, GPIO_Pin_7);
UART_TypeDef *uart = huart->Instance;
uint32_t mis = uart->MIS;
// Ошибки
if (mis & (UART_ITSource_ErrorFrame |
UART_ITSource_ErrorParity |
UART_ITSource_ErrorOverflow |
UART_ITSource_ErrorBreak))
{
if (huart->Config->ErrCallback)
huart->Config->ErrCallback();
UART_ErrorStatusClear(uart, UART_Error_All);
UART_ITStatusClear(uart, mis);
}
// RX FIFO
if (mis & UART_ITSource_RxFIFOLevel)
{
// Принимаем
if(__uart_fifo_receive(huart, 1))
{ // Когда всё приняли, флаги сбросили внутри функции
}
UART_ITStatusClear(uart, UART_ITSource_RxFIFOLevel);
}
// IDLE / Receive Timeout
if (mis & UART_ITSource_RecieveTimeout)
{
UART_ITStatusClear(uart, UART_ITSource_RecieveTimeout);
huart->RxBusy = 0;
// Выключаем RX прерывания до следующего вызова UART_Receive_IT
UART_ITCmd(uart,
UART_ITSource_RxFIFOLevel |
UART_ITSource_RecieveTimeout,
DISABLE);
if (huart->Config->IdleCallback)
huart->Config->IdleCallback();
}
// TX FIFO
if (mis & UART_ITSource_TxFIFOLevel)
{
// Отправляем
if(__uart_fifo_transmit(huart, 1))
{ // Когда всё отправили, флаги сбросили внутри функции
}
UART_ITStatusClear(uart, UART_ITSource_TxFIFOLevel);
}
// Конец передачи
if (mis & UART_ITSource_TransmitDone)
{
UART_ITStatusClear(uart, UART_ITSource_TransmitDone);
huart->TxBusy = 0;
if (huart->Config->TxCallback)
huart->Config->TxCallback();
#if USE_TX_QUEUE==1
// Если есть очередь, обрабатываем следующий пакет
__uart_tx_queue_process(huart);
#endif
}
// GPIO_ClearBits(GPIOA, GPIO_Pin_7);
}
//-- UART GPIO functions -------------------------------------------------------
/**
* @brief Инициализация GPIO для UART0
*/
void uart0_gpio_init(void)
{
#if USE_UART0==1
// Получаем структуры
GPIO_Init_TypeDef *tx_config = gpio_get_init(UART0_GPIO_Port, UART0_Tx_Pin);
GPIO_Init_TypeDef *rx_config = gpio_get_init(UART0_GPIO_Port, UART0_Rx_Pin);
// TX пин
if (uart0_config.UART_Init.Tx == ENABLE && tx_config != NULL)
{
GPIO_StructInit(tx_config);
tx_config->AltFunc = ENABLE;
tx_config->Digital = ENABLE;
tx_config->Pin = UART0_Tx_Pin;
GPIO_Init(UART0_GPIO_Port, tx_config);
}
// RX пин
if (uart0_config.UART_Init.Rx == ENABLE && rx_config != NULL)
{
GPIO_StructInit(rx_config);
rx_config->AltFunc = ENABLE;
rx_config->Digital = ENABLE;
rx_config->Pin = UART0_Rx_Pin;
GPIO_Init(UART0_GPIO_Port, rx_config);
}
#endif
}
/**
* @brief Деинициализация GPIO для UART0
*/
void uart0_gpio_deinit(void)
{
#if USE_UART0==1
// Получаем структуры
GPIO_Init_TypeDef *tx_config = gpio_get_init(UART0_GPIO_Port, UART0_Tx_Pin);
GPIO_Init_TypeDef *rx_config = gpio_get_init(UART0_GPIO_Port, UART0_Rx_Pin);
// Восстанавливаем оригинальные настройки из таблицы
if (tx_config != NULL)
{
GPIO_StructInit(rx_config);
rx_config->Pin = UART0_Tx_Pin;
GPIO_Init(UART0_GPIO_Port, tx_config);
}
if (rx_config != NULL)
{
GPIO_StructInit(rx_config);
rx_config->Pin = UART0_Rx_Pin;
GPIO_Init(UART0_GPIO_Port, rx_config);
}
#endif
}
/**
* @brief Инициализация GPIO для UART1
*/
void uart1_gpio_init(void)
{
#if USE_UART1==1
// Получаем структуры
GPIO_Init_TypeDef *tx_config = gpio_get_init(UART1_GPIO_Port, UART1_Tx_Pin);
GPIO_Init_TypeDef *rx_config = gpio_get_init(UART1_GPIO_Port, UART1_Rx_Pin);
// TX пин
if (uart1_config.UART_Init.Tx == ENABLE && tx_config != NULL)
{
GPIO_StructInit(tx_config);
tx_config->AltFunc = ENABLE;
tx_config->Digital = ENABLE;
tx_config->Pin = UART1_Tx_Pin;
GPIO_Init(UART1_GPIO_Port, tx_config);
}
// RX пин
if (uart1_config.UART_Init.Rx == ENABLE && rx_config != NULL)
{
;
GPIO_StructInit(rx_config);
rx_config->AltFunc = ENABLE;
rx_config->Digital = ENABLE;
rx_config->Pin = UART1_Rx_Pin;
GPIO_Init(UART1_GPIO_Port, rx_config);
}
#endif
}
/**
* @brief Деинициализация GPIO для UART1
*/
void uart1_gpio_deinit(void)
{
#if USE_UART1==1
// Получаем структуры
GPIO_Init_TypeDef *tx_config = gpio_get_init(UART1_GPIO_Port, UART1_Tx_Pin);
GPIO_Init_TypeDef *rx_config = gpio_get_init(UART1_GPIO_Port, UART1_Rx_Pin);
// Восстанавливаем оригинальные настройки из таблицы
if (tx_config != NULL)
{
GPIO_StructInit(rx_config);
rx_config->Pin = UART1_Tx_Pin;
GPIO_Init(UART1_GPIO_Port, tx_config);
}
if (rx_config != NULL)
{
GPIO_StructInit(rx_config);
rx_config->Pin = UART1_Rx_Pin;
GPIO_Init(UART1_GPIO_Port, rx_config);
}
#endif
}
//-- UART private functions ----------------------------------------------------
/**
* @brief Приём данных из RX FIFO
* @param huart указатель на хендл UART
* @param it_mode режим работы (0 — polling, 1 — прерывания)
* @retval int 1 — приём завершён, 0 — данные ещё принимаются
*/
static int __uart_fifo_receive(UART_HandleTypeDef *huart, uint8_t it_mode)
{
while (!UART_FlagStatus(huart->Instance, UART_Flag_RxFIFOEmpty) &&
huart->RxCount < huart->RxSize)
{
huart->RxBufPtr[huart->RxCount++] = UART_RecieveData(huart->Instance);
if (huart->RxCount == huart->RxSize)
{
huart->RxBusy = 0;
if(it_mode)
{
// Выключаем RX прерывания
UART_ITCmd(huart->Instance,
UART_ITSource_RxFIFOLevel |
UART_ITSource_RecieveTimeout,
DISABLE);
}
if (huart->Config->RxCallback)
huart->Config->RxCallback();
return 1;
}
}
return 0;
}
/**
* @brief Передача данных в TX FIFO
* @param huart указатель на хендл UART
* @param it_mode режим работы (0 — polling, 1 — прерывания)
* @retval int 1 — передача завершена, 0 — данные ещё передаются
*/
static int __uart_fifo_transmit(UART_HandleTypeDef *huart, uint8_t it_mode)
{
while (!UART_FlagStatus(huart->Instance, UART_Flag_TxFIFOFull) &&
huart->TxCount < huart->TxSize)
{
UART_SendData(huart->Instance, huart->TxBufPtr[huart->TxCount++]);
}
if (huart->TxCount == huart->TxSize)
{
if(it_mode)
{
// Выключаем FIFO прерывание
UART_ITCmd(huart->Instance, UART_ITSource_TxFIFOLevel, DISABLE);
// Включаем TransmitDone прерывание, коллбек будет по нему
UART_ITCmd(huart->Instance, UART_ITSource_TransmitDone, ENABLE);
}
else
{
while(!UART_FlagStatus(huart->Instance, UART_Flag_TxFIFOEmpty)); // ждем пока не опустошится буфер
huart->TxBusy = 0;
if (huart->Config->TxCallback)
huart->Config->TxCallback();
}
return 1;
}
return 0;
}
#if USE_TX_QUEUE==1
static void __uart_tx_queue_process(UART_HandleTypeDef *huart)
{
if (!huart->TxBusy && !huart->TxQueue.QueueEmpty)
{
__uart_tx_queue_pop(huart);
}
}
static OperationStatus __uart_tx_queue_push(UART_HandleTypeDef *huart, const uint8_t *buf, uint16_t size)
{
if (!huart || !buf || size == 0 || huart->TxQueue.QueueFull)
return ERROR;
huart->TxQueue.Queue[huart->TxQueue.QueueHead].Buf = buf;
huart->TxQueue.Queue[huart->TxQueue.QueueHead].Size = size;
huart->TxQueue.Queue[huart->TxQueue.QueueHead].InUse = 1;
huart->TxQueue.QueueHead = (huart->TxQueue.QueueHead + 1) % TX_QUEUE_SIZE;
huart->TxQueue.QueueCount++;
huart->TxQueue.QueueEmpty = 0;
if (huart->TxQueue.QueueCount == TX_QUEUE_SIZE)
huart->TxQueue.QueueFull = 1;
return OK;
}
static OperationStatus __uart_tx_queue_pop(UART_HandleTypeDef *huart)
{
if (!huart || huart->TxQueue.QueueEmpty)
return ERROR;
const uint8_t *buf = huart->TxQueue.Queue[huart->TxQueue.QueueTail].Buf;
uint16_t size = huart->TxQueue.Queue[huart->TxQueue.QueueTail].Size;
if (huart->TxBusy)
return ERROR;
huart->TxBufPtr = buf;
huart->TxSize = size;
huart->TxCount = 0;
huart->TxBusy = 1;
UART_ITCmd(huart->Instance, UART_ITSource_TxFIFOLevel, ENABLE);
__uart_fifo_transmit(huart, 1);
huart->TxQueue.Queue[huart->TxQueue.QueueTail].Buf = NULL;
huart->TxQueue.Queue[huart->TxQueue.QueueTail].Size = 0;
huart->TxQueue.Queue[huart->TxQueue.QueueTail].InUse = 0;
huart->TxQueue.QueueTail = (huart->TxQueue.QueueTail + 1) % TX_QUEUE_SIZE;
huart->TxQueue.QueueCount--;
huart->TxQueue.QueueFull = 0;
if (huart->TxQueue.QueueCount == 0)
huart->TxQueue.QueueEmpty = 1;
return OK;
}
#endif