№10 / 2018 / статья 8

Беспроводной выключатель на базе СС13хх

Александр Калачев (г. Барнаул)

Любые решения интернета вещей немыслимы без устройств, обеспечивающих непосредственное взаимодействие с пользователем с помощью выключателей и реле. Давайте рассмотрим процесс разработки макета адресуемого беспроводного выключателя на основе базовых программных проектов для серии CC13xx от TI.

Автоматизация зданий и территорий любого назначения (жилых домов, коммерческой недвижимости, производственных площадей) всегда являлась одной из интересных задач, не имеющих общего решения. В подавляющем большинстве случаев системы типа умный дом и интеллектуальная среда разрабатываются с учетом требований и реалий конечного потребителя. Беспроводные технологии играют в таких системах не последнюю роль, а во многих случаях они являются стержнем всей системы.

Основными задачами беспроводных устройств являются сбор информации и передача команд управления на исполнительные узлы. В простейшем случае это – сигнализация, выключатели, реле и прочее. Достаточно часто реализация беспроводного канала конечных устройств является однонаправленной, то есть в целях экономии узел оснащается простым приемником (ресивером). Нельзя сказать, что такое решение является плохим, но иногда появляется необходимость в обратном канале для мониторинга состояния узла или подтверждения получения команды. В современных условиях более правильным решением для конечного беспроводного узла будет все-таки оснащение его полноценным приемопередатчиком, благо из-за конкуренции их стоимость является вполне приемлемой. С учетом того, что конечный узел должен будет выполнять какие-либо функции, а также участвовать в работе сети, в целях снижения размеров, количества компонентов (BOM) и энергопотребления беспроводная система-на-кристалле (СНК, или проще – беспроводной микроконтроллер) – вполне ожидаемый выбор.

Обычные критерии при выборе беспроводной СНК – возможные частотные диапазоны, поддерживаемые стандарты, энергопотребление, набор периферийных устройств. Если рассматривать СНК производства компании Texas Instruments, то на текущий момент для систем автоматизации очень привлекательны контроллеры развивающейся серии CC13xx семейства SimpleLink.

Одной из первых (а возможно и первая) компания TI применила в беспроводных СНК многоядерную технологию. Серия CC13xx предлагает разработчику два доступных для программирования ядра – высокопроизводительное 32-разрядное Cortex-M3 и малопотребляющее 16-разрядное RISC.

Типовым разделением задач для ядер является обслуживание стека протоколов и взаимодействие с пользователем для 32-разрядного ядра и работа с периферийными устройствами – для 16-разрядного. Вторым плюсом устройств серии CC1350 является малый ток потребления и радиочасти, и микроконтроллерных ядер, а также – малые токи в режиме ожидания.

Простейшая автоматизация – беспроводной выключатель

Рассмотрим построение простого канала управления конечным устройством на базе микросхем CC1350. В качестве базы для беспроводных узлов будут использованы модули MBee-DUAL-3.3-UFL-SOLDER-1350-UFL, выбор модулей объясняется достаточно просто: они компактные, с достаточным количеством выводов, не требуют дополнительных компонентов.

Функциональность предусмотрим следующую. Пусть в нашей системе присутствует узел с кнопкой включения/выключения (выключатель) и узел, управляющий нагрузкой посредством твердотельного реле. Также для узла-кнопки предусмотрим вариант подключения сенсорной кнопки. Для обслуживания сенсорной кнопки задействуем 16-битное RICK-ядро SensorController, это позволит, с одной стороны, не сильно менять исходный код примера, с другой – даст лишь небольшой прирост потребляемого тока по сравнению с реализацией кнопки на основном ядре.

Схематично структура простейшего устройства управления нагрузкой представлена на рисунке 1:

  • узел-выключатель с двумя кнопками – простой и сенсорной;
  • узел исполнитель с твердотельным реле, к которому можно подключить нагрузку, работающую от постоянного или от переменного тока.

Предполагается, что оба узла имеют автономное батарейное питание.

Рис. 1. Схематичное представление беспроводного выключателя и исполнительного устройства

