1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-18 12:52:44 +01:00

Merge pull request #6995 from gebart/pr/frdm-kw41z

cpu+board: Add support for NXP Kinetis KW41Z and FRDM-KW41Z development board
This commit is contained in:
Joakim Nohlgård 2018-03-02 17:04:08 +01:00 committed by GitHub
commit ab0a2f7033
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 39335 additions and 10 deletions

24
boards/common/frdm/dist/openocd-klx.cfg vendored Normal file
View File

@ -0,0 +1,24 @@
#
# NXP Kinetis Freedom developer board
#
# OpenSDA is the on-board debugger, some boards have a CMSIS-DAP compatible
# interface, other boards comes pre-flashed with a Segger J-Link compatible
# firmware. The OpenSDA controller can be re-flashed to provide either of the two.
# Both interfaces work with OpenOCD, but we need to tell which one we have on
# our debugger.
# CMSIS-DAP (DAPLink) compatible OpenSDA firmware binary images can be found at:
# http://www.nxp.com/opensda
# Kinetis L only supports SWD
transport select swd
# Kinetis L series CPUs
source [find target/klx.cfg]
reset_config srst_only
$_TARGETNAME configure -event gdb-attach {
halt
}
$_TARGETNAME configure -rtos auto

View File

@ -0,0 +1,3 @@
MODULE = board
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,6 @@
ifneq (,$(filter saul_default,$(USEMODULE)))
USEMODULE += saul_gpio
USEMODULE += saul_adc
endif
include $(RIOTCPU)/kinetis/Makefile.dep

View File

@ -0,0 +1,15 @@
# Put defined MCU peripherals here (in alphabetical order)
FEATURES_PROVIDED += periph_adc
FEATURES_PROVIDED += periph_i2c
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_rtt
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += periph_uart
# The board MPU family (used for grouping by the CI system)
FEATURES_MCU_GROUP = cortex_m0_2
include $(RIOTCPU)/kinetis/Makefile.features
# Remove this line after TRNG driver is implemented
FEATURES_PROVIDED := $(filter-out periph_hwrng,$(FEATURES_PROVIDED))

View File

@ -0,0 +1,13 @@
# define the cpu used by the board
export CPU = kinetis
export CPU_MODEL = mkw41z512vht4
# OpenOCD v0.10.0-dev (current development version) or later is required for
# flashing KW41Z devices
# See http://openocd.zylin.com/#/c/4104/ for the upstreaming process
export USE_OLD_OPENOCD ?= 0
# This board comes with OpenSDA configured for JLink compatibility
export DEBUG_ADAPTER ?= jlink
# Include default FRDM board config
include $(RIOTBOARD)/common/frdm/Makefile.include

44
boards/frdm-kw41z/board.c Normal file
View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2017 Eistec AB
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/
/**
* @ingroup boards_frdm-kw41z
* @{
*
* @file
* @brief Board specific initialization for the FRDM-KW41Z
*
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
*
* @}
*/
#include "board.h"
#include "periph/gpio.h"
#include "periph/rtt.h"
void board_init(void)
{
/* initialize the CPU core */
cpu_init();
#if MODULE_XTIMER && !(KINETIS_XTIMER_SOURCE_PIT)
/* Start the RTT, used as time base for xtimer when using LPTMR backend */
rtt_init();
#endif
/* initialize and turn off LEDs */
gpio_init(LED0_PIN, GPIO_OUT);
gpio_set(LED0_PIN);
gpio_init(LED1_PIN, GPIO_OUT);
gpio_set(LED1_PIN);
gpio_init(LED2_PIN, GPIO_OUT);
gpio_set(LED2_PIN);
gpio_init(LED3_PIN, GPIO_OUT);
gpio_set(LED3_PIN);
}

View File

@ -0,0 +1,81 @@
/*
* Copyright (C) 2017 Eistec AB
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup boards_frdm_kw41z
* @{
*
* @file
* @brief Board specific configuration of direct mapped ADC
*
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
*/
#ifndef ADC_PARAMS_H
#define ADC_PARAMS_H
#include "board.h"
#include "saul/periph.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief ADC configuration
*/
static const saul_adc_params_t saul_adc_params[] =
{
{
.name = "coretemp",
.line = ADC_LINE(0),
.res = ADC_RES_16BIT,
},
{
.name = "corebandgap",
.line = ADC_LINE(1),
.res = ADC_RES_16BIT,
},
{
.name = "corevrefh",
.line = ADC_LINE(2),
.res = ADC_RES_16BIT,
},
{
.name = "corevrefl",
.line = ADC_LINE(3),
.res = ADC_RES_16BIT,
},
{
.name = "dcdcvbat",
.line = ADC_LINE(4),
.res = ADC_RES_16BIT,
},
{
.name = "ADC0_DP-DM",
.line = ADC_LINE(5),
.res = ADC_RES_16BIT,
},
{
.name = "ADC0_SE2",
.line = ADC_LINE(6),
.res = ADC_RES_16BIT,
},
{
.name = "ADC0_SE3",
.line = ADC_LINE(7),
.res = ADC_RES_16BIT,
},
};
#ifdef __cplusplus
}
#endif
#endif /* ADC_PARAMS_H */
/** @} */

View File

