№5 / 2017 / статья 3

CC26хх/СС13хх – от созерцания к практике

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

Автор представляет основанную на собственном инженерном опыте практическую пошаговую инструкцию создания узла сенсорной сети или Интернета вещей на базе беспроводной системы-на-кристалле СС2650/CC2640 производства Texas Instruments с протоколом BLE.

Системы-на-кристалле (с-н-к) серий СС26хх/CC13хх, входящие в семейство SimpleLink производства компании Texas Instruments являются на текущий момент одной из передовых платформ для реализации распределенных беспроводных систем различного назначения, включая Интернет вещей (Internet of Things, IoT). В числе явных преимуществ:

  • малое энергопотребление при равной дальности связи по сравнению с конкурирующими решениями;
  • бесплатно предоставляемая RTOS для всей серии микроконтроллеров;
  • возможность явно разделить задачи управления процессами и/или сбора данных с задачами сетевого обмена [1].

Малое энергопотребление достигается за счет нововведений в схемотехнике приемопередатчика и микроконтроллера, а также продвинутому техпроцессу. TIRTOS существенно упрощает жизнь программисту в плане управления ресурсами с-н-к, режимами энергопотребления, взаимодействием со стеками протоколов. И, конечно же, главная отличительная особенность – 3-ядерное решение с-н-к: производительное ядро Cortex-M3 для прикладных задач и высокоуровневых сетевых взаимодействий, экономичное ядро Cortex-M0 для управления радиочастью и микромощный специализированный RISC-контроллер для опроса аналоговых и цифровых датчиков (Sensor Controller, SC), не зависящий от остальных ядер. Для программирования доступны два микроконтроллерных ядра – Cortex-M3 и Sensor Controller.

Многим аспектам и приемам работы с данными представителями семейства SimpleLink посвящен ряд публикаций [2…4], том числе – и на популярных среди прикладных программистов и гиков ресурсах [5, 6].

Одной из привлекательных возможностей с-н-к СС26хх/СС13хх является способность реализовать стратегию «разделяй и властвуй», а именно – разнести код на различные микроконтроллерные ядра. Подобный подход актуален в случаях, когда требуется:

  • снижение энергопотребления за счет преимущественной работы ядра SC при опросе и первичной обработке датчиков;
  • снижение сложности приложения, работающего со стеком протоколов;
  • упрощение миграции прикладных задач при переходе с одного протокола на другой;
  • сокращение «времени входа/адаптации» для разработчиков за счет интеграции функциональности в предлагаемые производителем готовые программные проекты, например, проекты сетевых процессоров, беспроводной замены интерфейсов или маячков;
  • уменьшение размера кода для основного микроконтроллерного ядра Cortex-M3, особенно в случае использования «тяжеловесных» стеков протоколов типа ZigBee или BLE.

Постановка задачи

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

Предполагаем, что узел сенсорной или IoT-сети будет реализовываться на с-н-к серии СС26хх, а именно – на СС2650/CC2640 с протоколом BLE. Выбор протокола BLE обусловлен желанием обеспечить дружественный пользовательский интерфейс за счет отображения данных и управления ими при помощи любого смартфона с поддержкой BLE. В приоритет ставим энергопотребление – рутинные операции по опросу датчиков с сохранением результатов переводим на SC, а доступ к данным организуем из основного приложения. Предполагаем, что в течение довольно длительного периода узлу будет доступна связь с центральным устройством, на которое накопленные данные необходимо будет передать. Предположим также, что центральное устройство в случае его наличия/включения/появления в зоне доступности узла будет готово к сеансу связи (ожидать его) и будет готово принять данные.

Инструментарий

Из относительно недорогих отладочных средств для начала работы с сериями СС13хх/СС26хх можно выделить отладочные платы SimpleLink LaunchPad [2], демонстрационные платы SensorTag [3, 7] (рисунок 1). Оба типа плат обладают преимуществами в различных ситуациях.

а)

а)

б)

б)

Рис. 1. Отладочные платы: а) SimpleLink LaunchPad; б) SensorTag

Платы LaunchPad имеют встроенный программатор и достаточно большое количество удобных для макетирования разъемов. Более того, часть платы с программатором может быть полностью изолирована от микроконтроллера, и возможна автономная работа контроллера, например, в составе макета конечного устройства.

В свою очередь, плата SensorTag ввиду малых размеров и большого разнообразия установленных на ней датчиков подходит для тестирования wearable-проектов, работы с различными типами датчиков, а иногда используется и как законченное решение. Минус – необходим внешний программатор, и из-за специфических разъемов не всегда удобно подключать свои периферийные устройства.

Собственно, из-за наличия датчиков будем ориентироваться на платы SensorTag с тайной мыслью применения ее в качестве логгера данных или трекера. Тем более, что на самой плате установлена еще и достаточно большая микросхема Flash-памяти (W25X20CLUXIG 2 Мбит), которую можно использовать для этого при условии, что не будет задействовано обновление ПО «по воздуху».

Sensor Controller – сохранение энергии для длительной работы

Контроллер датчиков (SC) является небольшим 16-битным микромощным ядром с достаточно богатым набором периферийных устройств. Плюс к этому он располагается во внешнем домене питания основного ядра (AUX), что позволяет ему быть активным даже во время отключения или периодов неактивности основного ядра. SC обладает 2 кбайт оперативной памяти, что для него означает 1024 16-битных слов данных или инструкций. Энергопотребление SC в активном режиме составляет всего 8,2 мкA/МГц (+500 мкА на питание тактового генератора 24 МГц). В режиме Standby контроллер тактируется от 32 кГц и его ток потребления не превышает 1 мкА. С учетом того, что измерения и их запись в память занимают достаточно короткий промежуток времени, а остальное время занимает ожидание события (истечения интервала времени или прерывания по внешним выводам), средний ток остается очень малым.

SC находится в активном режиме в процессе выполнения кода задачи, и в режиме ожидания (standby) в остальное время. Процесс переключения между режимами прозрачен для приложения (приятная опция в стиле процессоров XMOS и GreenArrayChips).

Общее энергопотребление SC можно оценить, исходя из режима его работы и тактовой частоты, на которой он работает в текущем режиме (таблица 1).

Таблица 1. Режимы тактирования Sensor Controller

Режим System CPU/MPU domain активен System CPU/MPU domain
в режиме ожидания
Sensor Controller активен – (выполнение линейного активного кода) SCLK_HF/2 = 24 МГц SCLK_HF/2 = 24 МГц
Sensor Controller исполняет только код обработчиков событий SCLK_HF/2 = 24 МГц SCLK_LF = 32КГц
Sensor Controller Standby (Without Event Trigger) SCLK_HF/2 = 24 МГц Нет тактирования

Взаимодействие с основным ядром (так называемый MCU domain) осуществляется посредством двух типов уведомлений:

  • READY – для запуска или остановки задач на SC;
  • ALERT – для пробуждения основного ядра и инициализации обмена данными.

Программируется SC отдельно. А если точнее, исходный код для него генерируется отдельно от кода для основного ядра инструментарием Sensor Controller Studio и впоследствии размещается в коде основного проекта.

С бубном и плясками: запускаем пример

Из всех датчиков, установленных на SensorTag, пример для SC есть только под датчик освещенности OPT3001 [8] производства Texas Instruments. Между прочим, очень хороший датчик, и для видимого спектра, особенно в диапазоне высоких яркостей, обладает отличными передаточными характеристиками – в частности, даже в условиях ясного неба и прямого освещения на высоте 1,7 км датчик не входит в насыщение. С его запуска и начнем.

Перед началом работы следует установить:

  • Code Composer Studio (6.1.1.00022);
  • Sensor Controller Studio (1.4.0.42970);
  • Flash Programmer 2 (1.7.5);
  • BLE stack (2.02.01.18);
  • возможно, дополнительно придется установить TIRTOS (2.20.01.08) из AppsCenter в CCS;
  • при желании можно установить и примеры с обучающим материалом – Simple Link Academy.

Рассмотрим процесс запуска штатного тестового примера – I²C Light Sensor из Sensor Controller Studio (SCS).

Запускается SCS (рисунок 2) и находится данный пример – он последний в списке поля Example.

Открывается пример, и в полях прописываются пути для выходных файлов проекта, серия контроллера, тип корпуса, версия SDK. Как правило, все поля заполнены по умолчанию, и обычно этого достаточно. Ключевым здесь является поле «версия SDK». В нем необходимо указать ту версию операционной системы TIRTOS, которая была установлена (при установке, скажем, стека BLE, TIRTOS устанавливается автоматически), так как в противном случае дальнейшая работа с примером будет затруднена (рисунок 3).

В случае успешного открытия проекта появляется поле-навигатор проекта и показывается общая информация о проекте (рисунок 4). В проект могут входить одна или несколько задач. В проекте I²C Light Sensor задача одна.

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

  • внешние выводы и периферийные устройства;
  • способы взаимодействия с основным ядром (Cortex M3);
  • источники прерываний;
  • настройки специфики выполнения кода контроллером датчиков.

Клик на значок «?» напротив каждой из позиций приводит к выдаче справки по указанной тематике.

В I²C Light Sensor настройки следующие: первый запуск по событию RTC таймера, вызов обработчика событий по таймеру AUX Timer 1, задействуется интерфейс I²C, взаимодействие с основным кодом посредством генерации прерываний ALERT.