Рис. 1. Схематичное представление беспроводного выключателя и исполнительного устройства

Разработка программного обеспечения для узлов

Рассмотрев и протестировав возможные варианты среди примеров, предлагаемых TI из входящих в SDK для СС13ХХ (в частности – EasylinkRX, EasylinkTX, EasylinkNP, rfWakeOnRadioRX, rfWakeOnRadioTx, sensor1350lp), был остановлен выбор на паре rfWakeOnRadioRX + rfWakeOnRadioTx.

Выбор связки примеров rfWakeOnRadioRx + rfWakeOnRadioTx для отладочной платы CC1350 LaunchPad обусловлен еще и тем, что она практически полностью реализует функционал беспроводного выключателя. Кроме того, данная связка позволяет использовать один и тот же код и для узлов с автономным питанием, и для узлов, имеющих стационарное питание.

rfWakeOnRadioRx переводит радиомодуль СС1350 в режим Wake-On-Radio, имеющий существенные преимущества в потреблении тока по сравнению с просто режимом приема – средний ток потребления снижается в несколько раз. Модуль автоматически переходит в режим приема при обнаружении приходящего пакета.

Из аппаратных устройств отладочной платы задействована пара светодиодов:

  • Board_PIN_LED0 (красный диод) загорается в то время, когда трансивер находится в режиме приема, остальное время мигает, показывая фоновую активность трансивера в режиме Wake-On-Radio;
  • Board_PIN_LED1 (зеленый) – изменяет свое состояние после каждого успешного приема пакета, в начале работы программы не светится.

rfWakeOnRadioTx работает в паре с rfWakeOnRadioRx – по нажатию кнопки он передает пакет специального формата (по сравнению с обычным увеличена длина преамбулы) и переключает состояние светодиода после каждой передачи пакета.

Задействуются следующие ресурсы платы LaunchPad:

  • Board_PIN_LED1 (зеленый) – переключается после каждой передачи пакета;
  • Board_PIN_BUTTON0 – инициирует процедуру передачи пакета.

Также выдаются сообщения о ходе выполнения программы по интерфейсу UART и обновляется информация на модуле LCD, если он подключен.

На рисунке 2 представлена схема подключения светодиодов и кнопок к модулю MBee-DUAL-3.3-UFL-SOLDER-1350-UFL, позволяющая запускать и наблюдать выполнение примеров rfWakeOnRadioTx/Rx.

Рис. 2. Схема подключения светодиодов и кнопок к модулю MBee-DUAL-3.3-UFL-SOLDER-1350-UFL, позволяющая запускать и наблюдать выполнение примеров rfWakeOnRadioTx/Rx

Рис. 2. Схема подключения светодиодов и кнопок к модулю MBee-DUAL-3.3-UFL-SOLDER-1350-UFL, позволяющая запускать и наблюдать выполнение примеров rfWakeOnRadioTx/Rx

Структура rfWakeOnRadioRx

Структура программного проекта rfWakeOnRadioRx достаточно проста. В нем всего одна задача rxTask и несколько call-back функций.

Функция main (файл rfWakeOnRadioRx.c):

/*

*  ======== main ========

*/

int main(void)

{

/* Общая инициализация платы и драйверов */

Board_initGeneral();

/* Настройка линий для управления светодиодами */

ledPinHandle = PIN_open(&ledPinState, pinTable);

Assert_isTrue(ledPinHandle != NULL, NULL);

/* Инициализация задачи rxTask */

rxTaskInit();

/* Запуск операционной системы */

BIOS_start();

return (0);

}

rxTaskInit настраивает параметры задачи – размер стека, приоритет, функцию задачи. Основная рабочая функция данного проекта – rxTaskFunction. В ней производится настройка параметров трансивера, создается очередь сообщений, настраивается прием в режиме прослушивания (режим Wake-On-Radio) и в бесконечном цикле ожидается приход пакетов:

/* RX task function. Executed in Task context by TI-RTOS when the scheduler starts. */

static void rxTaskFunction(UArg arg0, UArg arg1)