@ -0,0 +1,99 @@
/*
* Copyright (C) 2017 Eistec AB
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/
/**
* @defgroup boards_frdm-kw41z Freescale FRDM-KW41Z Board
* @ingroup boards
* @brief Board specific implementations for the FRDM-KW41Z
* @{
*
* @file
* @brief Board specific definitions for the FRDM-KW41Z
*
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
*/
#ifndef BOARD_H
#define BOARD_H
#include "cpu.h"
#include "periph_conf.h"
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @name LED pin definitions and handlers
* @{
*/
#define LED0_PIN GPIO_PIN(PORT_B, 0)
#define LED0_MASK (1 << 0)
#define LED0_ON (GPIOB->PCOR = LED0_MASK)
#define LED0_OFF (GPIOB->PSOR = LED0_MASK)
#define LED0_TOGGLE (GPIOB->PTOR = LED0_MASK)
#define LED1_PIN GPIO_PIN(PORT_C, 1)
#define LED1_MASK (1 << 1)
#define LED1_ON (GPIOC->PCOR = LED1_MASK)
#define LED1_OFF (GPIOC->PSOR = LED1_MASK)
#define LED1_TOGGLE (GPIOC->PTOR = LED1_MASK)
#define LED2_PIN GPIO_PIN(PORT_A, 19)
#define LED2_MASK (1 << 19)
#define LED2_ON (GPIOA->PCOR = LED2_MASK)
#define LED2_OFF (GPIOA->PSOR = LED2_MASK)
#define LED2_TOGGLE (GPIOA->PTOR = LED2_MASK)
#define LED3_PIN GPIO_PIN(PORT_A, 18)
#define LED3_MASK (1 << 18)
#define LED3_ON (GPIOA->PCOR = LED3_MASK)
#define LED3_OFF (GPIOA->PSOR = LED3_MASK)
#define LED3_TOGGLE (GPIOA->PTOR = LED3_MASK)
/** @} */
/**
* @name xtimer configuration
* @{
*/
#if KINETIS_XTIMER_SOURCE_PIT
/* PIT xtimer configuration */
#define XTIMER_DEV (TIMER_PIT_DEV(0))
#define XTIMER_CHAN (0)
/* Default xtimer settings should work on the PIT */
#else
/* LPTMR xtimer configuration */
#define XTIMER_DEV (TIMER_LPTMR_DEV(0))
#define XTIMER_CHAN (0)
/* LPTMR is 16 bits wide and runs at 32768 Hz (clocked by the RTC) */
#define XTIMER_WIDTH (16)
#define XTIMER_BACKOFF (5)
#define XTIMER_ISR_BACKOFF (5)
#define XTIMER_OVERHEAD (4)
#define XTIMER_HZ (32768ul)
#endif
/** @} */
/**
* @name NOR flash hardware configuration
* @{
*/
#define FRDM_NOR_SPI_DEV SPI_DEV(0)
#define FRDM_NOR_SPI_CLK SPI_CLK_5MHZ
#define FRDM_NOR_SPI_CS SPI_HWCS(0) /**< Flash CS pin */
/** @} */
/**
* @brief Initialize board specific hardware, including clock, LEDs and standard I/O
*/
void board_init(void);
#ifdef __cplusplus
}
#endif
#endif /* BOARD_H */
/** @} */

View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2017 SKF AB
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup boards_frdm_kw41z
* @{
*
* @file
* @brief Board specific configuration of direct mapped GPIOs
*
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
*/
#ifndef GPIO_PARAMS_H
#define GPIO_PARAMS_H
#include "board.h"
#include "saul/periph.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief LED configuration
*/
static const saul_gpio_params_t saul_gpio_params[] =
{
/* These LEDs are marked on the board silkscreen with "LED3" for the red LED,
* and "LED4" for the RGB LED, hence the names for the SAUL actuators don't
* match the LEDx_PIN macros in board.h */
/* LED1 and LED2 on the board are wired to the target CPU reset pin, and the
* power supply line and are not software controllable */
{
.name = "LED3",
.pin = LED0_PIN,
.mode = GPIO_OUT,
.flags = (SAUL_GPIO_INVERTED | SAUL_GPIO_INIT_CLEAR),
},
{
.name = "LED4_R",
.pin = LED1_PIN,
.mode = GPIO_OUT,
.flags = (SAUL_GPIO_INVERTED | SAUL_GPIO_INIT_CLEAR),
},
{
.name = "LED4_G",
.pin = LED2_PIN,
.mode = GPIO_OUT,
.flags = (SAUL_GPIO_INVERTED | SAUL_GPIO_INIT_CLEAR),
},
{
.name = "LED4_B",
.pin = LED3_PIN,
.mode = GPIO_OUT,
.flags = (SAUL_GPIO_INVERTED | SAUL_GPIO_INIT_CLEAR),
},
{
.name = "SW3",
.pin = GPIO_PIN(PORT_C, 4),
.mode = GPIO_IN_PU
},
{
.name = "SW4",
.pin = GPIO_PIN(PORT_C, 5),
.mode = GPIO_IN_PU
},
};
#ifdef __cplusplus
}
#endif
#endif /* GPIO_PARAMS_H */
/** @} */

View File