I/O Mapping позволяет посмотреть выводы, задействованные SC всеми своими задачами (рисунок 5).

Рис. 2. Стартовое окно Sensor Controller Studio

Рис. 2. Стартовое окно Sensor Controller Studio

Рис. 3. Выбор версии TIRTOS для примера I2C Light Sensor

Рис. 3. Выбор версии TIRTOS для примера I2C Light Sensor

Рис. 4. Отображение общих сведений о проекте I2C Light Sensor

Рис. 4. Отображение общих сведений о проекте I2C Light Sensor

Рис. 5. Отображение ресурсов SC, задействованных в проекте

Рис. 5. Отображение ресурсов SC, задействованных в проекте

Код отдельной задачи SC содержит четыре секции:

  • Initialization Code – блок инициализации, запускаемый единожды при старте задачи;
  • Execution Code – исполняемый код приложения, вызываемый по срабатыванию AUX-RTC-таймера (задействован канал таймера AON_RTC channel 2);
  • Event Handler Code – обработчики событий внешнего таймера, событий на выводах;
  • Termination Сode – код, выполняемый по завершению задачи.

SC не поддерживает механизм прерываний в его классическом виде. Блоки кода в случае их запуска выполняются до конца – то есть полностью без переключения на выполнение. При этом переключение контекста осуществляется перед непосредственным переходом SC в режим ожидания (если кто-то работал со старыми многозадачными форт-системами – это аналог принудительного включения слова PAUSE для перехода на следующую задачу).

Рассмотрим исходные тексты каждого из блоков.

Initialization Code

Содержит единственную строчку кода, предписывающую запустить выполнимый код при следующем срабатывании RTC-таймера (формально – с задержкой на один тик таймера).

// Schedule the first execution

fwScheduleTask(1);

Execution Code

Данная секция выполняется каждый раз по срабатыванию таймера AUX-RTC. Частота срабатывания таймера может быть настроена в коде приложения для основного ядра при помощи функций scifStartRtcTicksNow()/scifStartRtcTicks().

Последовательность действий в данной секции примера следующая:

  • Конфигурируется и запускается датчик OPT3001 в режиме одиночного измерения [8]. Время интегрирования – 100 мс.

// Configure and start the next measurement

i2cStart();

i2cTx(I2C_OP_WRITE | ALS_I2C_ADDR);

i2cTx(ALS_REG_CFG);

i2cTx(ALS_CFG_ONE_SHOT >> 8);

i2cTx(ALS_CFG_ONE_SHOT >> 0);

i2cStop();
  •  Устанавливается задержка срабатывания события по таймеру AUX Timer 1, SC переводится в режим Standby до срабатывания триггера таймера. Задержка задается в количестве 4 кГц тактовых импульсов, генерируемых RTC. Первый аргумент функции – всегда 0 в для текущих версий (не используется). Второй аргумент – мантисса числа mant × 2^exp [4 kHz ticks] (диапазон 1…255), третий аргумент – его экспонента (диапазон 0…15). То есть в данном случае задержка срабатывания – 120 × 22 импульсов или 120 × 4 = 480 импульсов по 0,25 мс каждый, итого 120 мс.
// Read the result after 100 milliseconds + a 20% margin

evhSetupTimerTrigger(0, 120, 2);
  •  После выхода из обработчика события запускаем повтор кода секции через одно срабатывание таймера AUX-RTC:

// Schedule the next execution

fwScheduleTask(1);

Event Handler Code

В коде обработчика событий производится чтение результатов измерений датчика освещенности, перевод их в целочисленный вид. Если показания выходят за пределы диапазона cfg.lowThreshold – cfg.highThreshold:


// If a measurement was successfully started during the last execution ...

if (state.i2cStatus == 0x0000) {

 // Select the result register

 i2cStart();

 i2cTx(I2C_OP_WRITE | ALS_I2C_ADDR);

 i2cTx(ALS_REG_RESULT);

 // If successful ...

 if (state.i2cStatus == 0x0000) {

 U16 resultRegH;

 U16 resultRegL;

 // Read the result

 i2cRepeatedStart();

 i2cTx(I2C_OP_READ | ALS_I2C_ADDR);

 i2cRxAck(resultRegH);

 i2cRxNack(resultRegL);

 i2cStop();

 // Convert the result (4-bit exponent + 12-bit mantissa) into 16-bit fixed-point

 U16 exp = resultRegH >> 4;

 U16 mant = (resultRegH << 12) | (resultRegL << 4);

 // The exponent is in range 0 to 11

 U16 value = mant >> (11– exp);

 output.value = value;

 // Notify the application with the result is below the low threshold or above the high threshold

 if (value < cfg.lowThreshold) {

 fwGenAlertInterrupt();

 }

 if (value > cfg.highThreshold) {

 fwGenAlertInterrupt();

 }

 } else {

 i2cStop();

 }

}

Termination Сode

Данная секция содержит код, выполняемый по завершении задачи SC. В примере I²C Light Sensor датчик освещенности переводится в неактивный режим и последней строчкой запрещаются срабатывания событий.

// Shut down the light sensor

i2cStart();

i2cTx(I2C_OP_WRITE | ALS_I2C_ADDR);

i2cTx(ALS_REG_CFG);

i2cTx(ALS_CFG_RESET >> 8);

i2cTx(ALS_CFG_RESET >> 0);

i2cStop();

 

// Cancel the potentially active event trigger

evhCancelTrigger(0);

Рис. 6. Вкладка «Генерация кода» Sensor Controller Studio

Рис. 6. Вкладка «Генерация кода» Sensor Controller Studio

Рис. 7. Вкладка «Тестирование» SCS

Рис. 7. Вкладка «Тестирование» SCS

Рис. 8. Тестирование проекта I2C Light Sensor на отладочной плате SensorTag

Рис. 8. Тестирование проекта I2C Light Sensor на отладочной плате SensorTag

Вкладка Code Generation запускает инструментарий для запуска генерации исходных текстов проекта SC для последующего встраивания в код основного приложения. Кнопка “Generate driver source code” запускает трансляцию исходного кода проекта SC (рисунок 6). В случае успеха генерируется набор *.с файлов драйверов, а также указываются затраты ресурсов на приложение. Так, для I²C Light Sensor задействовано 34,5% памяти SC.

Вкладка Task Testing позволяет при наличии отладочного набора запустить тестирование и отладку SC-приложения (рисунок 7).

В данном случае используется связка SensorTag+XDS110. Нажимаем коннект. После его установления можно запускать выполнение кода (рисунок 8). Запускается графическое отображение изменений выходного сигнала с датчика OPT3001 – отображается значение переменной output.value, которая также будет доступной и приложению основного ядра.

Рис. 9. Вид среды Code Composter Studio после успешного импорта проекта I2C Light Sensor и открытия основного файла main_tirtos.c

Рис. 9. Вид среды Code Composter Studio после успешного импорта проекта I2C
Light Sensor и открытия основного файла main_tirtos.c

Что интересно – тестирование проекта в Sensor Controller Studio никоим образом не затрагивает текущую прошивку системы. Это объясняется тем фактом, что код SC выполняется из ОЗУ.

Финальный проект можно прошить непосредственно в SensorTag.

Для начала необходимо импортировать данный проект в CCS. Наиболее простой путь – запускаем CCS, меню Project → Import CCS Project и ищем проект-пример (рисунок 9). При путях установки по умолчанию он будет располагаться здесь: C:\ti\simplelink_academy_01_11_00_0000\modules\projects\sc_basic_lab1\i2c_light_sensor\projects. После успешного импортирования проект появится в рабочем пространстве CCS (так называемом «workspace») и будет доступен для редактирования и компиляции.

Основной код проекта располагается в файле main_tirtos.c.

Вначале идет блок подключения исходников, сгенерированных SCS к проекту.



#include «ex_include_tirtos.h»
#include «scif.h»
#define BV(n) (1 << (n))

 

// Display error message if the SCIF driver has been generated with incorrect operating system setting

#ifndef SCIF_OSAL_TIRTOS_H
#error «SCIF driver has incorrect operating system configuration for this example. Please change to ‘TI-RTOS’ in the Sensor Controller Studio project panel and re-generate the driver.»
#endif

 

// Display error message if the SCIF driver has been generated with incorrect target chip package

#ifndef SCIF_TARGET_CHIP_PACKAGE_QFN48_7X7_RGZ

#error «SCIF driver has incorrect target chip package configuration for this example. Please change to ‘QFN48 7x7 RGZ’ in the Sensor Controller Studio project panel and re-generate the driver.»

#endif

Объявляются call-back-процедуры для обработки уведомлений SC. В нашем случае обработка сообщения типа ALERT.

// Task data

Task_Struct myTask;

Char myTaskStack[1024];

 

// Semaphore used to wait for Sensor Controller task ALERT event

static Semaphore_Struct semScTaskAlert;

 

void scCtrlReadyCallback(void) {

} // scCtrlReadyCallback

 

void scTaskAlertCallback(void) {

// Wake up the OS task

Semaphore_post(Semaphore_handle(&semScTaskAlert));

} // scTaskAlertCallback

Определяем конфигурацию выводов – линии, подключенные к светодиодам платы, устанавливаем на выход.

PIN_Config pLedPinTable[] = {

Board_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,

Board_LED2 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,

PIN_TERMINATE

};

PIN_State ledPinState;
    