{

/* Инициализируем параметры приемопередатчика */

RF_Params rfParams;

RF_Params_init(&rfParams);

/* Вывод, подключенный к LED1, настраивается таким образом, чтобы отображать активность трансивера в режиме приема */

PINCC26XX_setMux(ledPinHandle, Board_PIN_LED0, PINCC26XX_MUX_RFC_GPO0);

/* Создание очереди сообщений */

if (RFQueue_defineQueue(&dataQueue,

rxDataEntryBuffer,

sizeof(rxDataEntryBuffer),

NUM_DATA_ENTRIES,

MAX_LENGTH + NUM_APPENDED_BYTES))

{

/* Failed to allocate space for all data entries */

while(1);

}

/* Настройка sniff-режима на прием */

initializeSniffCmdFromRxCmd(&RF_cmdPropRxSniff, &RF_cmdPropRx);

/* Настройка приемника */

RF_cmdPropRxSniff.pQueue = &dataQueue;

RF_cmdPropRxSniff.pOutput = (uint8_t*)&rxStatistics;

RF_cmdPropRxSniff.maxPktLen = MAX_LENGTH;

/* Настройка отбрасывания пакетов с ошибкой контрольной суммы из очереди пакетов */

RF_cmdPropRxSniff.rxConf.bAutoFlushIgnored = 1;

RF_cmdPropRxSniff.rxConf.bAutoFlushCrcErr = 1;

/* Calculate datarate from prescaler and rate word */

uint32_t datarate = calculateSymbolRate(RF_cmdPropRadioDivSetup.symbolRate.preScale,RF_cmdPropRadioDivSetup.symbolRate.rateWord);

/* Configure Sniff-mode part of the RX_SNIFF command */

configureSniffCmd(&RF_cmdPropRxSniff, WOR_MODE, datarate, WOR_WAKEUPS_PER_SECOND);

/* Получение доступа к трансиверу, по факту – инициализация работы через драйвер */

rfHandle = RF_open(&rfObject, &RF_prop, (RF_RadioSetup*)&RF_cmdPropRadioDivSetup, &rfParams);

/* Настройка рабочей частоты */

RF_runCmd(rfHandle, (RF_Op*)&RF_cmdFs, RF_PriorityNormal, &callback, 0);

/* Сохраняем для отсчета тайминга текущее системное время приемника */

RF_cmdPropRxSniff.startTime = RF_getCurrentTime();

/* Основной рабочий цикл */

while(1)

{

/* Задаем интервал для следующего пробуждения */

RF_cmdPropRxSniff.startTime += WOR_WAKE_UP_INTERVAL_RAT_TICKS(WOR_WAKEUPS_PER_SECOND);

/* Проверяем приход пакета в режиме прослушивания, в случае успешного приема будет выполнена процедура callback */

RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropRxSniff, RF_PriorityNormal, &callback, RF_EventRxEntryDone);

/* Фиксируем результат прослушивания эфира */

switch(RF_cmdPropRxSniff.status) {

case PROP_DONE_IDLE:

/* Idle based on RSSI */

worStatistics.doneIdle++;

break;

case PROP_DONE_IDLETIMEOUT:

/* Idle based on PQT */

worStatistics.doneIdleTimeout++;

break;

case PROP_DONE_RXTIMEOUT:

/* Got valid preamble on the air, but did not find sync word */

worStatistics.doneRxTimeout++;

break;

case PROP_DONE_OK:

/* Received packet */

worStatistics.doneOk++;

break;

default:

/* Unhandled status */

break;

};

}

}

В случае успешного приема пакета вызывается процедура callback, в которая изменяет состояние зеленого светодиода:

void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)