@ -0,0 +1,306 @@
/*
* Copyright (C) 2017 Eistec AB
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/
/**
* @ingroup boards_frdm-kw41z
* @{
*
* @file
* @name Peripheral MCU configuration for the FRDM-KW41Z
*
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
*/
#ifndef PERIPH_CONF_H
#define PERIPH_CONF_H
#include "periph_cpu.h"
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @name Clock system configuration
* @{
*/
static const clock_config_t clock_config = {
/*
* This configuration results in the system running with the internal clock
* with the following clock frequencies:
* Core: 48 MHz
* Bus: 24 MHz
* Flash: 24 MHz
*/
.clkdiv1 = SIM_CLKDIV1_OUTDIV1(0) | SIM_CLKDIV1_OUTDIV4(1),
/* Using FEI mode by default, the external crystal settings below are only
* used if mode is changed to an external mode (PEE, FBE, or FEE) */
.default_mode = KINETIS_MCG_MODE_FEI,
/* The crystal connected to RSIM OSC is 32 MHz */
.erc_range = KINETIS_MCG_ERC_RANGE_VERY_HIGH,
.fcrdiv = 0, /* Fast IRC divide by 1 => 4 MHz */
.oscsel = 0, /* Use RSIM for external clock */
.clc = 0, /* no load cap configuration */
.fll_frdiv = 0b101, /* Divide by 1024 */
.fll_factor_fei = KINETIS_MCG_FLL_FACTOR_1464, /* FEI FLL freq = 48 MHz */
.fll_factor_fee = KINETIS_MCG_FLL_FACTOR_1280, /* FEE FLL freq = 40 MHz */
.enable_oscillator = true, /* Use RF module oscillator */
.select_fast_irc = true,
.enable_mcgirclk = true, /* Used for LPUART clocking */
};
/* Radio xtal frequency, either 32 MHz or 26 MHz */
#define CLOCK_RADIOXTAL (32000000ul)
/* CPU core clock, the MCG clock output frequency */
#define CLOCK_CORECLOCK (48000000ul)
#define CLOCK_BUSCLOCK (CLOCK_CORECLOCK / 2)
#define CLOCK_MCGIRCLK (4000000ul)
/** @} */
/**
* @name Timer configuration
* @{
*/
#define PIT_NUMOF (1U)
#define PIT_CONFIG { \
{ \
.prescaler_ch = 0, \
.count_ch = 1, \
}, \
}
#define LPTMR_NUMOF (1U)
#define LPTMR_CONFIG { \
{ \
.dev = LPTMR0, \
.irqn = LPTMR0_IRQn, \
} \
}
#define TIMER_NUMOF ((PIT_NUMOF) + (LPTMR_NUMOF))
#define PIT_BASECLOCK (CLOCK_BUSCLOCK)
#define LPTMR_ISR_0 isr_lptmr0
/** @} */
/**
* @name UART configuration
* @{
*/
static const uart_conf_t uart_config[] = {
{
.dev = LPUART0,
.freq = CLOCK_MCGIRCLK,
.pin_rx = GPIO_PIN(PORT_C, 6),
.pin_tx = GPIO_PIN(PORT_C, 7),
.pcr_rx = PORT_PCR_MUX(4),
.pcr_tx = PORT_PCR_MUX(4),
.irqn = LPUART0_IRQn,
.scgc_addr = &SIM->SCGC5,
.scgc_bit = SIM_SCGC5_LPUART0_SHIFT,
.mode = UART_MODE_8N1,
.type = KINETIS_LPUART,
},
};
#define UART_NUMOF (sizeof(uart_config) / sizeof(uart_config[0]))
#define LPUART_0_ISR isr_lpuart0
/* Use MCGIRCLK (internal reference 4 MHz clock) */
#define LPUART_0_SRC 3
/** @} */
/**
* @name ADC configuration
* @{
*/
static const adc_conf_t adc_config[] = {
/* dev, pin, channel */
[ 0] = { ADC0, GPIO_UNDEF, 26 }, /* internal: temperature sensor */
/* Note: the band gap buffer uses a bit of current and is turned off by default,
* Set PMC->REGSC |= PMC_REGSC_BGBE_MASK before reading or the input will be floating */
[ 1] = { ADC0, GPIO_UNDEF, 27 }, /* internal: band gap */
[ 2] = { ADC0, GPIO_UNDEF, 29 }, /* internal: V_REFH */
[ 3] = { ADC0, GPIO_UNDEF, 30 }, /* internal: V_REFL */
[ 4] = { ADC0, GPIO_UNDEF, 23 }, /* internal: DCDC divided battery level */
[ 5] = { ADC0, GPIO_UNDEF, 0 | ADC_SC1_DIFF_MASK }, /* ADC0_DP-ADC0_DM differential reading */
[ 6] = { ADC0, GPIO_PIN(PORT_B, 3), 2 }, /* ADC0_SE2 */
[ 7] = { ADC0, GPIO_PIN(PORT_B, 2), 3 }, /* ADC0_SE3 */
};
#define ADC_NUMOF (sizeof(adc_config) / sizeof(adc_config[0]))
/*
* KW41Z ADC reference settings:
* 0: VREFH external pin or VREF_OUT 1.2 V signal (if VREF module is enabled)
* 1: VDDA (analog supply input voltage)
* 2-3: reserved
*
* VREF_OUT and VREFH shares the pin on KW41Z and is only connected to a 100 nF
* capacitor on the FRDM-KW41Z board. So use VDDA by default on this board
* unless the application enables the VREF module.
*/
#define ADC_REF_SETTING 1
/** @} */
/**
* @name SPI configuration
*
* Clock configuration values based on the configured 16Mhz module clock.
*
* Auto-generated by:
* cpu/kinetis/dist/calc_spi_scalers/calc_spi_scalers.c
*
* @{
*/
static const uint32_t spi_clk_config[] = {
(
SPI_CTAR_PBR(2) | SPI_CTAR_BR(5) | /* -> 100000Hz */
SPI_CTAR_PCSSCK(2) | SPI_CTAR_CSSCK(4) |
SPI_CTAR_PASC(2) | SPI_CTAR_ASC(4) |
SPI_CTAR_PDT(2) | SPI_CTAR_DT(4)
),
(
SPI_CTAR_PBR(2) | SPI_CTAR_BR(3) | /* -> 400000Hz */
SPI_CTAR_PCSSCK(2) | SPI_CTAR_CSSCK(2) |
SPI_CTAR_PASC(2) | SPI_CTAR_ASC(2) |
SPI_CTAR_PDT(2) | SPI_CTAR_DT(2)
),
(
SPI_CTAR_PBR(0) | SPI_CTAR_BR(3) | /* -> 1000000Hz */
SPI_CTAR_PCSSCK(0) | SPI_CTAR_CSSCK(3) |
SPI_CTAR_PASC(0) | SPI_CTAR_ASC(3) |
SPI_CTAR_PDT(0) | SPI_CTAR_DT(3)
),
(
SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | /* -> 4000000Hz */
SPI_CTAR_PCSSCK(0) | SPI_CTAR_CSSCK(1) |
SPI_CTAR_PASC(0) | SPI_CTAR_ASC(1) |
SPI_CTAR_PDT(0) | SPI_CTAR_DT(1)
),
(
SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | /* -> 4000000Hz */
SPI_CTAR_PCSSCK(0) | SPI_CTAR_CSSCK(0) |
SPI_CTAR_PASC(0) | SPI_CTAR_ASC(0) |
SPI_CTAR_PDT(0) | SPI_CTAR_DT(0)
)
};
static const spi_conf_t spi_config[] = {
{
.dev = SPI0,
.pin_miso = GPIO_PIN(PORT_C, 18),
.pin_mosi = GPIO_PIN(PORT_C, 17),
.pin_clk = GPIO_PIN(PORT_C, 16),
.pin_cs = {
GPIO_PIN(PORT_C, 19),
GPIO_UNDEF,
GPIO_UNDEF,
GPIO_UNDEF,
GPIO_UNDEF
},
.pcr = GPIO_AF_2,
.simmask = SIM_SCGC6_SPI0_MASK
},
{
.dev = SPI1,
.pin_miso = GPIO_PIN(PORT_A, 17),
.pin_mosi = GPIO_PIN(PORT_A, 16),
.pin_clk = GPIO_PIN(PORT_A, 18),
.pin_cs = {
GPIO_PIN(PORT_A, 19),
GPIO_UNDEF,
GPIO_UNDEF,
GPIO_UNDEF,
GPIO_UNDEF
},
.pcr = GPIO_AF_2,
.simmask = SIM_SCGC6_SPI1_MASK
}
};
#define SPI_NUMOF (sizeof(spi_config) / sizeof(spi_config[0]))
/** @} */
/**
* @name I2C configuration
* @{
*/
/* This CPU has I2C0 clocked by the bus clock and I2C1 clocked by the system
* clock. This causes trouble with the current implementation in kinetis
* which only supports one set of frequency dividers at a time */
/* The current configuration sets the dividers so that the I2C0 bus will run at
* half the requested speed, to avoid exceeding the requested speed on I2C1 with
* the same configuration */
#define I2C_NUMOF (2U)
#define I2C_0_EN 1
/* Disabled while waiting for a rewritten i2c driver which supports different
* clock sources for each i2c module */
#define I2C_1_EN 1
/* Low (10 kHz): MUL = 2, SCL divider = 1792, total: 3584 */
#define KINETIS_I2C_F_ICR_LOW (0x3A)
#define KINETIS_I2C_F_MULT_LOW (1)
/* Normal (100 kHz): MUL = 1, SCL divider = 320, total: 320 */
#define KINETIS_I2C_F_ICR_NORMAL (0x25)
#define KINETIS_I2C_F_MULT_NORMAL (0)
/* Fast (400 kHz): MUL = 1, SCL divider = 80, total: 80 */
#define KINETIS_I2C_F_ICR_FAST (0x14)
#define KINETIS_I2C_F_MULT_FAST (0)
/* Fast plus (1000 kHz): MUL = 1, SCL divider = 32, total: 32 */
#define KINETIS_I2C_F_ICR_FAST_PLUS (0x09)
#define KINETIS_I2C_F_MULT_FAST_PLUS (0)
/* I2C 0 device configuration */
#define I2C_0_DEV I2C0
#define I2C_0_CLKEN() (bit_set32(&SIM->SCGC4, SIM_SCGC4_I2C0_SHIFT))
#define I2C_0_CLKDIS() (bit_clear32(&SIM->SCGC4, SIM_SCGC4_I2C0_SHIFT))
#define I2C_0_IRQ I2C0_IRQn
#define I2C_0_IRQ_HANDLER isr_i2c0
/* I2C 0 pin configuration */
#define I2C_0_PORT PORTB
#define I2C_0_PORT_CLKEN() (bit_set32(&SIM->SCGC5, SIM_SCGC5_PORTB_SHIFT))
#define I2C_0_PIN_AF 3
#define I2C_0_SDA_PIN 1
#define I2C_0_SCL_PIN 0
#define I2C_0_PORT_CFG (PORT_PCR_MUX(I2C_0_PIN_AF))
/* I2C 1 device configuration */
#define I2C_1_DEV I2C1
#define I2C_1_CLKEN() (bit_set32(&SIM->SCGC4, SIM_SCGC4_I2C1_SHIFT))
#define I2C_1_CLKDIS() (bit_clear32(&SIM->SCGC4, SIM_SCGC4_I2C1_SHIFT))
#define I2C_1_IRQ I2C1_IRQn
#define I2C_1_IRQ_HANDLER isr_i2c1
/* I2C 1 pin configuration */
#define I2C_1_PORT PORTC
#define I2C_1_PORT_CLKEN() (bit_set32(&SIM->SCGC5, SIM_SCGC5_PORTC_SHIFT))
#define I2C_1_PIN_AF 3
#define I2C_1_SDA_PIN 3
#define I2C_1_SCL_PIN 2
#define I2C_1_PORT_CFG (PORT_PCR_MUX(I2C_0_PIN_AF))
/** @} */
/**
* @name RTT and RTC configuration
* @{
*/
#define RTT_NUMOF (1U)
#define RTC_NUMOF (1U)
#define RTT_DEV RTC
#define RTT_IRQ RTC_IRQn
#define RTT_IRQ_PRIO 10
#define RTT_UNLOCK() (bit_set32(&SIM->SCGC6, SIM_SCGC6_RTC_SHIFT))
#define RTT_ISR isr_rtc
#define RTT_FREQUENCY (1)
#define RTT_MAX_VALUE (0xffffffff)
/** @} */
/**
* @name Random Number Generator configuration
* @{
*/
#define KINETIS_TRNG TRNG
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* PERIPH_CONF_H */
/** @} */

