Открытый проект модуля IoT K66BLEZv1 продолжает развиваться.
Здесь рассмотрим следующие вопросы важные на первом этапе освоения:
— технология быстрого создания приложений для модуля в среде IAR Embedded Workbench без сложных SDK
— время активизации программы от момента подачи питания
— максимальная скорость программного переключения состояния пинов
— пример управления светодиодом по прерываниям на основе автомата состояний



Знакомство с модулем K66BLEZ1 было начато в этих статьях:
Модуль универсального контроллера для интернета вещей. Вдыхаем жизнь
Модуль универсального контроллера для интернета вещей. Тестирование FatFs

Схема модуля
Репозитарий проекта

Технология быстрой разработки.


В последнее время программное обеспечение для микроконтроллеров на ядре ARM Cortex-M стало таким сложным, что иногда кажется без специального API нельзя даже пином пошевелить. Производители чипов не жалеют сил чтобы максимально «абстрагировать» разработчиков от периферии снабжая нас различными SDK с библиотеками драйверов, уровнями абстракций (HAL), логическими уровнями и прочими прослойками. Это вроде бы должно ускорить разработку приложений, упростить вхождение в тему, избавить от необходимости чтения справочных руководств, обеспечить реюзинг кода.

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

Поэтому я всегда начинаю с создания своей минималистичной библиотеки функций работы с периферией. В моих функциях я не пытаюсь создать единые интерфейсы доступа к разной периферии, везде где можно обращаюсь к периферии напрямую, не придаю особое значение реюзингу низкоуровневых функций и часто переписываю их код или делаю его полный рефактринг.
Здесь я продемонстрирую несколько простых исходников с помощью которых начинающие могут завести и эффективно использовать модуль K66BLEZv1 без необходимости изучения и использования библиотек драйверов и уровня абстракций периферии из стандартного SDK для микроконтроллеров семейства Kinetis.

Установка среды разработки.


Начнём с установки среды разработки. Это будет IAR Embedded Workbench. Пакет коммерческий, но нам достаточно возможностей тестовой бесплатной версии. Качество и быстрота компиляции в IAR не уступают уровню Keil MDK и ARM GCC. Актуальный сравнительный анализ можно найти здесь.
Скачивается IAR Embedded Workbench легко и через простую форму регистрации получается триальная лицензия.

Пишем приложение.



Точнее изучаем, поскольку приложение уже создано. Проект находится здесь.
Скриншот первого простейшего приложения показан ниже. Традиционно моргаем светодиодом.
Пишем на языке C, но с расширениями, предоставляемыми компилятором IAR. Перейти на C++ в IAR не составляет труда, достаточно у файлов c расширением .c поменять расширение на .cpp.

Первое с чего приходится начинать писать программу для таких развитых микроконтроллеров как Kinetis это инициализация подсистемы тактирования ядра и периферии. В чипе несколько генераторов, умножителей частоты, делителей и прочего. Все нужно правильно настроить и включить в строго определённом порядке. В стандартном SDK это реализовано весьма избыточно, запутанно и в стиле магических чисел. В этом примере у меня все поместилось в одну простую процедуру в файле K66BLEZ1_INIT_SYS.c
Вот её текст:
Нажать сюда
//-------------------------------------------------------------------------------------------------------
//  Инициализация чипа MK66FN2M0VLQ18 на плате K66BLEZ1
//-------------------------------------------------------------------------------------------------------
int Init_MK66FN2M0VLQ18_K66BLEZ1(void)
{
  WDOG_MemMapPtr WDOG = WDOG_BASE_PTR;

  // Выключать WATCHDOG надо сразу, иначе может возникнуть нестабильный запуск кода после холодного рестарта
  //--------------------------------------------------------------------------------------------------------------------------------------
  WDOG->UNLOCK = 0xC520; // Откроем доступ на запись в регитры управления WDOG
  WDOG->UNLOCK = 0xD928;
  WDOG->STCTRLH = 0
                  + LSHIFT(0x00, 14) // DISTESTWDOG | Allows the WDOG’s functional test mode to be disabled permanently| 0 WDOG functional test mode is not disabled.
                  + LSHIFT(0x00, 12) // BYTESEL[1:0]| This 2-bit field select the byte to be tested ...                | 00 Byte 0 selected
                  + LSHIFT(0x00, 11) // TESTSEL     | Selects the test to be run on the watchdog timer                 | 0 Quick test
                  + LSHIFT(0x00, 10) // TESTWDOG    | Puts the watchdog in the functional test mode                    |
                  + LSHIFT(0x01, 8)  // Reserved    |
                  + LSHIFT(0x01, 7)  // WAITEN      | Enables or disables WDOG in wait mode.                           | 1 WDOG is enabled in CPU wait mode.
                  + LSHIFT(0x01, 6)  // STOPEN      | Enables or disables WDOG in stop mode                            | 1 WDOG is enabled in CPU stop mode.
                  + LSHIFT(0x00, 5)  // DBGEN       | Enables or disables WDOG in Debug mode                           | 0 WDOG is disabled in CPU Debug mode.
                  + LSHIFT(0x01, 4)  // ALLOWUPDATE | Enables updates to watchdog write once registers                 | 1 WDOG write once registers can be unlocked for updating
                  + LSHIFT(0x00, 3)  // WINEN       | Enable windowing mode.                                           | 0 Windowing mode is disabled.
                  + LSHIFT(0x00, 2)  // IRQRSTEN    | Used to enable the debug breadcrumbs feature                     | 0 WDOG time-out generates reset only.
                  + LSHIFT(0x01, 1)  // CLKSRC      | Selects clock source for the WDOG                                | 1 WDOG clock sourced from alternate clock source
                  + LSHIFT(0x00, 0)  // WDOGEN      | Enables or disables the WDOG’s operation                         | 1 WDOG is enabled.
  ;


  Init_pins();
  Init_cpu();


  return 1;
}