Основная «рабочая» функция данного проекта – taskFxn.

void taskFxn(UArg a0, UArg a1) {

Задаем конфигурацию внешних линий

PIN_Handle hLedPins;

// Enable LED pins

hLedPins = PIN_open(&ledPinState, pLedPinTable);

Инициализируем SC и задаем интервал срабатывания RTC-таймера.


// Initialize the Sensor Controller

scifOsalInit();

scifOsalRegisterCtrlReadyCallback(scCtrlReadyCallback);

scifOsalRegisterTaskAlertCallback(scTaskAlertCallback);

scifInit(&scifDriverSetup);

 

// Set the Sensor Controller task tick interval to 1 second

scifStartRtcTicksNow(0x00010000);

Задаем пороги срабатывания для задачи, исполняемой на SC, и запускаем ее на выполнение.

// Configure to trigger interrupt at first result, and start the Sensor Controller’s I2C Light

// Sensor task (not to be confused with OS tasks)

int lowThreshold = scifTaskData.i2cLightSensor.cfg.lowThreshold = 1;

int highThreshold = scifTaskData.i2cLightSensor.cfg.highThreshold = 0;

scifStartTasksNbl(BV(SCIF_I2C_LIGHT_SENSOR_TASK_ID));

В бесконечном цикле ожидаем уведомления ALERT от SC, считываем показания датчика OPT3001, опрошенного SC, сравниваем с нижним и верхним порогами. В случае выхода текущего значения за границы диапазона, определенного переменными lowThreshold и highThreshold, «мигаем» нужным светодиодом (при этом даем шанс выполнению других задач на основном ядре, прерывая выполнение текущей задачи посредством Task_sleep() ). После этого пороги, вызывающие срабатывание ALERT на SC, обновляются на основе текущего значения.

// Main loop

while (1) {

 // Wait for an ALERT callback

 Semaphore_pend(Semaphore_handle( & semScTaskAlert), BIOS_WAIT_FOREVER);

 // Clear the ALERT interrupt source

 scifClearAlertIntSource();

 // The light sensor value is outside of the configured window ...

 uint16_t value = scifTaskData.i2cLightSensor.output.value;

 if (value < lowThreshold) {

 // Below the low threshold, so blink LED1

 PIN_setOutputValue(hLedPins, Board_LED1, Board_LED_ON);

 Task_sleep(10000 / Clock_tickPeriod);

 PIN_setOutputValue(hLedPins, Board_LED1, Board_LED_OFF);

 } else if (value > highThreshold) {

 // Above the high threshold, so blink LED2

 PIN_setOutputValue(hLedPins, Board_LED2, Board_LED_ON);

 Task_sleep(10000 / Clock_tickPeriod);

 PIN_setOutputValue(hLedPins, Board_LED2, Board_LED_OFF);

 }

 // Update the thresholds to +/-100 from the current value, with saturation

 lowThreshold = value– 100;

 if (lowThreshold < 10) lowThreshold = 0;

 scifTaskData.i2cLightSensor.cfg.lowThreshold = lowThreshold;

 highThreshold = value + 100;

 if (highThreshold > 65535) highThreshold = 65535;

 scifTaskData.i2cLightSensor.cfg.highThreshold = highThreshold;

 // Acknowledge the alert event

 scifAckAlertEvents();

}

} // taskFxn

Основная функция проекта – main(). В данном примере в ней задается конфигурация внешних линий, создается задача для TIRTOS, настраиваются семафоры и запускается сама TIRTOS.

int main(void) {

 Task_Params taskParams;

 // Initialize the PIN driver

 PIN_init(BoardGpioInitTable);

 // Configure the OS task

 Task_Params_init( & taskParams);

 taskParams.stack = myTaskStack;

 taskParams.stackSize = sizeof(myTaskStack);

 taskParams.priority = 3;

 Task_construct( & myTask, taskFxn, & taskParams, NULL);

 // Create the semaphore used to wait for Sensor Controller ALERT events

 Semaphore_Params semParams;

 Semaphore_Params_init( & semParams);

 semParams.mode = Semaphore_Mode_BINARY;

 Semaphore_construct( & semScTaskAlert, 0, & semParams);

 // Start TI-RTOS

 BIOS_start();

 //Power_sleep(1);

 return 0;

 } // main
Рис. 10. Настройка среды CSS на генерацию файлов формата HEX

Рис. 10. Настройка среды CSS на генерацию файлов формата HEX

Разрешив опцию создания HEX-файлов (Project → Properties, рисунок 10), при построении проекта (Project → Build) получим образ прошивки, который можно будет прошить в SensorTag посредством Flash Programmer 2.

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

Для отслеживания тока потребления удалим батарейку-«таблетку» и подсоединим внешний аккумулятор: «-» аккумулятора к контакту «BAT-», «+» через выключатель, разъем-перемычку и низкоомный шунт – к контакту «BAT+». Замыкая перемычку через амперметр, можно наблюдать средний ток потребления платы, а наблюдая за падением напряжения на шунте, осциллографом можно оценить мгновенные токи потребления.

Подключая амперметр, наблюдаем на нем ток примерно 3,5 мА. Несколько странно, учитывая, что должен работать, в основном, SC, и лишь в короткие промежутки времени – основное ядро. И в документации утверждается, что TIRTOS по умолчанию автоматически управляет режимами энергопотребления (а в нашем примере никаких дополнительных условий, отменяющих«умолчания», не вводилось).

Разгадка кроется именно в том, что используется SensorTag – на плате установлено более шести различных датчиков и многое другое. Пожалуй, наиболее удачное руководство к действию было найдено на форуме ресурса 430oh [9]. Отключаем, следуя рекомендациям, питание гироскопа-акселерометра и переводим линии SPI в режим входов.

Для этого перед main() определяем еще одну структуру для управления внешними выводами:

PIN_State unusedPinState;

PIN_Config unusedPinTable[] =

{

Board_MPU_POWER | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX, // MPU power off

Board_SPI0_MOSI | PIN_INPUT_DIS | PIN_PULLDOWN, // disable input, enable pulldown to give a defined level on the bus

Board_SPI0_CLK | PIN_INPUT_DIS | PIN_PULLDOWN, // disable input, enable pulldown to give a defined level on the bus

PIN_TERMINATE

};

PIN_Handle unusedPinHandle;

//==========================

// Open unused pins to set correct default state

// Define unusedPinHandle and unusedPinState somewhere, either static or global

Конфигурируем неиспользуемые выводы, размещая строчку после PIN_init(BoardGpioInitTable).

unusedPinHandle = PIN_open(&unusedPinState, unusedPinTable);

Компилируем и прошиваем проект. Что и требовалось доказать – новые замеры дают уже порядка 290 мкА – существенно лучше. При этом у нас активно работают на плате датчик освещенности (~1,8 мкА в активном режиме) и ИК-датчик температуры (по умолчанию он находится в режиме измерений и потребляет порядка 270 мкА). Поскольку изначально планировалось использовать TMP007 в дальнейшей работе, мы пока не будем его отключать.

Запуск опроса двух I²C-датчиков на SC

Добавим к примеру с датчиком OPT3001 опрос датчика ИК-излучения TMP007 [10]. Из-за относительной инерционности TMP007 нет особого смысла запускать в режиме одиночных измерений (да и нет у него данного режима в чистом виде ). Настроим датчик на работу в режиме измерений один раз в секунду с интервалом idle 0,75 с и усреднением по одному измерению. Ток потребления датчика в таком режиме снизится до 85 мкА.

Для этого в уже знакомом проекте I²C Light Sensor в секции Initialization добавим перед строчкой fwScheduleTask(1), по аналогии c OPT3001, код для настройки нужного режима TMP007. Сам датчик TMP007, согласно схеме SensorTag, включен таким образом, что его адрес I²C равен 0x88 (0b10001000, а в документации указано 0b1000100, это следует понимать как 7 старших бит адреса. Иногда из-за особенностей реализации I²C-библиотек могут возникать разночтения).

// tmp007 config 1s conversion with 85uA consumption.

i2cStart();

i2cTx(I2C_OP_WRITE | 0x88); //1000100_0->88

i2cTx(0x02);

i2cTx(0x1A);

i2cTx(0x40);

i2cStop();

Представленный блок кода записывает в регистр конфигурации TMP007 (его внутренний адрес 0х02) конфигурационное слово режима (0х1А40 – регистры в TMP007 16-битные) – сначала старший байт, затем младший.

Рис. 11. Добавление в проект SCS I2C Light Sensor обработки ИК -датчика температуры TMP007

Рис. 11. Добавление в проект SCS I2C Light Sensor обработки ИК -датчика температуры TMP007

В секции Event Handler Code после завершения чтения данных с OPT3001 (с 40-ой строки) добавим процедуру чтения результатов измерения температуры.

Просто считать данные ИК-температуры с TMP007 можно, обратившись с процедурой чтения к внутреннему регистру по адресу 0х03 (рисунок 11). Считываем побайтно, начиная со старшего байта, и компонуем в 16-битный результат, сбросив два младших бита (бит 1 – незначащий, бит 0 – флаг корректности результата, который в данном случае игнорируем).

В результате получится значение ИК-температуры в дополнительном коде, умноженное на 4 (сам датчик 14-битный, 0,03125°С на младший бит).

//==========TMP007====================

// Select the result register

i2cStart();