256
cpu/kinetis/include/bme.h Normal file
View File

@ -0,0 +1,256 @@
/*
* Copyright (C) 2017 Eistec AB
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/
/**
* @defgroup cpu_kinetis_bme Kinetis Bit Manipulation Engine (BME)
* @ingroup cpu_kinetis
* @brief Macros for using decorated memory accesses with the Bit
* Manipulation Engine available in Kinetis Cortex-M0+ devices
*
* @{
* @file
* @brief Macro definitions for the Kinetis Bit Manipulation Engine (BME)
*
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
*/
#ifndef BME_H
#define BME_H
#include <stdint.h>
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief Tell bit.h that we provide CPU specific bit manipulation functions
*/
#define BITBAND_FUNCTIONS_PROVIDED 1
#define BME_AND_MASK (1 << 26) /**< AND decoration bitmask */
#define BME_OR_MASK (1 << 27) /**< OR decoration bitmask */
#define BME_XOR_MASK (3 << 26) /**< XOR decoration bitmask */
#define BME_LAC1_MASK(BIT) ((1 << 27) | ((BIT) << 21)) /**< Load-and-clear 1 bit */
#define BME_LAS1_MASK(BIT) ((3 << 26) | ((BIT) << 21)) /**< Load-and-set 1 bit */
/**
* @brief Bit field extraction bitmask
*
* @param bit LSB of the bitfield within the word/halfword/byte
* @param width Number of bits to extract
*/
#define BME_BF_MASK(bit, width) ((1 << 28) | ((bit) << 23) | (((width) - 1 ) << 19))
/**
* @brief Bit field address macro
*
* @pre The target address must lie within a part of the peripheral address
* space 0x40000000 - 0x40070000
*
* @param[in] ptr Pointer to target register
* @param[in] bit Location of the LSB of the bitfield within the register
* @param[in] width Width of the the bitfield, in bits
*
* @return bitfield address as an uintptr_t
*/
static inline volatile void *bme_bf_addr(volatile void *ptr, uintptr_t bit, uintptr_t width)
{
return (volatile void *)(((uintptr_t)ptr) | BME_BF_MASK(bit, width));
}
/**
* @brief Access a bitfield (32 bit load/store)
*
* This macro can be used both for store `(*bme_bitfield32(xxx) = y)` and
* load `(y = *bme_bitfield32(ptr, bit))`
*
* @pre The target address must lie within a part of the peripheral address
* space 0x40000000 - 0x40070000
*
* @param[in] ptr Pointer to target register
* @param[in] bit Location of the LSB of the bitfield within the register
* @param[in] width Width of the the bitfield, in bits
*
* @return bitfield extracted as a (modifiable) lvalue
*/
static inline volatile uint32_t *bme_bitfield32(volatile uint32_t *ptr, uint8_t bit, uint8_t width)
{
return (volatile uint32_t *)(bme_bf_addr(ptr, bit, width));
}
/**
* @brief Access a bitfield (16 bit load/store)
*
* This macro can be used both for store `(*bme_bitfield16(xxx) = y)` and
* load `(y = *bme_bitfield16(ptr, bit))`
*
* @pre The target address must lie within a part of the peripheral address
* space 0x40000000 - 0x40070000
*
* @param[in] ptr Pointer to target register
* @param[in] bit Location of the LSB of the bitfield within the register
* @param[in] width Width of the the bitfield, in bits
*
* @return bitfield extracted as a (modifiable) lvalue
*/
static inline volatile uint16_t *bme_bitfield16(volatile uint16_t *ptr, uint8_t bit, uint8_t width)
{
return (volatile uint16_t *)(bme_bf_addr(ptr, bit, width));
}
/**
* @brief Access a bitfield (8 bit load/store)
*
* This macro can be used both for store `(*bme_bitfield8(xxx) = y)` and
* load `(y = *bme_bitfield8(ptr, bit))`
*
* @pre The target address must lie within a part of the peripheral address
* space 0x40000000 - 0x40070000
*
* @param[in] ptr Pointer to target register
* @param[in] bit Location of the LSB of the bitfield within the register
* @param[in] width Width of the the bitfield, in bits
*
* @return bitfield extracted as a (modifiable) lvalue
*/
static inline volatile uint8_t *bme_bitfield8(volatile uint8_t *ptr, uint8_t bit, uint8_t width)
{
return (volatile uint8_t *)(bme_bf_addr(ptr, bit, width));
}
/* For compatibility with the M3/M4 bitbanding macros: */
/**
* @brief Set a single bit in the 32 bit word pointed to by @p ptr
*
* The effect is the same as for the following snippet:
*
* @code{c}
* *ptr |= (1 << bit);
* @endcode
*
* There is a read-modify-write cycle occurring within the core, but this cycle
* is atomic and can not be disrupted by IRQs
*
* @param[in] ptr pointer to target word
* @param[in] bit bit number within the word
*/
static inline void bit_set32(volatile uint32_t *ptr, uint8_t bit)
{
*((volatile uint32_t *)(((uintptr_t)ptr) | BME_OR_MASK)) = (uint32_t)((1ul << bit));
}
/**
* @brief Set a single bit in the 16 bit word pointed to by @p ptr
*
* The effect is the same as for the following snippet:
*
* @code{c}
* *ptr |= (1 << bit);
* @endcode
*
* There is a read-modify-write cycle occurring within the core, but this cycle
* is atomic and can not be disrupted by IRQs
*
* @param[in] ptr pointer to target word
* @param[in] bit bit number within the word
*/
static inline void bit_set16(volatile uint16_t *ptr, uint8_t bit)
{
*((volatile uint16_t *)(((uintptr_t)ptr) | BME_OR_MASK)) = (uint16_t)((1ul << bit));
}
/**
* @brief Set a single bit in the 8 bit byte pointed to by @p ptr
*
* The effect is the same as for the following snippet:
*
* @code{c}
* *ptr |= (1 << bit);
* @endcode
*
* There is a read-modify-write cycle occurring within the core, but this cycle
* is atomic and can not be disrupted by IRQs
*
* @param[in] ptr pointer to target byte
* @param[in] bit bit number within the byte
*/
static inline void bit_set8(volatile uint8_t *ptr, uint8_t bit)
{
*((volatile uint8_t *)(((uintptr_t)ptr) | BME_OR_MASK)) = (uint8_t)((1ul << bit));
}
/**
* @brief Clear a single bit in the 32 bit word pointed to by @p ptr
*
* The effect is the same as for the following snippet:
*
* @code{c}
* *ptr &= ~(1 << bit);
* @endcode
*
* There is a read-modify-write cycle occurring within the core, but this cycle
* is atomic and can not be disrupted by IRQs
*
* @param[in] ptr pointer to target word
* @param[in] bit bit number within the word
*/
static inline void bit_clear32(volatile uint32_t *ptr, uint8_t bit)
{
*((volatile uint32_t *)(((uintptr_t)ptr) | BME_AND_MASK)) = (uint32_t)(~(1ul << bit));
}
/**
* @brief Clear a single bit in the 16 bit word pointed to by @p ptr
*
* The effect is the same as for the following snippet:
*
* @code{c}
* *ptr &= ~(1 << bit);
* @endcode
*
* There is a read-modify-write cycle occurring within the core, but this cycle
* is atomic and can not be disrupted by IRQs
*
* @param[in] ptr pointer to target word
* @param[in] bit bit number within the word
*/
static inline void bit_clear16(volatile uint16_t *ptr, uint8_t bit)
{
*((volatile uint16_t *)(((uintptr_t)ptr) | BME_AND_MASK)) = (uint16_t)(~(1ul << bit));
}
/**
* @brief Clear a single bit in the 8 bit byte pointed to by @p ptr
*
* The effect is the same as for the following snippet:
*
* @code{c}
* *ptr &= ~(1 << bit);
* @endcode
*
* There is a read-modify-write cycle occurring within the core, but this cycle
* is atomic and can not be disrupted by IRQs
*
* @param[in] ptr pointer to target byte
* @param[in] bit bit number within the byte
*/
static inline void bit_clear8(volatile uint8_t *ptr, uint8_t bit)
{
*((volatile uint8_t *)(((uintptr_t)ptr) | BME_AND_MASK)) = (uint8_t)(~(1ul << bit));
}
#ifdef __cplusplus
}
#endif
#endif /* BME_H */
/** @} */