//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
void Init_cpu(void)
{
  //SCB_MemMapPtr  SCB  = SystemControl_BASE_PTR;
  //NVIC_MemMapPtr NVIC = NVIC_BASE_PTR;
  SIM_MemMapPtr  SIM  = SIM_BASE_PTR;
  RTC_MemMapPtr  RTC  = RTC_BASE_PTR;
  MCG_MemMapPtr  MCG  = MCG_BASE_PTR;
  PIT_MemMapPtr  PIT  = PIT_BASE_PTR;
  FMC_MemMapPtr  FMC  = FMC_BASE_PTR;
  //CRC_MemMapPtr  CRC  = CRC_BASE_PTR;
  RCM_MemMapPtr  RCM  = RCM_BASE_PTR;
  PMC_MemMapPtr  PMC  = PMC_BASE_PTR;

  MPU_BASE_PTR->CESR = 0;    // 0 MPU is disabled. All accesses from all bus masters are allowed.




  //--------------------------------------------------------------------------------------------------------------------------------------
  SIM->SCGC6 |= BIT(29); // RTC | RTC clock gate control | 1 Clock is enabled.
  if ( (RTC->CR & BIT(8)) == 0u ) // If 0, 32.768 kHz oscillator is disabled.
  {
    RTC->CR = 0
              + LSHIFT(0x00, 13) // SC2P | Oscillator 2pF load configure  | 0 Disable the load.
              + LSHIFT(0x00, 12) // SC4P | Oscillator 4pF load configure  | 0 Disable the load.
              + LSHIFT(0x00, 11) // SC8P | Oscillator 8pF load configure  | 0 Disable the load.
              + LSHIFT(0x00, 10) // SC16P| Oscillator 16pF load configure | 0 Disable the load.
              + LSHIFT(0x00, 9)  // CLKO | Clock Output                   | 1 The 32kHz clock is not output to other peripherals
              + LSHIFT(0x01, 8)  // OSCE | Oscillator Enable              | 1 32.768 kHz oscillator is enabled.
              + LSHIFT(0x00, 3)  // UM   | Update Mode                    | 0 Registers cannot be written when locked.
              + LSHIFT(0x00, 2)  // SUP  | Supervisor Access              | 0 Non-supervisor mode write accesses are not supported and generate a bus error.
              + LSHIFT(0x00, 1)  // WPE  | Wakeup Pin Enable              | 0 Wakeup pin is disabled.
              + LSHIFT(0x00, 0)  // SWR  | Software Reset                 | 0 No effect
    ;
  }




  //--------------------------------------------------------------------------------------------------------------------------------------
  // Загрузка регистров из области чипа с заводскими установками
  if ( *((uint8_t *)0x03FFU) != 0xFFU )
  {
    MCG->C3 = *((uint8_t *)0x03FFU);
    MCG->C4 = (MCG_C4 & 0xE0U) | ((*((uint8_t *)0x03FEU)) & 0x1FU);
  }

  //--------------------------------------------------------------------------------------------------------------------------------------
  SIM->CLKDIV1 = 0
                 + LSHIFT(0x00, 28) // OUTDIV1 | Divide value for the core/system clock                                  | 0000 Divide-by-1.  | core/system clock = 180 MHz = CPU_CORE_CLK_HZ_CONFIG_3
                 + LSHIFT(0x02, 24) // OUTDIV2 | Divide value for the peripheral clock                                   | 0010 Divide-by-3.  | bus clock = 60 MHz = CPU_BUS_CLK_HZ_CONFIG_3
                 + LSHIFT(0x06, 20) // OUTDIV3 | Divide value for the FlexBus clock driven to the external pin (FB_CLK). | 0110 Divide-by-7.  | FlexBus clock = 25.7 MHz = CPU_FLEXBUS_CLK_HZ_CONFIG_3
                 + LSHIFT(0x06, 16) // OUTDIV4 | Divide value for the flash clock                                        | 0110 Divide-by-7.  | flash clock = 25.7 MHz = CPU_FLASH_CLK_HZ_CONFIG_3
  ;

  SIM->CLKDIV4 = 0
                 + LSHIFT(0x01, 01) // TRACEDIV   | Trace clock divider divisor
                 + LSHIFT(0x00, 00) // TRACEFRAC  | Trace clock divider fraction
  ;
  //
  SIM->SOPT2 = 0
               + LSHIFT(0x00, 28) // ESDHCSRC    | ESDHC perclk source select            | 00 Core/system clock
               + LSHIFT(0x01, 26) // LPUARTSRC   | LPUART clock source select            | 01 MCGFLLCLK , or MCGPLLCLK, or IRC48M, or USB1 PFD
               + LSHIFT(0x01, 24) // TPMSRC      | TPM clock source select               | 01 MCGFLLCLK , or MCGPLLCLK, or IRC48M, or USB1 PFD
               + LSHIFT(0x00, 20) // TIMESRC     | Ethernet timestamp clock source select| 00 System platform clock
               + LSHIFT(0x00, 19) // RMIISRC     | RMII clock source select              | 0 EXTAL clock
               + LSHIFT(0x01, 18) // USBSRC      | USB clock source select               | 1 MCGFLLCLK, or MCGPLLCLK, or IRC48M, or USB1 PFD
               + LSHIFT(0x01, 16) // PLLFLLSEL   | PLL/FLL clock select                  | 01 MCGPLL0CLK !!!
               + LSHIFT(0x01, 12) // TRACECLKSEL | Debug trace clock select              | 0 MCGCLKOUT
               + LSHIFT(0x03, 8)  // FBSL        | Flexbus security level                | 11 Off-chip op code accesses and data accesses are allowed.
               + LSHIFT(0x02, 5)  // CLKOUTSEL   | Clock out select                      | 010 Flash ungated clock
               + LSHIFT(0x00, 4)  // RTCCLKOUTSEL| RTC clock out select                  | 0 RTC 1 Hz clock drives RTC CLKOUT.
               + LSHIFT(0x00, 1)  // USBREGEN    | USB PHY PLL Regulator Enable          | 1 USB PHY PLL Regulator enabled.
               + LSHIFT(0x00, 0)  // USBSLSRC    | USB Slow Clock Source                 | 0 MCGIRCLK
  ;
  SIM->SOPT1 = 0
               + LSHIFT(0x01, 31) // USBREGEN  | USB voltage regulator enable
               + LSHIFT(0x00, 30) // USBSSTBY  | UUSB voltage regulator in standby mode during Stop, VLPS, LLS or VLLS
               + LSHIFT(0x00, 29) // USBVSTBY  | USB voltage regulator in standby mode during VLPR or VLPW
               + LSHIFT(0x02, 18) // OSC32KSEL | 32K oscillator clock select | 10 RTC 32.768kHz oscillator
  ;


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

  MCG->C7 = 0; // OSCSEL | MCG OSC Clock Select | 0 Selects System Oscillator (OSCCLK). 1 Selects 32 kHz RTC Oscillator.
               // При подключении к FLL синала 32 kHz RTC не происходит установка битов IREFS и CLKS
               // Поэтому подключается сигнал с OSC0 который в данной схеме принимает внешний тактовый сигнал 50 МГц


  // Конфигурируем осцилятор 0 (кварц 32 МГц)
  MCG->C2 = 0
            + LSHIFT(0x00, 7) // LOCRE0 | Loss of Clock Reset Enable     | 0 Interrupt request is generated on a loss of OSC0 external reference clock.
            + LSHIFT(0x00, 6) // FCFTRIM| Fast Internal Reference Clock Fine Trim     | FCFTRIM controls the smallest adjustment of the fast internal reference clock frequency
            + LSHIFT(0x02, 4) // RANGE0 | Frequency Range Select         | 1X Encoding 2 — Very high frequency range selected for the crystal oscillator ..
            + LSHIFT(0x00, 3) // HGO0   | High Gain Oscillator Select    | 1 Configure crystal oscillator for high-gain operation.
            + LSHIFT(0x01, 2) // EREFS0 | External Reference Select      | 1 Oscillator requested.
            + LSHIFT(0x00, 1) // LP     | Low Power Select               | 0 FLL (or PLL) is not disabled in bypass modes.
            + LSHIFT(0x01, 0) // IRCS   | Internal Reference Clock Select| 1 Fast internal reference clock selected.
  ;


  // Установка емкостей на осциляторе 0
  OSC_CR = 0
            + LSHIFT(0x01, 7) // ERCLKEN  | External Reference Enable (OSCERCLK)       | 1 External reference clock is enabled.
            + LSHIFT(0x00, 5) // EREFSTEN | External Reference Stop Enable             | 0 External reference clock is disabled in Stop mode.
            + LSHIFT(0x00, 3) // SC2P     | Oscillator 2  pF Capacitor Load Configure  | 1 Add 2 pF capacitor to the oscillator load.
            + LSHIFT(0x00, 2) // SC4P     | Oscillator 4  pF Capacitor Load Configure  | 1 Add 4 pF capacitor to the oscillator load.
            + LSHIFT(0x00, 1) // SC8P     | Oscillator 8  pF Capacitor Load Configure  | 1 Add 8 pF capacitor to the oscillator load.
            + LSHIFT(0x01, 0) // SC16P    | Oscillator 16 pF Capacitor Load Configure  | 1 Add 16 pF capacitor to the oscillator load.
  ;

  // Переход в режим FBE - FLL Bypassed External ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  MCG->C1 = 0
           + LSHIFT(0x02, 6) // CLKS     | Clock Source Select             | 10 Encoding 2 — External reference clock is selected.
           + LSHIFT(0x03, 3) // FRDIV    | FLL External Reference Divider  | 011 If RANGE 0 = 0 or OSCSEL=1 , Divide Factor is 32; for all other RANGE 0 values, Divide Factor is 1024.
           + LSHIFT(0x00, 2) // IREFS    | Internal Reference Select       | 0 External reference clock is selected.
           + LSHIFT(0x01, 1) // IRCLKEN  | Internal Reference Clock Enable | 1 MCGIRCLK active.
           + LSHIFT(0x00, 0) // IREFSTEN | Internal Reference Stop Enable  | 0 Internal reference clock is disabled in Stop mode.
  ;

  // Настройка FLL
  MCG->C4 = 0
           + LSHIFT(0x00, 7) // DMX32    | DCO Maximum Frequency with 32.768 kHz Reference  | 0 DCO has a default range of 25%.
           + LSHIFT(0x00, 5) // DRST_DRS | DCO Range Select                                 | 00 Encoding 0 — Low range (reset default).
           + LSHIFT(0x00, 1) // FCTRIM   | Fast Internal Reference Clock Trim Setting       |
           + LSHIFT(0x00, 0) // SCFTRIM  | Slow Internal Reference Clock Fine Trim          |
  ;


  //
  // Конфигурация PLL0 на 180 МГц
  MCG->C5 = 0
           + LSHIFT(0x00, 6) // PLLCLKEN  | PLL Clock Enable                | 0 MCGPLL0CLK and MCGPLL0CLK2X are inactive
           + LSHIFT(0x00, 5) // PLLSTEN   | PLL Stop Enable                 | 0 MCGPLL0CLK and MCGPLL0CLK2X are disabled in any of the Stop modes.
           + LSHIFT(0x00, 0) // PRDIV     | PLL External Reference Divider  | 011 Divide Factor 4. Selects the amount to divide down the external reference clock for the PLL0. The resulting frequency must be in the range of 8 MHz to 16 MHz.
  ;

  MCG->C6 = 0
           + LSHIFT(0x00, 7) // LOLIE0  | Loss of Lock Interrrupt Enable | 0 No interrupt request is generated on loss of lock.
           + LSHIFT(0x00, 6) // PLLS    | PLL Select                     | 0 FLL is selected.
           + LSHIFT(0x00, 5) // CME0    | Clock Monitor Enable           | 0 External clock monitor is disabled for OSC0.
           + LSHIFT(0x0E, 0) // VDIV    | VCO Divider                    | Умножаем на 30 (чтобы получить 180 МГц от 12 МГц кварца)
  ;


  MCG->C11 = 0
            + LSHIFT(0x00, 4) // PLLCS      | PLL Clock Select                | 0 PLL0 output clock is selected
  ;


  while ((MCG->S & BIT(1)) == 0) // OSCINIT0 Ждем стабилизации осцилятора OSC0
  {
  }
  while ((MCG->S & BIT(4)) != 0) // IREFST Ждем пока FLL начнет работать от внешнего источника тактирования
  {
  }
  while ((MCG->S & 0x0CU) != 0x08U)  // CLKST  Ждем пока мультиплексор CLKS установиться в режим FBE - FLL Bypassed External
  {
  }

  // Переход в режим PBE - PLL Bypassed External ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  MCG->C6 = 0
           + LSHIFT(0x00, 7) // LOLIE0  | Loss of Lock Interrrupt Enable | 0 No interrupt request is generated on loss of lock.
           + LSHIFT(0x01, 6) // PLLS    | PLL Select                     | 1 PLLCS output clock is selected
           + LSHIFT(0x00, 5) // CME0    | Clock Monitor Enable           | 0 External clock monitor is disabled for OSC0.
           + LSHIFT(0x0E, 0) // VDIV0   | VCO0 Divider                   | Умножаем на 30 (чтобы получить 180 МГц от 12 МГц кварца)
  ;


  while ((MCG->S & 0x0CU) != 0x08U)  // CLKST  Ждем пока мультиплексор CLKS установиться в режим FBE - FLL Bypassed External
  {
  }

  while ((MCG->S & BIT(6)) == 0x00U) // LOCK0 Ждем пока PLL0 защелкнется
  {
  }

  // Переход в режим PEE - PLL Engaged External ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  MCG->C1 = 0
           + LSHIFT(0x00, 6) // CLKS     | Clock Source Select             | 00 Encoding 0 — Output of FLL or PLLCS is selected (depends on PLLS control bit).
           + LSHIFT(0x03, 3) // FRDIV    | FLL External Reference Divider  | 101 If RANGE 0 = 0 or OSCSEL=1 , Divide Factor is 32; for all other RANGE 0 values, Divide Factor is 1024.
           + LSHIFT(0x00, 2) // IREFS    | Internal Reference Select       | 0 External reference clock is selected.
           + LSHIFT(0x01, 1) // IRCLKEN  | Internal Reference Clock Enable | 1 MCGIRCLK active.
           + LSHIFT(0x00, 0) // IREFSTEN | Internal Reference Stop Enable  | 0 Internal reference clock is disabled in Stop mode.
  ;

  while ((MCG->S & 0x0CU) != 0x0CU)  // CLKST  Ждем пока мультиплексор CLKS установиться в режим PEE - PLL Engaged External
  {
  }

  MCG->C6 |= BIT(5);     // CME0 = 1 | 1 External clock monitor is enabled for OSC0.


  //--------------------------------------------------------------------------------------------------------------------------------------
  // Установки  Reset Control Module (RCM)
  RCM->RPFW = 0
              + LSHIFT(0x1F, 0) // RSTFLTSEL | Selects the reset pin bus clock filter width.| 11111 Bus clock filter count is 32
  ;
  RCM->RPFC = 0
              + LSHIFT(0x00, 2) // RSTFLTSS  | Selects how the reset pin filter is enabled in STOP and VLPS modes. | 0 All filtering disabled
              + LSHIFT(0x01, 0) // RSTFLTSRW | Selects how the reset pin filter is enabled in run and wait modes.  | 01 Bus clock filter enabled for normal operation
  ;

  if ((PMC->REGSC & BIT(3)) != 0) PMC->REGSC |= BIT(3); // Сброс бита ACKISO, чтобы разблокировать некоторую периферию

  //--------------------------------------------------------------------------------------------------------------------------------------
  // Установки Power Management Controller (PMC)
  PMC->REGSC = 0
               + LSHIFT(0x00, 3) // ACKISO | Acknowledge Isolation | 0 Peripherals and I/O pads are in normal run state|
                                 //          Writing one to this bit when it is set releases the I/O pads and certain peripherals to their normal run mode state
               + LSHIFT(0x00, 0) // BGBE   | Bandgap Buffer Enable | 0 Bandgap buffer not enabled
  ;


  PMC->LVDSC1 = 0
                + LSHIFT(0x01, 6) // LVDACK | Low-Voltage Detect Acknowledge     | This write-only bit is used to acknowledge low voltage detection errors (write 1 to clear LVDF). Reads always return 0.
                + LSHIFT(0x00, 5) // LVDIE  | Low-Voltage Detect Interrupt Enable| 0 Hardware interrupt disabled (use polling)
                + LSHIFT(0x01, 4) // LVDRE  | Low-Voltage Detect Reset Enable    | 1 Force an MCU reset when LVDF = 1
                + LSHIFT(0x00, 0) // LVDV   | Low-Voltage Detect Voltage Select  | 00 Low trip point selected (V LVD = V LVDL )
  ;

  PMC->LVDSC2 = 0
                + LSHIFT(0x01, 6) // LVWACK | Low-Voltage Warning Acknowledge      |
                + LSHIFT(0x00, 5) // LVWIE  | Low-Voltage Warning Interrupt Enable | 0 Hardware interrupt disabled (use polling)
                + LSHIFT(0x00, 0) // LVWV   | Low-Voltage Warning Voltage Select   | 00 Low trip point selected (V LVW = V LVW1 )
  ;


  //--------------------------------------------------------------------------------------------------------------------------------------
  // Инициализация Periodic Interrupt timer
  SIM->SCGC6 |= BIT(23); // PIT | PIT clock gate control | 1 Clock is enabled.

  PIT->MCR  = 0
              + LSHIFT(0x00, 1) // MDIS | Module Disable | 0 Clock for PIT Timers is enabled.
              + LSHIFT(0x01, 0) // FRZ  | Freeze         | 1 Timers are stopped in debug mode.
  ;



  //--------------------------------------------------------------------------------------------------------------------------------------
  // Инициализация Flash Access Protection Register для разрешения доступа к Flash по DMA и от прочих мастеров
  FMC->PFAPR = 0
               + LSHIFT(0x01, 23) // M7PFD     | 1 Prefetching for this master is disabled.   (Ethernet)
               + LSHIFT(0x01, 22) // M6PFD     | 1 Prefetching for this master is disabled.   (USB HS)
               + LSHIFT(0x01, 21) // M5PFD     | 1 Prefetching for this master is disabled.   ()
               + LSHIFT(0x01, 20) // M4PFD     | 1 Prefetching for this master is disabled.   ()
               + LSHIFT(0x00, 19) // M3PFD     | 0 Prefetching for this master is enabled.    (SDHC, NFC, USB FS)
               + LSHIFT(0x00, 18) // M2PFD     | 0 Prefetching for this master is enabled.    (DMA, EzPort)
               + LSHIFT(0x00, 17) // M1PFD     | 0 Prefetching for this master is enabled.    (ARM core system bus)
               + LSHIFT(0x00, 16) // M0PFD     | 0 Prefetching for this master is enabled.    (ARM core code bus)
               + LSHIFT(0x00, 14) // M7AP[1:0] | 00 No access may be performed by this master.
               + LSHIFT(0x00, 12) // M6AP[1:0] | 00 No access may be performed by this master.
               + LSHIFT(0x00, 10) // M5AP[1:0] | 00 No access may be performed by this master.
               + LSHIFT(0x00, 8)  // M4AP[1:0] | 00 No access may be performed by this master.
               + LSHIFT(0x03, 6)  // M3AP[1:0] | 11 Both read and write accesses may be performed by this master
               + LSHIFT(0x03, 4)  // M2AP[1:0] | 11 Both read and write accesses may be performed by this master
               + LSHIFT(0x03, 2)  // M1AP[1:0] | 11 Both read and write accesses may be performed by this master
               + LSHIFT(0x03, 0)  // M0AP[1:0] | 11 Both read and write accesses may be performed by this master
  ;

  SIM->SCGC6 |= BIT(18); // Включаем модуль CRC

  SIM->SCGC4 |= BIT(19); // Включаем модуль CMP (аналоговые компараторы)

}


