mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #2265 from jfischer-phytec-iot/pr@kinetis_common
Support for Freescale Kinetis MCUs, kinetis_common
This commit is contained in:
commit
890262e6ff
4
cpu/kinetis_common/Makefile
Normal file
4
cpu/kinetis_common/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
# define the module that is build
|
||||
MODULE = kinetis_common
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
2
cpu/kinetis_common/Makefile.include
Normal file
2
cpu/kinetis_common/Makefile.include
Normal file
@ -0,0 +1,2 @@
|
||||
# include module specific includes
|
||||
export INCLUDES += -I$(RIOTCPU)/kinetis_common/include
|
426
cpu/kinetis_common/adc.c
Normal file
426
cpu/kinetis_common/adc.c
Normal file
@ -0,0 +1,426 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Freie Universität Berlin
|
||||
* Copyright (C) 2014 PHYTEC Messtechnik GmbH
|
||||
* Copyright (C) 2015 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 cpu_kinetis_common_adc
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level ADC driver implementation
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
* @author Johann Fischer <j.fischer@phytec.de>
|
||||
* @author Jonas Remmert <j.remmert@phytec.de>
|
||||
* @author Joakim Gebart <joakim.gebart@eistec.se>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
#include <stdio.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "periph/adc.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
/* guard in case that no ADC device is defined */
|
||||
#if ADC_NUMOF
|
||||
|
||||
typedef struct {
|
||||
int max_value;
|
||||
int bits;
|
||||
} adc_config_t;
|
||||
|
||||
adc_config_t adc_config[ADC_NUMOF];
|
||||
|
||||
/**
|
||||
* @brief Perform ADC calibration routine.
|
||||
*
|
||||
* This is a recipe taken straight from the Kinetis K60 reference manual. It has
|
||||
* been tested on MK60DN512VLL10.
|
||||
*
|
||||
* @param[in] ADC_ptr Pointer to ADC device to operate on.
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return <0 on errors
|
||||
*/
|
||||
int kinetis_adc_calibrate(ADC_Type *ADC_ptr)
|
||||
{
|
||||
uint16_t cal;
|
||||
|
||||
ADC_ptr->SC3 |= ADC_SC3_CAL_MASK;
|
||||
|
||||
while (ADC_ptr->SC3 & ADC_SC3_CAL_MASK); /* wait for calibration to finish */
|
||||
|
||||
while (!(ADC_ptr->SC1[0] & ADC_SC1_COCO_MASK));
|
||||
|
||||
if (ADC_ptr->SC3 & ADC_SC3_CALF_MASK) {
|
||||
/* calibration failed for some reason, possibly SC2[ADTRG] is 1 ? */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Following the steps in the reference manual:
|
||||
*/
|
||||
/* 1. Initialize or clear a 16-bit variable in RAM. */
|
||||
/* 2. Add the plus-side calibration results CLP0, CLP1, CLP2, CLP3, CLP4, and
|
||||
* CLPS to the variable. */
|
||||
cal = ADC_ptr->CLP0 + ADC_ptr->CLP1 + ADC_ptr->CLP2 + ADC_ptr->CLP3 +
|
||||
ADC_ptr->CLP4 + ADC_ptr->CLPS;
|
||||
/* 3. Divide the variable by two. */
|
||||
cal /= 2;
|
||||
/* 4. Set the MSB of the variable. */
|
||||
cal |= (1 << 15);
|
||||
/* (5. The previous two steps can be achieved by setting the carry bit,
|
||||
* rotating to the right through the carry bit on the high byte and again on
|
||||
* the low byte.)
|
||||
* We ignore the above optimization suggestion, we most likely only perform
|
||||
* this calibration on startup and it will only save nanoseconds. */
|
||||
/* 6. Store the value in the plus-side gain calibration register PG. */
|
||||
ADC_ptr->PG = cal;
|
||||
|
||||
/* 7. Repeat the procedure for the minus-side gain calibration value. */
|
||||
cal = ADC_ptr->CLM0 + ADC_ptr->CLM1 + ADC_ptr->CLM2 + ADC_ptr->CLM3 +
|
||||
ADC_ptr->CLM4 + ADC_ptr->CLMS;
|
||||
cal /= 2;
|
||||
cal |= (1 << 15);
|
||||
ADC_ptr->MG = cal;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int adc_init(adc_t dev, adc_precision_t precision)
|
||||
{
|
||||
ADC_Type *adc = 0;
|
||||
PORT_Type *port[ADC_MAX_CHANNELS];
|
||||
uint8_t pins[ADC_MAX_CHANNELS];
|
||||
uint8_t af[ADC_MAX_CHANNELS];
|
||||
int channels = 0;
|
||||
uint32_t mode = 0;
|
||||
uint32_t div = 0;
|
||||
int i = 0;
|
||||
uint32_t clock = 0;
|
||||
|
||||
adc_poweron(dev);
|
||||
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
|
||||
case ADC_0:
|
||||
adc = ADC_0_DEV;
|
||||
port[0] = ADC_0_CH0_PORT;
|
||||
port[1] = ADC_0_CH1_PORT;
|
||||
port[2] = ADC_0_CH2_PORT;
|
||||
port[3] = ADC_0_CH3_PORT;
|
||||
port[4] = ADC_0_CH4_PORT;
|
||||
port[5] = ADC_0_CH5_PORT;
|
||||
pins[0] = ADC_0_CH0_PIN;
|
||||
pins[1] = ADC_0_CH1_PIN;
|
||||
pins[2] = ADC_0_CH2_PIN;
|
||||
pins[3] = ADC_0_CH3_PIN;
|
||||
pins[4] = ADC_0_CH4_PIN;
|
||||
pins[5] = ADC_0_CH5_PIN;
|
||||
af[0] = ADC_0_CH0_PIN_AF;
|
||||
af[1] = ADC_0_CH1_PIN_AF;
|
||||
af[2] = ADC_0_CH2_PIN_AF;
|
||||
af[3] = ADC_0_CH3_PIN_AF;
|
||||
af[4] = ADC_0_CH4_PIN_AF;
|
||||
af[5] = ADC_0_CH5_PIN_AF;
|
||||
channels = ADC_0_CHANNELS;
|
||||
clock = ADC_0_MODULE_CLOCK;
|
||||
ADC_0_PORT_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
|
||||
case ADC_1:
|
||||
adc = ADC_1_DEV;
|
||||
port[0] = ADC_1_CH0_PORT;
|
||||
port[1] = ADC_1_CH1_PORT;
|
||||
port[2] = ADC_1_CH2_PORT;
|
||||
port[3] = ADC_1_CH3_PORT;
|
||||
port[4] = ADC_1_CH4_PORT;
|
||||
port[5] = ADC_1_CH5_PORT;
|
||||
pins[0] = ADC_1_CH0_PIN;
|
||||
pins[1] = ADC_1_CH1_PIN;
|
||||
pins[2] = ADC_1_CH2_PIN;
|
||||
pins[3] = ADC_1_CH3_PIN;
|
||||
pins[4] = ADC_1_CH4_PIN;
|
||||
pins[5] = ADC_1_CH5_PIN;
|
||||
af[0] = ADC_1_CH0_PIN_AF;
|
||||
af[1] = ADC_1_CH1_PIN_AF;
|
||||
af[2] = ADC_1_CH2_PIN_AF;
|
||||
af[3] = ADC_1_CH3_PIN_AF;
|
||||
af[4] = ADC_1_CH4_PIN_AF;
|
||||
af[5] = ADC_1_CH5_PIN_AF;
|
||||
channels = ADC_1_CHANNELS;
|
||||
clock = ADC_1_MODULE_CLOCK;
|
||||
ADC_1_PORT_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (channels > ADC_MAX_CHANNELS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set precision, these numbers are valid for the K60 */
|
||||
switch (precision) {
|
||||
case ADC_RES_6BIT:
|
||||
/* Not supported by hardware, use 8 bit mode. */
|
||||
mode = ADC_CFG1_MODE(0);
|
||||
adc_config[dev].bits = 8;
|
||||
break;
|
||||
|
||||
case ADC_RES_8BIT:
|
||||
mode = ADC_CFG1_MODE(0);
|
||||
adc_config[dev].bits = 8;
|
||||
break;
|
||||
|
||||
case ADC_RES_10BIT:
|
||||
mode = ADC_CFG1_MODE(2);
|
||||
adc_config[dev].bits = 10;
|
||||
break;
|
||||
|
||||
case ADC_RES_12BIT:
|
||||
mode = ADC_CFG1_MODE(1);
|
||||
adc_config[dev].bits = 12;
|
||||
break;
|
||||
|
||||
case ADC_RES_14BIT:
|
||||
/* Not supported by hardware, use 16 bit mode. */
|
||||
mode = ADC_CFG1_MODE(3);
|
||||
adc_config[dev].bits = 16;
|
||||
break;
|
||||
|
||||
case ADC_RES_16BIT:
|
||||
mode = ADC_CFG1_MODE(3);
|
||||
adc_config[dev].bits = 16;
|
||||
break;
|
||||
}
|
||||
|
||||
adc_config[dev].max_value = (1 << adc_config[dev].bits) - 1;
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
/* Check whether we need to set the pin mux for this channel. */
|
||||
if (port[i] != NULL) {
|
||||
port[i]->PCR[pins[i]] = PORT_PCR_MUX(af[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* The ADC requires at least 2 MHz module clock for full accuracy, and less
|
||||
* than 12 MHz */
|
||||
/* For the calibration it is important that the ADC clock is <= 4 MHz */
|
||||
if (clock > 4000000 * 8) {
|
||||
/* Need to divide by 16, we set the clock input to BusClock/2 and
|
||||
* divide clock by 8 (the maximum divider) */
|
||||
div = ADC_CFG1_ADIV(3) | ADC_CFG1_ADICLK(1);
|
||||
}
|
||||
else if (clock > 4000000 * 4) {
|
||||
/* divide clock by 8 */
|
||||
div = ADC_CFG1_ADIV(3);
|
||||
}
|
||||
else if (clock > 4000000 * 2) {
|
||||
/* divide clock by 4 */
|
||||
div = ADC_CFG1_ADIV(2);
|
||||
}
|
||||
else if (clock > 4000000 * 1) {
|
||||
/* divide clock by 2 */
|
||||
div = ADC_CFG1_ADIV(1);
|
||||
}
|
||||
else {
|
||||
/* no clock divider */
|
||||
div = ADC_CFG1_ADIV(0);
|
||||
}
|
||||
|
||||
/* set configuration register 1: clocking and precision */
|
||||
/* Set long sample time */
|
||||
adc->CFG1 = ADC_CFG1_ADLSMP_MASK | mode | div;
|
||||
|
||||
/* select ADxxb channels, longest sample time (20 extra ADC cycles) */
|
||||
adc->CFG2 = ADC_CFG2_MUXSEL_MASK | ADC_CFG2_ADLSTS(0);
|
||||
|
||||
/* select software trigger, external ref pins */
|
||||
adc->SC2 = ADC_SC2_REFSEL(0);
|
||||
|
||||
/* select hardware average over 32 samples */
|
||||
adc->SC3 = ADC_SC3_AVGE_MASK | ADC_SC3_AVGS(3);
|
||||
|
||||
/* set an (arbitrary) input channel, single-ended mode */
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH0);
|
||||
adc->SC1[1] = ADC_SC1_ADCH(ADC_0_CH0);
|
||||
|
||||
/* perform calibration routine */
|
||||
if (kinetis_adc_calibrate(adc) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adc_sample(adc_t dev, int channel)
|
||||
{
|
||||
ADC_Type *adc = 0;
|
||||
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
|
||||
case ADC_0:
|
||||
adc = ADC_0_DEV;
|
||||
|
||||
/* start single conversion on corresponding channel */
|
||||
switch (channel) {
|
||||
case 0:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH0);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH1);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH2);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH3);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH4);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_0_CH5);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
|
||||
case ADC_1:
|
||||
adc = ADC_1_DEV;
|
||||
|
||||
/* start single conversion on corresponding channel */
|
||||
switch (channel) {
|
||||
case 0:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH0);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH1);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH2);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH3);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH4);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
adc->SC1[0] = ADC_SC1_ADCH(ADC_1_CH5);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* wait until conversion is complete */
|
||||
/* TODO: Use interrupts and yield the thread */
|
||||
while (!(adc->SC1[0] & ADC_SC1_COCO_MASK));
|
||||
|
||||
/* read and return result */
|
||||
return (int)adc->R[0];
|
||||
}
|
||||
|
||||
void adc_poweron(adc_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
|
||||
case ADC_0:
|
||||
ADC_0_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
|
||||
case ADC_1:
|
||||
ADC_1_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void adc_poweroff(adc_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
|
||||
case ADC_0:
|
||||
ADC_0_CLKDIS();
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
|
||||
case ADC_1:
|
||||
ADC_1_CLKDIS();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
int adc_map(adc_t dev, int value, int min, int max)
|
||||
{
|
||||
int scale = max - min;
|
||||
|
||||
/* NB: There is additional room for the multiplication result if using the
|
||||
* actual precision setting of the ADC as the limit in this if statement: */
|
||||
if (scale < (1 << 16)) {
|
||||
/* The ADCs on these CPUs are limited to 16 bits, the result of
|
||||
* value x scale will be 31 bits long or less. We use the upper part
|
||||
* of a 32 bit word when scaling */
|
||||
int32_t tmp = value * scale;
|
||||
/* Divide by ADC range to get the scaled result */
|
||||
return (tmp / (1 << adc_config[dev].bits));
|
||||
}
|
||||
else {
|
||||
/* Target scale is too large to use int32_t as an in between result */
|
||||
int64_t tmp = scale;
|
||||
/* Make sure the compiler does not generate code which will truncate the
|
||||
* result. */
|
||||
tmp *= value;
|
||||
/* Divide by ADC range to get the scaled result */
|
||||
tmp /= (1 << adc_config[dev].bits);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
float adc_mapf(adc_t dev, int value, float min, float max)
|
||||
{
|
||||
return ((max - min) / ((float)adc_config[dev].max_value)) * value;
|
||||
}
|
||||
|
||||
#endif /* ADC_NUMOF */
|
30
cpu/kinetis_common/cpuid.c
Normal file
30
cpu/kinetis_common/cpuid.c
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2014 PHYTEC Messtechnik GmbH
|
||||
*
|
||||
* 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 cpu_kinetis_common_cpuid
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level CPUID driver implementation
|
||||
*
|
||||
* @author Johann Fischer <j.fischer@phytec.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "cpu-conf.h"
|
||||
|
||||
#include "periph/cpuid.h"
|
||||
|
||||
void cpuid_get(void *id)
|
||||
{
|
||||
memcpy(id, CPUID_ID_PTR, CPUID_ID_LEN);
|
||||
}
|
328
cpu/kinetis_common/doc.txt
Normal file
328
cpu/kinetis_common/doc.txt
Normal file
@ -0,0 +1,328 @@
|
||||
/**
|
||||
* @defgroup cpu_kinetis_common Freescale Kinetis MCU
|
||||
* @ingroup cpu
|
||||
* @brief Common Drivers for Freescale Kinetis MCUs
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup cpu_kinetis_common_adc Kinetis ADC
|
||||
* @ingroup cpu_kinetis_common
|
||||
* @brief ADC driver.
|
||||
*
|
||||
* ### ADC Configuration Example (for periph_conf.h) ###
|
||||
*
|
||||
* #define ADC_NUMOF (1U)
|
||||
* #define ADC_0_EN 1
|
||||
* #define ADC_MAX_CHANNELS 1
|
||||
*
|
||||
* // ADC 0 configuration
|
||||
* #define ADC_0_DEV ADC0
|
||||
* #define ADC_0_MODULE_CLOCK CLOCK_CORECLOCK
|
||||
* #define ADC_0_CHANNELS 1
|
||||
* #define ADC_0_CLKEN() (SIM->SCGC6 |= (SIM_SCGC6_ADC0_MASK))
|
||||
* #define ADC_0_CLKDIS() (SIM->SCGC6 &= ~(SIM_SCGC6_ADC0_MASK))
|
||||
* #define ADC_0_PORT_CLKEN() (SIM->SCGC5 |= (SIM_SCGC5_PORTE_MASK))
|
||||
*
|
||||
* #define ADC_0_CH5 11
|
||||
* #define ADC_0_CH5_PIN 1
|
||||
* #define ADC_0_CH5_PIN_AF 0
|
||||
* #define ADC_0_CH5_PORT PORTE
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup cpu_kinetis_common_cpuid Kinetis CPUID
|
||||
* @ingroup cpu_kinetis_common
|
||||
* @brief CPUID driver.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup cpu_kinetis_common_gpio Kinetis GPIO
|
||||
* @ingroup cpu_kinetis_common
|
||||
* @brief GPIO driver.
|
||||
*
|
||||
* ### GPIO Configuration Example (for periph_conf.h) ###
|
||||
*
|
||||
* #define GPIO_NUMOF 1
|
||||
* #define GPIO_0_EN 0
|
||||
* #define GPIO_IRQ_PRIO 1
|
||||
* #define ISR_PORT_D isr_portd
|
||||
*
|
||||
* #define GPIO_22_DEV GPIOD
|
||||
* #define GPIO_22_PORT PORTD
|
||||
* #define GPIO_22_PIN 1
|
||||
* #define GPIO_22_CLKEN() (SIM->SCGC5 |= (SIM_SCGC5_PORTD_MASK))
|
||||
* #define GPIO_22_IRQ PORTD_IRQn
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup cpu_kinetis_common_i2c Kinetis I2C
|
||||
* @ingroup cpu_kinetis_common
|
||||
* @brief I2C driver.
|
||||
*
|
||||
* ### I2C Configuration Example (for periph_conf.h) ###
|
||||
*
|
||||
* #define I2C_NUMOF (1U)
|
||||
* #define I2C_CLK (48e6)
|
||||
* #define I2C_0_EN 1
|
||||
* #define I2C_IRQ_PRIO 1
|
||||
*
|
||||
* // I2C 0 device configuration
|
||||
* #define I2C_0_DEV I2C1
|
||||
* #define I2C_0_CLKEN() (SIM->SCGC4 |= (SIM_SCGC4_I2C1_MASK))
|
||||
* #define I2C_0_CLKDIS() (SIM->SCGC4 &= ~(SIM_SCGC4_I2C1_MASK))
|
||||
* #define I2C_0_IRQ I2C1_IRQn
|
||||
* #define I2C_0_IRQ_HANDLER isr_i2c1
|
||||
* // I2C 0 pin configuration
|
||||
* #define I2C_0_PORT PORTE
|
||||
* #define I2C_0_PORT_CLKEN() (SIM->SCGC5 |= (SIM_SCGC5_PORTE_MASK))
|
||||
* #define I2C_0_PIN_AF 6
|
||||
* #define I2C_0_SDA_PIN 0
|
||||
* #define I2C_0_SCL_PIN 1
|
||||
* #define I2C_0_PORT_CFG (PORT_PCR_MUX(I2C_0_PIN_AF) | PORT_PCR_ODE_MASK)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup cpu_kinetis_common_pwm Kinetis PWM
|
||||
* @ingroup cpu_kinetis_common
|
||||
* @brief PWM driver.
|
||||
*
|
||||
* ### PWM Configuration Example (for periph_conf.h) ###
|
||||
*
|
||||
* #define PWM_NUMOF (1U)
|
||||
* #define PWM_0_EN 1
|
||||
* #define PWM_MAX_CHANNELS 2
|
||||
*
|
||||
* // PWM 0 device configuration
|
||||
* #define PWM_0_DEV FTM0
|
||||
* #define PWM_0_CHANNELS 2
|
||||
* #define PWM_0_CLK (48e6)
|
||||
* #define PWM_0_CLKEN() (SIM->SCGC6 |= (SIM_SCGC6_FTM0_MASK))
|
||||
* #define PWM_0_CLKDIS() (SIM->SCGC6 &= ~(SIM_SCGC6_FTM0_MASK))
|
||||
* // PWM 0 pin configuration
|
||||
* #define PWM_0_PORT_CLKEN() (SIM->SCGC5 |= (SIM_SCGC5_PORTD_MASK | SIM_SCGC5_PORTA_MASK))
|
||||
*
|
||||
* #define PWM_0_PIN_CH0 4
|
||||
* #define PWM_0_FTMCHAN_CH0 1
|
||||
* #define PWM_0_PORT_CH0 PORTA
|
||||
* #define PWM_0_PIN_AF_CH0 3
|
||||
*
|
||||
* #define PWM_0_PIN_CH1 4
|
||||
* #define PWM_0_FTMCHAN_CH1 4
|
||||
* #define PWM_0_PORT_CH1 PORTD
|
||||
* #define PWM_0_PIN_AF_CH1 4
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup cpu_kinetis_common_rnga Kinetis RNGA
|
||||
* @ingroup cpu_kinetis_common
|
||||
* @brief Driver for Freescale's RNGA module. RNGA generates data that
|
||||
* looks random. Reference Manual recommends to use the RNGA as entropy
|
||||
* source.
|
||||
*
|
||||
* ### RNGA Configuration Example (for periph_conf.h) ###
|
||||
*
|
||||
* #define RANDOM_NUMOF (1U)
|
||||
* #define KINETIS_RNGA RNG
|
||||
* #define RANDOM_CLKEN() (SIM->SCGC6 |= (1 << 9))
|
||||
* #define RANDOM_CLKDIS() (SIM->SCGC6 &= ~(1 << 9))
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup cpu_kinetis_common_rngb Kinetis RNGB
|
||||
* @ingroup cpu_kinetis_common
|
||||
* @brief Low-level random number generator driver implementation.
|
||||
* Driver for Freescale's RNGB module. RNGB generates data that
|
||||
* looks random. Reference Manual recommends to use the RNGB as entropy
|
||||
* source.
|
||||
*
|
||||
* ### RNGB Configuration Example (for periph_conf.h) ###
|
||||
*
|
||||
* #define RANDOM_NUMOF (1U)
|
||||
* #define KINETIS_RNGB RNG
|
||||
* #define RANDOM_CLKEN() (SIM->SCGC6 |= (1 << 9))
|
||||
* #define RANDOM_CLKDIS() (SIM->SCGC6 &= ~(1 << 9))
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup cpu_kinetis_common_rtc Kinetis RTC
|
||||
* @ingroup cpu_kinetis_common
|
||||
* @brief RTC is clocked by a 32.768 kHz clock.
|
||||
* Please note the manual of your MCU or SiP for the
|
||||
* clock setting for the RTC module. After initilization
|
||||
* Time Seconds Register (TSR) increments once a second.
|
||||
* The TSR (also TAR) value will be converted to the stuct tm
|
||||
* and back with the help of stdlib functions that are
|
||||
* defined in time.h.
|
||||
* The driver supports alarm, it is stored in the
|
||||
* Time Alarm Registers (TAR) and the unit is seconds.
|
||||
*
|
||||
* ### RTC Configuration Example (for periph_conf.h) ###
|
||||
*
|
||||
* #define RTC_NUMOF (1U)
|
||||
* #define RTC_DEV RTC
|
||||
* #define RTC_UNLOCK() (SIM->SCGC6 |= (SIM_SCGC6_RTC_MASK))
|
||||
*
|
||||
* Optional settings:
|
||||
*
|
||||
* #define RTC_LOAD_CAP_BITS 0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup cpu_kinetis_common_spi Kinetis SPI
|
||||
* @ingroup cpu_kinetis_common
|
||||
* @brief Kinetis SPI driver for MCUs with Cortex-M4 core.
|
||||
*
|
||||
* If necessary, it is possible to define two RIOT SPI buses for
|
||||
* each Kinetis hardware SPI module by specifying different CTAS
|
||||
* (timing register number) for the two buses. It is then possible to
|
||||
* initialize the two RIOT SPI buses with different baud rates or
|
||||
* polarity settings.
|
||||
*
|
||||
* SPI_x_INDEX should be set to the index on the hardware module
|
||||
* used (SPI0 => 0, SPI1 => 1 etc). spi_acquire and spi_release will
|
||||
* share the same lock for all SPI buses defined with the same
|
||||
* SPI_x_INDEX.
|
||||
*
|
||||
* Finer tuning of timings than the RIOT SPI API is capable of is
|
||||
* supported by setting macros SPI_0_TCSC_FREQ, SPI_0_TASC_FREQ,
|
||||
* SPI_0_TDT_FREQ. These macros define the desired maximum frequency
|
||||
* of the t<SUB>CSC</SUB>, t<SUB>ASC</SUB>, and t<SUB>DT</SUB> SPI
|
||||
* timings (i.e. reciprocal of time). See the reference manual for
|
||||
* your Kinetis CPU (Chapter: "SPI module, Functional description,
|
||||
* Module baud rate and clock delay generation") for a description of
|
||||
* each delay. Set to 0 or leave unset to default to using the same
|
||||
* delay timing as the baudrate.
|
||||
*
|
||||
* ### SPI Configuration Example (for periph_conf.h): ###
|
||||
*
|
||||
* // SPI 0 device config
|
||||
* #define SPI_0_DEV SPI0
|
||||
* #define SPI_0_INDEX 0
|
||||
* #define SPI_0_CTAS 0
|
||||
* #define SPI_0_CLKEN() (SIM->SCGC6 |= (SIM_SCGC6_SPI0_MASK))
|
||||
* #define SPI_0_CLKDIS() (SIM->SCGC6 &= ~(SIM_SCGC6_SPI0_MASK))
|
||||
* #define SPI_0_IRQ SPI0_IRQn
|
||||
* #define SPI_0_IRQ_HANDLER isr_spi0
|
||||
* #define SPI_0_FREQ (48e6)
|
||||
*
|
||||
* // SPI 0 pin configuration
|
||||
* #define SPI_0_SCK_PORT PORTC
|
||||
* #define SPI_0_SOUT_PORT PORTC
|
||||
* #define SPI_0_SIN_PORT PORTC
|
||||
* #define SPI_0_PCS0_PORT PORTC
|
||||
*
|
||||
* #define SPI_0_SCK_PORT_CLKEN() (SIM->SCGC5 |= (SIM_SCGC5_PORTC_MASK))
|
||||
* #define SPI_0_SOUT_PORT_CLKEN() (SIM->SCGC5 |= (SIM_SCGC5_PORTC_MASK))
|
||||
* #define SPI_0_SIN_PORT_CLKEN() (SIM->SCGC5 |= (SIM_SCGC5_PORTC_MASK))
|
||||
* #define SPI_0_PCS0_PORT_CLKEN() (SIM->SCGC5 |= (SIM_SCGC5_PORTC_MASK))
|
||||
*
|
||||
* #define SPI_0_SCK_AF 2
|
||||
* #define SPI_0_SOUT_AF 2
|
||||
* #define SPI_0_SIN_AF 2
|
||||
* #define SPI_0_PCS0_AF 2
|
||||
*
|
||||
* #define SPI_0_PCS0_PIN 4
|
||||
* #define SPI_0_SCK_PIN 5
|
||||
* #define SPI_0_SOUT_PIN 6
|
||||
* #define SPI_0_SIN_PIN 7
|
||||
*
|
||||
* #define SPI_0_PCS0_ACTIVE_LOW 1
|
||||
*
|
||||
* Alternative Configuration Example:
|
||||
*
|
||||
* // SPI 0 device config
|
||||
* #define SPI_0_DEV SPI0
|
||||
* #define SPI_0_INDEX 0
|
||||
* #define SPI_0_CTAS 0
|
||||
* #define SPI_0_CLKEN() (SIM->SCGC6 |= (SIM_SCGC6_SPI0_MASK))
|
||||
* #define SPI_0_CLKDIS() (SIM->SCGC6 &= ~(SIM_SCGC6_SPI0_MASK))
|
||||
* #define SPI_0_IRQ SPI0_IRQn
|
||||
* #define SPI_0_IRQ_HANDLER isr_spi0
|
||||
* #define SPI_0_FREQ (48e6)
|
||||
*
|
||||
* // SPI 0 pin configuration
|
||||
* #define SPI_0_PORT PORTC
|
||||
* #define SPI_0_PORT_CLKEN() (SIM->SCGC5 |= (SIM_SCGC5_PORTC_MASK))
|
||||
* #define SPI_0_AF 2
|
||||
*
|
||||
* #define SPI_0_PCS0_PIN 4
|
||||
* #define SPI_0_SCK_PIN 5
|
||||
* #define SPI_0_SOUT_PIN 6
|
||||
* #define SPI_0_SIN_PIN 7
|
||||
*
|
||||
* #define SPI_0_PCS0_ACTIVE_LOW 1
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup cpu_kinetis_common_timer Kinetis Timer
|
||||
* @ingroup cpu_kinetis_common
|
||||
* @brief Periodic Interrupt Timer (PIT) driver.
|
||||
* Implementation of riot-os low level timer interface
|
||||
* for the Kinetis Periodic Interrupt Timer.
|
||||
* The PIT is a count down timer, in order to use it with riot-os
|
||||
* a count up timer will be simulated. The PIT has four channels,
|
||||
* each two channels are cascaded. The n-1 channel is a prescaler
|
||||
* and the n channel a down counter. In standard configuration
|
||||
* with four channels, two simulated count up timer are possible.
|
||||
*
|
||||
* ### Timer configuration Example (for periph_conf.h) ###
|
||||
*
|
||||
* #define TIMER_NUMOF (1U)
|
||||
* #define TIMER_0_EN 1
|
||||
* #define TIMER_1_EN 0
|
||||
* #define TIMER_IRQ_PRIO 1
|
||||
* #define TIMER_DEV PIT
|
||||
* #define TIMER_MAX_VALUE (0xffffffff)
|
||||
* #define TIMER_CLOCK CLOCK_CORECLOCK
|
||||
* #define TIMER_CLKEN() (SIM->SCGC6 |= (SIM_SCGC6_PIT_MASK))
|
||||
*
|
||||
* // Timer 0 configuration
|
||||
* #define TIMER_0_PRESCALER_CH 0
|
||||
* #define TIMER_0_COUNTER_CH 1
|
||||
* #define TIMER_0_ISR isr_pit1
|
||||
* #define TIMER_0_IRQ_CHAN PIT1_IRQn
|
||||
*
|
||||
* // Timer 1 configuration
|
||||
* #define TIMER_1_PRESCALER_CH 2
|
||||
* #define TIMER_1_COUNTER_CH 3
|
||||
* #define TIMER_1_ISR isr_pit3
|
||||
* #define TIMER_1_IRQ_CHAN PIT3_IRQn
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup cpu_kinetis_common_uart Kinetis UART
|
||||
* @ingroup cpu_kinetis_common
|
||||
* @brief Kinetis UART driver.
|
||||
* There are different implementations of the UART interface.
|
||||
* The treatment of interrupts is also slightly different.
|
||||
* The register UARTx_BDH to UARTx_C4 look almost the same.
|
||||
* We distinguish the type of the UART
|
||||
* using the BRFA field in the UART C4 register.
|
||||
* Currently, only the base functionality is available.
|
||||
*
|
||||
* ### UART configuration Example (for periph_conf.h) ###
|
||||
*
|
||||
* #define UART_NUMOF (1U)
|
||||
* #define UART_0_EN 1
|
||||
* #define UART_IRQ_PRIO 1
|
||||
* #define UART_CLK (48e6)
|
||||
*
|
||||
* // UART 0 device configuration
|
||||
* #define KINETIS_UART UART0_Type
|
||||
* #define UART_0_DEV UART0
|
||||
* #define UART_0_CLKEN() (SIM->SCGC4 |= (SIM_SCGC4_UART0_MASK))
|
||||
* #define UART_0_CLK UART_CLK
|
||||
* #define UART_0_IRQ_CHAN UART0_IRQn
|
||||
* #define UART_0_ISR isr_uart0
|
||||
* // UART 0 pin configuration
|
||||
* #define UART_0_PORT_CLKEN() (SIM->SCGC5 |= (SIM_SCGC5_PORTA_MASK))
|
||||
* #define UART_0_PORT PORTA
|
||||
* #define UART_0_RX_PIN 1
|
||||
* #define UART_0_TX_PIN 2
|
||||
* #define UART_0_AF 2
|
||||
*
|
||||
* Optional settings:
|
||||
*
|
||||
* #define KINETIS_UART_ADVANCED 1
|
||||
*/
|
3520
cpu/kinetis_common/gpio.c
Normal file
3520
cpu/kinetis_common/gpio.c
Normal file
File diff suppressed because it is too large
Load Diff
223
cpu/kinetis_common/hwtimer_arch.c
Normal file
223
cpu/kinetis_common/hwtimer_arch.c
Normal file
@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright (C) 2014 PHYTEC Messtechnik GmbH
|
||||
*
|
||||
* 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 cpu_kinetis_common_hwtimer
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of the kernels hwtimer interface.
|
||||
* hwtimer uses Freescale Low Power Timer lptmr0.
|
||||
*
|
||||
* @author Johann Fischer <j.fischer@phytec.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "arch/hwtimer_arch.h"
|
||||
#include "hwtimer_cpu.h"
|
||||
#include "cpu-conf.h"
|
||||
#include "thread.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#define LPTMR_MAXTICKS (0x0000FFFF)
|
||||
|
||||
#ifndef LPTIMER_CNR_NEEDS_LATCHING
|
||||
#warning LPTIMER_CNR_NEEDS_LATCHING is not defined in cpu-conf.h! Defaulting to 1
|
||||
#define LPTIMER_CNR_NEEDS_LATCHING 1
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t counter32b;
|
||||
uint32_t cmr32b;
|
||||
uint32_t diff;
|
||||
} hwtimer_stimer32b_t;
|
||||
|
||||
static hwtimer_stimer32b_t stimer;
|
||||
|
||||
/**
|
||||
* @brief Reference to the hwtimer callback
|
||||
*/
|
||||
void (*timeout_handler)(int);
|
||||
|
||||
inline static uint32_t lptmr_get_cnr(void)
|
||||
{
|
||||
#if LPTIMER_CNR_NEEDS_LATCHING
|
||||
/* Write some garbage to CNR to latch the current value */
|
||||
LPTIMER_DEV->CNR = 42;
|
||||
return (uint32_t)LPTIMER_DEV->CNR;
|
||||
#else
|
||||
/* The early revisions of the Kinetis CPUs do not need latching of the CNR
|
||||
* register. However, this may lead to corrupt values, we read it twice to
|
||||
* ensure that we got a valid value */
|
||||
int i;
|
||||
uint32_t tmp;
|
||||
uint32_t cnr = LPTIMER_DEV->CNR;
|
||||
|
||||
/* you get three retries */
|
||||
for (i = 0; i < 3; ++i) {
|
||||
tmp = LPTIMER_DEV->CNR;
|
||||
|
||||
if (tmp == cnr) {
|
||||
return cnr;
|
||||
}
|
||||
|
||||
cnr = tmp;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
inline static void hwtimer_start(void)
|
||||
{
|
||||
LPTIMER_DEV->CSR |= LPTMR_CSR_TEN_MASK;
|
||||
}
|
||||
|
||||
inline static void hwtimer_stop(void)
|
||||
{
|
||||
LPTIMER_DEV->CSR &= ~LPTMR_CSR_TEN_MASK;
|
||||
}
|
||||
|
||||
void hwtimer_arch_init(void (*handler)(int), uint32_t fcpu)
|
||||
{
|
||||
timeout_handler = handler;
|
||||
|
||||
/* unlock LPTIMER_DEV */
|
||||
LPTIMER_CLKEN();
|
||||
/* set lptmr's IRQ priority */
|
||||
NVIC_SetPriority(LPTIMER_IRQ_CHAN, LPTIMER_IRQ_PRIO);
|
||||
/* reset lptmr */
|
||||
LPTIMER_DEV->CSR = 0;
|
||||
|
||||
switch (LPTIMER_CLKSRC) {
|
||||
case LPTIMER_CLKSRC_MCGIRCLK:
|
||||
/* Select MCGIRCLK as clock source */
|
||||
LPTIMER_DEV->PSR = LPTMR_PSR_PRESCALE(LPTIMER_CLK_PRESCALE) | LPTMR_PSR_PCS(0);
|
||||
break;
|
||||
|
||||
case LPTIMER_CLKSRC_OSCERCLK:
|
||||
/* Select OSCERCLK(4 MHz) as clock source */
|
||||
LPTIMER_DEV->PSR = LPTMR_PSR_PRESCALE(LPTIMER_CLK_PRESCALE) | LPTMR_PSR_PCS(3);
|
||||
break;
|
||||
|
||||
case LPTIMER_CLKSRC_ERCLK32K:
|
||||
/* Select rtc oscillator output as clock source for ERCLK32K, */
|
||||
/* it needs functioning RTC module and driver. */
|
||||
SIM->SOPT1 &= ~(SIM_SOPT1_OSC32KSEL_MASK);
|
||||
SIM->SOPT1 |= SIM_SOPT1_OSC32KSEL(2);
|
||||
/* select ERCLK32K as clock source for lptmr0 */
|
||||
LPTIMER_DEV->PSR = LPTMR_PSR_PBYP_MASK | LPTMR_PSR_PCS(2);
|
||||
break;
|
||||
|
||||
case LPTIMER_CLKSRC_LPO:
|
||||
default:
|
||||
/* select LPO as clock source (1 kHz)*/
|
||||
LPTIMER_DEV->PSR = LPTMR_PSR_PBYP_MASK | LPTMR_PSR_PCS(1);
|
||||
}
|
||||
|
||||
LPTIMER_DEV->CMR = (uint16_t)(LPTMR_MAXTICKS);
|
||||
/* enable lptrm interrupt */
|
||||
LPTIMER_DEV->CSR = LPTMR_CSR_TIE_MASK;
|
||||
|
||||
stimer.counter32b = 0;
|
||||
stimer.cmr32b = 0;
|
||||
stimer.diff = 0;
|
||||
|
||||
hwtimer_arch_enable_interrupt();
|
||||
hwtimer_start();
|
||||
}
|
||||
|
||||
void hwtimer_arch_enable_interrupt(void)
|
||||
{
|
||||
NVIC_EnableIRQ(LPTIMER_IRQ_CHAN);
|
||||
}
|
||||
|
||||
void hwtimer_arch_disable_interrupt(void)
|
||||
{
|
||||
NVIC_DisableIRQ(LPTIMER_IRQ_CHAN);
|
||||
}
|
||||
|
||||
void hwtimer_arch_set(unsigned long offset, short timer)
|
||||
{
|
||||
(void)timer;
|
||||
stimer.counter32b += lptmr_get_cnr();
|
||||
hwtimer_stop();
|
||||
|
||||
stimer.cmr32b = stimer.counter32b + offset;
|
||||
stimer.diff = offset;
|
||||
|
||||
if (stimer.diff > LPTMR_MAXTICKS) {
|
||||
stimer.diff = LPTMR_MAXTICKS;
|
||||
}
|
||||
|
||||
DEBUG("cntr: %lu, cmr: %lu, diff: %lu\n", stimer.counter32b, stimer.cmr32b, stimer.diff);
|
||||
|
||||
LPTIMER_DEV->CMR = (uint16_t)(stimer.diff);
|
||||
hwtimer_start();
|
||||
}
|
||||
|
||||
void hwtimer_arch_set_absolute(unsigned long value, short timer)
|
||||
{
|
||||
(void)timer;
|
||||
stimer.counter32b += lptmr_get_cnr();
|
||||
hwtimer_stop();
|
||||
|
||||
stimer.cmr32b = value;
|
||||
stimer.diff = stimer.cmr32b - stimer.counter32b;
|
||||
|
||||
if (stimer.diff > LPTMR_MAXTICKS) {
|
||||
stimer.diff = LPTMR_MAXTICKS;
|
||||
}
|
||||
|
||||
DEBUG("cntr: %lu, cmr: %lu, diff: %lu\n", stimer.counter32b, stimer.cmr32b, stimer.diff);
|
||||
|
||||
LPTIMER_DEV->CMR = (uint16_t)(stimer.diff);
|
||||
hwtimer_start();
|
||||
}
|
||||
|
||||
void hwtimer_arch_unset(short timer)
|
||||
{
|
||||
stimer.counter32b += lptmr_get_cnr();
|
||||
hwtimer_stop();
|
||||
stimer.diff = 0;
|
||||
stimer.cmr32b = 0;
|
||||
LPTIMER_DEV->CMR = (uint16_t)(LPTMR_MAXTICKS);
|
||||
hwtimer_start();
|
||||
|
||||
}
|
||||
|
||||
unsigned long hwtimer_arch_now(void)
|
||||
{
|
||||
return (unsigned int)((lptmr_get_cnr() + stimer.counter32b));
|
||||
}
|
||||
|
||||
void isr_lptmr0(void)
|
||||
{
|
||||
stimer.counter32b += (uint32_t)LPTIMER_DEV->CMR;
|
||||
/* clear compare flag (w1c bit) */
|
||||
LPTIMER_DEV->CSR |= LPTMR_CSR_TCF_MASK;
|
||||
|
||||
if (stimer.diff) {
|
||||
if (stimer.cmr32b > stimer.counter32b) {
|
||||
hwtimer_arch_set_absolute(stimer.cmr32b, 0);
|
||||
}
|
||||
else {
|
||||
stimer.diff = 0;
|
||||
timeout_handler((short)0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
hwtimer_arch_unset(0);
|
||||
}
|
||||
|
||||
if (sched_context_switch_request) {
|
||||
thread_yield();
|
||||
}
|
||||
}
|
436
cpu/kinetis_common/i2c.c
Normal file
436
cpu/kinetis_common/i2c.c
Normal file
@ -0,0 +1,436 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Freie Universität Berlin
|
||||
* Copyright (C) 2014 PHYTEC Messtechnik GmbH
|
||||
*
|
||||
* 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 cpu_kinetis_common_i2c
|
||||
*
|
||||
* @note This driver only implements the 7-bit addressing master mode.
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level I2C driver implementation
|
||||
*
|
||||
* @author Johann Fischer <j.fischer@phytec.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "irq.h"
|
||||
#include "mutex.h"
|
||||
#include "periph_conf.h"
|
||||
#include "periph/i2c.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
/* guard file in case no I2C device is defined */
|
||||
#if I2C_NUMOF
|
||||
|
||||
/**
|
||||
* @brief Array holding one pre-initialized mutex for each I2C device
|
||||
*/
|
||||
static mutex_t locks[] = {
|
||||
#if I2C_0_EN
|
||||
[I2C_0] = MUTEX_INIT,
|
||||
#endif
|
||||
#if I2C_1_EN
|
||||
[I2C_1] = MUTEX_INIT,
|
||||
#endif
|
||||
#if I2C_2_EN
|
||||
[I2C_2] = MUTEX_INIT
|
||||
#endif
|
||||
#if I2C_3_EN
|
||||
[I2C_3] = MUTEX_INIT
|
||||
#endif
|
||||
};
|
||||
|
||||
int i2c_acquire(i2c_t dev)
|
||||
{
|
||||
if (dev >= I2C_NUMOF) {
|
||||
return -1;
|
||||
}
|
||||
mutex_lock(&locks[dev]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_release(i2c_t dev)
|
||||
{
|
||||
if (dev >= I2C_NUMOF) {
|
||||
return -1;
|
||||
}
|
||||
mutex_unlock(&locks[dev]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_init_master(i2c_t dev, i2c_speed_t speed)
|
||||
{
|
||||
I2C_Type *i2c;
|
||||
PORT_Type *i2c_port;
|
||||
int pin_scl = 0;
|
||||
int pin_sda = 0;
|
||||
|
||||
/* TODO: read speed configuration */
|
||||
switch (speed) {
|
||||
case I2C_SPEED_NORMAL:
|
||||
break;
|
||||
|
||||
case I2C_SPEED_FAST:
|
||||
break;
|
||||
|
||||
default:
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* read static device configuration */
|
||||
switch (dev) {
|
||||
#if I2C_0_EN
|
||||
|
||||
case I2C_0:
|
||||
i2c = I2C_0_DEV;
|
||||
i2c_port = I2C_0_PORT;
|
||||
pin_scl = I2C_0_SCL_PIN;
|
||||
pin_sda = I2C_0_SDA_PIN;
|
||||
I2C_0_CLKEN();
|
||||
I2C_0_PORT_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* configure pins, alternate output */
|
||||
i2c_port->PCR[pin_scl] = I2C_0_PORT_CFG;
|
||||
i2c_port->PCR[pin_sda] = I2C_0_PORT_CFG;
|
||||
|
||||
/*
|
||||
* TODO: Add baud rate selection function
|
||||
* See the Chapter "I2C divider and hold values":
|
||||
* Kinetis K60 Reference Manual, section 51.4.1.10, Table 51-41.
|
||||
* Kinetis MKW2x Reference Manual, section 52.4.1.10, Table 52-41.
|
||||
*
|
||||
* baud rate = I2C_module_clock / (mul × ICR)
|
||||
*
|
||||
* The assignment below will set baud rate to I2C_module_clock / (240 x 2).
|
||||
*/
|
||||
i2c->F = I2C_F_MULT(1) | I2C_F_ICR(0x1f);
|
||||
|
||||
/* enable i2c-module and interrupt */
|
||||
i2c->C1 = I2C_C1_IICEN_MASK | I2C_C1_IICIE_MASK | I2C_C1_TXAK_MASK;
|
||||
i2c->C2 = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_init_slave(i2c_t dev, uint8_t address)
|
||||
{
|
||||
/* TODO: implement slave mode */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int _i2c_start(I2C_Type *dev, uint8_t address, uint8_t rw_flag)
|
||||
{
|
||||
/* bus free ? */
|
||||
if (dev->S & I2C_S_BUSY_MASK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev->S = I2C_S_IICIF_MASK;
|
||||
|
||||
dev->C1 = I2C_C1_IICEN_MASK | I2C_C1_MST_MASK | I2C_C1_TX_MASK;
|
||||
dev->D = address << 1 | (rw_flag & 1);
|
||||
|
||||
/* wait for bus-busy to be set */
|
||||
while (!(dev->S & I2C_S_BUSY_MASK));
|
||||
|
||||
/* wait for address transfer to complete */
|
||||
while (!(dev->S & I2C_S_IICIF_MASK));
|
||||
|
||||
dev->S = I2C_S_IICIF_MASK;
|
||||
|
||||
/* check for receive acknowledge */
|
||||
if (dev->S & I2C_S_RXAK_MASK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int _i2c_restart(I2C_Type *dev, uint8_t address, uint8_t rw_flag)
|
||||
{
|
||||
/* put master in rx mode and repeat start */
|
||||
dev->C1 |= I2C_C1_RSTA_MASK;
|
||||
dev->D = address << 1 | (rw_flag & 1);
|
||||
|
||||
/* wait for address transfer to complete */
|
||||
while (!(dev->S & I2C_S_IICIF_MASK));
|
||||
|
||||
dev->S = I2C_S_IICIF_MASK;
|
||||
|
||||
/* check for receive acknowledge */
|
||||
if (dev->S & I2C_S_RXAK_MASK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int _i2c_receive(I2C_Type *dev, uint8_t *data, int length)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
/* set receive mode */
|
||||
dev->C1 = I2C_C1_IICEN_MASK | I2C_C1_MST_MASK;
|
||||
|
||||
if (length == 1) {
|
||||
/* no ack signal */
|
||||
dev->C1 |= I2C_C1_TXAK_MASK;
|
||||
}
|
||||
|
||||
/* dummy read */
|
||||
dev->D;
|
||||
|
||||
while (length > 0) {
|
||||
while (!(dev->S & I2C_S_IICIF_MASK));
|
||||
|
||||
dev->S = I2C_S_IICIF_MASK;
|
||||
|
||||
if (length == 2) {
|
||||
/* no ack signal is sent on the following receiving byte */
|
||||
dev->C1 |= I2C_C1_TXAK_MASK;
|
||||
}
|
||||
|
||||
if (length == 1) {
|
||||
/* generate stop */
|
||||
dev->C1 &= ~I2C_C1_MST_MASK;
|
||||
}
|
||||
|
||||
data[n] = (char)dev->D;
|
||||
length--;
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static inline int _i2c_transmit(I2C_Type *dev, uint8_t *data, int length)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
while (length > 0) {
|
||||
dev->D = data[n];
|
||||
|
||||
while (!(dev->S & I2C_S_IICIF_MASK));
|
||||
|
||||
dev->S = I2C_S_IICIF_MASK;
|
||||
|
||||
if (dev->S & I2C_S_RXAK_MASK) {
|
||||
return n;
|
||||
}
|
||||
|
||||
n++;
|
||||
length--;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static inline void _i2c_stop(I2C_Type *dev)
|
||||
{
|
||||
/* put bus in idle state */
|
||||
dev->C1 = I2C_C1_IICEN_MASK;
|
||||
|
||||
/* wait for bus idle */
|
||||
while (dev->S & I2C_S_BUSY_MASK);
|
||||
}
|
||||
|
||||
|
||||
int i2c_read_byte(i2c_t dev, uint8_t address, char *data)
|
||||
{
|
||||
return i2c_read_bytes(dev, address, data, 1);
|
||||
}
|
||||
|
||||
int i2c_read_bytes(i2c_t dev, uint8_t address, char *data, int length)
|
||||
{
|
||||
I2C_Type *i2c;
|
||||
int n = 0;
|
||||
|
||||
switch (dev) {
|
||||
#if I2C_0_EN
|
||||
|
||||
case I2C_0:
|
||||
i2c = I2C_0_DEV;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_i2c_start(i2c, address, I2C_FLAG_READ)) {
|
||||
_i2c_stop(i2c);
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = _i2c_receive(i2c, (uint8_t *)data, length);
|
||||
_i2c_stop(i2c);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int i2c_write_byte(i2c_t dev, uint8_t address, char data)
|
||||
{
|
||||
return i2c_write_bytes(dev, address, &data, 1);
|
||||
}
|
||||
|
||||
int i2c_write_bytes(i2c_t dev, uint8_t address, char *data, int length)
|
||||
{
|
||||
I2C_Type *i2c;
|
||||
int n = 0;
|
||||
|
||||
switch (dev) {
|
||||
#if I2C_0_EN
|
||||
|
||||
case I2C_0:
|
||||
i2c = I2C_0_DEV;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_i2c_start(i2c, address, I2C_FLAG_WRITE)) {
|
||||
_i2c_stop(i2c);
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = _i2c_transmit(i2c, (uint8_t *)data, length);
|
||||
_i2c_stop(i2c);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int i2c_read_reg(i2c_t dev, uint8_t address, uint8_t reg, char *data)
|
||||
{
|
||||
return i2c_read_regs(dev, address, reg, data, 1);
|
||||
|
||||
}
|
||||
|
||||
int i2c_read_regs(i2c_t dev, uint8_t address, uint8_t reg, char *data, int length)
|
||||
{
|
||||
I2C_Type *i2c;
|
||||
int n = 0;
|
||||
|
||||
switch (dev) {
|
||||
#if I2C_0_EN
|
||||
|
||||
case I2C_0:
|
||||
i2c = I2C_0_DEV;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_i2c_start(i2c, address, I2C_FLAG_WRITE)) {
|
||||
_i2c_stop(i2c);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* send reg */
|
||||
n = _i2c_transmit(i2c, ®, 1);
|
||||
|
||||
if (!n) {
|
||||
_i2c_stop(i2c);
|
||||
return n;
|
||||
}
|
||||
|
||||
if (_i2c_restart(i2c, address, I2C_FLAG_READ)) {
|
||||
_i2c_stop(i2c);
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = _i2c_receive(i2c, (uint8_t *)data, length);
|
||||
_i2c_stop(i2c);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
int i2c_write_reg(i2c_t dev, uint8_t address, uint8_t reg, char data)
|
||||
{
|
||||
return i2c_write_regs(dev, address, reg, &data, 1);
|
||||
}
|
||||
|
||||
int i2c_write_regs(i2c_t dev, uint8_t address, uint8_t reg, char *data, int length)
|
||||
{
|
||||
I2C_Type *i2c;
|
||||
int n = 0;
|
||||
|
||||
switch (dev) {
|
||||
#if I2C_0_EN
|
||||
|
||||
case I2C_0:
|
||||
i2c = I2C_0_DEV;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_i2c_start(i2c, address, I2C_FLAG_WRITE)) {
|
||||
_i2c_stop(i2c);
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = _i2c_transmit(i2c, ®, 1);
|
||||
|
||||
if (!n) {
|
||||
_i2c_stop(i2c);
|
||||
return n;
|
||||
}
|
||||
|
||||
n = _i2c_transmit(i2c, (uint8_t *)data, length);
|
||||
_i2c_stop(i2c);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void i2c_poweron(i2c_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if I2C_0_EN
|
||||
|
||||
case I2C_0:
|
||||
I2C_0_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void i2c_poweroff(i2c_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if I2C_0_EN
|
||||
|
||||
case I2C_0:
|
||||
I2C_0_CLKDIS();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* I2C_NUMOF */
|
113
cpu/kinetis_common/include/hwtimer_cpu.h
Normal file
113
cpu/kinetis_common/include/hwtimer_cpu.h
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2015 PHYTEC Messtechnik GmbH
|
||||
*
|
||||
* 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_common_hwtimer Kinetis hwtimer
|
||||
* @ingroup cpu_kinetis_common
|
||||
* @brief Driver uses Freescale Low Power Timer lptmr0.
|
||||
* There are four clock sources available:
|
||||
*
|
||||
* LPTIMER_CLKSRC_MCGIRCLK - slow or fast internal reference clock
|
||||
* LPTIMER_CLKSRC_LPO - PMC 1kHz output
|
||||
* LPTIMER_CLKSRC_ERCLK32K - OSC32KCLK or the RTC clock
|
||||
* LPTIMER_CLKSRC_OSCERCLK - system oscillator output
|
||||
*
|
||||
* Tested clock sources:
|
||||
*
|
||||
* LPO - 1kHz
|
||||
* RTC - 32768Hz
|
||||
*
|
||||
* Possibly, additional settings in System Integration Module are necessary.
|
||||
* Please consult the Reference Manual of your MCU for proper clock settings.
|
||||
*
|
||||
* ### LPTMR Configuration Example (for cpu-conf.h) ###
|
||||
*
|
||||
* #define LPTIMER_CLKSRC LPTIMER_CLKSRC_LPO
|
||||
* #define LPTIMER_CLKEN() (SIM->SCGC5 |= SIM_SCGC5_LPTMR_MASK)
|
||||
* #define LPTIMER_CLKDIS() (SIM->SCGC5 &= ~SIM_SCGC5_PTMR_MASK)
|
||||
* #define LPTIMER_CNR_NEEDS_LATCHING 1
|
||||
*
|
||||
* Optional settings:
|
||||
*
|
||||
* #define LPTIMER_DEV LPTMR0
|
||||
* #define LPTIMER_IRQ_PRIO 1
|
||||
* #define LPTIMER_IRQ_CHAN LPTMR0_IRQn
|
||||
* @{
|
||||
|
||||
* @file
|
||||
* @brief Interface definition for the Kinetis hwtimer driver.
|
||||
*
|
||||
* @author Johann Fischer <j.fischer@phytec.de>
|
||||
*/
|
||||
|
||||
#ifndef __HWTIMER_CPU_H
|
||||
#define __HWTIMER_CPU_H
|
||||
|
||||
#include "cpu-conf.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name Clock settings for the lptmr timer
|
||||
*/
|
||||
#define LPTIMER_CLKSRC_MCGIRCLK 0 /**< internal reference clock (4MHz) */
|
||||
#define LPTIMER_CLKSRC_LPO 1 /**< PMC 1kHz output */
|
||||
#define LPTIMER_CLKSRC_ERCLK32K 2 /**< RTC clock 32768Hz */
|
||||
#define LPTIMER_CLKSRC_OSCERCLK 3 /**< system oscillator output, clock from RF-Part */
|
||||
|
||||
#ifndef LPTIMER_CLKSRC
|
||||
#define LPTIMER_CLKSRC LPTIMER_CLKSRC_LPO /**< default clock source */
|
||||
#endif
|
||||
|
||||
#if (LPTIMER_CLKSRC == LPTIMER_CLKSRC_MCGIRCLK)
|
||||
#define LPTIMER_CLK_PRESCALE 1
|
||||
#define LPTIMER_SPEED 1000000
|
||||
#elif (LPTIMER_CLKSRC == LPTIMER_CLKSRC_OSCERCLK)
|
||||
#define LPTIMER_CLK_PRESCALE 1
|
||||
#define LPTIMER_SPEED 1000000
|
||||
#elif (LPTIMER_CLKSRC == LPTIMER_CLKSRC_ERCLK32K)
|
||||
#define LPTIMER_CLK_PRESCALE 0
|
||||
#define LPTIMER_SPEED 32768
|
||||
#else
|
||||
#define LPTIMER_CLK_PRESCALE 0
|
||||
#define LPTIMER_SPEED 1000
|
||||
#endif
|
||||
|
||||
#ifndef LPTIMER_DEV
|
||||
/** default Low Power Timer device */
|
||||
#define LPTIMER_DEV LPTMR0
|
||||
#endif
|
||||
|
||||
#ifndef LPTIMER_IRQ_PRIO
|
||||
/** IRQ priority for hwtimer interrupts */
|
||||
#define LPTIMER_IRQ_PRIO 1
|
||||
#endif
|
||||
|
||||
#ifndef LPTIMER_IRQ_CHAN
|
||||
/** IRQ channel for hwtimer interrupts */
|
||||
#define LPTIMER_IRQ_CHAN LPTMR0_IRQn
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name Hardware timer configuration
|
||||
* @{
|
||||
*/
|
||||
#define HWTIMER_MAXTIMERS 1 /**< the CPU implementation supports 1 HW timer */
|
||||
#define HWTIMER_MAXTICKS (0xFFFFFFFF) /**< simulating 32-bit timer behavior */
|
||||
#define HWTIMER_SPEED LPTIMER_SPEED /**< speed depends on clock source and prescale */
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __HWTIMER_CPU_H */
|
||||
/** @} */
|
137
cpu/kinetis_common/include/mcg.h
Normal file
137
cpu/kinetis_common/include/mcg.h
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2015 PHYTEC Messtechnik GmbH
|
||||
*
|
||||
* 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_common_mcg Kinetis MCG
|
||||
* @ingroup cpu_kinetis_common
|
||||
* @brief Implementation of the Kinetis Multipurpose Clock Generator
|
||||
* (MCG) driver.
|
||||
* Please add mcg.h in cpu conf.h
|
||||
* and MCG configuration to periph_conf.h
|
||||
*
|
||||
* MCG neighbor modes matrix:
|
||||
*
|
||||
* x | FEI | FEE | FBI | FBE | BLPI | BLPE | PBE | PEE
|
||||
* :---:|:---:|:---:|:---:|:---:|:----:|:----:|:---:|:---:
|
||||
* PEE | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0
|
||||
* PBE | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1
|
||||
* BLPE | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0
|
||||
* BLPI | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0
|
||||
* FBE | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 0
|
||||
* FBI | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 0
|
||||
* FEE | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0
|
||||
* FEI | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0
|
||||
*
|
||||
* Each neighbor mode can be selected directly.
|
||||
* Further, the paths between the following modes are defined:
|
||||
* - FEI -> PEE
|
||||
* - BLPE -> PEE
|
||||
* - PEE -> BLPE
|
||||
* - FEE -> BLPE
|
||||
* - FEE -> BLPI
|
||||
* - BLPE -> FEE
|
||||
* - BLPI -> FEE
|
||||
*
|
||||
* ### MCG Configuration Examples (for periph_conf.h) ###
|
||||
*
|
||||
* Example for FEI Mode (MCGOUTCLK = 20MHz ... 25MHz):
|
||||
*
|
||||
* #define KINETIS_MCG_USE_ERC 0
|
||||
* #define KINETIS_MCG_DCO_RANGE (24000000U)
|
||||
*
|
||||
*
|
||||
* Example for FEE Mode, 32768Hz Crystal,
|
||||
* (MCGOUTCLK = 24MHz):
|
||||
*
|
||||
* #define KINETIS_MCG_USE_ERC 1
|
||||
* #define KINETIS_MCG_USE_PLL 0
|
||||
* #define KINETIS_MCG_DCO_RANGE (24000000U)
|
||||
* #define KINETIS_MCG_ERC_OSCILLATOR 1
|
||||
* #define KINETIS_MCG_ERC_FRDIV 0
|
||||
* #define KINETIS_MCG_ERC_RANGE 0
|
||||
* #define KINETIS_MCG_ERC_FREQ (32768U)
|
||||
*
|
||||
*
|
||||
* Example for FEE Mode, external 4MHz reference
|
||||
* (\f$MCGOUTCLK
|
||||
* = \frac{ERC_{\mathrm{freq}}\cdot FFL_{\mathrm{Factor}}}{FRDIV}
|
||||
* = \frac{4\,\mathrm{MHz}\cdot 732}{128} = 22.875\,\mathrm{MHz}\f$):
|
||||
*
|
||||
* #define KINETIS_MCG_USE_ERC 1
|
||||
* #define KINETIS_MCG_USE_PLL 0
|
||||
* #define KINETIS_MCG_DCO_RANGE (24000000U)
|
||||
* #define KINETIS_MCG_ERC_OSCILLATOR 0
|
||||
* #define KINETIS_MCG_ERC_FRDIV 2
|
||||
* #define KINETIS_MCG_ERC_RANGE 1
|
||||
* #define KINETIS_MCG_ERC_FREQ (4000000U)
|
||||
*
|
||||
* Example for PEE Mode, external 4MHz reference
|
||||
* (\f$MCGOUTCLK
|
||||
* = \frac{ERC_{\mathrm{freq}}\cdot VDIV0}{PRDIV}
|
||||
* = \frac{4\,\mathrm{MHz}\cdot 24}{2} = 48\,\mathrm{MHz}\f$):
|
||||
*
|
||||
* #define KINETIS_MCG_USE_ERC 1
|
||||
* #define KINETIS_MCG_USE_PLL 1
|
||||
* #define KINETIS_MCG_DCO_RANGE (24000000U)
|
||||
* #define KINETIS_MCG_ERC_OSCILLATOR 0
|
||||
* #define KINETIS_MCG_ERC_FRDIV 2
|
||||
* #define KINETIS_MCG_ERC_RANGE 1
|
||||
* #define KINETIS_MCG_ERC_FREQ (4000000U)
|
||||
* #define KINETIS_MCG_PLL_PRDIV 1
|
||||
* #define KINETIS_MCG_PLL_VDIV0 0
|
||||
* #define KINETIS_MCG_PLL_FREQ (48000000U)
|
||||
*
|
||||
*
|
||||
* The desired mode can be selected in cpu.c:cpu_clock_init()
|
||||
* with kinetis_mcg_set_mode(KINETIS_MCG_PEE);
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Interface definition for the Kinetis MCG driver.
|
||||
*
|
||||
* @author Johann Fischer <j.fischer@phytec.de>
|
||||
*/
|
||||
|
||||
#ifndef __MCG_CPU_H
|
||||
#define __MCG_CPU_H
|
||||
|
||||
#include "cpu-conf.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
#if KINETIS_CPU_USE_MCG
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
typedef enum kinetis_mcg_mode {
|
||||
KINETIS_MCG_PEE = 0, /**< PLL Engaged External Mode */
|
||||
KINETIS_MCG_PBE, /**< PLL Bypassed External Mode */
|
||||
KINETIS_MCG_BLPE, /**< FLL Bypassed Low Power External Mode */
|
||||
KINETIS_MCG_BLPI, /**< FLL Bypassed Low Power Internal Mode */
|
||||
KINETIS_MCG_FBE, /**< FLL Bypassed External Mode */
|
||||
KINETIS_MCG_FBI, /**< FLL Bypassed Internal Mode */
|
||||
KINETIS_MCG_FEE, /**< FLL Engaged External Mode */
|
||||
KINETIS_MCG_FEI, /**< FLL Engaged Internal Mode */
|
||||
} kinetis_mcg_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize the MCG
|
||||
*
|
||||
*/
|
||||
int kinetis_mcg_set_mode(kinetis_mcg_mode_t mode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __MCG_CPU_H */
|
||||
/** @} */
|
||||
|
||||
#endif /* KINETIS_CPU_USE_MCG */
|
484
cpu/kinetis_common/mcg.c
Normal file
484
cpu/kinetis_common/mcg.c
Normal file
@ -0,0 +1,484 @@
|
||||
/*
|
||||
* Copyright (C) 2015 PHYTEC Messtechnik GmbH
|
||||
*
|
||||
* 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 cpu_kinetis_common_mcg
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of the Kinetis Multipurpose Clock Generator
|
||||
*
|
||||
* @author Johann Fischer <j.fischer@phytec.de>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "mcg.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
#if KINETIS_CPU_USE_MCG
|
||||
|
||||
/* MCG neighbor modes matrix */
|
||||
static uint8_t mcg_pm[8] = {0x02, 0x15, 0x12, 0x20, 0xe6, 0xd8, 0xb0, 0xf0};
|
||||
|
||||
static uint8_t current_mode = KINETIS_MCG_FEI;
|
||||
|
||||
#define KINETIS_MCG_FLL_FACTOR_640 0
|
||||
#define KINETIS_MCG_FLL_FACTOR_732 (MCG_C4_DRST_DRS(0) | MCG_C4_DMX32_MASK)
|
||||
#define KINETIS_MCG_FLL_FACTOR_1280 (MCG_C4_DRST_DRS(1))
|
||||
#define KINETIS_MCG_FLL_FACTOR_1464 (MCG_C4_DRST_DRS(1) | MCG_C4_DMX32_MASK)
|
||||
#define KINETIS_MCG_FLL_FACTOR_1920 (MCG_C4_DRST_DRS(2))
|
||||
#define KINETIS_MCG_FLL_FACTOR_2197 (MCG_C4_DRST_DRS(2) | MCG_C4_DMX32_MASK)
|
||||
#define KINETIS_MCG_FLL_FACTOR_2560 (MCG_C4_DRST_DRS(3))
|
||||
#define KINETIS_MCG_FLL_FACTOR_2929 (MCG_C4_DRST_DRS(3) | MCG_C4_DMX32_MASK)
|
||||
|
||||
#ifndef KINETIS_MCG_DCO_RANGE
|
||||
#define KINETIS_MCG_DCO_RANGE (48000000U)
|
||||
#endif
|
||||
|
||||
/* Default DCO_RANGE should be defined in periph_conf.h */
|
||||
#if (KINETIS_MCG_DCO_RANGE == 24000000U)
|
||||
#define KINETIS_MCG_FLL_FACTOR_FEI KINETIS_MCG_FLL_FACTOR_640
|
||||
#define KINETIS_MCG_FLL_FACTOR_FEE KINETIS_MCG_FLL_FACTOR_732
|
||||
#elif (KINETIS_MCG_DCO_RANGE == 48000000U)
|
||||
#define KINETIS_MCG_FLL_FACTOR_FEI KINETIS_MCG_FLL_FACTOR_1280
|
||||
#define KINETIS_MCG_FLL_FACTOR_FEE KINETIS_MCG_FLL_FACTOR_1464
|
||||
#elif (KINETIS_MCG_DCO_RANGE == 72000000U)
|
||||
#define KINETIS_MCG_FLL_FACTOR_FEI KINETIS_MCG_FLL_FACTOR_1920
|
||||
#define KINETIS_MCG_FLL_FACTOR_FEE KINETIS_MCG_FLL_FACTOR_2197
|
||||
#elif (KINETIS_MCG_DCO_RANGE == 96000000U)
|
||||
#define KINETIS_MCG_FLL_FACTOR_FEI KINETIS_MCG_FLL_FACTOR_2560
|
||||
#define KINETIS_MCG_FLL_FACTOR_FEE KINETIS_MCG_FLL_FACTOR_2929
|
||||
#else
|
||||
#error "KINETIS_MCG_DCO_RANGE is wrong"
|
||||
#endif
|
||||
|
||||
#ifndef KINETIS_MCG_USE_FAST_IRC
|
||||
#define KINETIS_MCG_USE_FAST_IRC 0
|
||||
#endif
|
||||
|
||||
#if (KINETIS_MCG_USE_ERC == 0)
|
||||
#define KINETIS_MCG_USE_ERC 0
|
||||
#define KINETIS_MCG_USE_PLL 0
|
||||
#define KINETIS_MCG_ERC_FREQ 0
|
||||
#define KINETIS_MCG_ERC_FRDIV 0
|
||||
#define KINETIS_MCG_ERC_RANGE 0
|
||||
#define KINETIS_MCG_ERC_OSCILLATOR 0
|
||||
#endif
|
||||
|
||||
#if (KINETIS_MCG_USE_PLL == 0)
|
||||
#define KINETIS_MCG_PLL_PRDIV 0
|
||||
#define KINETIS_MCG_PLL_VDIV0 0
|
||||
#define KINETIS_MCG_PLL_FREQ 0
|
||||
#endif
|
||||
|
||||
#ifndef KINETIS_MCG_OSC_CLC
|
||||
#define KINETIS_MCG_OSC_CLC 0
|
||||
#endif
|
||||
|
||||
#ifndef KINETIS_MCG_ERC_OSCILLATOR
|
||||
#error "KINETIS_MCG_ERC_OSCILLATOR not defined in periph_conf.h"
|
||||
#endif
|
||||
|
||||
#ifndef KINETIS_MCG_ERC_FREQ
|
||||
#error "KINETIS_MCG_ERC_FREQ not defined in periph_conf.h"
|
||||
#endif
|
||||
|
||||
#ifndef KINETIS_MCG_ERC_FRDIV
|
||||
#error "KINETIS_MCG_ERC_FRDIV not defined in periph_conf.h"
|
||||
#endif
|
||||
|
||||
#ifndef KINETIS_MCG_ERC_RANGE
|
||||
#error "KINETIS_MCG_ERC_RANGE not defined in periph_conf.h"
|
||||
#endif
|
||||
|
||||
#ifndef KINETIS_MCG_PLL_PRDIV
|
||||
#error "KINETIS_MCG_PLL_PRDIV not defined in periph_conf.h"
|
||||
#endif
|
||||
|
||||
#ifndef KINETIS_MCG_PLL_VDIV0
|
||||
#error "KINETIS_MCG_PLL_VDIV0 not defined in periph_conf.h"
|
||||
#endif
|
||||
|
||||
#ifndef KINETIS_MCG_PLL_FREQ
|
||||
#error "KINETIS_MCG_PLL_FREQ not defined in periph_conf.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Disable Phase Locked Loop (PLL)
|
||||
*
|
||||
*/
|
||||
static inline void kinetis_mcg_disable_pll(void)
|
||||
{
|
||||
MCG->C5 = (uint8_t)0;
|
||||
MCG->C6 = (uint8_t)0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set Frequency Locked Loop (FLL) factor.
|
||||
*
|
||||
* The FLL will lock the DCO frequency to the FLL factor.
|
||||
* FLL factor will be selected by DRST_DRS and DMX32 bits
|
||||
* and depends on KINETIS_MCG_DCO_RANGE value.
|
||||
*/
|
||||
static inline void kinetis_mcg_set_fll_factor(uint8_t factor)
|
||||
{
|
||||
MCG->C4 &= ~(uint8_t)(MCG_C4_DMX32_MASK | MCG_C4_DRST_DRS_MASK);
|
||||
MCG->C4 |= (uint8_t)(factor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable Oscillator module
|
||||
*
|
||||
* The module settings depend on KINETIS_MCG_ERC_RANGE
|
||||
* KINETIS_MCG_ERC_OSCILLATOR values.
|
||||
*/
|
||||
static void kinetis_mcg_enable_osc(void)
|
||||
{
|
||||
if (KINETIS_MCG_ERC_RANGE == 1) {
|
||||
/* select high frequency range and oscillator clock */
|
||||
MCG->C2 = (uint8_t)(MCG_C2_RANGE0(1));
|
||||
}
|
||||
else if (KINETIS_MCG_ERC_RANGE == 2) {
|
||||
/* select very high frequency range and osciallor clock */
|
||||
MCG->C2 = (uint8_t)(MCG_C2_RANGE0(2));
|
||||
}
|
||||
else {
|
||||
/* select low frequency range and osciallor clock */
|
||||
MCG->C2 = (uint8_t)(MCG_C2_RANGE0(0));
|
||||
}
|
||||
|
||||
OSC0->CR = (uint8_t)(OSC_CR_ERCLKEN_MASK | OSC_CR_EREFSTEN_MASK
|
||||
| (KINETIS_MCG_OSC_CLC & 0xf));
|
||||
|
||||
/* Enable Oscillator */
|
||||
if (KINETIS_MCG_ERC_OSCILLATOR) {
|
||||
MCG->C2 |= (uint8_t)(MCG_C2_EREFS0_MASK);
|
||||
|
||||
/* wait fo OSC initialization */
|
||||
while ((MCG->S & MCG_S_OSCINIT0_MASK) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the FLL Engaged Internal Mode.
|
||||
*
|
||||
* MCGOUTCLK is derived from the FLL clock.
|
||||
* Clock source is the 32kHz slow Internal Reference Clock.
|
||||
* The FLL loop will lock the DCO frequency to the FLL-Factor.
|
||||
*/
|
||||
static void kinetis_mcg_set_fei(void)
|
||||
{
|
||||
kinetis_mcg_set_fll_factor(KINETIS_MCG_FLL_FACTOR_FEI);
|
||||
/* enable and select slow internal reference clock */
|
||||
MCG->C1 = (uint8_t)(MCG_C1_CLKS(0) | MCG_C1_IREFS_MASK);
|
||||
|
||||
/* set to defaults */
|
||||
MCG->C2 = (uint8_t)0;
|
||||
|
||||
/* source of the FLL reference clock shall be internal reference clock */
|
||||
while ((MCG->S & MCG_S_IREFST_MASK) == 0);
|
||||
|
||||
/* Wait until output of the FLL is selected */
|
||||
while (MCG->S & (MCG_S_CLKST_MASK));
|
||||
|
||||
kinetis_mcg_disable_pll();
|
||||
current_mode = KINETIS_MCG_FEI;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the FLL Engaged External Mode.
|
||||
*
|
||||
* MCGOUTCLK is derived from the FLL clock.
|
||||
* Clock source is the external reference clock (IRC or oscillator).
|
||||
* The FLL loop will lock the DCO frequency to the FLL-Factor.
|
||||
*/
|
||||
static void kinetis_mcg_set_fee(void)
|
||||
{
|
||||
kinetis_mcg_enable_osc();
|
||||
kinetis_mcg_set_fll_factor(KINETIS_MCG_FLL_FACTOR_FEE);
|
||||
|
||||
/* select external reference clock and divide factor */
|
||||
MCG->C1 = (uint8_t)(MCG_C1_CLKS(0) | MCG_C1_FRDIV(KINETIS_MCG_ERC_FRDIV));
|
||||
|
||||
/* Wait until output of FLL is selected */
|
||||
while ((MCG->S & MCG_S_CLKST_MASK) != MCG_S_CLKST(0));
|
||||
|
||||
kinetis_mcg_disable_pll();
|
||||
current_mode = KINETIS_MCG_FEE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the FLL Bypassed Internal Mode.
|
||||
*
|
||||
* MCGOUTCLK is derived from 32kHz IRC or 4MHz IRC.
|
||||
* FLL output is not used.
|
||||
* FLL clock source is internal 32kHz IRC.
|
||||
* The FLL loop will lock the DCO frequency to the FLL-Factor.
|
||||
* Next useful mode: BLPI or FEI.
|
||||
*/
|
||||
static void kinetis_mcg_set_fbi(void)
|
||||
{
|
||||
/* select IRC source */
|
||||
if (KINETIS_MCG_USE_FAST_IRC) {
|
||||
MCG->C2 = (uint8_t)(MCG_C2_IRCS_MASK);
|
||||
}
|
||||
else {
|
||||
MCG->C2 = (uint8_t)(0);
|
||||
}
|
||||
|
||||
kinetis_mcg_set_fll_factor(KINETIS_MCG_FLL_FACTOR_FEI);
|
||||
/* enable and select slow internal reference clock */
|
||||
MCG->C1 = (uint8_t)(MCG_C1_CLKS(1) | MCG_C1_IREFS_MASK);
|
||||
|
||||
/* Wait until output of IRC is selected */
|
||||
while ((MCG->S & MCG_S_CLKST_MASK) != MCG_S_CLKST(1));
|
||||
|
||||
/* source of the FLL reference clock shall be internal reference clock */
|
||||
while ((MCG->S & MCG_S_IREFST_MASK) == 0);
|
||||
|
||||
kinetis_mcg_disable_pll();
|
||||
current_mode = KINETIS_MCG_FBI;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the FLL Bypassed External Mode.
|
||||
*
|
||||
* MCGOUTCLK is derived from external reference clock (oscillator).
|
||||
* FLL output is not used.
|
||||
* Clock source is the external reference clock (oscillator).
|
||||
* The FLL loop will lock the DCO frequency to the FLL-Factor.
|
||||
*/
|
||||
static void kinetis_mcg_set_fbe(void)
|
||||
{
|
||||
kinetis_mcg_enable_osc();
|
||||
kinetis_mcg_set_fll_factor(KINETIS_MCG_FLL_FACTOR_FEE);
|
||||
|
||||
/* FLL is not disabled in bypass mode */
|
||||
MCG->C2 &= ~(uint8_t)(MCG_C2_LP_MASK);
|
||||
|
||||
/* select external reference clock and divide factor */
|
||||
MCG->C1 = (uint8_t)(MCG_C1_CLKS(2) | MCG_C1_FRDIV(KINETIS_MCG_ERC_FRDIV));
|
||||
|
||||
/* Wait until ERC is selected */
|
||||
while ((MCG->S & MCG_S_CLKST_MASK) != MCG_S_CLKST(2));
|
||||
|
||||
kinetis_mcg_disable_pll();
|
||||
current_mode = KINETIS_MCG_FBE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the FLL Bypassed Low Power Internal Mode.
|
||||
*
|
||||
* MCGOUTCLK is derived from IRC.
|
||||
* FLL and PLL are disable.
|
||||
* Previous and next allowed mode is FBI.
|
||||
*/
|
||||
static void kinetis_mcg_set_blpi(void)
|
||||
{
|
||||
MCG->C2 |= (uint8_t)(MCG_C2_LP_MASK);
|
||||
current_mode = KINETIS_MCG_BLPI;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the FLL Bypassed Low Power External Mode.
|
||||
*
|
||||
* MCGOUTCLK is derived from ERC.
|
||||
* FLL and PLL are disable.
|
||||
* Previous and next allowed mode: FBE or PBE.
|
||||
*/
|
||||
static void kinetis_mcg_set_blpe(void)
|
||||
{
|
||||
MCG->C2 |= (uint8_t)(MCG_C2_LP_MASK);
|
||||
current_mode = KINETIS_MCG_BLPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the PLL Bypassed External Mode.
|
||||
*
|
||||
* MCGOUTCLK is derived from external reference clock (oscillator).
|
||||
* PLL output is not used.
|
||||
* Clock source is the external reference clock (oscillator).
|
||||
* The PLL loop will locks to VDIV times the frequency
|
||||
* corresponding by PRDIV.
|
||||
* Previous allowed mode are FBE or BLPE.
|
||||
*/
|
||||
static void kinetis_mcg_set_pbe(void)
|
||||
{
|
||||
/* select external reference clock and divide factor */
|
||||
MCG->C1 = (uint8_t)(MCG_C1_CLKS(2) | MCG_C1_FRDIV(KINETIS_MCG_ERC_FRDIV));
|
||||
|
||||
/* Wait until ERC is selected */
|
||||
while ((MCG->S & MCG_S_CLKST_MASK) != MCG_S_CLKST(2));
|
||||
|
||||
/* PLL is not disabled in bypass mode */
|
||||
MCG->C2 &= ~(uint8_t)(MCG_C2_LP_MASK);
|
||||
|
||||
/* set external reference devider */
|
||||
MCG->C5 = (uint8_t)(MCG_C5_PRDIV0(KINETIS_MCG_PLL_PRDIV));
|
||||
|
||||
/* set external reference devider */
|
||||
MCG->C6 = (uint8_t)(MCG_C6_VDIV0(KINETIS_MCG_PLL_VDIV0));
|
||||
|
||||
/* select PLL */
|
||||
MCG->C6 |= (uint8_t)(MCG_C6_PLLS_MASK);
|
||||
|
||||
/* Wait until the source of the PLLS clock is PLL */
|
||||
while ((MCG->S & MCG_S_PLLST_MASK) == 0);
|
||||
|
||||
/* Wait until PLL locked */
|
||||
while ((MCG->S & MCG_S_LOCK0_MASK) == 0);
|
||||
|
||||
current_mode = KINETIS_MCG_PBE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the PLL Engaged External Mode.
|
||||
*
|
||||
* MCGOUTCLK is derived from PLL.
|
||||
* PLL output is used.
|
||||
* Previous and next allowed mode is PBE.
|
||||
*/
|
||||
static void kinetis_mcg_set_pee(void)
|
||||
{
|
||||
MCG->C1 &= ~(uint8_t)(MCG_C1_CLKS_MASK);
|
||||
|
||||
/* Wait until output of the PLL is selected */
|
||||
while ((MCG->S & MCG_S_CLKST_MASK) != MCG_S_CLKST(3));
|
||||
|
||||
current_mode = KINETIS_MCG_PEE;
|
||||
}
|
||||
|
||||
|
||||
int kinetis_mcg_set_mode(kinetis_mcg_mode_t mode)
|
||||
{
|
||||
if (mode > KINETIS_MCG_FEI) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mcg_pm[current_mode] & (1 << mode)) {
|
||||
if (mode == KINETIS_MCG_FEI) {
|
||||
kinetis_mcg_set_fei();
|
||||
}
|
||||
|
||||
if (mode == KINETIS_MCG_FBI) {
|
||||
kinetis_mcg_set_fbi();
|
||||
}
|
||||
|
||||
if (mode == KINETIS_MCG_FEE) {
|
||||
kinetis_mcg_set_fee();
|
||||
}
|
||||
|
||||
if (mode == KINETIS_MCG_FBE) {
|
||||
kinetis_mcg_set_fbe();
|
||||
}
|
||||
|
||||
if (mode == KINETIS_MCG_BLPI) {
|
||||
kinetis_mcg_set_blpi();
|
||||
}
|
||||
|
||||
if (mode == KINETIS_MCG_BLPE) {
|
||||
kinetis_mcg_set_blpe();
|
||||
}
|
||||
|
||||
if (mode == KINETIS_MCG_PBE) {
|
||||
kinetis_mcg_set_pbe();
|
||||
}
|
||||
|
||||
if (mode == KINETIS_MCG_PEE) {
|
||||
kinetis_mcg_set_pee();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case KINETIS_MCG_PEE:
|
||||
/* cppcheck-suppress duplicateExpression */
|
||||
if (!(KINETIS_MCG_USE_ERC || KINETIS_MCG_USE_PLL)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (current_mode == KINETIS_MCG_FEI) {
|
||||
/* set FBE -> PBE -> PEE */
|
||||
kinetis_mcg_set_fbe();
|
||||
kinetis_mcg_set_pbe();
|
||||
kinetis_mcg_set_pee();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (current_mode == KINETIS_MCG_BLPE) {
|
||||
/* set PBE -> PEE */
|
||||
kinetis_mcg_set_pbe();
|
||||
kinetis_mcg_set_pee();
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case KINETIS_MCG_BLPE:
|
||||
if (!KINETIS_MCG_USE_ERC) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (current_mode == KINETIS_MCG_PEE) {
|
||||
/* set PBE -> BLPE */
|
||||
kinetis_mcg_set_pbe();
|
||||
kinetis_mcg_set_blpe();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (current_mode == KINETIS_MCG_FEE) {
|
||||
/* set FBE -> BLPE */
|
||||
kinetis_mcg_set_fbe();
|
||||
kinetis_mcg_set_blpe();
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case KINETIS_MCG_BLPI:
|
||||
if (current_mode == KINETIS_MCG_FEE) {
|
||||
/* set FBI -> BLPI */
|
||||
kinetis_mcg_set_fbi();
|
||||
kinetis_mcg_set_blpi();
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case KINETIS_MCG_FEE:
|
||||
if (!KINETIS_MCG_USE_ERC) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (current_mode == KINETIS_MCG_BLPE) {
|
||||
/* set FBE -> FEE */
|
||||
kinetis_mcg_set_fbe();
|
||||
kinetis_mcg_set_fee();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (current_mode == KINETIS_MCG_BLPI) {
|
||||
/* set FBI -> FEE */
|
||||
kinetis_mcg_set_fbi();
|
||||
kinetis_mcg_set_fee();
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* KINETIS_CPU_USE_MCG */
|
274
cpu/kinetis_common/pwm.c
Normal file
274
cpu/kinetis_common/pwm.c
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Freie Universität Berlin
|
||||
* Copyright (C) 2014 PHYTEC Messtechnik GmbH
|
||||
*
|
||||
* 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 cpu_kinetis_common_pwm
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level PWM driver implementation
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
* @author Johann Fischer <j.fischer@phytec.de>
|
||||
* @author Jonas Remmert <j.remmert@phytec.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "periph/pwm.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
#include "hwtimer.h"
|
||||
|
||||
/* ignore file in case no PWM devices are defined */
|
||||
#if PWM_NUMOF
|
||||
|
||||
int pwm_init(pwm_t dev, pwm_mode_t mode, unsigned int frequency, unsigned int resolution)
|
||||
{
|
||||
FTM_Type *tim = NULL;
|
||||
PORT_Type *port[PWM_MAX_CHANNELS];
|
||||
/* cppcheck-suppress unassignedVariable */
|
||||
uint8_t pins[PWM_MAX_CHANNELS];
|
||||
uint8_t af[PWM_MAX_CHANNELS];
|
||||
/* cppcheck-suppress unassignedVariable */
|
||||
uint8_t ftmchan[PWM_MAX_CHANNELS];
|
||||
int channels = 0;
|
||||
uint32_t pwm_clk = 0;
|
||||
|
||||
pwm_poweron(dev);
|
||||
|
||||
switch (dev) {
|
||||
#if PWM_0_EN
|
||||
|
||||
case PWM_0:
|
||||
tim = PWM_0_DEV;
|
||||
port[0] = PWM_0_PORT_CH0;
|
||||
port[1] = PWM_0_PORT_CH1;
|
||||
port[2] = PWM_0_PORT_CH2;
|
||||
port[3] = PWM_0_PORT_CH3;
|
||||
pins[0] = PWM_0_PIN_CH0;
|
||||
pins[1] = PWM_0_PIN_CH1;
|
||||
pins[2] = PWM_0_PIN_CH2;
|
||||
pins[3] = PWM_0_PIN_CH3;
|
||||
ftmchan[0] = PWM_0_FTMCHAN_CH0;
|
||||
ftmchan[1] = PWM_0_FTMCHAN_CH1;
|
||||
ftmchan[2] = PWM_0_FTMCHAN_CH2;
|
||||
ftmchan[3] = PWM_0_FTMCHAN_CH3;
|
||||
af[0] = PWM_0_PIN_AF_CH0;
|
||||
af[1] = PWM_0_PIN_AF_CH1;
|
||||
af[2] = PWM_0_PIN_AF_CH2;
|
||||
af[3] = PWM_0_PIN_AF_CH3;
|
||||
channels = PWM_0_CHANNELS;
|
||||
pwm_clk = PWM_0_CLK;
|
||||
PWM_0_PORT_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (channels > PWM_MAX_CHANNELS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->MODE = (1 << FTM_MODE_WPDIS_SHIFT);
|
||||
|
||||
/* setup pins, reset timer match value */
|
||||
for (int i = 0; i < channels; i++) {
|
||||
port[i]->PCR[pins[i]] = PORT_PCR_MUX(af[i]);
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->CONTROLS[i].CnV = 0;
|
||||
}
|
||||
|
||||
/* reset timer configuration registers */
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->COMBINE = 0;
|
||||
|
||||
/* set prescale and mod registers to matching values for resolution and frequency */
|
||||
if (resolution > 0xffff || (resolution * frequency) > pwm_clk) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->SC = FTM_SC_PS((pwm_clk / (resolution * frequency)) - 1);
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->MOD = resolution;
|
||||
|
||||
/* set PWM mode */
|
||||
switch (mode) {
|
||||
case PWM_LEFT:
|
||||
for (int i = 0; i < channels; i++) {
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->CONTROLS[ftmchan[i]].CnSC = (1 << FTM_CnSC_MSB_SHIFT |
|
||||
1 << FTM_CnSC_ELSB_SHIFT);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PWM_RIGHT:
|
||||
for (int i = 0; i < channels; i++) {
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->CONTROLS[ftmchan[i]].CnSC = (1 << FTM_CnSC_MSB_SHIFT |
|
||||
1 << FTM_CnSC_ELSA_SHIFT);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PWM_CENTER:
|
||||
for (int i = 0; i < channels; i++) {
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->CONTROLS[ftmchan[i]].CnSC = (1 << FTM_CnSC_MSB_SHIFT);
|
||||
}
|
||||
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->SC |= (1 << FTM_SC_CPWMS_SHIFT);
|
||||
break;
|
||||
}
|
||||
|
||||
/* enable timer ergo the PWM generation */
|
||||
pwm_start(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pwm_set(pwm_t dev, int channel, unsigned int value)
|
||||
{
|
||||
FTM_Type *tim = NULL;
|
||||
|
||||
switch (dev) {
|
||||
#if PWM_0_EN
|
||||
|
||||
case PWM_0:
|
||||
tim = PWM_0_DEV;
|
||||
break;
|
||||
#endif
|
||||
#if PWM_1_EN
|
||||
|
||||
case PWM_1:
|
||||
tim = PWM_1_DEV;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* norm value to maximum possible value */
|
||||
if (value > 0xffff) {
|
||||
value = 0xffff;
|
||||
}
|
||||
|
||||
switch (channel) {
|
||||
case 0:
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->CONTROLS[PWM_0_FTMCHAN_CH0].CnV = value;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->CONTROLS[PWM_0_FTMCHAN_CH1].CnV = value;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->CONTROLS[PWM_0_FTMCHAN_CH2].CnV = value;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
/* cppcheck-suppress nullPointer */
|
||||
tim->CONTROLS[PWM_0_FTMCHAN_CH3].CnV = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pwm_start(pwm_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if PWM_0_EN
|
||||
|
||||
case PWM_0:
|
||||
PWM_0_DEV->SC |= FTM_SC_CLKS(1);
|
||||
break;
|
||||
#endif
|
||||
#if PWM_1_EN
|
||||
|
||||
case PWM_1:
|
||||
PWM_1_DEV->SC |= FTM_SC_CLKS(1);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void pwm_stop(pwm_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if PWM_0_EN
|
||||
|
||||
case PWM_0:
|
||||
PWM_0_DEV->SC &= ~FTM_SC_CLKS_MASK;
|
||||
break;
|
||||
#endif
|
||||
#if PWM_1_EN
|
||||
|
||||
case PWM_1:
|
||||
PWM_1_DEV->SC &= ~FTM_SC_CLKS_MASK;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void pwm_poweron(pwm_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if PWM_0_EN
|
||||
|
||||
case PWM_0:
|
||||
PWM_0_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
#if PWM_1_EN
|
||||
|
||||
case PWM_1:
|
||||
PWM_1_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void pwm_poweroff(pwm_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if PWM_0_EN
|
||||
|
||||
case PWM_0:
|
||||
PWM_0_CLKDIS();
|
||||
break;
|
||||
#endif
|
||||
#if PWM_1_EN
|
||||
|
||||
case PWM_1:
|
||||
PWM_1_CLKDIS();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* PWM_NUMOF */
|
82
cpu/kinetis_common/random_rnga.c
Normal file
82
cpu/kinetis_common/random_rnga.c
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Freie Universität Berlin
|
||||
* Copyright (C) 2014 PHYTEC Messtechnik GmbH
|
||||
*
|
||||
* 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 cpu_kinetis_common_rnga
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level random number generator driver implementation.
|
||||
*
|
||||
* @author Johann Fischer <j.fischer@phytec.de> (adaption for Freescale's RNGA)
|
||||
* @author Hauke Petersen <mail@haukepetersen.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "periph/random.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
#if RANDOM_NUMOF
|
||||
#ifdef KINETIS_RNGA
|
||||
|
||||
void random_init(void)
|
||||
{
|
||||
random_poweron();
|
||||
}
|
||||
|
||||
int random_read(char *buf, unsigned int num)
|
||||
{
|
||||
/* cppcheck-suppress variableScope */
|
||||
uint32_t tmp;
|
||||
int count = 0;
|
||||
|
||||
/* self-seeding */
|
||||
while (!(KINETIS_RNGA->SR & RNG_SR_OREG_LVL_MASK));
|
||||
|
||||
KINETIS_RNGA->ER = KINETIS_RNGA->OR ^ (uint32_t)buf;
|
||||
|
||||
while (count < num) {
|
||||
/* wait for random data to be ready to read */
|
||||
while (!(KINETIS_RNGA->SR & RNG_SR_OREG_LVL_MASK));
|
||||
|
||||
tmp = KINETIS_RNGA->OR;
|
||||
|
||||
/* copy data into result vector */
|
||||
for (int i = 0; i < 4 && count < num; i++) {
|
||||
buf[count++] = (char)tmp;
|
||||
tmp = tmp >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void random_poweron(void)
|
||||
{
|
||||
RANDOM_CLKEN();
|
||||
KINETIS_RNGA->CR = RNG_CR_INTM_MASK | RNG_CR_HA_MASK | RNG_CR_GO_MASK;
|
||||
}
|
||||
|
||||
void random_poweroff(void)
|
||||
{
|
||||
KINETIS_RNGA->CR = 0;
|
||||
RANDOM_CLKDIS();
|
||||
}
|
||||
|
||||
/*
|
||||
void isr_rng(void)
|
||||
{
|
||||
}
|
||||
*/
|
||||
|
||||
#endif /* KINETIS_RNGA */
|
||||
#endif /* RANDOM_NUMOF */
|
88
cpu/kinetis_common/random_rngb.c
Normal file
88
cpu/kinetis_common/random_rngb.c
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Freie Universität Berlin
|
||||
* Copyright (C) 2014 PHYTEC Messtechnik GmbH
|
||||
* Copyright (C) 2015 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 cpu_kinetis_common_rngb
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level random number generator driver implementation.
|
||||
*
|
||||
* @author Joakim Gebart <joakim.gebart@eistec.se> (adaption for Freescale's RNGB)
|
||||
* @author Johann Fischer <j.fischer@phytec.de> (adaption for Freescale's RNGA)
|
||||
* @author Hauke Petersen <mail@haukepetersen.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "periph/random.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
#if RANDOM_NUMOF
|
||||
#ifdef KINETIS_RNGB
|
||||
|
||||
|
||||
void random_init(void)
|
||||
{
|
||||
random_poweron();
|
||||
}
|
||||
|
||||
int random_read(char *buf, unsigned int num)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
while (count < num) {
|
||||
uint32_t tmp;
|
||||
|
||||
/* wait for random data to be ready to read */
|
||||
while (!(KINETIS_RNGB->SR & RNG_SR_FIFO_LVL_MASK));
|
||||
|
||||
tmp = KINETIS_RNGB->OUT;
|
||||
|
||||
/* copy data into result vector */
|
||||
for (int i = 0; i < 4 && count < num; i++) {
|
||||
buf[count++] = (char)tmp;
|
||||
tmp = tmp >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void random_poweron(void)
|
||||
{
|
||||
RANDOM_CLKEN();
|
||||
|
||||
if ((KINETIS_RNGB->VER & RNG_VER_TYPE_MASK) != 0b0001) {
|
||||
/* Wrong type of RNG */
|
||||
/* TODO: Handle */
|
||||
}
|
||||
|
||||
/* Software reset, bit is self-clearing */
|
||||
BITBAND_REG(KINETIS_RNGB->CMD, RNG_CMD_SR_SHIFT) = 1;
|
||||
/* Set up automatic reseed */
|
||||
KINETIS_RNGB->CR = RNG_CR_AR_MASK | RNG_CR_MASKERR_MASK | RNG_CR_MASKDONE_MASK;
|
||||
}
|
||||
|
||||
void random_poweroff(void)
|
||||
{
|
||||
KINETIS_RNGB->CR = 0;
|
||||
RANDOM_CLKDIS();
|
||||
}
|
||||
|
||||
/*
|
||||
void isr_rng(void)
|
||||
{
|
||||
}
|
||||
*/
|
||||
|
||||
#endif /* KINETIS_RNGB */
|
||||
#endif /* RANDOM_NUMOF */
|
177
cpu/kinetis_common/rtc.c
Normal file
177
cpu/kinetis_common/rtc.c
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (C) 2014 PHYTEC Messtechnik GmbH
|
||||
*
|
||||
* 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 cpu_kinetis_common_rtc
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level RTC driver implementation for Freescale Kinetis MCUs.
|
||||
*
|
||||
* @author Johann Fischer <j.fischer@phytec.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include "cpu.h"
|
||||
#include "periph/rtc.h"
|
||||
#include "periph_conf.h"
|
||||
#include "sched.h"
|
||||
#include "thread.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#if RTC_NUMOF
|
||||
|
||||
|
||||
#ifndef RTC_LOAD_CAP_BITS
|
||||
#define RTC_LOAD_CAP_BITS 0
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
rtc_alarm_cb_t cb; /**< callback called from RTC interrupt */
|
||||
void *arg; /**< argument passed to the callback */
|
||||
} rtc_state_t;
|
||||
|
||||
static rtc_state_t rtc_callback;
|
||||
|
||||
void rtc_init(void)
|
||||
{
|
||||
RTC_Type *rtc = RTC_DEV;
|
||||
|
||||
RTC_UNLOCK();
|
||||
/* Reset RTC */
|
||||
rtc->CR = RTC_CR_SWR_MASK;
|
||||
rtc->CR = 0;
|
||||
|
||||
if (rtc->SR & RTC_SR_TIF_MASK) {
|
||||
/* Clear TIF by writing TSR. */
|
||||
rtc->TSR = 0;
|
||||
}
|
||||
|
||||
/* Enable RTC oscillator and non-supervisor mode accesses. */
|
||||
/* Enable load capacitance as configured by periph_conf.h */
|
||||
rtc->CR = RTC_CR_OSCE_MASK | RTC_CR_SUP_MASK | RTC_LOAD_CAP_BITS;
|
||||
|
||||
/* Clear TAF by writing TAR. */
|
||||
rtc->TAR = 0xffffff42;
|
||||
|
||||
/* Disable all RTC interrupts. */
|
||||
rtc->IER = 0;
|
||||
|
||||
rtc_poweron();
|
||||
}
|
||||
|
||||
int rtc_set_time(struct tm *time)
|
||||
{
|
||||
RTC_Type *rtc = RTC_DEV;
|
||||
time_t t = mktime(time);
|
||||
|
||||
/* Disable Time Counter */
|
||||
rtc->SR &= ~RTC_SR_TCE_MASK;
|
||||
rtc->TSR = t;
|
||||
rtc->SR |= RTC_SR_TCE_MASK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtc_get_time(struct tm *time)
|
||||
{
|
||||
RTC_Type *rtc = RTC_DEV;
|
||||
time_t t;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
t = rtc->TSR;
|
||||
|
||||
if (t == (time_t)rtc->TSR) {
|
||||
gmtime_r(&t, time);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int rtc_set_alarm(struct tm *time, rtc_alarm_cb_t cb, void *arg)
|
||||
{
|
||||
RTC_Type *rtc = RTC_DEV;
|
||||
time_t t = mktime(time);
|
||||
|
||||
/* Disable Timer Alarm Interrupt */
|
||||
rtc->IER &= ~RTC_IER_TAIE_MASK;
|
||||
|
||||
rtc->TAR = t;
|
||||
|
||||
rtc_callback.cb = cb;
|
||||
rtc_callback.arg = arg;
|
||||
|
||||
/* Enable Timer Alarm Interrupt */
|
||||
rtc->IER |= RTC_IER_TAIE_MASK;
|
||||
|
||||
/* Enable RTC interrupts */
|
||||
NVIC_SetPriority(RTC_IRQn, 10);
|
||||
NVIC_EnableIRQ(RTC_IRQn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtc_get_alarm(struct tm *time)
|
||||
{
|
||||
RTC_Type *rtc = RTC_DEV;
|
||||
time_t t = rtc->TAR;
|
||||
|
||||
gmtime_r(&t, time);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rtc_clear_alarm(void)
|
||||
{
|
||||
RTC_Type *rtc = RTC_DEV;
|
||||
|
||||
NVIC_DisableIRQ(RTC_IRQn);
|
||||
/* Disable Timer Alarm Interrupt */
|
||||
rtc->IER &= ~RTC_IER_TAIE_MASK;
|
||||
rtc->TAR = 0;
|
||||
rtc_callback.cb = NULL;
|
||||
rtc_callback.arg = NULL;
|
||||
}
|
||||
|
||||
/* RTC module has independent power suply. We can not really turn it on/off. */
|
||||
|
||||
void rtc_poweron(void)
|
||||
{
|
||||
RTC_Type *rtc = RTC_DEV;
|
||||
/* Enable Time Counter */
|
||||
rtc->SR |= RTC_SR_TCE_MASK;
|
||||
}
|
||||
|
||||
void rtc_poweroff(void)
|
||||
{
|
||||
RTC_Type *rtc = RTC_DEV;
|
||||
/* Disable Time Counter */
|
||||
rtc->SR &= ~RTC_SR_TCE_MASK;
|
||||
}
|
||||
|
||||
void isr_rtc(void)
|
||||
{
|
||||
RTC_Type *rtc = RTC_DEV;
|
||||
|
||||
if (rtc->SR & RTC_SR_TAF_MASK) {
|
||||
rtc_callback.cb(rtc_callback.arg);
|
||||
rtc->TAR = 0;
|
||||
}
|
||||
|
||||
if (sched_context_switch_request) {
|
||||
thread_yield();
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* RTC_NUMOF */
|
1583
cpu/kinetis_common/spi.c
Normal file
1583
cpu/kinetis_common/spi.c
Normal file
File diff suppressed because it is too large
Load Diff
368
cpu/kinetis_common/timer.c
Normal file
368
cpu/kinetis_common/timer.c
Normal file
@ -0,0 +1,368 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Freie Universität Berlin
|
||||
* Copyright (C) 2014 PHYTEC Messtechnik GmbH
|
||||
*
|
||||
* 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 cpu_kinetis_common_timer
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level timer driver implementation
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
* @author Johann Fischer <j.fischer@phytec.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "board.h"
|
||||
#include "sched.h"
|
||||
#include "thread.h"
|
||||
#include "periph_conf.h"
|
||||
#include "periph/timer.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#if TIMER_NUMOF
|
||||
|
||||
/** Type for timer state */
|
||||
typedef struct {
|
||||
void (*cb)(int);
|
||||
} timer_conf_t;
|
||||
|
||||
/** Type for virtual count up timer */
|
||||
typedef struct {
|
||||
uint32_t counter32b;
|
||||
uint32_t diff;
|
||||
} count_up_timer_t;
|
||||
|
||||
static count_up_timer_t cu_timer[TIMER_NUMOF];
|
||||
|
||||
/** Timer state memory */
|
||||
static timer_conf_t config[TIMER_NUMOF];
|
||||
|
||||
inline static void pit_timer_start(uint8_t ch)
|
||||
{
|
||||
TIMER_DEV->CHANNEL[ch].TCTRL |= (PIT_TCTRL_TEN_MASK);
|
||||
}
|
||||
|
||||
inline static void pit_timer_stop(uint8_t ch)
|
||||
{
|
||||
TIMER_DEV->CHANNEL[ch].TCTRL &= ~(PIT_TCTRL_TEN_MASK);
|
||||
}
|
||||
|
||||
/** use channel n-1 as prescaler */
|
||||
inline static void timer_set_prescaler(uint8_t ch, unsigned int ticks_per_us)
|
||||
{
|
||||
TIMER_DEV->CHANNEL[ch].TCTRL = 0x0;
|
||||
TIMER_DEV->CHANNEL[ch].LDVAL = (TIMER_CLOCK / 1e6) / ticks_per_us;
|
||||
TIMER_DEV->CHANNEL[ch].TCTRL = (PIT_TCTRL_TEN_MASK);
|
||||
}
|
||||
|
||||
inline static void timer_set_counter(uint8_t ch)
|
||||
{
|
||||
TIMER_DEV->CHANNEL[ch].TCTRL = 0x0;
|
||||
TIMER_DEV->CHANNEL[ch].LDVAL = TIMER_MAX_VALUE;
|
||||
TIMER_DEV->CHANNEL[ch].TCTRL = (PIT_TCTRL_TIE_MASK | PIT_TCTRL_CHN_MASK);
|
||||
}
|
||||
|
||||
inline static uint32_t pit_timer_read(tim_t dev, uint8_t ch)
|
||||
{
|
||||
return cu_timer[dev].counter32b + (TIMER_DEV->CHANNEL[ch].LDVAL
|
||||
- TIMER_DEV->CHANNEL[ch].CVAL);
|
||||
}
|
||||
|
||||
inline static void pit_timer_set_max(uint8_t ch)
|
||||
{
|
||||
pit_timer_stop(ch);
|
||||
TIMER_DEV->CHANNEL[ch].LDVAL = TIMER_MAX_VALUE;
|
||||
pit_timer_start(ch);
|
||||
}
|
||||
|
||||
int timer_init(tim_t dev, unsigned int ticks_per_us, void (*callback)(int))
|
||||
{
|
||||
PIT_Type *timer = TIMER_DEV;
|
||||
|
||||
/* enable timer peripheral clock */
|
||||
TIMER_CLKEN();
|
||||
|
||||
timer->MCR = PIT_MCR_FRZ_MASK;
|
||||
|
||||
switch (dev) {
|
||||
#if TIMER_0_EN
|
||||
|
||||
case TIMER_0:
|
||||
NVIC_SetPriority(TIMER_0_IRQ_CHAN, TIMER_IRQ_PRIO);
|
||||
timer_set_prescaler(TIMER_0_PRESCALER_CH, ticks_per_us);
|
||||
timer_set_counter(TIMER_0_COUNTER_CH);
|
||||
break;
|
||||
#endif
|
||||
#if TIMER_1_EN
|
||||
|
||||
case TIMER_1:
|
||||
NVIC_SetPriority(TIMER_1_IRQ_CHAN, TIMER_IRQ_PRIO);
|
||||
timer_set_prescaler(TIMER_1_PRESCALER_CH, ticks_per_us);
|
||||
timer_set_counter(TIMER_1_COUNTER_CH);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TIMER_UNDEFINED:
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set callback function */
|
||||
config[dev].cb = callback;
|
||||
cu_timer[dev].counter32b = 0;
|
||||
cu_timer[dev].diff = 0;
|
||||
|
||||
/* enable the timer's interrupt */
|
||||
timer_irq_enable(dev);
|
||||
|
||||
/* start the timer */
|
||||
timer_start(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int timer_set(tim_t dev, int channel, unsigned int timeout)
|
||||
{
|
||||
unsigned int now = timer_read(dev);
|
||||
|
||||
return timer_set_absolute(dev, channel, now + timeout);
|
||||
}
|
||||
|
||||
int timer_set_absolute(tim_t dev, int channel, unsigned int value)
|
||||
{
|
||||
switch (dev) {
|
||||
#if TIMER_0_EN
|
||||
|
||||
case TIMER_0:
|
||||
pit_timer_stop(TIMER_0_COUNTER_CH);
|
||||
cu_timer[dev].counter32b = pit_timer_read(dev, TIMER_0_COUNTER_CH);
|
||||
cu_timer[dev].diff = value - cu_timer[dev].counter32b;
|
||||
TIMER_DEV->CHANNEL[TIMER_0_COUNTER_CH].LDVAL = cu_timer[dev].diff;
|
||||
pit_timer_start(TIMER_0_COUNTER_CH);
|
||||
break;
|
||||
#endif
|
||||
#if TIMER_1_EN
|
||||
|
||||
case TIMER_1:
|
||||
pit_timer_stop(TIMER_1_COUNTER_CH);
|
||||
cu_timer[dev].counter32b = pit_timer_read(dev, TIMER_1_COUNTER_CH);
|
||||
cu_timer[dev].diff = value - cu_timer[dev].counter32b;
|
||||
TIMER_DEV->CHANNEL[TIMER_1_COUNTER_CH].LDVAL = cu_timer[dev].diff;
|
||||
pit_timer_start(TIMER_1_COUNTER_CH);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TIMER_UNDEFINED:
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG("cntr: %lu, value: %u, diff: %lu\n",
|
||||
cu_timer[dev].counter32b, value, cu_timer[dev].diff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int timer_clear(tim_t dev, int channel)
|
||||
{
|
||||
switch (dev) {
|
||||
#if TIMER_0_EN
|
||||
|
||||
case TIMER_0:
|
||||
cu_timer[dev].counter32b = timer_read(dev);
|
||||
cu_timer[dev].diff = 0;
|
||||
pit_timer_set_max(TIMER_0_COUNTER_CH);
|
||||
break;
|
||||
#endif
|
||||
#if TIMER_1_EN
|
||||
|
||||
case TIMER_1:
|
||||
cu_timer[dev].counter32b = timer_read(dev);
|
||||
cu_timer[dev].diff = 0;
|
||||
pit_timer_set_max(TIMER_1_COUNTER_CH);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TIMER_UNDEFINED:
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG("clear\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int timer_read(tim_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if TIMER_0_EN
|
||||
|
||||
case TIMER_0:
|
||||
return pit_timer_read(dev, TIMER_0_COUNTER_CH);
|
||||
break;
|
||||
#endif
|
||||
#if TIMER_1_EN
|
||||
|
||||
case TIMER_1:
|
||||
return pit_timer_read(dev, TIMER_1_COUNTER_CH);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TIMER_UNDEFINED:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void timer_start(tim_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if TIMER_0_EN
|
||||
|
||||
case TIMER_0:
|
||||
pit_timer_start(TIMER_0_COUNTER_CH);
|
||||
break;
|
||||
#endif
|
||||
#if TIMER_1_EN
|
||||
|
||||
case TIMER_1:
|
||||
pit_timer_start(TIMER_1_COUNTER_CH);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TIMER_UNDEFINED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void timer_stop(tim_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if TIMER_0_EN
|
||||
|
||||
case TIMER_0:
|
||||
pit_timer_stop(TIMER_0_COUNTER_CH);
|
||||
break;
|
||||
#endif
|
||||
#if TIMER_1_EN
|
||||
|
||||
case TIMER_1:
|
||||
pit_timer_stop(TIMER_1_COUNTER_CH);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TIMER_UNDEFINED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void timer_irq_enable(tim_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if TIMER_0_EN
|
||||
|
||||
case TIMER_0:
|
||||
NVIC_EnableIRQ(TIMER_0_IRQ_CHAN);
|
||||
break;
|
||||
#endif
|
||||
#if TIMER_1_EN
|
||||
|
||||
case TIMER_1:
|
||||
NVIC_EnableIRQ(TIMER_1_IRQ_CHAN);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TIMER_UNDEFINED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void timer_irq_disable(tim_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if TIMER_0_EN
|
||||
|
||||
case TIMER_0:
|
||||
NVIC_DisableIRQ(TIMER_0_IRQ_CHAN);
|
||||
break;
|
||||
#endif
|
||||
#if TIMER_1_EN
|
||||
|
||||
case TIMER_1:
|
||||
NVIC_DisableIRQ(TIMER_1_IRQ_CHAN);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TIMER_UNDEFINED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void timer_reset(tim_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if TIMER_0_EN
|
||||
|
||||
case TIMER_0:
|
||||
pit_timer_set_max(TIMER_0_COUNTER_CH);
|
||||
break;
|
||||
#endif
|
||||
#if TIMER_1_EN
|
||||
|
||||
case TIMER_1:
|
||||
pit_timer_set_max(TIMER_1_COUNTER_CH);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TIMER_UNDEFINED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline static void pit_timer_irq_handler(tim_t dev, uint8_t ch)
|
||||
{
|
||||
cu_timer[dev].counter32b += TIMER_DEV->CHANNEL[ch].LDVAL;
|
||||
|
||||
TIMER_DEV->CHANNEL[ch].TFLG = PIT_TFLG_TIF_MASK;
|
||||
|
||||
if (cu_timer[dev].diff) {
|
||||
if (config[dev].cb != NULL) {
|
||||
config[dev].cb(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (sched_context_switch_request) {
|
||||
thread_yield();
|
||||
}
|
||||
}
|
||||
|
||||
#if TIMER_0_EN
|
||||
void TIMER_0_ISR(void)
|
||||
{
|
||||
pit_timer_irq_handler(TIMER_0, TIMER_0_COUNTER_CH);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TIMER_1_EN
|
||||
void TIMER_1_ISR(void)
|
||||
{
|
||||
pit_timer_irq_handler(TIMER_1, TIMER_1_COUNTER_CH);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
360
cpu/kinetis_common/uart.c
Normal file
360
cpu/kinetis_common/uart.c
Normal file
@ -0,0 +1,360 @@
|
||||
/*
|
||||
* Copyright (C) 2014 PHYTEC Messtechnik GmbH
|
||||
* Copyright (C) 2014 Freie Universität Berlin
|
||||
*
|
||||
* 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 cpu_kinetis_common_uart
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level UART driver implementation
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
* @author Johann Fischer <j.fischer@phytec.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "thread.h"
|
||||
#include "sched.h"
|
||||
#include "periph_conf.h"
|
||||
#include "periph/uart.h"
|
||||
|
||||
#ifndef KINETIS_UART_ADVANCED
|
||||
/**
|
||||
* Attempts to determine the type of the UART,
|
||||
* using the BRFA field in the UART C4 register.
|
||||
*/
|
||||
#ifdef UART_C4_BRFA
|
||||
#define KINETIS_UART_ADVANCED 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Each UART device has to store two callbacks.
|
||||
*/
|
||||
typedef struct {
|
||||
uart_rx_cb_t rx_cb;
|
||||
uart_tx_cb_t tx_cb;
|
||||
void *arg;
|
||||
} uart_conf_t;
|
||||
|
||||
/**
|
||||
* @brief Unified interrupt handler for all UART devices
|
||||
*
|
||||
* @param uartnum the number of the UART that triggered the ISR
|
||||
* @param uart the UART device that triggered the ISR
|
||||
*/
|
||||
static inline void irq_handler(uart_t uartnum, KINETIS_UART *uart);
|
||||
|
||||
/**
|
||||
* @brief Allocate memory to store the callback functions.
|
||||
*/
|
||||
static uart_conf_t config[UART_NUMOF];
|
||||
|
||||
static inline void kinetis_set_brfa(KINETIS_UART *dev, uint32_t baudrate, uint32_t clk)
|
||||
{
|
||||
#if KINETIS_UART_ADVANCED
|
||||
/* set baudrate fine adjust (brfa) */
|
||||
uint8_t brfa = ((((4 * clk) / baudrate) + 1) / 2) % 32;
|
||||
dev->C4 = UART_C4_BRFA(brfa);
|
||||
#endif
|
||||
}
|
||||
|
||||
int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, uart_tx_cb_t tx_cb, void *arg)
|
||||
{
|
||||
/* do basic initialization */
|
||||
int res = uart_init_blocking(uart, baudrate);
|
||||
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
/* remember callback addresses */
|
||||
config[uart].rx_cb = rx_cb;
|
||||
config[uart].tx_cb = tx_cb;
|
||||
config[uart].arg = arg;
|
||||
|
||||
/* enable receive interrupt */
|
||||
switch (uart) {
|
||||
#if UART_0_EN
|
||||
|
||||
case UART_0:
|
||||
NVIC_SetPriority(UART_0_IRQ_CHAN, UART_IRQ_PRIO);
|
||||
NVIC_EnableIRQ(UART_0_IRQ_CHAN);
|
||||
UART_0_DEV->C2 |= (1 << UART_C2_RIE_SHIFT);
|
||||
break;
|
||||
#endif
|
||||
#if UART_1_EN
|
||||
|
||||
case UART_1:
|
||||
NVIC_SetPriority(UART_1_IRQ_CHAN, UART_IRQ_PRIO);
|
||||
NVIC_EnableIRQ(UART_1_IRQ_CHAN);
|
||||
UART_1_DEV->C2 |= (1 << UART_C2_RIE_SHIFT);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -2;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uart_init_blocking(uart_t uart, uint32_t baudrate)
|
||||
{
|
||||
KINETIS_UART *dev;
|
||||
PORT_Type *port;
|
||||
uint32_t clk;
|
||||
uint16_t ubd;
|
||||
uint8_t tx_pin = 0;
|
||||
uint8_t rx_pin = 0;
|
||||
uint8_t af;
|
||||
|
||||
switch (uart) {
|
||||
#if UART_0_EN
|
||||
|
||||
case UART_0:
|
||||
dev = UART_0_DEV;
|
||||
port = UART_0_PORT;
|
||||
clk = UART_0_CLK;
|
||||
tx_pin = UART_0_TX_PIN;
|
||||
rx_pin = UART_0_RX_PIN;
|
||||
af = UART_0_AF;
|
||||
UART_0_PORT_CLKEN();
|
||||
UART_0_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
#if UART_1_EN
|
||||
|
||||
case UART_1:
|
||||
dev = UART_1_DEV;
|
||||
port = UART_1_PORT;
|
||||
clk = UART_1_CLK;
|
||||
tx_pin = UART_1_TX_PIN;
|
||||
rx_pin = UART_1_RX_PIN;
|
||||
af = UART_1_AF;
|
||||
UART_1_PORT_CLKEN();
|
||||
UART_1_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* configure RX and TX pins, set pin to use alternative function mode */
|
||||
port->PCR[rx_pin] = PORT_PCR_MUX(af);
|
||||
port->PCR[tx_pin] = PORT_PCR_MUX(af);
|
||||
|
||||
/* disable transmitter and receiver */
|
||||
dev->C2 &= ~(1 << UART_C2_TE_SHIFT | 1 << UART_C2_RE_SHIFT);
|
||||
/* set defaults, 8-bit mode, no parity */
|
||||
dev->C1 = 0;
|
||||
|
||||
/* calculate baudrate */
|
||||
ubd = (uint16_t)(clk / (baudrate * 16));
|
||||
|
||||
/* set baudrate */
|
||||
dev->BDH = (uint8_t)UART_BDH_SBR(ubd >> 8);
|
||||
dev->BDL = (uint8_t)UART_BDL_SBR(ubd);
|
||||
kinetis_set_brfa(dev, baudrate, clk);
|
||||
|
||||
/* enable transmitter and receiver */
|
||||
dev->C2 |= (1 << UART_C2_TE_SHIFT | 1 << UART_C2_RE_SHIFT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uart_tx_begin(uart_t uart)
|
||||
{
|
||||
switch (uart) {
|
||||
#if UART_0_EN
|
||||
|
||||
case UART_0:
|
||||
UART_0_DEV->C2 |= (1 << UART_C2_TIE_SHIFT);
|
||||
break;
|
||||
#endif
|
||||
#if UART_1_EN
|
||||
|
||||
case UART_1:
|
||||
UART_1_DEV->C2 |= (1 << UART_C2_TIE_SHIFT);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void uart_tx_end(uart_t uart)
|
||||
{
|
||||
switch (uart) {
|
||||
#if UART_0_EN
|
||||
|
||||
case UART_0:
|
||||
UART_0_DEV->C2 &= ~(1 << UART_C2_TIE_SHIFT);
|
||||
break;
|
||||
#endif
|
||||
#if UART_1_EN
|
||||
|
||||
case UART_1:
|
||||
UART_1_DEV->C2 &= ~(1 << UART_C2_TIE_SHIFT);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int uart_write(uart_t uart, char data)
|
||||
{
|
||||
switch (uart) {
|
||||
#if UART_0_EN
|
||||
|
||||
case UART_0:
|
||||
if (UART_0_DEV->S1 & UART_S1_TDRE_MASK) {
|
||||
UART_0_DEV->D = (uint8_t)data;
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
#if UART_1_EN
|
||||
|
||||
case UART_1:
|
||||
if (UART_1_DEV->S1 & UART_S1_TDRE_MASK) {
|
||||
UART_1_DEV->D = (uint8_t)data;
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -2;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uart_read_blocking(uart_t uart, char *data)
|
||||
{
|
||||
switch (uart) {
|
||||
#if UART_0_EN
|
||||
|
||||
case UART_0:
|
||||
while (!(UART_0_DEV->S1 & UART_S1_RDRF_MASK));
|
||||
|
||||
*data = (char)UART_0_DEV->D;
|
||||
break;
|
||||
#endif
|
||||
#if UART_1_EN
|
||||
|
||||
case UART_1:
|
||||
while (!(UART_1_DEV->S1 & UART_S1_RDRF_MASK));
|
||||
|
||||
*data = (char)UART_1_DEV->D;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -2;
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int uart_write_blocking(uart_t uart, char data)
|
||||
{
|
||||
switch (uart) {
|
||||
#if UART_0_EN
|
||||
|
||||
case UART_0:
|
||||
while (!(UART_0_DEV->S1 & UART_S1_TDRE_MASK));
|
||||
|
||||
UART_0_DEV->D = (uint8_t)data;
|
||||
break;
|
||||
#endif
|
||||
#if UART_1_EN
|
||||
|
||||
case UART_1:
|
||||
while (!(UART_1_DEV->S1 & UART_S1_TDRE_MASK));
|
||||
|
||||
UART_1_DEV->D = (uint8_t)data;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -2;
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if UART_0_EN
|
||||
void UART_0_ISR(void)
|
||||
{
|
||||
irq_handler(UART_0, UART_0_DEV);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UART_1_EN
|
||||
void UART_1_ISR(void)
|
||||
{
|
||||
irq_handler(UART_1, UART_1_DEV);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void irq_handler(uart_t uartnum, KINETIS_UART *dev)
|
||||
{
|
||||
/*
|
||||
* On Cortex-M0, it happens that S1 is read with LDR
|
||||
* instruction instead of LDRB. This will read the data register
|
||||
* at the same time and arrived byte will be lost. Maybe it's a GCC bug.
|
||||
*
|
||||
* Observed with: arm-none-eabi-gcc (4.8.3-8+..)
|
||||
* It does not happen with: arm-none-eabi-gcc (4.8.3-9+11)
|
||||
*/
|
||||
|
||||
if (dev->S1 & UART_S1_RDRF_MASK) {
|
||||
/* RDRF flag will be cleared when dev-D was read */
|
||||
uint8_t data = dev->D;
|
||||
|
||||
if (config[uartnum].rx_cb != NULL) {
|
||||
config[uartnum].rx_cb(config[uartnum].arg, data);
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->S1 & UART_S1_TDRE_MASK) {
|
||||
if (config[uartnum].tx_cb == NULL) {
|
||||
dev->C2 &= ~(UART_C2_TIE_MASK);
|
||||
}
|
||||
else if (config[uartnum].tx_cb(config[uartnum].arg) == 0) {
|
||||
dev->C2 &= ~(UART_C2_TIE_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
#if (KINETIS_UART_ADVANCED == 0)
|
||||
/* clear overrun flag */
|
||||
if (dev->S1 & UART_S1_OR_MASK) {
|
||||
dev->S1 = UART_S1_OR_MASK;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sched_context_switch_request) {
|
||||
thread_yield();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user