View File

@ -21,6 +21,15 @@
#include "cpu_conf_common.h"
#if (__CORTEX_M < 3)
/*
* Kinetis Cortex-M0+ devices have bit manipulation engine (BME) which provides
* the same functionality (and some more) as the bitband aliased memory found in
* Cortex-M3 and up
*/
#include "bme.h"
#endif
#ifdef __cplusplus
extern "C"
{

View File

@ -63,7 +63,18 @@
#elif defined(KINETIS_CORE_Z)
/* Kinetis KWxxZ */
/* TODO */
#if defined(CPU_MODEL_MKW21Z256VHT4) || \
defined(CPU_MODEL_MKW21Z512VHT4)
#include "vendor/MKW21Z4.h"
#elif defined(CPU_MODEL_MKW31Z256VHT4) || \
defined(CPU_MODEL_MKW31Z512CAT4) || \
defined(CPU_MODEL_MKW31Z512VHT4)
#include "vendor/MKW31Z4.h"
#elif defined(CPU_MODEL_MKW41Z256VHT4) || \
defined(CPU_MODEL_MKW41Z512CAT4) || \
defined(CPU_MODEL_MKW41Z512VHT4)
#include "vendor/MKW41Z4.h"
#endif
#endif /* KINETIS_CORE_x */
#ifdef __cplusplus

View File

@ -46,6 +46,8 @@ typedef uint16_t gpio_t;
*/
#define GPIO_PIN(x, y) (((x + 1) << 12) | (x << 6) | y)
#ifdef SIM_UIDH_UID_MASK
/* Kinetis Cortex-M4 has a 128 bit SIM UID */
/**
* @brief Starting offset of CPU_ID
*/
@ -55,6 +57,17 @@ typedef uint16_t gpio_t;
* @brief Length of the CPU_ID in octets
*/
#define CPUID_LEN (16U)
#else /* defined(SIM_UIDH_UID_MASK) */
/* Kinetis Cortex-M0+ has a 96 bit SIM UID */
/**
* @brief Starting offset of CPU_ID
*/
#define CPUID_ADDR (&SIM->UIDMH)
/**
* @brief Length of the CPU_ID in octets
*/
#define CPUID_LEN (12U)
#endif /* defined(SIM_UIDH_UID_MASK) */
/**
* @brief Generate GPIO mode bitfields
@ -67,11 +80,6 @@ typedef uint16_t gpio_t;
*/
#define GPIO_MODE(pu, pe, od, out) (pu | (pe << 1) | (od << 5) | (out << 7))
/**
* @brief Define the maximum number of PWM channels that can be configured
*/
#define PWM_CHAN_MAX (4U)
/**
* @brief Define a CPU specific SPI hardware chip select line macro
*
@ -135,7 +143,9 @@ typedef enum {
GPIO_AF_5 = PORT_PCR_MUX(5), /**< use alternate function 5 */
GPIO_AF_6 = PORT_PCR_MUX(6), /**< use alternate function 6 */
GPIO_AF_7 = PORT_PCR_MUX(7), /**< use alternate function 7 */
#ifdef PORT_PCR_ODE_MASK
GPIO_PCR_OD = (PORT_PCR_ODE_MASK), /**< open-drain mode */
#endif
GPIO_PCR_PD = (PORT_PCR_PE_MASK), /**< enable pull-down */
GPIO_PCR_PU = (PORT_PCR_PE_MASK | PORT_PCR_PS_MASK) /**< enable PU */
} gpio_pcr_t;
@ -186,8 +196,14 @@ typedef enum {
} adc_res_t;
/** @} */
#if defined(FTM_CnSC_MSB_MASK)
/**
* @name PWM mode configuration bits
* @brief Define the maximum number of PWM channels that can be configured
*/
#define PWM_CHAN_MAX (4U)
/**
* @name PWM mode configuration
* @{
*/
#define HAVE_PWM_MODE_T
@ -196,7 +212,7 @@ typedef enum {
PWM_RIGHT = (FTM_CnSC_MSB_MASK | FTM_CnSC_ELSA_MASK), /**< right aligned */
PWM_CENTER = (FTM_CnSC_MSB_MASK) /**< center aligned */
} pwm_mode_t;
/** @} */
#endif /* defined(FTM_CnSC_MSB_MASK) */
#endif /* ndef DOXYGEN */
/**
@ -279,6 +295,7 @@ typedef struct {
uint8_t irqn;
} lptmr_conf_t;
#ifdef FTM_CnSC_MSB_MASK
/**
* @brief PWM configuration structure
*/
@ -292,6 +309,7 @@ typedef struct {
uint8_t chan_numof; /**< number of actually configured channels */
uint8_t ftm_num; /**< FTM number used */
} pwm_conf_t;
#endif
/**
* @brief SPI module configuration options

View File

@ -121,6 +121,7 @@ void isr_lvd_lvw(void); /**< PMC controller low-voltage detect, low-voltage
void isr_mcg(void); /**< MCG interrupt handler */
void isr_mcm(void); /**< MCM normal interrupt handler */
void isr_pdb0(void); /**< PDB0 interrupt handler */
void isr_pit(void); /**< PIT any channel interrupt handler */
void isr_pit0(void); /**< PIT timer channel 0 interrupt handler */
void isr_pit1(void); /**< PIT timer channel 1 interrupt handler */
void isr_pit2(void); /**< PIT timer channel 2 interrupt handler */
@ -130,6 +131,9 @@ void isr_portb(void); /**< Port B pin detect interrupt handler */
void isr_portc(void); /**< Port C pin detect interrupt handler */
void isr_portd(void); /**< Port D pin detect interrupt handler */
void isr_porte(void); /**< Port E pin detect interrupt handler */
void isr_portb_portc(void); /**< Port B, C combined pin detect interrupt handler */
void isr_radio_0(void); /**< Radio transceiver INT0 interrupt handler */
void isr_radio_1(void); /**< Radio transceiver INT1 interrupt handler */
void isr_rng(void); /**< RNG interrupt handler */
void isr_rtc(void); /**< RTC interrupt handler */
void isr_rtc_seconds(void); /**< RTC seconds interrupt handler */
@ -141,6 +145,7 @@ void isr_swi(void); /**< Software interrupt handler */
void isr_tpm0(void); /**< TPM0 interrupt handler */
void isr_tpm1(void); /**< TPM1 interrupt handler */
void isr_tpm2(void); /**< TPM2 interrupt handler */
void isr_trng0(void); /**< TRNG0 interrupt handler */
void isr_tsi0(void); /**< TSI0 interrupt handler */
void isr_uart0_err(void); /**< UART0 error interrupt handler */
void isr_uart0_lon(void); /**< UART0 LON interrupt handler */