Текст конечно нельзя назвать коротким, но он все равно гораздо короче и понятней стандартных исходников SDK.

В процедуре Init_MK66FN2M0VLQ18_K66BLEZ1, как видно из текста перед инициализацией подсистемы тактирования стоит вызов функции Init_pins. Это единая функция инициализации всех выводов микроконтроллера. В SDK разработчики почему-то приняли метод инициализировать выводы в разных функциях драйверов совместно с периферией, которая через них будет работать. Это напоминает стиль драйверов под большие операционные системы. Но микроконтроллер — это не компьютер, всю программу включая драйвера пишет один человек (по крайней мере в моем случае), и ему не надо создавать себе искусственное разделение на «зоны ответственности». Но вот контроль за ресурсами в особенности за назначением функций выводам это ключевая проблема при разработке встраиваемых систем. Поэтому управление выводами я централизую и не отдаю его на откуп драйверам.
Вот текст процедуры инициализации выводов чипа:
Нажать сюда
typedef struct
{
  GPIO_MemMapPtr gpio;
  PORT_MemMapPtr port;
  unsigned char  pin_num;
  unsigned char  irqc; //  Interrupt Configuration
                       //  0000 Interrupt/DMA Request disabled.
                       //  0001 DMA Request on rising edge.
                       //  0010 DMA Request on falling edge.
                       //  0011 DMA Request on either edge.
                       //  0100 Reserved.
                       //  1000 Interrupt when logic zero.
                       //  1001 Interrupt on rising edge.
                       //  1010 Interrupt on falling edge.
                       //  1011 Interrupt on either edge.
                       //  1100 Interrupt when logic one.
  unsigned char  lock; //  if 1 Pin Control Register bits [15:0] are locked and cannot be updated until the next System Reset.
  unsigned char  mux;  //  Pin Mux Control
                       //  000 Pin Disabled (Analog).
                       //  001 Alternative 1 (GPIO).
                       //  010 Alternative 2 (chip specific).
                       //  011 Alternative 3 (chip specific).
                       //  100 Alternative 4 (chip specific).
                       //  101 Alternative 5 (chip specific).
                       //  110 Alternative 6 (chip specific).
                       //  111 Alternative 7 (chip specific / JTAG / NMI).
  unsigned char  DSE; // 0 Low drive strength is configured on the corresponding pin, if pin is configured as a digital output.
                      // 1 High drive strength is configured on the corresponding pin, if pin is configured as a digital output.
  unsigned char  SRE;  // 0 Fast slew rate is configured on the corresponding pin, if pin is configured as a digital output.
                       // 1 Slow slew rate is configured on the corresponding pin, if pin is configured as a digital output.
  unsigned char  ODE;  // 0 Open Drain output is disabled on the corresponding pin.
                       // 1 Open Drain output is enabled on the corresponding pin, provided pin is configured as a digital output.
  unsigned char  PFE;  // 0 Passive Input Filter is disabled on the corresponding pin.
                       // 1 Passive Input Filter is enabled on the corresponding pin.
  unsigned char  PUPD; // 00 Internal pull-up or pull-down resistor is not enabled on the corresponding pin.
                       // 10 Internal pull-down resistor is enabled on the corresponding pin, if the corresponding Port Pull Enable Register bit is set.
                       // 11 Internal pull-up resistor is enabled on the corresponding pin, if the corresponding Port Pull Enable Register bit is set.
  unsigned char  dir;  // 0 Pin is configured as general purpose input, if configured for the GPIO function
                       // 1 Pin is configured for general purpose output, if configured for the GPIO function
  unsigned char  init; // Init state

} T_IO_pins_configuration;

#define   ANAL          0  // Pin Disabled (Analog).
#define   ALT0          0  // Pin Disabled (Analog).
#define   GPIO          1  // Alternative 1 (GPIO).
#define   ALT1          1  // Alternative 1 (GPIO).
#define   ALT2          2  //
#define   ALT3          3  //
#define   ALT4          4  //
#define   ALT5          5  //
#define   ALT6          6  //
#define   ALT7          7  //

#define   DSE_LO        0 // 0 Low drive strength is configured on the corresponding pin, if pin is configured as a digital output.
#define   DSE_HI        1 // 1 High drive strength is configured on the corresponding pin, if pin is configured as a digital output.

#define   OD_DIS        0 // 0 Open Drain output is disabled on the corresponding pin.
#define   OD__EN        1 // 1 Open Drain output is enabled on the corresponding pin, provided pin is configured as a digital output.

#define   PFE_DIS       0 // 0 Passive Input Filter is disabled on the corresponding pin.
#define   PFE__EN       1 // 1 Passive Input Filter is enabled on the corresponding pin.

#define   FAST_SLEW     0 // 0 Fast slew rate is configured on the corresponding pin, if pin is configured as a digital output.
#define   SLOW_SLEW     1 // 1 Slow slew rate is configured on the corresponding pin, if pin is configured as a digital output.


#define   PUPD_DIS      0 // 00 Internal pull-up or pull-down resistor is not enabled on the corresponding pin.
#define   PULL__DN      2 // 10 Internal pull-down resistor is enabled on the corresponding pin, if the corresponding Port Pull Enable Register bit is set.
#define   PULL__UP      3 // 11 Internal pull-up resistor is enabled on the corresponding pin, if the corresponding Port Pull Enable Register bit is set.

#define   GP_INP        0 // 0 Pin is configured as general purpose input, if configured for the GPIO function
#define   GP_OUT        1 // 1 Pin is configured for general purpose output, if configured for the GPIO function

void Config_pin(const T_IO_pins_configuration pinc);

// Настройки линий ввода/вывода платы K66BLEZ1 на базе микроконтроллера MK66FN2M0VLQ18