{

/* Проверяем получение пакета и его доступность для чтения */

if (e & RF_EventRxEntryDone)

{

/* Запускается цикл обработки пакетов по всем пакетам, находящимся в очереди */

do

{

/* Если пакет получен – переключаем светодиод */

PIN_setOutputValue(ledPinHandle, Board_PIN_LED1, !PIN_getOutputValue(Board_PIN_LED1));

/* Получаем указатель на текущий необработанный пакет */

currentDataEntry = RFQueue_getDataEntry();

/* Обрабатываем пакет, расположенный в очереди по адресу &currentDataEntry->data:

* – Length – первый байт содержит текущую конфигурацию

* – Data – данные пакета начинаются со второго байта */

packetLength = *(uint8_t*)(&currentDataEntry->data);

packetDataPointer = (uint8_t*)(&currentDataEntry->data + 1);

/* Здесь можно добавлять свои процедуры для обработки пакета, например, фильтрацию адресов, разбор пакета и прочее */

volatile uint8_t dummy;

dummy = packetLength + packetDataPointer[0];

} while(RFQueue_nextEntry() == DATA_ENTRY_FINISHED);

}

}

Фактически, если исполнительное устройство одно и в зоне радиовидимости приемного узла нет других передатчиков – узел с проектом rfWakeOnRadioRx может смело управлять нагрузкой, достаточно подключить вход твердотельного реле к выводу Board_PIN_LED1.

В реальной практике лучше будет создать специальный формат пакета на стороне передатчика, добавить процедуры его разбора в приемнике перед изменением состояния светодиода и, соответственно, перед управлением нагрузкой.

Структура rfWakeOnRadioTx

Функция main в проекте передатчика rfWakeOnRadioTx аналогична примеру с приемником, это – инициализация драйверов, настройка выводов для кнопок и светодиодов, инициализация задачи txTask и запуск операционной системы.

Центральная рабочая функция в данном проекте – txTaskFunction():

/* TX task function. Executed in Task context by TI-RTOS when the scheduler starts. */

static void txTaskFunction(UArg arg0, UArg arg1)

{

/* Задание callback-функции для обработки нажатия кнопки */

PIN_Status status = PIN_registerIntCb(buttonPinHandle, &buttonCallbackFunction);

Assert_isTrue((status == PIN_SUCCESS), NULL);

/* Инициализация трансивера */

RF_Params rfParams;

RF_Params_init(&rfParams);

/* Инициализация режима TX_ADV */

initializeTxAdvCmdFromTxCmd(&RF_cmdPropTxAdv, &RF_cmdPropTx);

/* Задание значений полей пакета */

RF_cmdPropTxAdv.pktLen = PAYLOAD_LENGTH +1; /* +1 for length byte */

RF_cmdPropTxAdv.pPkt = packet;

RF_cmdPropTxAdv.preTrigger.triggerType = TRIG_REL_START;

RF_cmdPropTxAdv.preTime = WOR_PREAMBLE_TIME_RAT_TICKS(WOR_WAKEUPS_PER_SECOND);

/* Получение доступа задачи к трансиверу */

rfHandle = RF_open(&rfObject, &RF_prop, (RF_RadioSetup*)&RF_cmdPropRadioDivSetup, &rfParams);

/* Настройка рабочей частоты */

RF_runCmd(rfHandle, (RF_Op*)&RF_cmdFs, RF_PriorityNormal, NULL, 0);

/* Основной цикл задачи */

while(1)

{

RF_yield(rfHandle); // add

/* Ожидание нажатия кнопки: обработчик нажатия кнопки посылает сообщение в очередь задачи */

Semaphore_pend(txSemaphoreHandle, BIOS_WAIT_FOREVER);

/* Создается пакет с инкрементируемым номером (будет увеличиваться после каждой транзакции), содержимое пакета – случайная последовательность */

packet[0] = PAYLOAD_LENGTH;

packet[1] = (uint8_t)(seqNumber >> 8);

packet[2] = (uint8_t)(seqNumber++);

uint8_t i;

for (i = 3; i < PAYLOAD_LENGTH +1; i++)

{

packet[i] = rand();

}

/* Вызов команды для передачи пакета */

RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropTxAdv, RF_PriorityNormal, NULL, 0);

/* Переключение состояния светодиода */

PIN_setOutputValue(ledPinHandle, Board_PIN_LED1, !PIN_getOutputValue(Board_PIN_LED1));

}

}