12944
cpu/kinetis/include/vendor/MKW21Z4.h vendored Normal file

File diff suppressed because it is too large Load Diff

12312
cpu/kinetis/include/vendor/MKW31Z4.h vendored Normal file

File diff suppressed because it is too large Load Diff

13015
cpu/kinetis/include/vendor/MKW41Z4.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -169,6 +169,7 @@ WEAK_DEFAULT void isr_lvd_lvw(void);
WEAK_DEFAULT void isr_mcg(void);
WEAK_DEFAULT void isr_mcm(void);
WEAK_DEFAULT void isr_pdb0(void);
WEAK_DEFAULT void isr_pit(void);
WEAK_DEFAULT void isr_pit0(void);
WEAK_DEFAULT void isr_pit1(void);
WEAK_DEFAULT void isr_pit2(void);
@ -178,6 +179,9 @@ WEAK_DEFAULT void isr_portb(void);
WEAK_DEFAULT void isr_portc(void);
WEAK_DEFAULT void isr_portd(void);
WEAK_DEFAULT void isr_porte(void);
WEAK_DEFAULT void isr_portb_portc(void);
WEAK_DEFAULT void isr_radio_0(void);
WEAK_DEFAULT void isr_radio_1(void);
WEAK_DEFAULT void isr_rng(void);
WEAK_DEFAULT void isr_rtc(void);
WEAK_DEFAULT void isr_rtc_seconds(void);
@ -190,6 +194,7 @@ WEAK_DEFAULT void isr_tpm0(void);
WEAK_DEFAULT void isr_tpm1(void);
WEAK_DEFAULT void isr_tpm2(void);
WEAK_DEFAULT void isr_tsi0(void);
WEAK_DEFAULT void isr_trng0(void);
WEAK_DEFAULT void isr_uart0_err(void);
WEAK_DEFAULT void isr_uart0_lon(void);
WEAK_DEFAULT void isr_uart0_rx_tx(void);