i2cTx(I2C_OP_WRITE | 0x88);

i2cTx(0x03);

// If successful ...

if (state.i2cStatus == 0x0000) {

U16 resultRegH;

U16 resultRegL;

// Read the result

i2cRepeatedStart();

i2cTx(I2C_OP_READ | 0x88);

i2cRxAck(resultRegH);

i2cRxNack(resultRegL);

i2cStop();

U16 irT = ((resultRegH<<8)|resultRegL)&0xFFFC;

output.irtemp = irT;

} else {

i2cStop();

}

//===================================

В поле справа “Data structure”, выбрав структуру output, нажмем кнопку “Add” и добавим переменную irtemp, куда будем записывать показания TMP007.

Проводим процедуру генерации кода – замечаем, что с учетом наших добавлений затрачено 43% памяти SC. Этот факт следует учесть в том случае, если планируется запись данных на SPI-Flash SensorTag – при активном использовании памяти SC места может и не хватить.

Рис. 12. Тестирование работы SC с датчиками TMP007 (верхний график) и OPT3001 (нижний график)

Рис. 12. Тестирование работы SC с датчиками TMP007 (верхний график) и OPT3001 (нижний график)

Запустив тестирование и выставив просмотр переменных value и irtemp, наблюдаем два графика: в данном случае верхний – irtemp (TMP007), нижний – value (OPT3001) (рисунок 12). Заметим также, что значения irtemp обновляются несколько реже (читается одно и тоже значение), чем value.

Генерируем заново исходники для подключения проекта SC к CCS, запускаем CCS и пересобираем проект I2C Light Sensor с генерацией прошивки.

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

Тайная магия – запуск BLE-приложения

Одним из самых простых для разбора и стабильно компилирующихся/собирающихся приложений является проект ProjectZero из комплекта Simple Link Academy. Кроме того, возможно его достаточно легкое тестирование с пользовательской стороны – приложения для смартфонов, позволяющие взаимодействовать с ProjectZero, находятся в общем доступе [11].

Приложение ProjectZero позволяет управлять светодиодами на плате SensorTag, получать данные о состоянии кнопок, принимать и записывать строки данных (небольшие массивы данных). Также приложение ведет своеобразный лог своей работы – в UART выдаются сообщения об этапах работы, системных вызовах и вызовах callback-процедур.

Для начала ProjectZero импортируется в рабочее пространство – либо через Project Explorer (раздел Simpe Link Aсademy, BLE), либо через вкладку Project → Export Project и поиск его в папках Академии. В настройках проекта разрешается генерация HEX-файлов, производится компиляция и сборка проекта. Полученный HEX-файл при помощи Flash Programmer 2 прошивается в отладочную плату Sensor Tag при помощи программатора XDS110.

Рис. 13. Прошивка HEX-файлов проекта Project Zero (образ BLE-стека и образ приложения)

Рис. 13. Прошивка HEX-файлов проекта Project Zero (образ BLE-стека и образ приложения)

Важная деталь – проекты, задействующие BLE, состоят из двух связанных проектов – из, собственно, проекта приложения и проекта стека протоколов. В настройках проекта приложения необходимо указать так называемые «зависимости» – отметить, что данный проект связан с проектом стека. Работа начинается с построения проекта приложения, а затем автоматически собирается и проект стека. Прошивать следует и образ приложения, и образ стека. Удобнее всего использовать для этого решим прошивки «Multiple» (рисунок 13): сначала указывать образ стека, потом образ приложения, затем осуществлять прошивку одновременно.

Стоит обратить внимание, что связка XDS110 + SensorTag определяется операционной системой (здесь, собственно, речь о Windows ОС) как пара последовательных (СОМ) портов:

  • XDS110 Class Application/User UART может быть использован для связи с приложением и вывода отладочной информации (что и делается в ProjectZero – скорость 115200  б/с без управления потоком и проверки четности);
  • XDS110 Class Auxiliary Data Port используется непосредственно для программирования отладочной платы.

При отладке приложения при помощи лога в консоль (через User UART) рекомендуется предварительно закрыть Flash Programmer 2, открыть программу-терминал, настроить ее на работу с User UART (в примере на рисунке 14 это будет СОМ33). Потом можно снова запускать Flash Programmer 2 – в противном случае возможна неявная блокировка им всех СОМ-портов, предлагаемых отладочными средствами, и программа-терминал может не получить доступ к порту User UART (данная проблема характерна прежде всего для ОС Windows).

Со стороны смартфона взаимодействие с приложением возможно как минимум с помощью следующих приложений:

  • SensorTag for iOS;
  • LightBlue for iOS;
  • BLE Scanner for Android.

Поскольку под рукой только Android, как наиболее «народная» система, будет использоваться BLE Scanner (BLE Scanner by Bluepixel Technologies LLP, легко загружаемый из Play Store).

При включенной SensorTag BLE Scanner легко высвечивает ее в списке доступных устройств. После подключения к устройству доступен список его атрибутов и набор сервисов.

Сервисы имеют собственные идентификаторы (UUIDs) из диапазона F000XXXX-0451-4000-B000-000000000000, где – ХХХХ – 16-битная часть идентификатора сервиса, а остальная часть – это 128-битное пространство имен UUID Texas Instruments для демонстрационных целей.

Идентификаторы сервисов (ХХХХ – части):

  • 0x1110 – LED Service – управление светодиодами
    • 0х1111 – сервис для управления и получения информации о состоянии светодиода 0 на плате (для SensorTag – зеленый) – чтение/запись;
    • 0х1112 – сервис для управления и получения информации о состоянии светодиода 1 на плате (для SensorTag – красный) – чтение/запись.
  • 0x1120 – Button Service
    • 0x1121 – BUTTON0 State – чтение состояние кнопки 0 платы или подписка на уведомления;
    • 0x1122 – BUTTON1 State, чтение состояние кнопки 1 платы или подписка на уведомления.
  • 0x1130 – Data Service – сервис обмена данными
    • 0x1131 – String char, работа со строкой данных – чтение/запись;
    • Read/Write a long string;
    • 0x1132 – Stream char, потоковое чтение/запись символов без подтверждений или уведомлений.

Рис. 14. Вид программатора XDS110 c подключенной SensorTag со стороны ОС Windows (в частности, 7 версия О С)

Рис. 14. Вид программатора XDS110 c подключенной SensorTag со стороны ОС Windows (в частности, 7 версия ОС)