Обработчик нажатия кнопки просто посылает сообщение задаче передачи данных, а также обеспечивает программный антидребезг:

/* Pin interrupt Callback function board buttons configured in the pinTable. */

void buttonCallbackFunction(PIN_Handle handle, PIN_Id pinId) {

/* Ппростая реализация антидребезга (пауза и проверка, нажата ли еще кнопка) */

CPUdelay((uint32_t)((48000000/3)*0.050f));

if (!PIN_getInputValue(pinId)) {

/* Post TX semaphore to TX task */

Semaphore_post(txSemaphoreHandle);

}

}

Для работы в условиях нескольких узлов данный пример требует формирования пакета заранее определенного формата, а также ожидания подтверждения принятия пакета от исполнительного узла.

Реализация сенсорной кнопки на SensorController

Рассмотрим реализацию сенсорной кнопки для узла-выключателя. Основным преимуществом емкостных сенсорных кнопок является отсутствие подвижных элементов и контактов, способных окисляться или изнашиваться механически. Однако за эти преимущества приходится платить необходимостью ее обслуживания, то есть должен быть или аппаратный, или программный компонент, определяющий момент прикасания пользователя к поверхности.

В случае с CC13xx на помощь приходит SensorController, имеющий все необходимые периферийные устройства для обслуживания емкостной сенсорной кнопки без дополнительных внешних элементов – не требуется даже резисторов или конденсаторов.

Создаем проект в SensorControllerStudio. Поскольку в дальнейшем предполагается интеграция проекта rfWakeOnRadioTx – настраиваем сохранение сгенерированных исходников в папку проекта, например, C:/Users/111/workspace_v7/rfWakeOnRadioTx_CC1350_LAUNCHXL_tirtos_ccs.

Пример программных сенсорных кнопок для SensorController можно посмотреть в системе помощи SensorControllerStudio в разделе Peripherals → ISRC (а также – Peripherals → TDC, но первый вариант лучше).

Согласно справке, емкостная сенсорная кнопка может быть подключена к одному из выводов DIO23…DIO30. Задействуются встроенный источник тока ISRC, компаратор COMPA, опорный источник тока 2 мкА, встроенный резистор 400 кОм, TDC – преобразователь «время-код». На рисунке 3 показан выбор требуемых библиотек.

Рис. 3. Настройки библиотек в SensorControllerStudio для проекта сенсорной кнопки

Рис. 3. Настройки библиотек в SensorControllerStudio для проекта сенсорной кнопки

Также в справке приводится пример исходного кода для опроса сенсорных кнопок. Алгоритм следующий:

  1. разрешается работа компаратора и опорного источника тока 2 мкА с резистором 400 кОм;
  2. запускается источник тока;
  3. выбирается источник тактирования для преобразователя «время-код»;
  4. запускается отсчет преобразователя «время-код» с условием останова по достижении верхнего порога;
  5. выжидается время паузы;
  6. вывод кнопки-сенсора переключается на общий (сбрасывается заряд емкости кнопки);
  7. считываются показания преобразователя «время-код».

Рисунки 4 и 5 демонстрируют схему подключения сенсорной кнопки к модулю MBee Dual (к выводу DIO26) и настройки I/O Mapping в SensorControllerStudio соответственно.

Рис. 4. Схема подключения сенсорной кнопки к модулю MBee Dual

Рис. 4. Схема подключения сенсорной кнопки к модулю MBee Dual

Рис. 5. Настройки I/O Mapping в SensorControllerStudio

Рис. 5. Настройки I/O Mapping в SensorControllerStudio

На практике настройки источника тока для пункта 2 алгоритма и время ожидания в пункте 5 подбираются в зависимости от конфигурации, размеров контактов сенсорной кнопки и толщины и материала диэлектрического слоя.

Код проекта сенсорной кнопки содержит две непустые секции – Initialization Code и Execution Code. Код в них практически идентичный, но из-за особенностей формирования программного кода для SC приходится его дублировать в обеих секциях с незначительными изменениями.