View File

@ -31,6 +31,11 @@
#include "bit.h"
#include "periph/gpio.h"
#ifndef PORT_PCR_ODE_MASK
/* For compatibility with Kinetis CPUs without open drain GPIOs (e.g. KW41Z) */
#define PORT_PCR_ODE_MASK 0
#endif
/**
* @brief Get the OCR reg value from the gpio_mode_t value
*/
@ -347,3 +352,12 @@ void isr_portg(void)
irq_handler(PORTG, 6);
}
#endif /* ISR_PORT_G */
#if defined(PORTB_BASE) && defined(PORTC_BASE)
/* Combined ISR used in certain KL devices */
void isr_portb_portc(void)
{
irq_handler(PORTB, 1);
irq_handler(PORTC, 2);
}
#endif

View File

@ -29,6 +29,12 @@
#include "assert.h"
#include "periph/pwm.h"
/* This driver uses the FlexTimer module (FTM) for generating PWM signals, but
* not all Kinetis CPUs have such a module. This preprocessor condition prevents
* compilation errors when the CPU header lacks the register definitions for the
* FTM */
#ifdef FTM0
#define PRESCALER_MAX (7U)
static inline FTM_Type *ftm(pwm_t pwm)
@ -150,3 +156,5 @@ void pwm_poweroff(pwm_t pwm)
}
#endif
}
#endif /* defined(FTM0) */

View File