Рис. 15. Взаимодействие с Project Zero по BLE через приложение BLE Scanner (слева направо – чтение состояния светодиодов, управление светодиодами, запись и чтение строки «hello»

Рис. 15. Взаимодействие с Project Zero по BLE через приложение BLE Scanner (слева направо – чтение состояния светодиодов, управление светодиодами, запись и чтение строки «hello»

Сервисы LED Service, Button Service оперируют с байтовыми массивами (не символы, а HEX-коды как они есть).

Записывая значения 00 или 01 в сервисы 0х1111 или 0х1112, можно управлять свечением диодов платы.

Рис. 17. Консольный лог вызова процедур в Project Zero в терминале HyperTerminal

Рис. 17. Консольный лог вызова процедур в Project Zero в терминале HyperTerminal

Сервис 0х1131 позволяет считывать (получать) или записывать текстовую строку на устройство, а сервисы 0х1120х – получать состояния кнопок (рисунки 15 и 16).

Действия приложения и системные вызовы отображаюся в логе (рисунок 17), выдающемся с последовательного порта – отображается номер события, расположение обрабатывающей его функции и описание события.

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

Назад в исходники! Project Zero

Рассмотрим, что происходит внутри проекта Project Zero.

В основном файле проекта, – main.c, – происходит инициализация задач стека протоколов (Stack and GAP role), вызывается пользовательская функция ProjectZero_createTask() и запускается планировщик TIRTOS (по сути – запускается работа ОС и всего приложения в целом, включая BLE-стек).

ProjectZero_createTask() инициализирует и задает параметры пользовательской задачи и прописывает основную выполняющуюся в ней функцию – ProjectZero_taskFxn().

Первое, что происходит в ProjectZero_taskFxn() – это вызов функции ProjectZero_init(), которая конфигурирует BLE-стек, сервисы и периферийные устройства, задействованные в приложении. Также регистрируется набор callback-функций, задействованных в приложении, и с этого момента все действия приложения будут основаны на обработке событий. В финале ProjectZero_taskFxn() входит в бесконечный цикл обработки сообщений.

В течение жизненного цикла приложения callback-функции (прописаны ниже) будут вызываться и отправлять сообщения в поток задач для обработки в бесконечном цикле в ProjectZero_taskFxn().

Весь основной функционал пользовательского приложения расположен в файле project_zero.c.

Сам код project_zero.c достаточно большой, но неплохо структурирован. Рассмотрим основные блоки с разбором наиболее интересных моментов.

Первый логический блок – подключение библиотек

/*****************************************

* INCLUDES

Из стандартных библиотек подключается работа со строками, основные файлы TIRTOS и библиотеки для отображения отладочной информации. Далее подключаются файлы BLE-стека, файл поддержки конфигурации платы и определения для сервисов, созданные в Bluetooth Developer Studio. Сервисов три – светодиоды, кнопки и обмен данными.

Второй блок – определение констант и структур данных

/*****************************************

* CONSTANTS

Задается интервал рассылки запросов на подключение (Advertising interval), длительность времени рассылки приглашений, пароль по умолчанию для подключения. Также определяются приоритет пользовательской задачи и идентификаторы событий, которые будут обрабатываться задачей.

Блок определения типов внутренних сообщений и структур данных

/*****************************************

* TYPEDEFS

Блок определения локальных переменных

/*****************************************

* LOCAL VARIABLES
  • определения семафоров, очередей, используемых пользовательской задачей;
  • константы и переменные для формирования GAP, GAP GATT;
  • функции-драйвера внешних выводов (кнопки и светодиоды), задание конфигурации внешних выводов.

Блок объявления функций

/*****************************************

* LOCAL FUNCTIONS

Блок объявления callback-процедур

/*****************************************

* PROFILE CALLBACKS
  • GAP Role Callbacks;
  • GAP Bond Manager Callbacks;
  • пользовательские сервисы – функции, выполняющиеся при операциях чтения-записи характеристик сервисов.

Основной блок – то, ради чего были все предшествующие объявления

/*****************************************

* PUBLIC FUNCTIONS

Создание самой прикладной пользовательской задачи – ProjectZero_createTask().

Одна из самых важных функций – инициализация перед входом в цикл обработки сообщений – ProjectZero_init(void):

/*****************************************

// NO STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp
  • регистрация задачи в операционной системе, инициализация очереди сообщений;
/*****************************************

// Hardware initialization
  • настройки «железа» (пока только внешние выводы – кнопки и светодиоды);
/*****************************************

// BLE Stack initialization
  • инициализация BLE-стека – настройка профиля GAP Role: приглашения, интервалы рассылки оповещения и автоматического поиска подключения;
/*****************************************

// BLE Bond Manager initialization
  • настройка BLE Bond-менеджера – начальные установки;
/*****************************************

// BLE Service initialization
  • инициализация BLE-сервисов, предоставляемых устройством;
  • добавление сервисов в GATT-сервер;
  • задание имени устройства в GAP-профиле;
  • добавление сервисов на сервер GATT с указанием идентификатора этой задачи для запросов индикации;
  • регистрация callback-функций для обработки событий на запись величин характеристик со стороны внешнего устройства;
  • инициализация начальных характеристик сервисов;
  • запуск стека в режиме периферийного устройства.

Основная функция – пользовательская задача, ProjectZero_taskFxn(), которая вызывает функцию инициализации ProjectZero_init() и уходит в бесконечный цикл ожидания и обработки сообщений.

Ниже следуют определения callback-функций, обрабатывающих события, связанные с работой BLE-приложения:

  • реакции на запросы по изменению состояния светодиодов;
  • обнаружение и обработка нажатия кнопок;
  • прием данных про BLE-строки и потоковых байтов.

Рассмотрим действия приложения по этапам – наличие лога через UART-интерфейс позволяет отследить до некоторой степени последовательность вызовов callback-функций. Фактически именно на обработке callback-процедур и построена работа приложения.

Логи

Подключение внешнего устройства

#000030 [ 224.505 ] INFO: (project_zero.c:1258) (CB) GAP State change: 6, Sendin

g msg to app.

#000031 [ 224.505 ] INFO: (project_zero.c:782) Connected. Peer address: 0x700BC0

B3B7DD

user_gapStateChangeCB

user_processGapStateChangeEvt


Чтение состояние 0-го светодиода

#000032 [ 487.578 ] INFO: (led_service.c:401) ReadAttrCB : LED0 connHandle: 0 of

fset: 0 method: 0x0a

LED_Service_ReadAttrCB

Включение нулевого светодиода со стороны подключенного устройства

#000033 [ 520.330 ] INFO: (led_service.c:474) WriteAttrCB : LED0 connHandle(0) l

en(1) offset(0) method(0x52)

#000034 [ 520.330 ] INFO: (project_zero.c:1333) (CB) Characteristic value change

: svc(0x1110) paramID(0). Sending msg to app.

#000035 [ 520.330 ] INFO: (project_zero.c:868) Value Change msg: LED Service LED

0: 01

#000036 [ 520.330 ] INFO: (project_zero.c:876) Turning LED0 on

LED_Service_WriteAttrCB

user_service_ValueChangeCB

user_LedService_ValueChangeHandler

user_LedService_ValueChangeHandler


Чтение состояние кнопки

#000037 [ 597.934 ] INFO: (button_service.c:452) ReadAttrCB : BUTTON0 connHandle

: 0 offset: 0 method: 0x0a

Button_Service_ReadAttrCB

Подписка приложением на уведомления о состоянии кнопки 1

#000038 [ 620.585 ] INFO: (button_service.c:513) WriteAttrCB (CCCD): param: 0 co

nnHandle: 0 – OTA write

#000039 [ 620.585 ] INFO: (project_zero.c:1346) (CB) Char config change: svc(0x1

120) paramID(0). Sending msg to app.

#000040 [ 620.586 ] INFO: (project_zero.c:935) CCCD Change msg: Button Service

BUTTON0: Notifications enabled

Button_Service_WriteAttrCB

user_service_CfgChangeCB

user_ButtonService_CfgChangeHandler


Нажатие и отжатие кнопки 0

#000041 [ 744.611 ] INFO: (project_zero.c:1443) Button interrupt: Button 0

#000042 [ 744.661 ] INFO: (project_zero.c:824) Button 0 pressed

#000043 [ 744.661 ] INFO: (button_service.c:315) SetParameter : BUTTON0 len: 1

#000044 [ 744.661 ] INFO: (button_service.c:346) Trying to send noti/ind: connHa

ndle 0, Notification enabled

#000045 [ 744.661 ] INFO: (button_service.c:452) ReadAttrCB : BUTTON0 connHandle

: 0 offset: 0 method: 0xff

#000046 [ 744.982 ] INFO: (project_zero.c:1443) Button interrupt: Button 0

#000047 [ 745.032 ] INFO: (project_zero.c:824) Button 0 released

#000048 [ 745.032 ] INFO: (button_service.c:315) SetParameter : BUTTON0 len: 1

#000049 [ 745.032 ] INFO: (button_service.c:346) Trying to send noti/ind: connHa

ndle 0, Notification enabled

#000050 [ 745.033 ] INFO: (button_service.c:452) ReadAttrCB : BUTTON0 connHandle

: 0 offset: 0 method: 0xff

buttonCallbackFxn

user_handleButtonPress

ButtonService_SetParameter

ButtonService_SetParameter

Button_Service_ReadAttrCB


Чтение строковых данных со стороны внешнего устройства

#000051 [ 939.550 ] INFO: (data_service.c:442) ReadAttrCB : String connHandle: 0

offset: 0 method: 0x0a

#000052 [ 939.601 ] INFO: (data_service.c:442) ReadAttrCB : String connHandle: 0

offset: 22 method: 0x0c

Data_Service_ReadAttrCB

Запись строковых данных внешним устройством

#000053 [ 1057.806 ] INFO: (data_service.c:534) WriteAttrCB : String connHandle(

0) len(12) offset(0) method(0x12)

#000054 [ 1057.806 ] INFO: (project_zero.c:1333) (CB) Characteristic value chang

e: svc(0x1130) paramID(0). Sending msg to app.

#000055 [ 1057.807 ] INFO: (project_zero.c:987) Value Change msg: Data Service S

tring: Hello World!

Data_Service_WriteAttrCB

user_service_ValueChangeCB

user_DataService_ValueChangeHandler


Запись потоковых данных внешним устройством

#000056 [ 1268.267 ] INFO: (data_service.c:548) WriteAttrCB : Stream connHandle(

0) len(3) offset(0) method(0x52)

#000057 [ 1268.267 ] INFO: (project_zero.c:1333) (CB) Characteristic value chang

e: svc(0x1130) paramID(1). Sending msg to app.

#000058 [ 1268.267 ] INFO: (project_zero.c:994) Value Change msg: Data Service S

tream: 55:66:77...

Data_Service_WriteAttrCB

user_service_ValueChangeCB

user_DataService_ValueChangeHandler


Потеря связи с внешним устройством

#000059 [ 1901.051 ] INFO: (project_zero.c:1258) (CB) GAP State change: 5, Sendi

ng msg to app.

#000060 [ 1901.052 ] INFO: (project_zero.c:1258) (CB) GAP State change: 2, Sendi

ng msg to app.

#000061 [ 1901.052 ] INFO: (project_zero.c:795) Connection timed out

#000062 [ 1901.052 ] INFO: (project_zero.c:772) Advertising

user_gapStateChangeCB

user_processGapStateChangeEvt

Последующее восстановление связи

#000063 [ 3723.918 ] INFO: (project_zero.c:1258) (CB) GAP State change: 6, Sendi

ng msg to app.

#000064 [ 3723.918 ] INFO: (project_zero.c:782) Connected. Peer address: 0x700BC

0B3B7DD

user_gapStateChangeCB

user_processGapStateChangeEvt

Прошивка скомпилированного примера в SensorTag и последующие замеры тока потребления дают ожидаемые 3,5…3,7 мА. Для снижения потребления необходимо, как и в прошлом примере, отключить питание акселерометра.

Прописываем уже знакомую структуру PIN_State unusedPinState и PIN_Config unusedPinTable[] в блоке «hardware initialization» (можно и ниже), и – в функции ProjectZero_init(void) после инициализации выводов светодиодов и кнопок – строчку unusedPinHandle = PIN_open(&unusedPinState, unusedPinTable).

Повторная компиляция и прошивка проекта дают потребление SensorTag уже на уровне 0,550…0,570 мА (порядка 0,270…0,290 мА из этого потребляет постоянно работающий ИК-датчик температуры TMP007, состояние «по умолчанию» остальных датчиков, расположенных на SensorTag, также неплохо бы уточнить).

Назад в исходники! Разбор полета [13]

Первоначальная задача состояла, по сути, в следующем – научиться опрашивать датчики при помощи SC, вести лог сенсорных данных в энергонезависимой памяти, считывать лог данных посредством BLE. Для работы с внешней SPI-Flash будет задействована штатная библиотека ExtFlash, реализующая множество функций для работы с SPI-памятью. Из их множества понадобятся функции записи и чтения по определенному адресу, а также функции управления режимом работы энергонезависимой памяти для оптимизации энергопотребления.

SC будет накапливать небольшой массив данных в памяти (результаты нескольких десятков измерений с различных датчиков – OPT3001 и TMP007 в данном случае) и отсылать уведомление основному ядру. Основное ядро будет считывать массив данных от SC и записывать его в SPI-Flash. При наличии подключения по запросам приложения на смартфоне данные будут постепенно считываться из памяти и передаваться приложению.

Имея под руками всю мощь TIRTOS, вполне логично воспользоваться принципом «разделяй и властвуй!», а именно – разделить задачи. Как минимум, проглядываются две:

  • обрабатывающая события, генерируемые SC, и ведущая лог в SPI-Flash;
  • обеспечивающая чтение лога из SPI-Flash и работу с BLE.

Фактически необходимо «скрестить» два слегка модифицированных примера – I²C Light Sensor и Project Zero. Параллельно необходимо немного переработать пример для SC – убрать из исходного примера обработку порогов, управление светодиодами, и сделать запись считанных показаний в массив.

Рис. 18. Добавление констант и структур данных в проекте SCS

Рис. 18. Добавление констант и структур данных в проекте SCS

Background

Модифицируем код для SC (рисунок 18).

Изменения от нашей предыдущей модификации будут минимальными. В раздел констант добавится размер буфера под значения константа BUFFERSIZE = 64. В структурах данных преобразуем irtemp и value в массивы размерами BUFFERSIZE и добавим переменную – счетчик i.

В секции инициализации инициализируем счетчик нулем – output.i = 0;

Основная работа, как и прошлый раз, будет происходить в секции Event Handler Code.

Также остается опрос датчиков, но при этом данные будут писаться в массивы, а по заполнению массивов будет отправляться ALERT-оповещение основному ядру (предполагается, что получив его, основное ядро считывает массивы данных температуры и освещенности и записывает их в энергонезависимую память).

U16 n = output.i;

// If a measurement was successfully started during the last execution ...

if (state.i2cStatus == 0x0000) {

 // Select the result register

 i2cStart();

 i2cTx(I2C_OP_WRITE | ALS_I2C_ADDR);

 i2cTx(ALS_REG_RESULT);

 // If successful ...

 if (state.i2cStatus == 0x0000) {

 U16 resultRegH;

 U16 resultRegL;

 // Read the result

 i2cRepeatedStart();

 i2cTx(I2C_OP_READ | ALS_I2C_ADDR);

 i2cRxAck(resultRegH);

 i2cRxNack(resultRegL);

 i2cStop();

 // Convert the result (4-bit exponent + 12-bit mantissa) into 16-bit fixed-point

 U16 exp = resultRegH >> 4;

 U16 mant = (resultRegH << 12) | (resultRegL << 4);

 // The exponent is in range 0 to 11

 U16 value = mant >> (11– exp);

 output.value[n] = value;

 } else {

 i2cStop();

 }

 //==========TMP007=========================

 // Select the result register

 i2cStart();

 i2cTx(I2C_OP_WRITE | 0x88);

 i2cTx(0x03);

 // If successful ...

 if (state.i2cStatus == 0x0000) {

 U16 resultRegH;

 U16 resultRegL;

 // Read the result

 i2cRepeatedStart();

 i2cTx(I2C_OP_READ | 0x88);

 i2cRxAck(resultRegH);

 i2cRxNack(resultRegL);

 i2cStop();

 U16 irT = ((resultRegH << 8) | resultRegL) & 0xFFFC;

 output.irtemp[n] = irT;

 } else {

 i2cStop();

 }

 //===================================

 output.i = (output.i + 1) & 0x3F;

 if (output.i == 0) {

 fwGenAlertInterrupt();

 }

}
Рис. 19. Тестирование записи результатов измерений в массив – изменение индекса и запись данных в ячейки массива

Рис. 19. Тестирование записи результатов измерений

Тестируем работу приложения на SC, генерируем исходники для CSS (рисунок 19).

Протестируем связку приложения SC и задачи на TIRTOS, немного модифицировав пример I²C Ligth Sensor .

Добавляем пару отладочных «миганий» светодиодами и цикл чтения данных с SC с учетом изменений, произведенных в приложении SC.

Ниже приведены изменения в taslFxn после строчки scifStartTasksNbl(BV(SCIF_I2C_LIGHT_SENSOR_TASK_ID)); и до конца функции.

// Before starting copy array blink LED1 and LED2

PIN_setOutputValue(hLedPins, Board_LED1, Board_LED_ON);

PIN_setOutputValue(hLedPins, Board_LED2, Board_LED_ON);

Task_sleep(100000 / Clock_tickPeriod);

PIN_setOutputValue(hLedPins, Board_LED1, Board_LED_OFF);

PIN_setOutputValue(hLedPins, Board_LED2, Board_LED_OFF);

// Main loop

while (1) {

 // Wait for an ALERT callback

 Semaphore_pend(Semaphore_handle( & semScTaskAlert), BIOS_WAIT_FOREVER);

 // Clear the ALERT interrupt source

 scifClearAlertIntSource();

 uint16_t optArr[64]; // array for opt data

 uint16_t irArr[64]; // array for irtemp data

 // Befor starting copy array blink LED1

 PIN_setOutputValue(hLedPins, Board_LED1, Board_LED_ON);

 Task_sleep(100000 / Clock_tickPeriod);

 PIN_setOutputValue(hLedPins, Board_LED1, Board_LED_OFF);

 for (char j = 0; j < 64; j++) {

 optArr[j] = scifTaskData.i2cLightSensor.output.value[j];

 irArr[j] = scifTaskData.i2cLightSensor.output.irtemp[j];

 };

 // After all blink LED2

 PIN_setOutputValue(hLedPins, Board_LED2, Board_LED_ON);

 Task_sleep(100000 / Clock_tickPeriod);

 PIN_setOutputValue(hLedPins, Board_LED2, Board_LED_OFF);

 scifAckAlertEvents();

}

После компиляции проекта и прошивки его в SensorTag при старте приложения некоторое время светятся два диода, потом с периодичностью 64 секунды последовательно загораются и гаснут красный, потом зеленый светодиоды.

Таким образом, проект I²C Light Sensor позволяет, пока – независимо от Project Zero, протестировать работоспособность первой задачи (на данный момент – без записи во Flash).

Многозадачность в действии: задача первая

Интегрируем проект SC в проект с BLE – Project Zero. Для этого копируем из папки проекта SC (в данном случае, поскольку настройки «по умолчанию» в SCS сохранились – C:\ti\simplelink_academy_01_11_00_0000\modules\projects\sc_basic_lab1\i2c_light_sensor\source\) в папку Application проекта Project Zero, (опять же – C:\Users\111\workspace_v6_0\project_zero_app_cc2650stk\Application\), следующие файлы:

  • scif.c;
  • scif.h;
  • scif_framework.c;
  • scif_framework.h;
  • scif_osal_tirtos.c;
  • scif_osal_tirtos.h;
  • ex_include_tirtos.h.

В файле main.c проекта Project Zero производятся следующие изменения/дополнения – именно в нем будет определена задача, отвечающая за работу с SC и ведение лога во Flash-памяти.

В разделе подключений прописываем подключение библиотеки ExtFlash (и .h-, и .с-файлы) – поскольку доступ к SPI будет необходим и из второй задачи, определенной в project_zero.c, подключение библиотеки делается до подключения project_zero.h.

/***************************************

* INCLUDES

*/

#include <xdc/runtime/Error.h>

#include <ti/drivers/Power.h>

#include <ti/drivers/power/PowerCC26XX.h>

#include <ti/sysbios/BIOS.h>

#include <ti/mw/extflash/ExtFlash.h>

#include <ti/mw/extflash/ExtFlash.c>

#include «icall.h»

#include «hal_assert.h»

#include «bcomdef.h»

#include «peripheral.h»

#include «project_zero.h»

#include <ti/drivers/UART.h>

#include <uart_logs.h> 

/* Header files required to enable instruction fetch cache */

#include <inc/hw_memmap.h>

#include <driverlib/vims.h>

#ifndef USE_DEFAULT_USER_CFG

#include «ble_user_config.h» 

// BLE user defined configuration

bleUserCfg_t user0Cfg = BLE_USER_CFG;

 

#endif // USE_DEFAULT_USER_CFG

После секции EXTERNS прописываем подключение файлов, сгенерированных SC:

/****************************************

* EXTERNS

*/

extern void AssertHandler(uint8 assertCause, uint8 assertSubcause);

//****************************************************************************/

#include «ex_include_tirtos.h»

#include «scif.h»

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

Добавляем проверку версий ПО и корпуса чипа (скопировано из main_tirtos.c):

// Display error message if the SCIF driver has been generated with incorrect operating system setting

#ifndef SCIF_OSAL_TIRTOS_H

#error «SCIF driver has incorrect operating system configuration for this example. Please change to ‘TI-RTOS’ in the Sensor Controller Studio project panel and re-generate the driver.»

#endif

// Display error message if the SCIF driver has been generated with incorrect target chip package

#ifndef SCIF_TARGET_CHIP_PACKAGE_QFN48_7X7_RGZ

#error «SCIF driver has incorrect target chip package configuration for this example. Please change to ‘QFN48 7x7 RGZ’ in the Sensor Controller Studio project panel and re-generate the driver.»

#endif

Объявляются структуры задачи для SC, объявляются и определяются callback-функции для обработки событий от SC, в частности, будет задействовано событие ALERT, объявляется основная функция задачи – taskFxn.

// Task data

Task_Struct myTask;

Char myTaskStack[1024];

// Semaphore used to wait for Sensor Controller task ALERT event

static Semaphore_Struct semScTaskAlert;

void scCtrlReadyCallback(void) {

} // scCtrlReadyCallback

void scTaskAlertCallback(void) {

// Wake up the OS task

Semaphore_post(Semaphore_handle(&semScTaskAlert));

} // scTaskAlertCallback

static void taskFxn(UArg a0, UArg a1);

Конфигурацию выводов с-н-к оставляем без изменений:

PIN_Config pLedPinTable[] = {

Board_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,

Board_LED2 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,

PIN_TERMINATE

};

PIN_State ledPinState;

Объявляем переменные для работы с адресами внешней памяти. На всякий случай под лог отведем диапазон адресов с 0 по 0х10000. При старте задачи содержимое данных адресов будет стираться и постепенно заполняться данными с OPT3001 и TMP007. По достижении верхней границы адресов текущий адрес будет сброшен на начальный, а адреса с 0 по 0х10000 стерты, и запись пойдет заново. Циклический лог можно организовать и более элегантно, но это уже выходит за рамки поставленной задачи.

// writeable flash area – from 0 to 0x10000

size_t beg_addr=0x00000;

size_t end_addr=0x10000;

size_t cur_addr=0x00000;

Копируем из main_tirtos.c функцию taskFxn и немного ее изменяем для достижения поставленной задачи, а именно – ведения лога. Инициализация SC остается без изменений, период срабатывания таймера для запуска SC изменяем на 4 секунды (старшие 16 бит аргумента функции scifStartRtcTicksNow задают целые секунды, младшие 16 бит – доли секунды, соответственно, в них 1 бит = 1/65535 секунды).

void taskFxn(UArg a0, UArg a1) {

// Initialize the Sensor Controller

scifOsalInit();

scifOsalRegisterCtrlReadyCallback(scCtrlReadyCallback);

scifOsalRegisterTaskAlertCallback(scTaskAlertCallback);

scifInit(&scifDriverSetup);

// Set the Sensor Controller task tick interval

scifStartRtcTicksNow(0x00040000); //4s

//scifStartRtcTicksNow(0x00010000); //1s

//scifStartRtcTicksNow(0x00008000); //0,5s

Конфигурирование порогов можно оставить, но данные переменные никак не будут задействованы в дальнейшем:

// Configure to trigger interrupt at first result, and start the Sensor Controller’s I2C Light

// Sensor task (not to be confused with OS tasks)

int lowThreshold = scifTaskData.i2cLightSensor.cfg.lowThreshold = 1;

int highThreshold = scifTaskData.i2cLightSensor.cfg.highThreshold = 0;

Запускается задача на SC:

scifStartTasksNbl(BV(SCIF_I2C_LIGHT_SENSOR_TASK_ID));

Log_info0(«sc_start»);

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

После запуска SC выделяем память под буфер (будет использован при операциях с SPI-Flash в качестве временного), пытаемся проверить доступ к внешней памяти и в случае успеха проводим пробное чтение блока данных и стираем область памяти для лога. О ходе выполнения рапортуем в консоль:

uint8_t testbuf1[64];

if(ExtFlash_open()){

Log_info0(«sc_extflash_open»);

// trying to read extflash

if(ExtFlash_read(0x00000, 64, testbuf1)){

Log_info0(«sc_extflash_test_read»);

}

if(ExtFlash_erase(0, 0x10000)){

Log_info0(«sc_extflash_erase_done»);

}

ExtFlash_close();

}

else {

Log_info0(«sc_extflash_access_err»);

} //*/

cur_addr=beg_addr;

Немного о функциях работы с внешней памятью:

  • ExtFlash_open() выводит память из режима глубокого сна и переводит ее в режим готовности – возвращает флаг «успех/неудача»;
  • ExtFlash_close() переводит память в режим пониженного энергопотребления – «глубокий сон», возвращает флаг «успех/неудача»;
  • ExtFlash_read(addr, size, *buf) читает size-байт данных с адреса addr внешней памяти в область памяти *buf – возвращает флаг «успех/неудача»;
  • ExtFlash_erase(addr, size) стирает область внешней памяти size с адреса addr – возвращает флаг «успех/неудача». Адреса и размеры области лучше выравнивать по границе 4 Кбайт.

В основном цикле задача ожидает ALERT-события от SC, считывает данные в массивы optArr и irArr – данные с датчиков освещенности и ИК-температуры соответственно:

// Main loop

Log_info0(«sc_beg_Loop»);

while (1) {

// Wait for an ALERT callback

Semaphore_pend(Semaphore_handle(&semScTaskAlert), BIOS_WAIT_FOREVER);

Log_info0(«sc_semafore_pend»);

// Clear the ALERT interrupt source

scifClearAlertIntSource();

uint16_t optArr[16]; // array for opt data

uint16_t irArr[16]; // array for irtemp data

uint8_t samples=16;

 

for(uint8_t j=0; j<=samples; j++){

optArr[j]= scifTaskData.i2cLightSensor.output.value[j];

irArr[j]= scifTaskData.i2cLightSensor.output.irtemp[j];

};

Log_info0(«sc_data_copied»);

Полученные измерения от датчика освещенности сначала копируются из optArr во временный массив testbuf1, в нем для наглядности первые два байта замещаются символами «op» и затем он записывается во внешнюю память посредством функции ExtFlash_write (куда, сколько, откуда), в отличие от функции стирания памяти, запись может идти с шагом хоть в один байт. Аналогичные действия производятся относительно данных с ИК-датчика температуры. Во внешнюю память будут записаны по 15 отсчетов с датчиков с парой заголовочных символом – «op» для OPT3001, и «ir» для TMP007.

if(ExtFlash_open()){

// coping opt data into uint8 array

for (uint8_t j=0; j<=samples; j++){

testbuf1[2*j]=optArr[j]>>8;

testbuf1[2*j-1]=optArr[j];

}

testbuf1[0]=’o’;

testbuf1[1]=’p’;

if(ExtFlash_write(cur_addr, samples*2, testbuf1)){

cur_addr+=(samples*2);

Log_info0(«sc_write1»);

}

//ir writes

for (char j=0; j<=samples; j++){

testbuf1[2*j]=irArr[j]>>8;

testbuf1[2*j-1]=irArr[j];

}

testbuf1[0]=’i’;

testbuf1[1]=’r’;

if(ExtFlash_write(cur_addr, samples*2, testbuf1)){

cur_addr+=(samples*2);

Log_info0(«sc_write2»);

}

ExtFlash_close();

}

Обработка случая выхода за границы отведенной для лога внешней памяти и закрытие обработки события ALERT:

if(cur_addr>=end_addr){

cur_addr=beg_addr;

if(ExtFlash_open()){

if(ExtFlash_erase(0, 0x10000)){

Log_info0(«sc_erase2»);

}

ExtFlash_close();

}

} // */

// Acknowledge the alert event

scifAckAlertEvents();

Log_info0(«sc_ack_alert»);

}

} // taskFxn

В функции main() комментируем блок #ifndef POWER_SAVING, – он отключает автоматическое управление энергопотреблением в TIRTOS, – и добавляем блок инициализации задачи, работающей с SC (копирование из main_tirtos.c). Все, что находится ниже определения main(), остается без изменений.

int main()

{

/* Register Application callback to trap asserts raised in the Stack */

RegisterAssertCback(AssertHandler);

PIN_init(BoardGpioInitTable);

//#ifndef POWER_SAVING

// /* Set constraints for Standby, powerdown and idle mode */

// Power_setConstraint(PowerCC26XX_SB_DISALLOW);

// Power_setConstraint(PowerCC26XX_IDLE_PD_DISALLOW);

//#endif // POWER_SAVING

/* Initialize the RTOS Log formatting and output to UART in Idle thread.

* Note: Define xdc_runtime_Log_DISABLE_ALL to remove all impact of Log.

* Note: NULL as Params gives 115200,8,N,1 and Blocking mode */

UART_init();

UartLog_init(UART_open(Board_UART, NULL));

/* Initialize ICall module */

ICall_init();

/* Start tasks of external images – Priority 5 */

ICall_createRemoteTasks();

/* Kick off profile – Priority 3 */

GAPRole_createTask();

ProjectZero_createTask();

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

// init SC task procedure begin

Task_Params taskParams;

Log_info0(«sc0>»);

// Configure the OS task

Task_Params_init(&taskParams);

Log_info0(«sc1>»);

taskParams.stack = myTaskStack;

taskParams.stackSize = sizeof(myTaskStack);

taskParams.priority = 3;

Task_construct(&myTask, taskFxn, &taskParams, NULL);

Log_info0(«sc2>»);

// Create the semaphore used to wait for Sensor Controller ALERT events

Semaphore_Params semParams;

Semaphore_Params_init(&semParams);

semParams.mode = Semaphore_Mode_BINARY;

Semaphore_construct(&semScTaskAlert, 0, &semParams);

Log_info0(«sc3>»);

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

/* enable interrupts and start SYS/BIOS */

BIOS_start();

return 0;

}

Многозадачность в действии: задача вторая

Произведем модификации для чтения порций лога посредством BLE. Воспользуемся сервисом данных 0x1130 – Data Service:

  • 0x1131 – String char, работа со строкой данных (чтение/запись);
  • 0x1132 – Stream char, потоковое чтение/запись символов без подтверждений или уведомлений.

Из функционала, с учетом скромного интерфейса приложения BLE Scanner – через изменение характеристики 0x1132 – Stream char будет задаваться адрес SPI-Flash, откуда следует прочитать блок данных. А путем чтения характеристики 0x1131 – String char будем получать данные (для примера считывание будет блоками по 32 байта).

Сервисы LED Service и Button Service остаются без изменений.

Все изменения будут только в файле project_zero.c.

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

#include <ti/mw/extflash/ExtFlash.h>

size_t flashReadAddr;

uint8_t flashReadBuff[32];

uint8_t N_samples=16;

Запись по BLE со стороны внешнего устройства характеристики 0х1132 вызывает срабатывание цепочки callback-функций:

Data_Service_WriteAttrCB

user_service_ValueChangeCB

user_DataService_ValueChangeHandler

Модификации подвергнется функция user_DataService_ValueChangeHandler(), которая получает в качестве параметра указатель на структуру с данными для модификации характеристик сервиса Data Service. Функция определяет, было ли обращение на запись к строковой характеристике или к потоковой. В последнем случае из первых трех байт формируется 24-битный адрес для обращения к SPI-Flash (для удобства работы через приложение BLE Scanner первый полученный байт будет старшим, третий – младшим, что даст привычный порядок записи адреса для пользователя). Сформировав адрес flashReadAddr, пытаемся получить доступ к SPI-Flash и прочитать 32 байта в массив flashReadBuff.

Считанный массив затем устанавливаем как значение строковой характеристики через вызов функции DataService_SetParameter(DS_STRING_ID, N_samples*2, flashReadBuff).

И при обращении по BLE на чтение характеристики 0x1131 будет считано скопированное содержимое массива flashReadBuff.

void user_DataService_ValueChangeHandler(char_data_t *pCharData)

{

// Value to hold the received string for printing via Log, as Log printouts

// happen in the Idle task, and so need to refer to a global/static variable.

static uint8_t received_string[DS_STRING_LEN] = {0};

switch (pCharData->paramID)

{

case DS_STRING_ID:

// Do something useful with pCharData->data here

// ------------------------

// Copy received data to holder array, ensuring NULL termination.

memset(received_string, 0, DS_STRING_LEN);

memcpy(received_string, pCharData->data, DS_STRING_LEN-1);

// Needed to copy before log statement, as the holder array remains after

// the pCharData message has been freed and reused for something else.

// Log_info3(«Value Change msg: %s %s: %s»,

// (IArg)»Data Service»,

// (IArg)»String»,

// (IArg)received_string);

break;

case DS_STREAM_ID:

// Log_info3(«Value Change msg: Data Service Stream: %02x:%02x:%02x...»,

// (IArg)pCharData->data[0],

// (IArg)pCharData->data[1],

// (IArg)pCharData->data[2]);

// -------------------------

// Do something useful with pCharData->data here

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

// get address to read from external flash memory

flashReadAddr=0x00;

flashReadAddr=(IArg)pCharData->data[2];

flashReadAddr=flashReadAddr |(((IArg)pCharData->data[1])<<8);

flashReadAddr=flashReadAddr |(((IArg)pCharData->data[0])<<16);

//Log_info0(flashReadAddr);

if(ExtFlash_open()){

Log_info0(«BLE_extflash_open»);

// trying to read extflash

if(ExtFlash_read(flashReadAddr, N_samples*2, flashReadBuff)){

Log_info0(«BLE_extflash_test_read»);

}

ExtFlash_close();

}

DataService_SetParameter(DS_STRING_ID, N_samples*2, flashReadBuff);

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

break;

default:

return;

}

}
Рис. 20. Лог выполнения модифицированного проекта Project Zero в HyperTerminal

Рис. 20. Лог выполнения модифицированного проекта Project Zero в HyperTerminal

Компилируем и собираем проект. Подключаем SensorTag с программатором, запускаем программу-терминал для отслеживания консольного лога, запускаем Flash Programmer 2, обновляем его файлы на прошивку и программируем SensorTag.

В случае успеха можно наблюдать отчет в консоль о ходе работы программы – периодические срабатывания ALERT от SC, проведение операций с SPI-Flash, обработку событий BLE, связанных с записью адреса чтения и чтением данных из SPI (рисунок 20).

Вид со стороны приложения BLE Scanner:

  • задание адреса чтения (задается адрес 0x000040) (рисунок 21);
  • чтение характеристики 0х1131 после задания адреса 0х000040 (рисунок 22) – отображены данные с OPT3001 (их адреса будут 0х000000+0х000040*i, где i – номер измерения);
  • чтение характеристики 0х1131 после задания адреса 0х000020 – отображены данные с TMP007 (их адреса будут 0х000020+0х000040*i, где i – номер измерения).

Рис. 21. Задание адреса для чтения из внешней Flash посредством BLE Scanner

Рис. 21. Задание адреса для чтения из внешней Flash посредством BLE Scanner

Рис. 22. Чтение характеристики 0x1131, в которой отображены данные из внешней Flash – 32 байта, начиная с адреса 0х000040

Рис. 22. Чтение характеристики 0x1131, в которой отображены данные из внешней Flash – 32 байта, начиная с адреса 0х000040

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

Рис. 23. Осциллограммы тока потребления SensorTag при работе модифици- рованного проекта Project Zero – режим установленного соединения

Рис. 23. Осциллограммы тока потребления SensorTag при работе модифицированного проекта Project Zero – режим установленного соединения

Замеры потребления тока дают примерно следующие результаты – пиковое потребление ~300 мкА, порядка 90 мкА в фоне.

Цифры приведены для проекта, собранного после отключения UARTа, и, соответственно, при отсутствии лога в консоль из-за закомментированых в main() строках:

UART_init();

UartLog_init(UART_open(Board_UART, NULL));

Осциллограммы мгновенного тока потребления представлены на рисунке  23.

Заключение

Многозадачность TIRTOS и SC позволяет реализовывать прикладную функциональность узлов беспроводных сетей практически вне зависимости от стека протоколов, на базе которого работает сеть. Основную нагрузку на сопряжение нужных функций и обеспечение работы сети берут callback-функции.

При работе с SensorTag как с платформой для прототипирования и макетирования проектов необходимо отслеживать начальную конфигурацию сенсоров, установленных на плате: в первую очередь – для более корректной оценки уровня энергопотребления.

Описанный в статье проект можно загрузить в виде архива CC2650_SC_project_.zip.

Литература

  1. CC1350 – первый в мире микроконтроллер с двухдиапазонным радио 868_2400 МГц.
  2. LAUNCHXL-CC2650 – отладочная плата BLE_ZigBee_6LoWPAN для Интернета Вещей.
  3. Миниатюрный CC2650STK открывает дверь в «Интернет вещей».
  4. Журнал «Новости Электроники» №3/ 2016 «Датчики и модули для «Интернета вещей».
  5. Дмитрий Коржавин. Как создать себе IoT. Учимся делать интернет вещей на Android и хардкорном железе – «Хакер».
  6. Как создать себе IoT. Часть 2_ завершение проекта – «Хакер».
  7. А. Калачев. Отладочная плата устройств Internet of Things – SimpleLink SensorTag – CC2650STK. Часть 1 – теория.
  8. OPT3001 Ambient Light Sensor (ALS).
  9. CC2650 SensorTag_ Low power checklist , SensorTag, 43oh.
  10. BLE Android Project Zero sample code. CC2650F128RGZT Texas Instruments.
  11. CC2650 Launchpad Project 0.
  12. SimpleLink multi-standard 2.4 GHz ultra-low power wireless MCU 48-VQFN -40 to 85 Reference Design.
О компании Texas Instruments

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

Наличие на складах
Наименование Наличие Цена
TMP007AIYZFT (TI) 5 7.8642 $ 461.33 руб. от 64 шт
TMP007EVM (TI) 119 94.45 $ 5541 руб. от 2 шт
TMP007AIYZFR (TI) 0
OPT3001DNPT (TI) 3 769 1.5704 $ 92.12 руб. от 319 шт
OPT3001EVM (TI) 37 32.01 $ 1878 руб. от 16 шт
OPT3001DNPR (TI) 3 017 2.3833 $ 139.81 руб. от 1 500 шт