Создаются следующие переменные, доступные и для основного ядра:

  • cfg.tdch и cfg.tdcL – переменные, хранящие время заряда емкостной кнопки в ненажатом состоянии, значение в них записывается при инициализации задача в SC;
  • output.tdch и output.tdcL – переменные, хранящие время заряда емкостной кнопки в нажатом состоянии;
  • output.touch – переменная-флаг, показывающая факт срабатывания сенсорной кнопки, которая устанавливается в 1 при обнаружении нажатия и сбрасывается в 0 в начале каждого цикла опроса.

Ниже приведен код для опроса сенсорной кнопки в секции Execution Code с комментариями отличий от секции Initialization Code:

output.touch=0;

// Разрешение работы COMPA, включая источник тока 2 мкА с нагрузкой в виде резистора 400 кОм с порогом 0,8 В от источника опорного напряжения

compaEnableWithCapTouchRef();

// Запускается источник тока со значением выходного тока 1,0 мкА

U16 current = BV_ISRC_CURR_1P0U; // *Подбирается вручную

isrcEnable(current);

// Выбирается источник тактирования преобразователя «время-код»

tdcSetCntSource(TDC_CNTSRC_96M_RCOSC);

// Запускается преобразователь «время-код» со срабатыванием по факту заряда емкости контактов кнопки

tdcSetTriggers(TDC_STARTTRIG_ISRC_RELEASE, TDC_STOPTRIG_COMPA_HIGH, 0);

tdcEnable();

// Выбирается линия ввода-вывода с сенсорным контактом

compaSelectGpioInput(AUXIO_AXD_CAP_SENS_BUTTON);

// Подготавливается преобразователь «время-код» и запускается процесс измерений

tdcArm(TDC_START_ASYNC);

isrcRelease(AUXIO_AXD_CAP_SENS_BUTTON);

// Пауза ожидания в 100 мкс

tdcWaitUs(100); // *Подбирается вручную

// Сенсорный контакт переключается на «землю»

// Принудительно разряжается емкость контактов

isrcClamp(AUXIO_AXD_CAP_SENS_BUTTON);

// Считывается значение преобразователя «время-код»

tdcGetValue(output.tdcH, output.tdcL);

// tdcGetValue(cfg.tdcH, cfg.tdcL); – для секции Initialization

// Останавливается работа COMPA, ISRC and TDC

tdcDisable();

isrcDisable();

compaDisable();

// Задаем порог для исключения ложного срабатывания от помех и прочего

U16 sens_level = cfg.tdcL + (cfg.tdcL >> 3);   // нет в секции Initialization

// Если порог превышен – уведомляем основное ядро через сообщение ALERT

// Alert application                           // нет в секции Initialization

if(output.tdcL>sens_level) {                   // нет в секции Initialization

output.touch=1;                                // нет в секции Initialization

fwGenAlertInterrupt();                         // нет в секции Initialization

}                                              // нет в секции Initialization

// Schedule execution next tick

fwScheduleTask(1);

На рисунке 6 представлен макетный вариант связки сенсорной кнопки и модуля Mbee-DUAL. Рисунок 7 иллюстрирует работу программы опроса емкостной сенсорной кнопки: показан фоновый уровень output.tdcL и пики его возрастания в моменты прикосновения к сенсору. Срабатывание порога отраженно в графике переменной output.touch: видно, что мелкие отклонения от фонового уровня успешно фильтруются.

Рис. 6. Макетный вариант связки сенсорной кнопки и модуля Mbee-DUAL

Рис. 6. Макетный вариант связки сенсорной кнопки и модуля Mbee-DUAL

Рис. 7. Тестирование работы сенсорной кнопки

Рис. 7. Тестирование работы сенсорной кнопки

Осталось интегрировать проект в rfWakeOnRadioTx.

Интеграция сенсорной кнопки проект в rfWakeOnRadioTx

Включение в основной проект кода для SensorController достаточно простая задача, которая занимает всего несколько шагов. В SensorControllerStudio генерируем набор исходных файлов, который и будет внесен в проект.