@ -32,6 +32,16 @@
#include "periph_conf.h"
#include "periph/timer.h"
#ifdef PIT_LTMR64H_LTH_MASK
/* The KW41Z PIT module provides only one IRQ for all PIT channels combined. */
/* TODO: find a better way to distinguish which Kinetis CPUs have separate PIT
* channel interrupts */
#define KINETIS_PIT_COMBINED_IRQ 1
#else
/* K60, K64F etc have a separate IRQ number for each PIT channel */
#define KINETIS_PIT_COMBINED_IRQ 0
#endif
#define ENABLE_DEBUG (0)
#include "debug.h"
@ -189,10 +199,15 @@ static inline int pit_init(uint8_t dev, uint32_t freq, timer_cb_t cb, void *arg)
/* Clear IRQ flag */
PIT->CHANNEL[pit_config[dev].count_ch].TFLG = PIT_TFLG_TIF_MASK;
#if KINETIS_PIT_COMBINED_IRQ
/* One IRQ for all channels */
/* NVIC_ClearPendingIRQ(PIT_IRQn); */ /* does it make sense to clear this IRQ flag? */
NVIC_EnableIRQ(PIT_IRQn);
#else
/* Refactor the below lines if there are any CPUs where the PIT IRQs are not sequential */
NVIC_ClearPendingIRQ(PIT0_IRQn + pit_config[dev].count_ch);
NVIC_EnableIRQ(PIT0_IRQn + pit_config[dev].count_ch);
#endif
/* Reset up-counter */
pit[dev].count = PIT_MAX_VALUE;
pit[dev].ldval = PIT_MAX_VALUE;
@ -711,6 +726,22 @@ void timer_stop(tim_t dev)
/* ****** ISR instances ****** */
void isr_pit(void)
{
/* Some of the lower end Kinetis CPUs combine the individual PIT interrupt
* flags into a single NVIC IRQ signal. This means that software needs to
* test which timer(s) went off when an IRQ occurs. */
for (size_t i = 0; i < PIT_NUMOF; ++i) {
if (PIT->CHANNEL[pit_config[i].count_ch].TCTRL & PIT_TCTRL_TIE_MASK) {
/* Interrupt is enabled */
if (PIT->CHANNEL[pit_config[i].count_ch].TFLG) {
/* Timer interrupt flag is set */
pit_irq_handler(_pit_tim_t(i));
}
}
}
}
#ifdef PIT_ISR_0
void PIT_ISR_0(void)
{

View File

@ -38,6 +38,7 @@
#define FTFL_Collision_IRQn Read_Collision_IRQn
#define PMC_IRQn LVD_LVW_IRQn
#define Watchdog_IRQn WDOG_EWM_IRQn
#define LVD_LVW_DCDC_IRQn LVD_LVW_IRQn
#include "vectors_kinetis.h"
@ -125,21 +126,29 @@ ISR_VECTOR(1) const isr_t vector_cpu[CPU_IRQ_NUMOF] = {
#elif defined(DMA_INT_INT15_MASK)
[DMA15_IRQn ] = isr_dma15, /* DMA Channel 15 Transfer Complete */
#endif
#ifndef KINETIS_CORE_Z
[DMA_Error_IRQn ] = isr_dma_error, /* DMA Error Interrupt */
#endif
#endif /* defined(DMA0) */
#ifdef MCM
#if defined(MCM) && !defined(KINETIS_CORE_Z)
[MCM_IRQn ] = isr_mcm, /* Normal Interrupt */
#endif
#if defined(FTFA)
[FTFA_IRQn ] = isr_ftfa, /* FTFA command complete */
#ifndef KINETIS_CORE_Z
[FTFA_Collision_IRQn] = isr_ftfa_collision, /* FTFA read collision */
#endif
#elif defined(FTFE)
[FTFE_IRQn ] = isr_ftfe, /* FTFE command complete */
#ifndef KINETIS_CORE_Z
[FTFE_Collision_IRQn] = isr_ftfe_collision, /* FTFE read collision */
#endif
#elif defined(FTFL)
[FTFL_IRQn ] = isr_ftfl, /* FTFL command complete */
#ifndef KINETIS_CORE_Z
[FTFL_Collision_IRQn] = isr_ftfl_collision, /* FTFL read collision */
#endif
#endif
#ifdef PMC
[LVD_LVW_IRQn ] = isr_lvd_lvw, /* Low Voltage Detect, Low Voltage Warning */
#endif
@ -245,11 +254,15 @@ ISR_VECTOR(1) const isr_t vector_cpu[CPU_IRQ_NUMOF] = {
[RTC_Seconds_IRQn] = isr_rtc_seconds, /* RTC seconds interrupt */
#endif
#ifdef PIT
#ifdef KINETIS_CORE_Z
[PIT_IRQn ] = isr_pit, /* PIT any channel interrupt */
#else
[PIT0_IRQn ] = isr_pit0, /* PIT timer channel 0 interrupt */
[PIT1_IRQn ] = isr_pit1, /* PIT timer channel 1 interrupt */
[PIT2_IRQn ] = isr_pit2, /* PIT timer channel 2 interrupt */
[PIT3_IRQn ] = isr_pit3, /* PIT timer channel 3 interrupt */
#endif
#endif /* defined(PIT) */
#ifdef PDB0
[PDB0_IRQn ] = isr_pdb0, /* PDB0 Interrupt */
#endif
@ -274,6 +287,11 @@ ISR_VECTOR(1) const isr_t vector_cpu[CPU_IRQ_NUMOF] = {
#ifdef PORTA
[PORTA_IRQn ] = isr_porta, /* Port A interrupt */
#endif
#ifdef KINETIS_CORE_Z
#if defined(PORTB) && defined(PORTC)
[PORTB_PORTC_IRQn] = isr_portb_portc, /* Port B, C combined interrupt */
#endif
#else
#ifdef PORTB
[PORTB_IRQn ] = isr_portb, /* Port B interrupt */
#endif
@ -286,6 +304,7 @@ ISR_VECTOR(1) const isr_t vector_cpu[CPU_IRQ_NUMOF] = {
#ifdef PORTE
[PORTE_IRQn ] = isr_porte, /* Port E interrupt */
#endif
#endif
#if __CORTEX_M >= 3
[SWI_IRQn ] = isr_swi, /* Software interrupt */
#endif
@ -350,6 +369,12 @@ ISR_VECTOR(1) const isr_t vector_cpu[CPU_IRQ_NUMOF] = {
#ifdef USBHS
[USBHS_IRQn ] = isr_usbhs, /* USB high speed OTG interrupt */
#endif
#ifdef BTLE_RF
[Radio_0_IRQn ] = isr_radio_0, /* Radio INT0 interrupt */
#endif
#ifdef ZLL
[Radio_1_IRQn ] = isr_radio_1, /* Radio INT1 interrupt */
#endif
};
/** @} */

View File

@ -99,6 +99,7 @@ ARM_CORTEX_M_BOARDS := airfy-beacon \
fox \
frdm-k22f \
frdm-k64f \
frdm-kw41z \
iotlab-a8-m3 \
iotlab-m3 \
limifrog-v1 \