const T_IO_pins_configuration K66BLEZ1_pins_conf[] =
{
//  gpio          port            num  irqc  lock  mux   DSE     SRE        ODE     PFE      PUPD      dir     init
  { PTA_BASE_PTR, PORTA_BASE_PTR,   0,   0,   0,   ALT7, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // JTCLK/SWC            50 # PTA0 # Default=(JTAG_TCLK/SWD_CLK/EZP_CLK)  ALT0=(TSI0_CH1)  ALT1=(PTA0)  ALT2=(UART0_CTS_b/UART0_COL_b)  ALT3=(FTM0_CH5)  ALT4=()  ALT5=(LPUART0_CTS_b)  ALT6=()  ALT7=(JTAG_TCLK/SWD_CLK)  EZPort=(EZP_CLK)
  { PTA_BASE_PTR, PORTA_BASE_PTR,   1,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_OUT,   1 }, // JTDI (LED)           51 # PTA1 # Default=(JTAG_TDI/EZP_DI)  ALT0=(TSI0_CH2)  ALT1=(PTA1)  ALT2=(UART0_RX)  ALT3=(FTM0_CH6)  ALT4=(I2C3_SDA)  ALT5=(LPUART0_RX)  ALT6=()  ALT7=(JTAG_TDI)  EZPort=(EZP_DI)
  { PTA_BASE_PTR, PORTA_BASE_PTR,   2,   0,   0,   ALT7, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // JTDO/SWO             52 # PTA2 # Default=(JTAG_TDO/TRACE_SWO/EZP_DO)  ALT0=(TSI0_CH3)  ALT1=(PTA2)  ALT2=(UART0_TX)  ALT3=(FTM0_CH7)  ALT4=(I2C3_SCL)  ALT5=(LPUART0_TX)  ALT6=()  ALT7=(JTAG_TDO/TRACE_SWO)  EZPort=(EZP_DO)
  { PTA_BASE_PTR, PORTA_BASE_PTR,   3,   0,   0,   ALT7, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // JTMS/SWD             53 # PTA3 # Default=(JTAG_TMS/SWD_DIO)  ALT0=(TSI0_CH4)  ALT1=(PTA3)  ALT2=(UART0_RTS_b)  ALT3=(FTM0_CH0)  ALT4=()  ALT5=(LPUART0_RTS_b)  ALT6=()  ALT7=(JTAG_TMS/SWD_DIO)  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,   4,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      54 # PTA4/LLWU_P3 # Default=(NMI_b/EZP_CS_b)  ALT0=(TSI0_CH5)  ALT1=(PTA4/LLWU_P3)  ALT2=()  ALT3=(FTM0_CH1)  ALT4=()  ALT5=()  ALT6=()  ALT7=(NMI_b)  EZPort=(EZP_CS_b)
  { PTA_BASE_PTR, PORTA_BASE_PTR,   5,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      55 # PTA5 # Default=(DISABLED)  ALT0=()  ALT1=(PTA5)  ALT2=(USB0_CLKIN)  ALT3=(FTM0_CH2)  ALT4=(RMII0_RXER/MII0_RXER)  ALT5=(CMP2_OUT)  ALT6=(I2S0_TX_BCLK)  ALT7=(JTAG_TRST_b)  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,   6,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      58 # PTA6 # Default=(DISABLED)  ALT0=()  ALT1=(PTA6)  ALT2=()  ALT3=(FTM0_CH3)  ALT4=()  ALT5=(CLKOUT)  ALT6=()  ALT7=(TRACE_CLKOUT)  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,   7,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      59 # PTA7 # Default=(ADC0_SE10)  ALT0=(ADC0_SE10)  ALT1=(PTA7)  ALT2=()  ALT3=(FTM0_CH4)  ALT4=()  ALT5=(RMII0_MDIO/MII0_MDIO)  ALT6=()  ALT7=(TRACE_D3)  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,   8,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      60 # PTA8 # Default=(ADC0_SE11)  ALT0=(ADC0_SE11)  ALT1=(PTA8)  ALT2=()  ALT3=(FTM1_CH0)  ALT4=()  ALT5=(RMII0_MDC/MII0_MDC)  ALT6=(FTM1_QD_PHA/TPM1_CH0)  ALT7=(TRACE_D2)  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,   9,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      61 # PTA9 # Default=(DISABLED)  ALT0=()  ALT1=(PTA9)  ALT2=()  ALT3=(FTM1_CH1)  ALT4=(MII0_RXD3)  ALT5=()  ALT6=(FTM1_QD_PHB/TPM1_CH1)  ALT7=(TRACE_D1)  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,  10,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      62 # PTA10/LLWU_P22 # Default=(DISABLED)  ALT0=()  ALT1=(PTA10/LLWU_P22)  ALT2=()  ALT3=(FTM2_CH0)  ALT4=(MII0_RXD2)  ALT5=()  ALT6=(FTM2_QD_PHA/TPM2_CH0)  ALT7=(TRACE_D0)  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,  11,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      63 # PTA11/LLWU_P23 # Default=(DISABLED)  ALT0=()  ALT1=(PTA11/LLWU_P23)  ALT2=()  ALT3=(FTM2_CH1)  ALT4=(MII0_RXCLK)  ALT5=(I2C2_SDA)  ALT6=(FTM2_QD_PHB/TPM2_CH1)  ALT7=()  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,  12,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      64 # PTA12 # Default=(CMP2_IN0)  ALT0=(CMP2_IN0)  ALT1=(PTA12)  ALT2=(CAN0_TX)  ALT3=(FTM1_CH0)  ALT4=(RMII0_RXD1/MII0_RXD1)  ALT5=(I2C2_SCL)  ALT6=(I2S0_TXD0)  ALT7=(FTM1_QD_PHA/TPM1_CH0)  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,  13,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      65 # PTA13/LLWU_P4 # Default=(CMP2_IN1)  ALT0=(CMP2_IN1)  ALT1=(PTA13/LLWU_P4)  ALT2=(CAN0_RX)  ALT3=(FTM1_CH1)  ALT4=(RMII0_RXD0/MII0_RXD0)  ALT5=(I2C2_SDA)  ALT6=(I2S0_TX_FS)  ALT7=(FTM1_QD_PHB/TPM1_CH1)  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,  14,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      66 # PTA14 # Default=(DISABLED)  ALT0=()  ALT1=(PTA14)  ALT2=(SPI0_PCS0)  ALT3=(UART0_TX)  ALT4=(RMII0_CRS_DV/MII0_RXDV)  ALT5=(I2C2_SCL)  ALT6=(I2S0_RX_BCLK)  ALT7=(I2S0_TXD1)  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,  15,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      67 # PTA15 # Default=(CMP3_IN1)  ALT0=(CMP3_IN1)  ALT1=(PTA15)  ALT2=(SPI0_SCK)  ALT3=(UART0_RX)  ALT4=(RMII0_TXEN/MII0_TXEN)  ALT5=()  ALT6=(I2S0_RXD0)  ALT7=()  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,  16,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      68 # PTA16 # Default=(CMP3_IN2)  ALT0=(CMP3_IN2)  ALT1=(PTA16)  ALT2=(SPI0_SOUT)  ALT3=(UART0_CTS_b/UART0_COL_b)  ALT4=(RMII0_TXD0/MII0_TXD0)  ALT5=()  ALT6=(I2S0_RX_FS)  ALT7=(I2S0_RXD1)  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,  17,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      69 # PTA17 # Default=(ADC1_SE17)  ALT0=(ADC1_SE17)  ALT1=(PTA17)  ALT2=(SPI0_SIN)  ALT3=(UART0_RTS_b)  ALT4=(RMII0_TXD1/MII0_TXD1)  ALT5=()  ALT6=(I2S0_MCLK)  ALT7=()  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,  18,   0,   0,   ALT0, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // EXTAL                72 # PTA18 # Default=(EXTAL0)  ALT0=(EXTAL0)  ALT1=(PTA18)  ALT2=()  ALT3=(FTM0_FLT2)  ALT4=(FTM_CLKIN0)  ALT5=()  ALT6=()  ALT7=(TPM_CLKIN0)  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,  19,   0,   0,   ALT0, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // XTAL                 73 # PTA19 # Default=(XTAL0)  ALT0=(XTAL0)  ALT1=(PTA19)  ALT2=()  ALT3=(FTM1_FLT0)  ALT4=(FTM_CLKIN1)  ALT5=()  ALT6=(LPTMR0_ALT1)  ALT7=(TPM_CLKIN1)  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,  24,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      75 # PTA24 # Default=(CMP3_IN4)  ALT0=(CMP3_IN4)  ALT1=(PTA24)  ALT2=()  ALT3=()  ALT4=(MII0_TXD2)  ALT5=()  ALT6=(FB_A29)  ALT7=()  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,  25,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      76 # PTA25 # Default=(CMP3_IN5)  ALT0=(CMP3_IN5)  ALT1=(PTA25)  ALT2=()  ALT3=()  ALT4=(MII0_TXCLK)  ALT5=()  ALT6=(FB_A28)  ALT7=()  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,  26,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      77 # PTA26 # Default=(DISABLED)  ALT0=()  ALT1=(PTA26)  ALT2=()  ALT3=()  ALT4=(MII0_TXD3)  ALT5=()  ALT6=(FB_A27)  ALT7=()  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,  27,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      78 # PTA27 # Default=(DISABLED)  ALT0=()  ALT1=(PTA27)  ALT2=()  ALT3=()  ALT4=(MII0_CRS)  ALT5=()  ALT6=(FB_A26)  ALT7=()  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,  28,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      79 # PTA28 # Default=(DISABLED)  ALT0=()  ALT1=(PTA28)  ALT2=()  ALT3=()  ALT4=(MII0_TXER)  ALT5=()  ALT6=(FB_A25)  ALT7=()  EZPort=()
  { PTA_BASE_PTR, PORTA_BASE_PTR,  29,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      80 # PTA29 # Default=(DISABLED)  ALT0=()  ALT1=(PTA29)  ALT2=()  ALT3=()  ALT4=(MII0_COL)  ALT5=()  ALT6=(FB_A24)  ALT7=()  EZPort=()

  { PTB_BASE_PTR, PORTB_BASE_PTR,   0,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      81 # PTB0/LLWU_P5 # Default=(ADC0_SE8/ADC1_SE8/TSI0_CH0)  ALT0=(ADC0_SE8/ADC1_SE8/TSI0_CH0)  ALT1=(PTB0/LLWU_P5)  ALT2=(I2C0_SCL)  ALT3=(FTM1_CH0)  ALT4=(RMII0_MDIO/MII0_MDIO)  ALT5=(SDRAM_CAS_b)  ALT6=(FTM1_QD_PHA/TPM1_CH0)  ALT7=()  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,   1,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      82 # PTB1 # Default=(ADC0_SE9/ADC1_SE9/TSI0_CH6)  ALT0=(ADC0_SE9/ADC1_SE9/TSI0_CH6)  ALT1=(PTB1)  ALT2=(I2C0_SDA)  ALT3=(FTM1_CH1)  ALT4=(RMII0_MDC/MII0_MDC)  ALT5=(SDRAM_RAS_b)  ALT6=(FTM1_QD_PHB/TPM1_CH1)  ALT7=()  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,   2,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      83 # PTB2 # Default=(ADC0_SE12/TSI0_CH7)  ALT0=(ADC0_SE12/TSI0_CH7)  ALT1=(PTB2)  ALT2=(I2C0_SCL)  ALT3=(UART0_RTS_b)  ALT4=(ENET0_1588_TMR0)  ALT5=(SDRAM_WE)  ALT6=(FTM0_FLT3)  ALT7=()  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,   3,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      84 # PTB3 # Default=(ADC0_SE13/TSI0_CH8)  ALT0=(ADC0_SE13/TSI0_CH8)  ALT1=(PTB3)  ALT2=(I2C0_SDA)  ALT3=(UART0_CTS_b/UART0_COL_b)  ALT4=(ENET0_1588_TMR1)  ALT5=(SDRAM_CS0_b)  ALT6=(FTM0_FLT0)  ALT7=()  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,   4,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      85 # PTB4 # Default=(ADC1_SE10)  ALT0=(ADC1_SE10)  ALT1=(PTB4)  ALT2=()  ALT3=()  ALT4=(ENET0_1588_TMR2)  ALT5=(SDRAM_CS1_b)  ALT6=(FTM1_FLT0)  ALT7=()  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,   5,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      86 # PTB5 # Default=(ADC1_SE11)  ALT0=(ADC1_SE11)  ALT1=(PTB5)  ALT2=()  ALT3=()  ALT4=(ENET0_1588_TMR3)  ALT5=()  ALT6=(FTM2_FLT0)  ALT7=()  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,   6,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      87 # PTB6 # Default=(ADC1_SE12)  ALT0=(ADC1_SE12)  ALT1=(PTB6)  ALT2=()  ALT3=()  ALT4=()  ALT5=(FB_AD23/SDRAM_D23)  ALT6=()  ALT7=()  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,   7,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // INT from LDC1000     88 # PTB7 # Default=(ADC1_SE13)  ALT0=(ADC1_SE13)  ALT1=(PTB7)  ALT2=()  ALT3=()  ALT4=()  ALT5=(FB_AD22/SDRAM_D22)  ALT6=()  ALT7=()  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,   8,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      89 # PTB8 # Default=(DISABLED)  ALT0=()  ALT1=(PTB8)  ALT2=()  ALT3=(UART3_RTS_b)  ALT4=()  ALT5=(FB_AD21/SDRAM_D21)  ALT6=()  ALT7=()  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,   9,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_OUT,   1 }, // SPI1_PCS1            90 # PTB9 # Default=(DISABLED)  ALT0=()  ALT1=(PTB9)  ALT2=(SPI1_PCS1)  ALT3=(UART3_CTS_b)  ALT4=()  ALT5=(FB_AD20/SDRAM_D20)  ALT6=()  ALT7=()  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,  10,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      91 # PTB10 # Default=(ADC1_SE14)  ALT0=(ADC1_SE14)  ALT1=(PTB10)  ALT2=(SPI1_PCS0)  ALT3=(UART3_RX)  ALT4=()  ALT5=(FB_AD19/SDRAM_D19)  ALT6=(FTM0_FLT1)  ALT7=()  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,  11,   0,   0,   ALT2, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // SPI1_SCK             92 # PTB11 # Default=(ADC1_SE15)  ALT0=(ADC1_SE15)  ALT1=(PTB11)  ALT2=(SPI1_SCK)  ALT3=(UART3_TX)  ALT4=()  ALT5=(FB_AD18/SDRAM_D18)  ALT6=(FTM0_FLT2)  ALT7=()  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,  16,   0,   0,   ALT2, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // SPI1_SOUT            95 # PTB16 # Default=(TSI0_CH9)  ALT0=(TSI0_CH9)  ALT1=(PTB16)  ALT2=(SPI1_SOUT)  ALT3=(UART0_RX)  ALT4=(FTM_CLKIN0)  ALT5=(FB_AD17/SDRAM_D17)  ALT6=(EWM_IN)  ALT7=(TPM_CLKIN0)  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,  17,   0,   0,   ALT2, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // SPI1_SIN             96 # PTB17 # Default=(TSI0_CH10)  ALT0=(TSI0_CH10)  ALT1=(PTB17)  ALT2=(SPI1_SIN)  ALT3=(UART0_TX)  ALT4=(FTM_CLKIN1)  ALT5=(FB_AD16/SDRAM_D16)  ALT6=(EWM_OUT_b)  ALT7=(TPM_CLKIN1)  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,  18,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      97 # PTB18 # Default=(TSI0_CH11)  ALT0=(TSI0_CH11)  ALT1=(PTB18)  ALT2=(CAN0_TX)  ALT3=(FTM2_CH0)  ALT4=(I2S0_TX_BCLK)  ALT5=(FB_AD15/SDRAM_A23)  ALT6=(FTM2_QD_PHA/TPM2_CH0)  ALT7=()  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,  19,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      98 # PTB19 # Default=(TSI0_CH12)  ALT0=(TSI0_CH12)  ALT1=(PTB19)  ALT2=(CAN0_RX)  ALT3=(FTM2_CH1)  ALT4=(I2S0_TX_FS)  ALT5=(FB_OE_b)  ALT6=(FTM2_QD_PHB/TPM2_CH1)  ALT7=()  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,  20,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      99 # PTB20 # Default=(DISABLED)  ALT0=()  ALT1=(PTB20)  ALT2=(SPI2_PCS0)  ALT3=()  ALT4=()  ALT5=(FB_AD31/SDRAM_D31)  ALT6=(CMP0_OUT)  ALT7=()  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,  21,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      100 # PTB21 # Default=(DISABLED)  ALT0=()  ALT1=(PTB21)  ALT2=(SPI2_SCK)  ALT3=()  ALT4=()  ALT5=(FB_AD30/SDRAM_D30)  ALT6=(CMP1_OUT)  ALT7=()  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,  22,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      101 # PTB22 # Default=(DISABLED)  ALT0=()  ALT1=(PTB22)  ALT2=(SPI2_SOUT)  ALT3=()  ALT4=()  ALT5=(FB_AD29/SDRAM_D29)  ALT6=(CMP2_OUT)  ALT7=()  EZPort=()
  { PTB_BASE_PTR, PORTB_BASE_PTR,  23,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      102 # PTB23 # Default=(DISABLED)  ALT0=()  ALT1=(PTB23)  ALT2=(SPI2_SIN)  ALT3=(SPI0_PCS5)  ALT4=()  ALT5=(FB_AD28/SDRAM_D28)  ALT6=(CMP3_OUT)  ALT7=()  EZPort=()

  { PTC_BASE_PTR, PORTC_BASE_PTR,   0,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      103 # PTC0 # Default=(ADC0_SE14/TSI0_CH13)  ALT0=(ADC0_SE14/TSI0_CH13)  ALT1=(PTC0)  ALT2=(SPI0_PCS4)  ALT3=(PDB0_EXTRG)  ALT4=(USB0_SOF_OUT)  ALT5=(FB_AD14/SDRAM_A22)  ALT6=(I2S0_TXD1)  ALT7=()  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,   1,   0,   0,   ALT4, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_OUT,   0 }, // FTM0_CH0(Solenoid)   104 # PTC1/LLWU_P6 # Default=(ADC0_SE15/TSI0_CH14)  ALT0=(ADC0_SE15/TSI0_CH14)  ALT1=(PTC1/LLWU_P6)  ALT2=(SPI0_PCS3)  ALT3=(UART1_RTS_b)  ALT4=(FTM0_CH0)  ALT5=(FB_AD13/SDRAM_A21)  ALT6=(I2S0_TXD0)  ALT7=()  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,   2,   0,   0,   ALT4, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // FTM0_CH1(LDC1000 clk)105 # PTC2 # Default=(ADC0_SE4b/CMP1_IN0/TSI0_CH15)  ALT0=(ADC0_SE4b/CMP1_IN0/TSI0_CH15)  ALT1=(PTC2)  ALT2=(SPI0_PCS2)  ALT3=(UART1_CTS_b)  ALT4=(FTM0_CH1)  ALT5=(FB_AD12/SDRAM_A20)  ALT6=(I2S0_TX_FS)  ALT7=()  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,   3,   0,   0,   ALT4, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      106 # PTC3/LLWU_P7 # Default=(CMP1_IN1)  ALT0=(CMP1_IN1)  ALT1=(PTC3/LLWU_P7)  ALT2=(SPI0_PCS1)  ALT3=(UART1_RX)  ALT4=(FTM0_CH2)  ALT5=(CLKOUT)  ALT6=(I2S0_TX_BCLK)  ALT7=()  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,   4,   0,   0,   ALT4, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      109 # PTC4/LLWU_P8 # Default=(DISABLED)  ALT0=()  ALT1=(PTC4/LLWU_P8)  ALT2=(SPI0_PCS0)  ALT3=(UART1_TX)  ALT4=(FTM0_CH3)  ALT5=(FB_AD11/SDRAM_A19)  ALT6=(CMP1_OUT)  ALT7=()  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,   5,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      110 # PTC5/LLWU_P9 # Default=(DISABLED)  ALT0=()  ALT1=(PTC5/LLWU_P9)  ALT2=(SPI0_SCK)  ALT3=(LPTMR0_ALT2)  ALT4=(I2S0_RXD0)  ALT5=(FB_AD10/SDRAM_A18)  ALT6=(CMP0_OUT)  ALT7=(FTM0_CH2)  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,   6,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      111 # PTC6/LLWU_P10 # Default=(CMP0_IN0)  ALT0=(CMP0_IN0)  ALT1=(PTC6/LLWU_P10)  ALT2=(SPI0_SOUT)  ALT3=(PDB0_EXTRG)  ALT4=(I2S0_RX_BCLK)  ALT5=(FB_AD9/SDRAM_A17)  ALT6=(I2S0_MCLK)  ALT7=()  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,   7,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      112 # PTC7 # Default=(CMP0_IN1)  ALT0=(CMP0_IN1)  ALT1=(PTC7)  ALT2=(SPI0_SIN)  ALT3=(USB0_SOF_OUT)  ALT4=(I2S0_RX_FS)  ALT5=(FB_AD8/SDRAM_A16)  ALT6=()  ALT7=()  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,   8,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      113 # PTC8 # Default=(ADC1_SE4b/CMP0_IN2)  ALT0=(ADC1_SE4b/CMP0_IN2)  ALT1=(PTC8)  ALT2=()  ALT3=(FTM3_CH4)  ALT4=(I2S0_MCLK)  ALT5=(FB_AD7/SDRAM_A15)  ALT6=()  ALT7=()  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,   9,   0,   0,   ALT3, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      114 # PTC9 # Default=(ADC1_SE5b/CMP0_IN3)  ALT0=(ADC1_SE5b/CMP0_IN3)  ALT1=(PTC9)  ALT2=()  ALT3=(FTM3_CH5)  ALT4=(I2S0_RX_BCLK)  ALT5=(FB_AD6/SDRAM_A14)  ALT6=(FTM2_FLT0)  ALT7=()  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,  10,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      115 # PTC10 # Default=(ADC1_SE6b)  ALT0=(ADC1_SE6b)  ALT1=(PTC10)  ALT2=(I2C1_SCL)  ALT3=(FTM3_CH6)  ALT4=(I2S0_RX_FS)  ALT5=(FB_AD5/SDRAM_A13)  ALT6=()  ALT7=()  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,  11,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      116 # PTC11/LLWU_P11 # Default=(ADC1_SE7b)  ALT0=(ADC1_SE7b)  ALT1=(PTC11/LLWU_P11)  ALT2=(I2C1_SDA)  ALT3=(FTM3_CH7)  ALT4=(I2S0_RXD1)  ALT5=(FB_RW_b)  ALT6=()  ALT7=()  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,  12,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      117 # PTC12 # Default=(DISABLED)  ALT0=()  ALT1=(PTC12)  ALT2=()  ALT3=(UART4_RTS_b)  ALT4=(FTM_CLKIN0)  ALT5=(FB_AD27/SDRAM_D27)  ALT6=(FTM3_FLT0)  ALT7=(TPM_CLKIN0)  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,  13,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      118 # PTC13 # Default=(DISABLED)  ALT0=()  ALT1=(PTC13)  ALT2=()  ALT3=(UART4_CTS_b)  ALT4=(FTM_CLKIN1)  ALT5=(FB_AD26/SDRAM_D26)  ALT6=()  ALT7=(TPM_CLKIN1)  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,  14,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      119 # PTC14 # Default=(DISABLED)  ALT0=()  ALT1=(PTC14)  ALT2=()  ALT3=(UART4_RX)  ALT4=()  ALT5=(FB_AD25/SDRAM_D25)  ALT6=()  ALT7=()  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,  15,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      120 # PTC15 # Default=(DISABLED)  ALT0=()  ALT1=(PTC15)  ALT2=()  ALT3=(UART4_TX)  ALT4=()  ALT5=(FB_AD24/SDRAM_D24)  ALT6=()  ALT7=()  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,  16,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      123 # PTC16 # Default=(DISABLED)  ALT0=()  ALT1=(PTC16)  ALT2=(CAN1_RX)  ALT3=(UART3_RX)  ALT4=(ENET0_1588_TMR0)  ALT5=(FB_CS5_b/FB_TSIZ1/FB_BE23_16_BLS15_8_b/SDRAM_DQM2)  ALT6=()  ALT7=()  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,  17,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      124 # PTC17 # Default=(DISABLED)  ALT0=()  ALT1=(PTC17)  ALT2=(CAN1_TX)  ALT3=(UART3_TX)  ALT4=(ENET0_1588_TMR1)  ALT5=(FB_CS4_b/FB_TSIZ0/FB_BE31_24_BLS7_0_b/SDRAM_DQM3)  ALT6=()  ALT7=()  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,  18,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      125 # PTC18 # Default=(DISABLED)  ALT0=()  ALT1=(PTC18)  ALT2=()  ALT3=(UART3_RTS_b)  ALT4=(ENET0_1588_TMR2)  ALT5=(FB_TBST_b/FB_CS2_b/FB_BE15_8_BLS23_16_b/SDRAM_DQM1)  ALT6=()  ALT7=()  EZPort=()
  { PTC_BASE_PTR, PORTC_BASE_PTR,  19,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      126 # PTC19 # Default=(DISABLED)  ALT0=()  ALT1=(PTC19)  ALT2=()  ALT3=(UART3_CTS_b)  ALT4=(ENET0_1588_TMR3)  ALT5=(FB_CS3_b/FB_BE7_0_BLS31_24_b/SDRAM_DQM0)  ALT6=(FB_TA_b)  ALT7=()  EZPort=()

  { PTD_BASE_PTR, PORTD_BASE_PTR,   0,   0,   0,   ALT4, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      127 # PTD0/LLWU_P12 # Default=(DISABLED)  ALT0=()  ALT1=(PTD0/LLWU_P12)  ALT2=(SPI0_PCS0)  ALT3=(UART2_RTS_b)  ALT4=(FTM3_CH0)  ALT5=(FB_ALE/FB_CS1_b/FB_TS_b)  ALT6=()  ALT7=()  EZPort=()
  { PTD_BASE_PTR, PORTD_BASE_PTR,   1,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      128 # PTD1 # Default=(ADC0_SE5b)  ALT0=(ADC0_SE5b)  ALT1=(PTD1)  ALT2=(SPI0_SCK)  ALT3=(UART2_CTS_b)  ALT4=(FTM3_CH1)  ALT5=(FB_CS0_b)  ALT6=()  ALT7=()  EZPort=()
  { PTD_BASE_PTR, PORTD_BASE_PTR,   2,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      129 # PTD2/LLWU_P13 # Default=(DISABLED)  ALT0=()  ALT1=(PTD2/LLWU_P13)  ALT2=(SPI0_SOUT)  ALT3=(UART2_RX)  ALT4=(FTM3_CH2)  ALT5=(FB_AD4/SDRAM_A12)  ALT6=()  ALT7=(I2C0_SCL)  EZPort=()
  { PTD_BASE_PTR, PORTD_BASE_PTR,   3,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      130 # PTD3 # Default=(DISABLED)  ALT0=()  ALT1=(PTD3)  ALT2=(SPI0_SIN)  ALT3=(UART2_TX)  ALT4=(FTM3_CH3)  ALT5=(FB_AD3/SDRAM_A11)  ALT6=()  ALT7=(I2C0_SDA)  EZPort=()
  { PTD_BASE_PTR, PORTD_BASE_PTR,   4,   0,   0,   ALT4, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      131 # PTD4/LLWU_P14 # Default=(DISABLED)  ALT0=()  ALT1=(PTD4/LLWU_P14)  ALT2=(SPI0_PCS1)  ALT3=(UART0_RTS_b)  ALT4=(FTM0_CH4)  ALT5=(FB_AD2/SDRAM_A10)  ALT6=(EWM_IN)  ALT7=(SPI1_PCS0)  EZPort=()
  { PTD_BASE_PTR, PORTD_BASE_PTR,   5,   0,   0,   ALT4, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      132 # PTD5 # Default=(ADC0_SE6b)  ALT0=(ADC0_SE6b)  ALT1=(PTD5)  ALT2=(SPI0_PCS2)  ALT3=(UART0_CTS_b/UART0_COL_b)  ALT4=(FTM0_CH5)  ALT5=(FB_AD1/SDRAM_A9)  ALT6=(EWM_OUT_b)  ALT7=(SPI1_SCK)  EZPort=()
  { PTD_BASE_PTR, PORTD_BASE_PTR,   6,   0,   0,   ALT4, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      133 # PTD6/LLWU_P15 # Default=(ADC0_SE7b)  ALT0=(ADC0_SE7b)  ALT1=(PTD6/LLWU_P15)  ALT2=(SPI0_PCS3)  ALT3=(UART0_RX)  ALT4=(FTM0_CH6)  ALT5=(FB_AD0)  ALT6=(FTM0_FLT0)  ALT7=(SPI1_SOUT)  EZPort=()
  { PTD_BASE_PTR, PORTD_BASE_PTR,   7,   0,   0,   ALT4, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      136 # PTD7 # Default=(DISABLED)  ALT0=()  ALT1=(PTD7)  ALT2=(CMT_IRO)  ALT3=(UART0_TX)  ALT4=(FTM0_CH7)  ALT5=(SDRAM_CKE)  ALT6=(FTM0_FLT1)  ALT7=(SPI1_SIN)  EZPort=()
  { PTD_BASE_PTR, PORTD_BASE_PTR,   8,   0,   0,   ALT2, DSE_HI, FAST_SLEW, OD__EN, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // SCL                  137 # PTD8/LLWU_P24 # Default=(DISABLED)  ALT0=()  ALT1=(PTD8/LLWU_P24)  ALT2=(I2C0_SCL)  ALT3=()  ALT4=()  ALT5=(LPUART0_RX)  ALT6=(FB_A16)  ALT7=()  EZPort=()
  { PTD_BASE_PTR, PORTD_BASE_PTR,   9,   0,   0,   ALT2, DSE_HI, FAST_SLEW, OD__EN, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // SDA                  138 # PTD9 # Default=(DISABLED)  ALT0=()  ALT1=(PTD9)  ALT2=(I2C0_SDA)  ALT3=()  ALT4=()  ALT5=(LPUART0_TX)  ALT6=(FB_A17)  ALT7=()  EZPort=()
  { PTD_BASE_PTR, PORTD_BASE_PTR,  10,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_OUT,   0 }, // PSEL                 139 # PTD10 # Default=(DISABLED)  ALT0=()  ALT1=(PTD10)  ALT2=()  ALT3=()  ALT4=()  ALT5=(LPUART0_RTS_b)  ALT6=(FB_A18)  ALT7=()  EZPort=()
  { PTD_BASE_PTR, PORTD_BASE_PTR,  11,   0,   0,   ALT2, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      140 # PTD11/LLWU_P25 # Default=(DISABLED)  ALT0=()  ALT1=(PTD11/LLWU_P25)  ALT2=(SPI2_PCS0)  ALT3=()  ALT4=(SDHC0_CLKIN)  ALT5=(LPUART0_CTS_b)  ALT6=(FB_A19)  ALT7=()  EZPort=()
  { PTD_BASE_PTR, PORTD_BASE_PTR,  12,   0,   0,   ALT2, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // MKW40_SPI1_SCK       141 # PTD12 # Default=(DISABLED)  ALT0=()  ALT1=(PTD12)  ALT2=(SPI2_SCK)  ALT3=(FTM3_FLT0)  ALT4=(SDHC0_D4)  ALT5=()  ALT6=(FB_A20)  ALT7=()  EZPort=()
  { PTD_BASE_PTR, PORTD_BASE_PTR,  13,   0,   0,   ALT2, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // MKW40_SPI1_SIN       142 # PTD13 # Default=(DISABLED)  ALT0=()  ALT1=(PTD13)  ALT2=(SPI2_SOUT)  ALT3=()  ALT4=(SDHC0_D5)  ALT5=()  ALT6=(FB_A21)  ALT7=()  EZPort=()
  { PTD_BASE_PTR, PORTD_BASE_PTR,  14,   0,   0,   ALT2, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // MKW40_SPI1_SOUT      143 # PTD14 # Default=(DISABLED)  ALT0=()  ALT1=(PTD14)  ALT2=(SPI2_SIN)  ALT3=()  ALT4=(SDHC0_D6)  ALT5=()  ALT6=(FB_A22)  ALT7=()  EZPort=()
  { PTD_BASE_PTR, PORTD_BASE_PTR,  15,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // INT                  144 # PTD15 # Default=(DISABLED)  ALT0=()  ALT1=(PTD15)  ALT2=(SPI2_PCS1)  ALT3=()  ALT4=(SDHC0_D7)  ALT5=()  ALT6=(FB_A23)  ALT7=()  EZPort=()

  { PTE_BASE_PTR, PORTE_BASE_PTR,   0,   0,   0,   ALT4, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // SD_D1                1 # PTE0 # Default=(ADC1_SE4a)  ALT0=(ADC1_SE4a)  ALT1=(PTE0)  ALT2=(SPI1_PCS1)  ALT3=(UART1_TX)  ALT4=(SDHC0_D1)  ALT5=(TRACE_CLKOUT)  ALT6=(I2C1_SDA)  ALT7=(RTC_CLKOUT)  EZPort=()
  { PTE_BASE_PTR, PORTE_BASE_PTR,   1,   0,   0,   ALT4, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // SD_D0                2 # PTE1/LLWU_P0 # Default=(ADC1_SE5a)  ALT0=(ADC1_SE5a)  ALT1=(PTE1/LLWU_P0)  ALT2=(SPI1_SOUT)  ALT3=(UART1_RX)  ALT4=(SDHC0_D0)  ALT5=(TRACE_D3)  ALT6=(I2C1_SCL)  ALT7=(SPI1_SIN)  EZPort=()
  { PTE_BASE_PTR, PORTE_BASE_PTR,   2,   0,   0,   ALT4, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // SD_CLK               3 # PTE2/LLWU_P1 # Default=(ADC1_SE6a)  ALT0=(ADC1_SE6a)  ALT1=(PTE2/LLWU_P1)  ALT2=(SPI1_SCK)  ALT3=(UART1_CTS_b)  ALT4=(SDHC0_DCLK)  ALT5=(TRACE_D2)  ALT6=()  ALT7=()  EZPort=()
  { PTE_BASE_PTR, PORTE_BASE_PTR,   3,   0,   0,   ALT4, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // SD_CMD               4 # PTE3 # Default=(ADC1_SE7a)  ALT0=(ADC1_SE7a)  ALT1=(PTE3)  ALT2=(SPI1_SIN)  ALT3=(UART1_RTS_b)  ALT4=(SDHC0_CMD)  ALT5=(TRACE_D1)  ALT6=()  ALT7=(SPI1_SOUT)  EZPort=()
  { PTE_BASE_PTR, PORTE_BASE_PTR,   4,   0,   0,   ALT4, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // SD_D3                7 # PTE4/LLWU_P2 # Default=(DISABLED)  ALT0=()  ALT1=(PTE4/LLWU_P2)  ALT2=(SPI1_PCS0)  ALT3=(UART3_TX)  ALT4=(SDHC0_D3)  ALT5=(TRACE_D0)  ALT6=()  ALT7=()  EZPort=()
  { PTE_BASE_PTR, PORTE_BASE_PTR,   5,   0,   0,   ALT4, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // SD_D2                8 # PTE5 # Default=(DISABLED)  ALT0=()  ALT1=(PTE5)  ALT2=(SPI1_PCS2)  ALT3=(UART3_RX)  ALT4=(SDHC0_D2)  ALT5=()  ALT6=(FTM3_CH0)  ALT7=()  EZPort=()
  { PTE_BASE_PTR, PORTE_BASE_PTR,   6,   0,   0,   ALT6, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      9 # PTE6/LLWU_P16 # Default=(DISABLED)  ALT0=()  ALT1=(PTE6/LLWU_P16)  ALT2=(SPI1_PCS3)  ALT3=(UART3_CTS_b)  ALT4=(I2S0_MCLK)  ALT5=()  ALT6=(FTM3_CH1)  ALT7=(USB0_SOF_OUT)  EZPort=()
  { PTE_BASE_PTR, PORTE_BASE_PTR,   7,   0,   0,   ALT6, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      10 # PTE7 # Default=(DISABLED)  ALT0=()  ALT1=(PTE7)  ALT2=()  ALT3=(UART3_RTS_b)  ALT4=(I2S0_RXD0)  ALT5=()  ALT6=(FTM3_CH2)  ALT7=()  EZPort=()
  { PTE_BASE_PTR, PORTE_BASE_PTR,   8,   0,   0,   ALT6, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      11 # PTE8 # Default=(DISABLED)  ALT0=()  ALT1=(PTE8)  ALT2=(I2S0_RXD1)  ALT3=()  ALT4=(I2S0_RX_FS)  ALT5=(LPUART0_TX)  ALT6=(FTM3_CH3)  ALT7=()  EZPort=()
  { PTE_BASE_PTR, PORTE_BASE_PTR,   9,   0,   0,   ALT6, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      12 # PTE9/LLWU_P17 # Default=(DISABLED)  ALT0=()  ALT1=(PTE9/LLWU_P17)  ALT2=(I2S0_TXD1)  ALT3=()  ALT4=(I2S0_RX_BCLK)  ALT5=(LPUART0_RX)  ALT6=(FTM3_CH4)  ALT7=()  EZPort=()
  { PTE_BASE_PTR, PORTE_BASE_PTR,  10,   0,   0,   ALT7, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // USB_HS_DI            13 # PTE10/LLWU_P18 # Default=(DISABLED)  ALT0=()  ALT1=(PTE10/LLWU_P18)  ALT2=(I2C3_SDA)  ALT3=()  ALT4=(I2S0_TXD0)  ALT5=(LPUART0_CTS_b)  ALT6=(FTM3_CH5)  ALT7=(USB1_ID)  EZPort=()
  { PTE_BASE_PTR, PORTE_BASE_PTR,  11,   0,   0,   ALT6, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      14 # PTE11 # Default=(DISABLED)  ALT0=()  ALT1=(PTE11)  ALT2=(I2C3_SCL)  ALT3=()  ALT4=(I2S0_TX_FS)  ALT5=(LPUART0_RTS_b)  ALT6=(FTM3_CH6)  ALT7=()  EZPort=()
  { PTE_BASE_PTR, PORTE_BASE_PTR,  12,   0,   0,   ALT6, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      15 # PTE12 # Default=(DISABLED)  ALT0=()  ALT1=(PTE12)  ALT2=()  ALT3=()  ALT4=(I2S0_TX_BCLK)  ALT5=()  ALT6=(FTM3_CH7)  ALT7=()  EZPort=()
  { PTE_BASE_PTR, PORTE_BASE_PTR,  24,   0,   0,   ALT3, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // Debug port           45 # PTE24 # Default=(ADC0_SE17)  ALT0=(ADC0_SE17)  ALT1=(PTE24)  ALT2=(CAN1_TX)  ALT3=(UART4_TX)  ALT4=()  ALT5=(I2C0_SCL)  ALT6=(EWM_OUT_b)  ALT7=()  EZPort=()
  { PTE_BASE_PTR, PORTE_BASE_PTR,  25,   0,   0,   ALT3, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, // Debug port           46 # PTE25/LLWU_P21 # Default=(ADC0_SE18)  ALT0=(ADC0_SE18)  ALT1=(PTE25/LLWU_P21)  ALT2=(CAN1_RX)  ALT3=(UART4_RX)  ALT4=()  ALT5=(I2C0_SDA)  ALT6=(EWM_IN)  ALT7=()  EZPort=()
  { PTE_BASE_PTR, PORTE_BASE_PTR,  26,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_INP,   0 }, //                      47 # PTE26 # Default=(DISABLED)  ALT0=()  ALT1=(PTE26)  ALT2=(ENET_1588_CLKIN)  ALT3=(UART4_CTS_b)  ALT4=()  ALT5=()  ALT6=(RTC_CLKOUT)  ALT7=(USB0_CLKIN)  EZPort=()
  { PTE_BASE_PTR, PORTE_BASE_PTR,  27,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_OUT,   1 }, //                      48 # PTE27 # Default=(DISABLED)  ALT0=()  ALT1=(PTE27)  ALT2=()  ALT3=(UART4_RTS_b)  ALT4=()  ALT5=()  ALT6=()  ALT7=()  EZPort=()
  { PTE_BASE_PTR, PORTE_BASE_PTR,  28,   0,   0,   GPIO, DSE_HI, FAST_SLEW, OD_DIS, PFE_DIS, PUPD_DIS, GP_OUT,   0 }, //                      49 # PTE28 # Default=(DISABLED)  ALT0=()  ALT1=(PTE28)  ALT2=()  ALT3=()  ALT4=()  ALT5=()  ALT6=()  ALT7=()  EZPort=()


};


/*------------------------------------------------------------------------------

 ------------------------------------------------------------------------------*/
void Config_pin(const T_IO_pins_configuration pinc)
{
  pinc.port->PCR[pinc.pin_num] = LSHIFT(pinc.irqc, 16) |
                                 LSHIFT(pinc.lock, 15) |
                                 LSHIFT(pinc.mux, 8) |
                                 LSHIFT(pinc.DSE, 6) |
                                 LSHIFT(pinc.ODE, 5) |
                                 LSHIFT(pinc.PFE, 4) |
                                 LSHIFT(pinc.SRE, 2) |
                                 LSHIFT(pinc.PUPD, 0);

  if ( pinc.init == 0 ) pinc.gpio->PCOR = LSHIFT(1, pinc.pin_num);
  else pinc.gpio->PSOR = LSHIFT(1, pinc.pin_num);
  pinc.gpio->PDDR = (pinc.gpio->PDDR & ~LSHIFT(1, pinc.pin_num)) | LSHIFT(pinc.dir, pinc.pin_num);
}


/*------------------------------------------------------------------------------

 ------------------------------------------------------------------------------*/
int Init_pins(void)
{
  int i;

  // Включаем тактирование на всех портах
  SIM_SCGC5 |=   SIM_SCGC5_PORTA_MASK | SIM_SCGC5_PORTB_MASK | SIM_SCGC5_PORTC_MASK | SIM_SCGC5_PORTD_MASK | SIM_SCGC5_PORTE_MASK;

  for (i = 0; i < (sizeof(K66BLEZ1_pins_conf) / sizeof(K66BLEZ1_pins_conf[0])); i++)
  {
    Config_pin(K66BLEZ1_pins_conf[i]);
  }

  return 0;
}


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

Наконец в файле main.c содержится основное приложение.
Переключение светодиода всего одной строчкой — GPIOA_PTOR = BIT(1);
Макрос GPIOA_PTOR объявлен в файле MK65F18.h и представляет собой просто ссылку на адрес регистра PTOR (Port Toggle Output Register) порта A. При записи единицы в определённый бит этого регистра на соответствующей линии порта A происходит переключение логического состояния (стр. 2191. Справочное руководство: K66P144M180SF5RMV2).
А макрос BIT(x) определён мной в файле main.h и означает всего лишь (1u << x), т.е. установленный в заданной позиции бит.

(Кликнуть для увеличения)


Важно заметить, в проекте только четыре файла с исполняемым кодом. Никаких библиотек CMSIS, никаких драйверов SDK и уровня HAL. Компиляция занимает доли секунды.
Это проще чем Arduino!

Вторая строчка: DELAY_ms(20); обеспечивает прецизионную программную задержку в 20 мс. Это макрос передающий пересчитанный аргумент в функцию на ассемблере вставленную мной в файл startup_MK66F18.s. Вот её вид:

         ; Для Cortex-M4
         ;  (R0+1)*7
Delay_m7
         SUBS     r0,r0,#1   ; 1
         NOP                 ; 1
         NOP                 ; 1
         NOP                 ; 1
         CMP      r0,#0x00   ; 1
         BGT      Delay_m7   ; 2/1
         NOP                 ; 1
         NOP                 ; 1

         NOP                 ; 1
         NOP                 ; 1
         BX       lr         ; 2
   
Функция выполняется количество тактов расчитываемое по формуле (R0+1)*7, где R0 — содержимое регистра R0.
Очень полезная функция как для организации точных произвольных задержек, так и для контроля быстродействия ядра и настройки тактирования с помощью дополнительных инструментов.

Загружаем приложение.



Среда IAR имеет готовые загрузчики Flash памяти для всех чипов, которые она поддерживает. Поэтому нам остаётся только подключить JTAG/SWD адаптер к модулю. IAR поддерживает много адаптеров. В их числе есть класс очень дешёвых адаптеров с прошивкой CMSIS DAP (встроен во все отладочные платы с Kinetis от NXP), можно работать через ST-Link как показано на фотографии в заголовке (вариант с гальвано изоляцией, к сожалению, с модулем не совместим). Я чаще использую J-Link, поскольку в нем присутствует виртуальный COM порт, есть поддержка Real Time Terminal (RTT), поддерживается неограниченное количество точек останова и ещё ряд полезных функций.

Тестирование времени активации программы после включения питания


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

Осциллограмма, показанная ниже даёт ответ на вопрос насколько быстро после подачи питания может начать выполняться программа на микроконтроллере. Начало нарастание напряжения питания 3.3В соответствует моменту подключения кабеля USB к плате.
Как видно первые инструкции программа начинает выполнять спустя приблизительно 1.5 мс от момента подачи напряжения 5В с USB интерфейса. А на полной скорости программа начинает выполняться спустя 3 мс.

(Кликнуть для увеличения)


С какой скоростью мы можем переключать логические состояния на выводах микроконтроллера?


Здесь есть нюанс. Он заключается в том, что программа, выполняющаяся из Flash памяти, не обладает детерминизмом.
Заменим нашу первую программу на следующий код:
Нажать сюда для просмотра
int main()
{
  for (;;)
  {
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
  }
}

Это 128 команд переключения состояния светодиода.
Но осциллограмма этих переключений будет выглядеть так:

(Кликнуть для увеличения)


Странные паузы между пачками монотонных переключений — это задержки при считывании блоков кода из Flash. Flash память работает на частоте гораздо более низкой чем частота ядра, и в чипе применено некое подобие кэширования блоков по 128 байт, это соответствует 32-м командам ядра ARM. В результате монотонная пачка переключений не может быть длиннее 32-х команд, а потом возникает пауза. Стоит также отметить влияние выравнивания команд во Flash памяти если наш фрагмент не превышает 32-х команд, от этого зависит появится ли в нашем фрагменте пауза на кэширование или нет.
Ситуацию можно исправить если поместить код в RAM. Для этого надо вызов функции написать следующим образом:
__ramfunc int main()
{
  for (;;)
  {
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    GPIOA_PTOR = BIT(1);
    .
    .
    .
  }
}
Тогда осциллограмма будет выглядеть так:

(Кликнуть для увеличения)


А частота переключений будет такая:

(Кликнуть для увеличения)


Т.е. получаем частоту в 90 МГц на светодиоде. Это означает, что команды вывода в порт выполняются с частотой ядра.

Пример управления светодиодом по прерываниям на основе автомата состояний.


Проект находится здесь.
В данном проекте показано как инициализировать системный таймер ядра ARM (он используется для вызова периодических прерываний), как очень просто можно инициализировать UART и выводить через него текст в терминал компьютера и как задавать диаграмму сигналов светодиода простым массивом.

Традиционный стиль реализации мигания светодиодом мог бы выглядеть так в нашей программе:
for(;;)
{
  GPIOA_PSOR = BIT(1); // Включить светодиод
  DELAY_ms(500);       // Задержка на 0.5 сек  
  GPIOA_PСOR = BIT(1); // Выключить светодиод
  DELAY_ms(500);       // Задержка на 0.5 сек  
}
Сложность здесь в том, что трудно вставить ещё какую-нибудь функциональность, не нарушив задержки в состояниях светодиода и его равномерное мигание. Если бы у нас была операционная система реального времени (RTOS), то такой сложности бы не возникло. Мы бы просто поместили эту процедуру в отдельную задачу.
Но за отсутствием RTOS приходится использовать автоматы состояний.


(Кликнуть для увеличения)


Для работы с этим примером нужна программ эмулятор терминала на PC с поддержкой управляющих символов по спецификации VT100. Такой программой может быть, например, TeraTerm.

Автомат состояний светодиода — это функция LEDS_state_automat вызываемая из процедуры обслуживания прерывания системного таймера. Она находится в файле LED_StateMachine.c. Чтобы задать тот или иной стиль мигания светодиода вызывается функция Set_LED_pattern находящаяся в том же файле. В качестве аргумента передаём массив с состояниями и длительностями состояний светодиода, например, такого вида:
const int32_t   LED_2_BLINK[] =
{
  LED__ON, 5,
  LED_OFF, 5,
  LED__ON, 5,
  LED_OFF, 35,
  0, 0
};  - 
Первое число в массиве это состояние (0 или 1 в нашем случае, но представленные макросами, чтобы в случае необходимости перейти на более разрядные числа для регулируемых светодиодов). Второе число — это длительность состояния в количестве циклов. Цикл — это период между прерываниями системного таймера.

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

Здесь хранятся все материалы, связанные с этим проектомhttps://github.com/Indemsys/K66BLEZ1
Поделиться с друзьями
-->

Комментарии (24)


  1. Mirn
    22.05.2016 20:24
    +2

    1. Зачем вытягивать максимальное время переключение светодиода на сферической в вакууме задаче? на практике если его не хватает — то пересматриваем требования и структуру, если нельзя то ставим CPLD и получаем сразу максимальное время реакции на уровне 10нс и меньше.

    2. Извините, но IoT тут причём? И где управление входами, и где силовое управление и всякие оптронные и тиристорные включатели чтоб лампочку и прочую нагрузку включить? Где модули связи (антенну на плате какую-то вижу, но в статье про неё не слова). Где внешние датчики и внутренние хотя-бы (температура и напряжение питания на МК например)? Вещь в себе пока что вижу. Остальное не вижу.

    3. Зачем автомат состояний на светодиоды вообще нужен? Мне вот например интересен автомат состояний на измерение аналоговых величин при помощи ДМА и хотя-бы с усреднением и медианной фильтрацией. Или автомат управления тиристором для ловли перехода через ноль. Вот это интересно особенно если подключить их парой строк можно — мне лет 10 назад надоело делать свои велосипеды каждый раз на такие тривиальные случаи.

    4. почему качество кода такое… хм, хм… странное? и откуда взялись все эти волшебные константы 0 + LSHIFT(0x00, 19) + LSHIFT(0x00, 18) и тд? эту магию может кто-нибудь когда-нибудь всё таки нормальными константами уже написал… надеюсь? Можно нормальную либу хотя-бы на регистры -самого МК? Или как нибудь можно это не делать в комментариях мне самому?

    5. Зачем терминал VT100? Очистку экрана при желании можно сделать кучей \n\r. Я то думал будем рисовать менюшки псевдографикой и мышью управлять? (классная тема кстати, особенно для производственных прошивок к которым некогда делать нормальный виндовый интерфейс)

    6. Честно не понял зачем контроллеру так быстро включаться? ну будет не 3мс а 50...100мс, чего плохого? всё равно его основной цикл работы вход в сон или глубже если делать упор на энергопотребление?


    1. Mirn
      22.05.2016 21:27
      +2

      и ещё добавлю:
      Советую поглядеть в сторону нормальных сред с полностью открытыми исходниками на все библиотеки.
      Стремление использовать ассемблер, недоверие к сторонним библиотекам и прочее, я часто видел у людей которые пишут на IAR и Кейл.
      Давно всё что надо писать на асме написано за Вас самим АРМ консорциумом, разработчиками ОС и тд.
      И вообще не пишите на ассемблере, он не для того создан. 21 век в конце концов.

      Не используйте задержки на НОПах. За них по рукам давали ещё во времена ДОС, в мк давно есть таймера и разнообразные счётчики тактов с системным таймером. Они удобны, честно!

      «Т.е. получаем частоту в 90 МГц на светодиоде.» — по осциллографу я вижу частоту чуть меньше чем 90мгц, всё таки одна инструкция перехода никуда не делась. Не удалось сделать из мощного МК простой генератор стабильной частоты без джиттинга.


      1. Dark_Purple
        22.05.2016 23:32

        Критика это здорово, просто статья «не для вас» написана, мне кажется. Автор старался на простых примерах всё разжевать для любителей ардуино, и понизить порог вхождения для простых смертных.


        1. Mirn
          23.05.2016 00:47
          +1

          На ЭТИ простыни кода которые назвали «простыми примерами» Вы вообще смотрели? Их прокручивать то замучаешься!
          Да ардуино тут рядом не стоял! это антипод ардуино потому что автор отрицает готовые библиотеки «Поэтому я всегда начинаю с создания _своей_ минималистичной библиотеки функций работы с периферией.»

          В статье проблемы:
          1. Адовый быдлокод (даже констант нормальных именованных нет, про макросы типа LSHIFT, BIT и тд я вообще молчу).
          2. Велосипедостроение (долой сдк!)
          3. Самопротиворечия (на асме задержка якобы идеальная… пока прерывания не включились, но при этом на осциллографе видим «Странные паузы между пачками монотонных переключений — это задержки при считывании блоков кода из Flash»)
          4. Явные ошибки включая метрологические и несоответствие осциллограмм и текста.
          5. Ассемблер там где он не нужен и даже недопустим (задержка и первичная инициализация МК файл стартапа)
          6. Решение надуманных проблем (зачем то надо превратить контроллер в делитель частоты на два или генератор 90Мгц — очень частая задача!).
          7. Оверинжениринг и подключение непонятно зачем лишних библиотек и технологий (время включения 3мс и VT100 терминал ради очистки экрана).

          это как учить новичка сортировке назвав пузырьковую сортировку слишком сложной и «давайте сравнивать и менять местами в случайные пары элементов массива, если массив в 10 элементов то через миллион случайных сравнений он точно будет отсортирован» а потом героически решить проблему миллиона сравнений сведя её до 1000 но оставив рандом.

          нет, давайте учиться сразу правильно.
          и вообще определитесь, или мы скетчи пишем, или вспоминаем 90ые — пишем на ассемблере простыни на тривиальные действия.


          1. Indemsys
            23.05.2016 09:15

            Эта статья для разработчиков. И конкурирует этот проект не с ардуино. Просто стало забавно как заморочено все в ардуино и по сути ардуино довольно закрытый фреймворк.

            Быдлокод не быдлокод, но я проповедую рефакторинг. Это значит что за секунды я могу превратить LSHIFT, BIT в ">>" или "<<" или во что угодно.
            Это не должно напрягать разработчика. Выбор имен у меня диктуется исключительно моими представлениями о красивом форматировании. В статье к сожалению невозможно воспроизвести оригинальный вид этих исходников в моем редакторе кода.
            Поэтому остается верить что там он красивее. Простыни также выглядят совсем не простынями.
            Кто видел что творят автогенераторы кода типа Kinetis Design Studio или инструменты подобные STM32 CubeMX тот должен оценить.

            Задержка помещается в 32-е команды, если внимательно читали то должны понять нюансы размещения и использования этой задержки.

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

            Статья не для начинающих программистов, а для тех кто начинает осваивать платформу KInetis.
            Поэтому внимание концентрируется на отличиях от других платформ или на вопросах которые не раскрываются обычно, но приводят к необъяснимым результатам.

            А примеры приложений я еще не окончил публиковать. У меня есть и с десяток реализаций медиан со сравнением быстродействия алгоритмов. Так что про сортировку может еще напишу…



            1. Mirn
              23.05.2016 09:42

              «Задержка помещается в 32-е команды, если внимательно читали то должны понять нюансы размещения и использования этой задержки.»
              проблемы задержки:
              1. включаем прерывания и она съезжает и уже не 20мс а 22 или 50мс в зависимости от загрузки проца в прерываниях.
              2. Сменили тактовую и она не 20мс а 160мс.
              3. Будут проблемы с разными компиляторами и разными режимами инструкций.
              И вообще зачем делать такую тривиальную вещь ТАК грубо?
              Аналогично относится к стартап файлу — зачем он на асме?
              средств Си не хватает?
              Пожалуйста изучите документацию на GCC (атрибуты и секции линковки и LD файл) — они не кусаются!

              «Пару дней отладки, и любой разработчик будет знать ассемблер на зубок, хочет он того или не хочет. „
              Извините но на календаре не 90ые!
              Это как надо не доверять компилятору или не уметь им пользоваться чтоб такое утверждать?
              Я за много лет так и не выучил написание кода на асме для АРМ потому что это реально не нужно.
              Мне хватает примерно прикинуть кол-во инструкций и примерно и очень грубо понять что куда пересылается и дальше PUSH/MOV не знаю.

              Спасибо но для пик микро и 8051 накодился на асме в 90ых ВДОВОЛЬ! и был страшно рад как только нормальные компиляторы Си для них вышли и сразу забыл как страшный сон.


              1. Indemsys
                23.05.2016 11:38

                Хочу отметить что GCC и другие компиляторы я рассматривать больше не буду, поскольку уже сделал выбор компилятора.
                Сравнения компиляторов я проводил много раз на своей практике. И даже дал ссылку на статью с исследованием.

                Задержка с точностью до такта нужна для калибровки таймеров и правильности настройки системных частот.
                И признаюсь эта задержка единственный такой длинный фрагмент на ассемблере который я написал за последние 10! лет.
                Так что в предыдущих и следующих статьях вы вряд ли увидите что-то от меня на ассемблере.


            1. olartamonov
              23.05.2016 10:03
              +1

              Помимо того, что сказал Mirn, — у вас банальная каша в подаче материала. То есть, непонятно, зачем вообще вы совершаете минимум половину пассов руками.

              Почему именно эта платформа? Почему именно эти чипы? Какое отношение всё это имеет к IoT (ответ: никакого)? Зачем вы проводите такие процедуры, какой смысл имеет то же время включения и не всё ли равно нам, 20 мс там или аж целых сорок (ответ: в 99,9999999 % случаев — всё равно)? Нафига вы, заявив IoT (ответ: для красного словца), дальше работаете не просто без RTOS (ответ: а нет для этой платформы приличных RTOS), а ещё и пихаете куски на ассемблере?

              Есть такое магическое выражение — use case. Оно должно отвечать на вопрос «зачем всё это?». Вот у вас ответа я не вижу, а вижу какую-то беспорядочную кашу.

              Ну то есть вы не поверите, но для примерно 99,9999999999 % людей «освоить Kinetis for the sake of it» не является значимой целью в жизни.


              1. Indemsys
                23.05.2016 11:30

                Я думаю Kinetis все сильнее будет теснить STM после покупки Freescal-а корпорацией NXP. Поэтому все больше разработчиков будет поглядывать на Kinetis.
                Я оцениваю их число в пару десятков тысяч.
                Этого достаточно чтобы писать статьи на эту тему.

                Почему эта платформа, я стараюсь показать во всех своих статьях. Первые статьи есть на хабре.
                Там же есть и перечисление более сложных разработок.

                Темы я беру из форумов по мотивам обсуждений. Это точно не надуманные use case, а вопросы интересующие электронщиков.
                Хотя может и наивные для кого-то.

                Конкретно как связать интернет с модулем я планирую описать позже. Не вижу смысла начинать с конца.
                И операционки есть, и не одна.
                Словом не торопите события.


                1. olartamonov
                  23.05.2016 12:13

                  Messed in so many ways I can't even count them all.

                  Ну то есть, у вас опять какая-то каша.

                  У вас в заголовке слова «интернет вещей». При чём тут какой-то STM? STM нет в IoT, потому что для IoT таки нужна связность, а у STM с SoC с радиочастью примерно никак. В IoT есть TI, Semtech, Nordic, Axsem, вот этот ваш Freescale/NXP. А STM — нет.

                  Почему эта платформа, вы пока не показали вообще никак. Ну платформа. Ну микроконтроллер. И что? Мы сейчас используем CC1310 и CC2650, имеет с них смысл переходить на KW40Z? Не имеет.

                  Зачем вы что-то берёте в форумах, а отвечаете здесь? Вам не кажется, что читателям было бы интересно видеть, не только ответ, но и собственно вопрос? Я вот не могу представить себе реального случая, в котором критично время старта процессора из POR — так может, вы мне расскажете? Или вы тоже его не представляете?

                  Связать модуль с интернетом можно тремя десятками способов. Можно ардуину связать с интернетом, причём за пять минут. В IoT, однако, есть набор общепризнанных протоколов — ZigBee, 6LoWPAN, LoRa и т.п. Для них есть реализации сетевых стеков, например, в проектах на 6LoWPAN обычно используют ОС Contiki или более новую RIOT.

                  Оно для вашего чипа есть? Я не вижу. Contiki его не поддерживает. RIOT его не поддерживает. FreeRTOS поддерживает, но там с сетевым стеком примерно никак.

                  Или вы планируете по UART через внешний модуль связываться, в лучших традициях ардуины? Поднять на KW40Z блютус и посмотреть на него со смартфона?

                  Я не уверен, что это те события, которые вообще имеет смысл торопить.


                  1. Indemsys
                    23.05.2016 13:33

                    Сетевые стеки не привязаны ни к какой RTOS. Поэтому я не думаю что буду описывать стеки вместе с какой-то OS.
                    Тот же LoRa или 6LoWPAN c успехом будут работать и без OS и на FreeRTOS и на uCOS.

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


                    1. olartamonov
                      23.05.2016 13:43

                      Тот же LoRa или 6LoWPAN c успехом будут работать и без OS и на FreeRTOS и на uCOS


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

                      Тот же LoRa или 6LoWPAN c успехом будут работать и без OS и на FreeRTOS и на uCOS


                      Вот только соотношение геморрой / бенефиты реализации IPv6 или LoRaWAN на всём этом будут такие, что никто в здравом уме этого не делает. И за то время, пока вы будете IPv6 запиливать без ОС, конкурирующая контора не только решение на RIOT продаст заказчику, но и успеет уже радостно пропить заработанное.

                      Потом не забывайте что это не рекламная статья и стоит меня провоцировать на рекламу


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


            1. hoary
              23.05.2016 14:07

              >Насчет ассемблера ваша мысль не ясна. Пару дней отладки, и любой разработчик будет знать ассемблер на зубок, хочет он того или не хочет.
              Смею с Вами не согласиться.
              Сам начинал embedded путь с завета уважаемого DIHALT и писал на ассемблере для AVR, потому понимаю этот самый ассемблер для AVR и могу читать свободно.
              Сейчас пишу для различных платформ — stm32, stm8, atxmega и не припоминаю случая, чтобы мне пришлось глядеть, что творит компилятор.
              Возможно, это не такой востребованный навык?


    1. olartamonov
      22.05.2016 22:30
      +2

      Извините, но IoT тут причём?


      А его сейчас модно в любую тему пихать, чтоб был. Есть Wi-Fi — IoT. Есть Bluetooth — IoT. Дико избыточная 6-слойка с огромным и дорогим процессором, на которую ради ZigBee поставили ещё один процессор — тоже IoT. Мигаем на ней светодиодами — считай, целый IoT-проект сделали.

      И пофигу, что ни один вменяемый человек это в настоящем IoT использовать никогда не будет. Писать «изучаем работу с микроконтроллерами NXP» теперь нельзя, потому что надо про IoT.

      KW40Z сам по себе был бы интересен в IoT, но… автор, а стек 6LoWPAN на него есть?


      1. PoltoS
        22.05.2016 23:00

        +1

        Помню 10 лет назад научник вернулся с заседания президиума РАН, где обсуждалось перераспределение денег в сторону более модных в то время направлений, со словами:
        — Всё, меняем название наших работ. Записывай: "Нанотехнологии, исследование крупномасштабных структур в галактиках"

        Шутки шутками, но мода обязывает делать странные вещи…


        1. olartamonov
          23.05.2016 00:05

          Шутки шутками, но мода обязывает делать странные вещи…


          Так она не обязывает, вот в чём проблема. Это просто карго-культ — «давайте теперь везде будет этикетка IoT»; при этом без этикетки будет ничуть не хуже, а местами даже лучше — но вера в этикетку непоколебима.

          Причём эта движуха настоящему IoT на самом деле помогает, правда, немного косвенно: когда люди понимают, что из ардуины и вайфая они себе ни SCADA, ни АСУНО не сделают, к ним можно придти с коммерческим предложением на нормальную систему.

          P.S. Посмотрел, что у KW40Z с IoT. А ничего. Рекомендуемая операционка — FreeRTOS, ни в Contiki, ни в RIOT поддержки чипа нет.


      1. Dark_Purple
        22.05.2016 23:20

        с огромным и дорогим процессором

        HS USB наверно сильно хотелось.
        A всякие там IoT в реальной жизни нафик никому не нужны, да и не знает никто что это такое. )))


        1. olartamonov
          22.05.2016 23:53

          A всякие там IoT в реальной жизни нафик никому не нужны, да и не знает никто что это такое. )))


          Здесь я не просто смеялся, здесь я от души хохотал.

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


          1. celeron366
            23.05.2016 07:31

            А можно пример IoT устройств, которыми пользуется не только разработчики этих устройств? Кроме NEST ничего не приходит в голову.


            1. olartamonov
              23.05.2016 08:35

              http://www.cisco.com/c/dam/en_us/solutions/industries/docs/manufacturing/c36-732293-00-stanley-cs.pdf
              http://www.cisco.com/c/dam/en_us/solutions/industries/retail/downloads/bc-hydro-cisco.pdf

              Подойдёт?

              Вообще промышленные системы сбора данных и управления всех их сортов уже начали переезжать на типовые IoT-технологии и по железу (IP-сети, беспроводные сети), и по методам сбора и обработки данных, и будут очень активно это делать ближайшее десятилетие. Более того, этими системами обзаводятся отрасли, в которых классические технологии неприменимы вообще или применимы плохо — конвейер ещё туда-сюда, но в сельском хозяйстве к датчикам состояния почвы, например, RS-485 проложить проблематично.


              1. Dark_Purple
                23.05.2016 17:36

                А для простых людей есть что нибудь, ну так стоб в быту?


                1. olartamonov
                  23.05.2016 17:39

                  Старый добрый умный дом? Zigbee, Z-Wave, SmartThings там всякий?


  1. YarikYar
    22.05.2016 20:25
    +1

    Спасибо, интересные статьи.
    Правда, уже в трёх статьях так и не затронута тема IoT.
    Жду продолжения!


  1. Costic
    23.05.2016 14:46
    +1

    Господа, зачем же вы автора так клюёте? Такими темпами мы рискуем не увидеть четвёртую часть. Замечаний много справедливых, но в целом очень любопытно, что автор из этого всего сделает, какое же IoT-устройство появится.