В файле rfWakeOnRadioTx.c после подключения основных заголовочных файлов (после строчки #include «smartrf_settings/smartrf_settings.h») добавляем подключение файлов SC и объявляем прототипы функций обработчиков сообщений от SC:

// +++++++++++++++

#include "scif.h"

#define BV(n)               (1 << (n))

#include "scif_framework.h"

#include "scif_osal_tirtos.h"

/*+++ Prototypes *****/

static void ctrlReadyCallback(void);

static void taskAlertCallback(void);

//++++++++++++++++++++++++++

Перед определением функции main добавляем функции-обработчики сообщений READY и ALERT от SensorController, а также функцию инициализации и запуска SensorController.

Обработчик сообщения READY будет пустым, в данном проекте это сообщение не генерируется.

//++++++++++

void scCtrlReadyCallback(void) {

} // scCtrlReadyCallback

Обработка сообщения ALERT включает в себя снятие флага прерывания от SC, передачу сообщения задаче txTask через семафор и уведомление SC об успешной обработке его сообщения:

void scTaskAlertCallback(void) {

scifClearAlertIntSource();

Semaphore_post(txSemaphoreHandle);

// Acknowledge the alert event

scifAckAlertEvents();

}

Функция scTaskInit производит настройку и запуск SC:

void scTaskInit(){

// Инициализация SC

// Настройка обработчиков

scifOsalInit();

scifOsalRegisterCtrlReadyCallback(scCtrlReadyCallback);

scifOsalRegisterTaskAlertCallback(scTaskAlertCallback);

scifInit(&scifDriverSetup);

// Настройка частоты вызова функции опроса кнопки контроллером SC

// 0x00010000 соответствует 1 разу в секунду

// Подбирается вручную для достижения желаемой скорости отклика

scifStartRtcTicksNow(0x00007000);

scifStartTasksNbl(BV(SCIF_BUTTON_TASK_ID));

}

//++++++++++

В функцию main добавляется вызов функции инициализации SC:

...

/* Initialize task */

txTaskInit();

// start SC task

scTaskInit();

/* Start BIOS */

BIOS_start();

...

Проект компилируется, и полученный образ прошивается в узел-выключатель. Теперь передачу пакета будет инициализировать или прикосновение к сенсорной площадке, или нажатие кнопки. Средний ток потребления при этом вырос всего на 17…20 мкА.

Приближение к реальности

В качестве простейшего примера немного модифицируем основной код примеров rfWakeOnRadioTx/Rx до минимально полезной функциональности, такой как адресная передача и прием пакетов и передача состояния нагрузки (выкл/вкл). Для этого добавим в проекты переменные состояния нагрузки и ее адрес.

Для этого в проекте rfWakeOnRadioTx помещаем перед определениями функции txTaskFunction объявления следующих переменных:

uint8_t switch_state=0; // Состояние ключа: 0 – выкл, не 0 – вкл

uint8_t addrL=0x55;     // Младший байт адреса

uint8_t addrH=0x77;     // Старший байт адреса

#define Clock_tickPeriod ti_sysbios_knl_Clock_tickPeriod //Определение для перевода внутренних тиков во временные интервалы

В основном рабочем цикле функции txTaskFunction вводим изменение и передачу переменной состояния нагрузки и заменяем переключение состояния светодиода индикации на его мигание при отправке пакета:

...

/* Enter main TX loop */

while(1)

{

// Инвертировали состояние переменной-ключа

switch_state=~switch_state;

RF_yield(rfHandle); // add

/* Ожидание нажатия кнопки – обработчик нажатия кнопки посылает сообщение в очередь задачи */

Semaphore_pend(txSemaphoreHandle, BIOS_WAIT_FOREVER);

// Формируем пакет данных

packet[0] = PAYLOAD_LENGTH;            // Длина пакета

packet[1] = (uint8_t)(seqNumber >> 8); // Порядковый номер

packet[2] = (uint8_t)(seqNumber++);

packet[3] = addrL;                     // Младший байт адреса

packet[4] = addrH;                     // Старший байт адреса

packet[5] = switch_state; // Состояние ключа: начальное 0 – выключено

/* Передаем сформированный пакет */

RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropTxAdv, RF_PriorityNormal, NULL, 0);

// «Мигаем» светодиодом индикации

PIN_setOutputValue(ledPinHandle, Board_PIN_LED1, 1); // Зажигаем диод

Task_sleep(50000 / Clock_tickPeriod);                // Пауза 50 мс

PIN_setOutputValue(ledPinHandle, Board_PIN_LED1, 0); // Гасим диод

// Уведомление ядра SC об успешной обработке его прерывания

scifAckAlertEvents();

}

...

Аналогичные изменения проведем и в проекте rfWakeOnRadioRx. Перед функцией rxTaskFunction определим переменные для хранения состояния нагрузки/ключа, полученного в пакете адреса и адреса самого узла:

uint8_t switch_state=0;    // Состояние ключа

uint8_t addrL=0x55;        // Младший байт адреса узла

uint8_t addrH=0x77;        // Старший байт адреса узла

uint8_t RXaddrL=0;         // Младший байт адреса, полученного в пакете

uint8_t RXaddrH=0;         // Старший байт адреса, полученного в пакете

Весь анализ пакета будет проводится в функции callback, вызываемой в случае успешного приема пакета:

void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)

{

/* Если пакет получен успешно и доступен для чтения - переходим к его анализу */

if (e & RF_EventRxEntryDone)

{

do

/* Берем первую непрочитанную запись из очереди пакетов */

currentDataEntry = RFQueue_getDataEntry();

/* В данном примере обрабатываются только адрес получателя и текущее желаемое состояние ключа */

// Считали младшую и старшую часть адреса в принятом пакете – соответственно 3-й и 4-й байты

RXaddrL = *(uint8_t*)(&currentDataEntry->data+3);

RXaddrH = *(uint8_t*)(&currentDataEntry->data+4);

// Считывается состояние ключа

switch_state = *(uint8_t*)(&currentDataEntry->data+5);

// Простая проверка на совпадение адреса узла и принятого адреса

if((RXaddrL==addrL)&&(RXaddrH==addrH)){

// Изменение состояния нагрузки в соответствии с полученным значением

PIN_setOutputValue(ledPinHandle, Board_PIN_LED1, switch_state);

};

} while(RFQueue_nextEntry() == DATA_ENTRY_FINISHED);

}

}

Средний ток потребления узла-выключателя составляет порядка 10 мкА, узла-приемника 35 мкА. Обычная емкость батарейки-таблетки 3 В размера CR2032 – в районе 200 мА·ч. При питании наших узлов от подобных батареек получим длительность работы 20000 (около 2 лет) и 5700 (8 месяцев) часов для узла-выключателя и узла-приемника соответственно (при расчете не учитывался ток управления нагрузкой в узле-приемнике).

Заключение

Беспроводные СНК серии СС13хх являются привлекательными кандидатами для простых беспроводных устройств в виду высокой интеграции разнообразных аппаратных ресурсов и малого потребления. Добавление нового функционала в устройства и системы на их основе также реализуется достаточно просто благодаря возможности разделения задач между ядрами, входящими в состав СНК.

Дополнительным преимуществом для серии СС1350 является возможность параллельно с работой сети управления транслировать состояние узлов в диапазоне 2400 МГц через широковещательные BLE-пакеты, что позволяет отображать эти данные на мобильных устройствах пользователя – смартфонах, планшетах или ноутбуках.

Архив проекта

Наши информационные каналы

О компании Texas Instruments

В середине 2001 г. компании Texas Instruments и КОМПЭЛ заключили официальное дистрибьюторское соглашение, которое явилось результатом длительной и успешной работы КОМПЭЛ в качестве официального дистрибьютора фирмы Burr-Brown. (Как известно, Burr-Brown вошла в состав TI так же, как и компании Unitrode, Power Trend и Klixon). С этого времени компания КОМПЭЛ получила доступ к поставке всей номенклатуры производимых компанией TI компонентов, ...читать далее