PSOC6: reuse FUTURE_SEQUANA porting layer

Copy the porting layer from TARGET_PSOC6_FUTURE to TARGET_PSOC6.
This commit is intended to make the history and changes applied easier
to follow.
ipcpipe_transport.c, ipcpipe_transport.h, rpc_api.h, rpc_defs.h
are excluded (not used by Cypress port).
PeripheralNames.h is moved to BSP layer introduced in subsequent
commits (the peripheral names and count are board-specific).
pull/9481/head
Volodymyr Medvid 2019-01-25 18:10:53 -08:00
parent 6b0658dd30
commit cd98eb619a
23 changed files with 5067 additions and 0 deletions

View File

@ -0,0 +1,62 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_PERIPHERALPINS_H
#define MBED_PERIPHERALPINS_H
#include "pinmap.h"
#include "PeripheralNames.h"
// //*** I2C ***
#if DEVICE_I2C
extern const PinMap PinMap_I2C_SDA[];
extern const PinMap PinMap_I2C_SCL[];
#endif
//*** PWM ***
#if DEVICE_PWMOUT
extern const PinMap PinMap_PWM_OUT[];
#endif
//*** SERIAL ***
#if DEVICE_SERIAL
extern const PinMap PinMap_UART_TX[];
extern const PinMap PinMap_UART_RX[];
extern const PinMap PinMap_UART_RTS[];
extern const PinMap PinMap_UART_CTS[];
#endif
//*** SPI ***
#if DEVICE_SPI
extern const PinMap PinMap_SPI_MOSI[];
extern const PinMap PinMap_SPI_MISO[];
extern const PinMap PinMap_SPI_SCLK[];
extern const PinMap PinMap_SPI_SSEL[];
#endif
//*** ADC ***
#if DEVICE_ANALOGIN
extern const PinMap PinMap_ADC[];
#endif
//*** DAC ***
#if DEVICE_ANALOGOUT
extern const PinMap PinMap_DAC[];
#endif
#endif

View File

@ -0,0 +1,84 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_PINNAMESTYPES_H
#define MBED_PINNAMESTYPES_H
#include "cmsis.h"
typedef enum {
PIN_INPUT = 0,
PIN_OUTPUT
} PinDirection;
typedef enum {
PullNone = 0,
PullUp = 1,
PullDown = 2,
OpenDrainDriveLow = 3,
OpenDrainDriveHigh = 4,
OpenDrain = OpenDrainDriveLow,
PushPull = 5,
AnalogMode = 6,
PullDefault = PullNone
} PinMode;
typedef struct {
en_hsiom_sel_t hsiom : 8;
en_clk_dst_t clock : 8;
PinMode mode : 4;
PinDirection dir : 1;
} PinFunction;
// Encode pin function.
// Output function
#define CY_PIN_FUNCTION(hsiom, clock, mode, dir) (int)(((dir) << 20) | ((mode) << 16) | ((clock) << 8) | (hsiom))
#define CY_PIN_OUT_FUNCTION(hsiom, clock) CY_PIN_FUNCTION(hsiom, clock, PushPull, PIN_OUTPUT)
#define CY_PIN_OD_FUNCTION(hsiom, clock) CY_PIN_FUNCTION(hsiom, clock, OpenDrain, PIN_OUTPUT)
#define CY_PIN_IN_FUNCTION(hsiom, clock) CY_PIN_FUNCTION(hsiom, clock, PullDefault, PIN_INPUT)
#define CY_PIN_PULLUP_FUNCTION(hsiom, clock) CY_PIN_FUNCTION(hsiom, clock, PullUp, PIN_INPUT)
#define CY_PIN_ANALOG_FUNCTION(clock) CY_PIN_FUNCTION(HSIOM_SEL_GPIO, clock, AnalogMode, 0)
// Create unique name to force 32-bit PWM usage on a pin.
#define CY_PIN_FORCE_PWM_32(pin) ((uint32_t)(pin) + 0x8000)
static inline en_hsiom_sel_t CY_PIN_HSIOM(int function)
{
return (en_hsiom_sel_t)(function & 0xFF);
}
static inline en_clk_dst_t CY_PIN_CLOCK(int function)
{
return (en_clk_dst_t)((function >> 8) & 0xFF);
}
static inline PinMode CY_PIN_MODE(int function)
{
return (PinMode)((function >> 16) & 0x0F);
}
static inline PinDirection CY_PIN_DIRECTION(int function)
{
return (PinDirection)((function >> 20) & 1);
}
static inline int CY_PERIPHERAL_BASE(int peripheral)
{
return peripheral & 0xffff0000;
}
#endif

View File

@ -0,0 +1,47 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_PORTNAMES_H
#define MBED_PORTNAMES_H
#ifdef __cplusplus
extern "C" {
#endif
// Port[15-0]
typedef enum {
Port0 = 0x0,
Port1 = 0x1,
Port2 = 0x2,
Port3 = 0x3,
Port4 = 0x4,
Port5 = 0x5,
Port6 = 0x6,
Port7 = 0x7,
Port8 = 0x8,
Port9 = 0x9,
Port10 = 0xA,
Port11 = 0xB,
Port12 = 0xC,
Port13 = 0xD,
Port14 = 0xE
} PortName;
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,171 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "device.h"
#include "analogin_api.h"
#include "cy_sar.h"
#include "psoc6_utils.h"
#include "mbed_assert.h"
#include "mbed_error.h"
#include "pinmap.h"
#include "PeripheralPins.h"
#include "platform/mbed_error.h"
#if DEVICE_ANALOGIN
const uint16_t ADC_MAX_VALUE = 0x0fff;
const uint32_t SAR_BASE_CLOCK_HZ = 18000000; // 18 MHz or less
/** Default SAR channel configuration.
* Notice, that because dynamic SAR MUX switching is disabled,
* per-channel MUX configuration is ignored, thus not configured here.
*/
#define DEFAULT_CHANNEL_CONFIG ( \
CY_SAR_CHAN_SINGLE_ENDED | \
CY_SAR_CHAN_AVG_ENABLE | \
CY_SAR_CHAN_SAMPLE_TIME_0 \
)
/** Global SAR configuration data, modified as channels are configured.
*/
static cy_stc_sar_config_t sar_config = {
.ctrl = CY_SAR_VREF_SEL_VDDA_DIV_2 |
CY_SAR_NEG_SEL_VREF |
CY_SAR_CTRL_COMP_DLY_12 |
CY_SAR_COMP_PWR_50 |
CY_SAR_SARSEQ_SWITCH_DISABLE, /**< Control register */
.sampleCtrl = CY_SAR_RIGHT_ALIGN |
CY_SAR_SINGLE_ENDED_UNSIGNED |
CY_SAR_AVG_CNT_16 |
CY_SAR_AVG_MODE_SEQUENTIAL_FIXED |
CY_SAR_TRIGGER_MODE_FW_ONLY, /**< Sample control register */
.sampleTime01 = (4uL << CY_SAR_SAMPLE_TIME0_SHIFT) |
(4uL << CY_SAR_SAMPLE_TIME1_SHIFT), /**< Sample time in ADC clocks for ST0 and ST1 */
.sampleTime23 = (4uL << CY_SAR_SAMPLE_TIME2_SHIFT) |
(4uL << CY_SAR_SAMPLE_TIME3_SHIFT), /**< Sample time in ADC clocks for ST2 and ST3 */
.rangeThres = 0, /**< Range detect threshold register for all channels (unused)*/
.rangeCond = 0, /**< Range detect mode for all channels (unused)*/
.chanEn = 0, /**< Enable bits for the channels */
.chanConfig = { /**< Channel configuration registers */
DEFAULT_CHANNEL_CONFIG, // chn 0
DEFAULT_CHANNEL_CONFIG, // chn 1
DEFAULT_CHANNEL_CONFIG, // chn 2
DEFAULT_CHANNEL_CONFIG, // chn 3
DEFAULT_CHANNEL_CONFIG, // chn 4
DEFAULT_CHANNEL_CONFIG, // chn 5
DEFAULT_CHANNEL_CONFIG, // chn 6
DEFAULT_CHANNEL_CONFIG, // chn 7
DEFAULT_CHANNEL_CONFIG, // chn 8
DEFAULT_CHANNEL_CONFIG, // chn 9
DEFAULT_CHANNEL_CONFIG, // chn 10
DEFAULT_CHANNEL_CONFIG, // chn 11
DEFAULT_CHANNEL_CONFIG, // chn 12
DEFAULT_CHANNEL_CONFIG, // chn 13
DEFAULT_CHANNEL_CONFIG, // chn 14
DEFAULT_CHANNEL_CONFIG, // chn 15
},
.intrMask = 0, /**< Interrupt enable mask */
.satIntrMask = 0, /**< Saturate interrupt mask register */
.rangeIntrMask = 0, /**< Range interrupt mask register */
.muxSwitch = 0, /**< SARMUX firmware switches to connect analog signals to SAR */
.muxSwitchSqCtrl = 0, /**< SARMUX Switch SAR sequencer control */
.configRouting = false, /**< Configure or ignore routing related registers (muxSwitch, muxSwitchSqCtrl) */
.vrefMvValue = 0, /**< Reference voltage in millivolts used in counts to volts conversion */
};
static bool sar_initialized = false;
static void sar_init(analogin_t *obj)
{
if (!sar_initialized) {
uint32_t sar_clock_divider = CY_INVALID_DIVIDER;
sar_initialized = true;
// Allocate and setup clock.
sar_clock_divider = cy_clk_allocate_divider(CY_SYSCLK_DIV_8_BIT);
if (sar_clock_divider == CY_INVALID_DIVIDER) {
error("SAR clock divider allocation failed.");
return;
}
Cy_SysClk_PeriphSetDivider(CY_SYSCLK_DIV_8_BIT,
sar_clock_divider,
((CY_CLK_PERICLK_FREQ_HZ + SAR_BASE_CLOCK_HZ / 2) / SAR_BASE_CLOCK_HZ) - 1);
Cy_SysClk_PeriphEnableDivider(CY_SYSCLK_DIV_8_BIT, sar_clock_divider);
Cy_SysClk_PeriphAssignDivider(obj->clock, CY_SYSCLK_DIV_8_BIT, sar_clock_divider);
Cy_SAR_Init(obj->base, &sar_config);
Cy_SAR_Enable(obj->base);
}
}
void analogin_init(analogin_t *obj, PinName pin)
{
uint32_t sar = 0;
uint32_t sar_function = 0;
MBED_ASSERT(obj);
MBED_ASSERT(pin != (PinName)NC);
sar = pinmap_peripheral(pin, PinMap_ADC);
if (sar != (uint32_t)NC) {
if (cy_reserve_io_pin(pin)) {
error("ANALOG IN pin reservation conflict.");
}
obj->base = (SAR_Type*)CY_PERIPHERAL_BASE(sar);
obj->pin = pin;
obj->channel_mask = 1 << CY_PIN(pin);
// Configure clock.
sar_function = pinmap_function(pin, PinMap_ADC);
obj->clock = CY_PIN_CLOCK(sar_function);
sar_init(obj);
pin_function(pin, sar_function);
} else {
error("ANALOG IN pinout mismatch.");
}
}
float analogin_read(analogin_t *obj)
{
uint16_t result = analogin_read_u16(obj);
return (float)result * (1.0 / ADC_MAX_VALUE);
}
uint16_t analogin_read_u16(analogin_t *obj)
{
uint32_t result = 0;
Cy_SAR_SetChanMask(obj->base, obj->channel_mask);
Cy_SAR_SetAnalogSwitch(obj->base, CY_SAR_MUX_SWITCH0, obj->channel_mask, CY_SAR_SWITCH_CLOSE);
Cy_SAR_StartConvert(obj->base, CY_SAR_START_CONVERT_SINGLE_SHOT);
if (Cy_SAR_IsEndConversion(obj->base, CY_SAR_WAIT_FOR_RESULT) == CY_SAR_SUCCESS) {
result = Cy_SAR_GetResult32(obj->base, CY_PIN(obj->pin));
} else {
error("ANALOG IN: measurement failed!");
}
Cy_SAR_SetAnalogSwitch(obj->base, CY_SAR_MUX_SWITCH0, obj->channel_mask, CY_SAR_SWITCH_OPEN);
// We are running 16x oversampling extending results to 16 bits.
return (uint16_t)(result);
}
#endif // DEVICE_ANALOGIN

View File

@ -0,0 +1,150 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "device.h"
#include "analogout_api.h"
#include "cy_ctdac.h"
#include "psoc6_utils.h"
#include "mbed_assert.h"
#include "mbed_error.h"
#include "pinmap.h"
#include "PeripheralPins.h"
#include "platform/mbed_error.h"
#if DEVICE_ANALOGOUT
#define CTDAC_NUM_BITS 12
const uint16_t CTDAC_MAX_VALUE = (uint16_t)((1UL << CTDAC_NUM_BITS) - 1);
const uint32_t CTDAC_BASE_CLOCK_HZ = 500000; // 500 kHz or less
#define CTDAC_DEGLITCH_CYCLES 35
/** Global CTDAC configuration data.
*/
static cy_stc_ctdac_config_t ctdac_config = {
.refSource = CY_CTDAC_REFSOURCE_VDDA, /**< Reference source: Vdda or externally through Opamp1 of CTB */
.formatMode = CY_CTDAC_FORMAT_UNSIGNED, /**< Format of DAC value: signed or unsigned */
.updateMode = CY_CTDAC_UPDATE_BUFFERED_WRITE, /**< Update mode: direct or buffered writes or hardware, edge or level */
.deglitchMode = CY_CTDAC_DEGLITCHMODE_UNBUFFERED, /**< Deglitch mode: disabled, buffered, unbuffered, or both */
.outputMode = CY_CTDAC_OUTPUT_VALUE, /**< Output mode: enabled (value or value + 1), high-z, Vssa, or Vdda */
.outputBuffer = CY_CTDAC_OUTPUT_UNBUFFERED, /**< Output path: Buffered through Opamp0 of CTB or connected directly to Pin 6 */
.deepSleep = CY_CTDAC_DEEPSLEEP_DISABLE, /**< Enable or disable the CTDAC during Deep Sleep */
.deglitchCycles = CTDAC_DEGLITCH_CYCLES, /**< Number of deglitch cycles from 0 to 63 */
.value = 0, /**< Current DAC value */
.nextValue = 0, /**< Next DAC value for double buffering */
.enableInterrupt = false, /**< If true, enable interrupt when next value register is transferred to value register */
.configClock = false, /**< Configure or ignore clock information */
};
static bool ctdac_initialized = 0;
static void ctdac_init(dac_t *obj)
{
if (!ctdac_initialized) {
uint32_t dac_clock_divider = CY_INVALID_DIVIDER;
ctdac_initialized = true;
// Allocate and setup clock.
dac_clock_divider = cy_clk_allocate_divider(CY_SYSCLK_DIV_8_BIT);
if (dac_clock_divider == CY_INVALID_DIVIDER) {
error("CTDAC clock divider allocation failed.");
return;
}
Cy_SysClk_PeriphSetDivider(CY_SYSCLK_DIV_8_BIT,
dac_clock_divider,
((CY_CLK_PERICLK_FREQ_HZ + CTDAC_BASE_CLOCK_HZ / 2) / CTDAC_BASE_CLOCK_HZ) - 1);
Cy_SysClk_PeriphEnableDivider(CY_SYSCLK_DIV_8_BIT, dac_clock_divider);
Cy_SysClk_PeriphAssignDivider(obj->clock, CY_SYSCLK_DIV_8_BIT, dac_clock_divider);
Cy_CTDAC_Init(obj->base, &ctdac_config);
Cy_CTDAC_Enable(obj->base);
}
}
void analogout_init(dac_t *obj, PinName pin)
{
uint32_t dac = 0;
uint32_t dac_function = 0;
MBED_ASSERT(obj);
MBED_ASSERT(pin != (PinName)NC);
dac = pinmap_peripheral(pin, PinMap_DAC);
if (dac != (uint32_t)NC) {
if (cy_reserve_io_pin(pin)) {
error("ANALOG OUT pin reservation conflict.");
}
obj->base = (CTDAC_Type*)CY_PERIPHERAL_BASE(dac);
obj->pin = pin;
// Configure clock.
dac_function = pinmap_function(pin, PinMap_DAC);
obj->clock = CY_PIN_CLOCK(dac_function);
pin_function(pin, dac_function);
ctdac_init(obj);
} else {
error("ANALOG OUT pinout mismatch.");
}
}
void analogout_free(dac_t *obj)
{
// Not supported yet.
}
void analogout_write(dac_t *obj, float value)
{
uint32_t val = 0;
if (value > 1.0) {
val = CTDAC_MAX_VALUE;
} else if (value > 0.0) {
val = value * CTDAC_MAX_VALUE;
}
Cy_CTDAC_SetValueBuffered(obj->base, val);
}
void analogout_write_u16(dac_t *obj, uint16_t value)
{
uint32_t val = 0;
val = (value >> (16 - CTDAC_NUM_BITS)); // Convert from 16-bit range.
Cy_CTDAC_SetValueBuffered(obj->base, val);
}
float analogout_read(dac_t *obj)
{
return (float)analogout_read_u16(obj) / 0xffff;
}
uint16_t analogout_read_u16(dac_t *obj)
{
uint16_t value = (obj->base->CTDAC_VAL_NXT >> CTDAC_CTDAC_VAL_NXT_VALUE_Pos) & CTDAC_CTDAC_VAL_NXT_VALUE_Msk;
value <<= (16 - CTDAC_NUM_BITS); // Convert to 16-bit range.
return value;
}
#endif // DEVICE_ANALOGIN

View File

@ -0,0 +1,92 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "device.h"
#include "flash_api.h"
#include "drivers/peripheral/flash/cy_flash.h"
#if DEVICE_FLASH
int32_t flash_init(flash_t *obj)
{
(void)(obj);
return 0;
}
int32_t flash_free(flash_t *obj)
{
(void)(obj);
return 0;
}
int32_t flash_erase_sector(flash_t *obj, uint32_t address)
{
(void)(obj);
int32_t status = 0;
if (Cy_Flash_EraseRow(address) != CY_FLASH_DRV_SUCCESS) {
status = -1;
}
return status;
}
int32_t flash_program_page(flash_t *obj, uint32_t address, const uint8_t *data, uint32_t size)
{
(void)(obj);
int32_t status = 0;
if (Cy_Flash_ProgramRow(address, (const uint32_t *)data) != CY_FLASH_DRV_SUCCESS) {
status = -1;
}
return status;
}
uint32_t flash_get_sector_size(const flash_t *obj, uint32_t address)
{
(void)(obj);
if ((address >= CY_FLASH_BASE) && (address < CY_FLASH_BASE + CY_FLASH_SIZE)) {
return CY_FLASH_SIZEOF_ROW;
}
return MBED_FLASH_INVALID_SIZE;
}
uint32_t flash_get_page_size(const flash_t *obj)
{
(void)(obj);
return CY_FLASH_SIZEOF_ROW;
}
uint32_t flash_get_start_address(const flash_t *obj)
{
(void)(obj);
return CY_FLASH_BASE;
}
uint32_t flash_get_size(const flash_t *obj)
{
(void)(obj);
return CY_FLASH_SIZE;
}
uint8_t flash_get_erase_value(const flash_t *obj)
{
(void)obj;
return 0x00;
}
#endif // DEVICE_FLASH

View File

@ -0,0 +1,99 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cmsis.h"
#include "device.h"
#include "mbed_assert.h"
#include "gpio_object.h"
#include "psoc6_utils.h"
#include "mbed_error.h"
#include "rtx_lib.h"
static inline void gpio_set_dir_mode(gpio_t *obj)
{
MBED_ASSERT(obj);
MBED_ASSERT(obj->port);
MBED_ASSERT(obj->pin != NC);
uint32_t pin = CY_PIN(obj->pin);
uint32_t cymode = gpio_get_cy_drive_mode(obj->dir, obj->mode);
Cy_GPIO_SetDrivemode(obj->port, pin, cymode);
if (obj->dir == PIN_INPUT) {
// Force output to enable pulls.
switch (cymode) {
case CY_GPIO_DM_PULLUP:
Cy_GPIO_Write(obj->port, pin, 1);
break;
case CY_GPIO_DM_PULLDOWN:
Cy_GPIO_Write(obj->port, pin, 0);
break;
}
}
}
void gpio_init(gpio_t *obj, PinName pin)
{
MBED_ASSERT(obj);
obj->pin = pin;
obj->dir = PIN_INPUT;
obj->mode = PullNone;
if (pin == NC) {
return;
}
MBED_ASSERT(CY_PIN(obj->pin) < 8); // PSoC6 architecture supports 8 pins per port.
/*
* Perform i/o reservation only if this is called outside of critical section/interrupt context.
* This is a workaround for mbed_die() implementation, which configures LED1 inside critical section.
* Normally user is advised to perform all of the i/o configuration at the program beginning,
* or elsewhere in the running thread context. when we detect that we are in the wrong context here,
* we assume it's explicitly called from mbed_die() or other fault handling, so eventual forcing
* of the pin mode is deliberate and should not cause more problems.
*/
if (!(IsIrqMode() || IsIrqMasked())) {
if (cy_reserve_io_pin(pin)) {
error("GPIO pin reservation conflict.");
}
}
obj->port = Cy_GPIO_PortToAddr(CY_PORT(obj->pin));
const uint32_t outputVal = 0;
Cy_GPIO_Pin_FastInit(obj->port, CY_PIN(obj->pin), CY_GPIO_DM_HIGHZ, outputVal, HSIOM_SEL_GPIO);
}
void gpio_mode(gpio_t *obj, PinMode mode)
{
MBED_ASSERT(obj);
MBED_ASSERT(obj->pin != NC);
obj->mode = mode;
gpio_set_dir_mode(obj);
}
void gpio_dir(gpio_t *obj, PinDirection direction)
{
MBED_ASSERT(obj);
MBED_ASSERT(obj->pin != NC);
obj->dir = direction;
gpio_set_dir_mode(obj);
}

View File

@ -0,0 +1,282 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cmsis.h"
#include "device.h"
#include "mbed_assert.h"
#include "gpio_object.h"
#include "gpio_irq_api.h"
#include "psoc6_utils.h"
#include "cy_sysint.h"
#define NUM_GPIO_PORTS IOSS_GPIO_GPIO_PORT_NR
#define NUM_GPIO_PORT_PINS 8
#define GPIO_DEFAULT_IRQ_PRIORITY 5
static gpio_irq_t *irq_objects[NUM_GPIO_PORTS][NUM_GPIO_PORT_PINS] = {NULL};
typedef struct irq_port_info_s {
IRQn_Type irqn;
uint32_t pin_mask;
} irq_port_info_t;
static irq_port_info_t irq_port_usage[NUM_GPIO_PORTS] = {{0, 0},};
static void gpio_irq_dispatcher(uint32_t port_id)
{
uint32_t pin;
gpio_irq_event event;
GPIO_PRT_Type *port = Cy_GPIO_PortToAddr(port_id);
for (pin = 0; pin < NUM_GPIO_PORT_PINS; ++pin) {
if (Cy_GPIO_GetInterruptStatusMasked(port, pin)) {
gpio_irq_t *obj = irq_objects[port_id][pin];
MBED_ASSERT(obj);
Cy_GPIO_ClearInterrupt(port, pin);
event = (obj->mode == IRQ_FALL)? IRQ_FALL : IRQ_RISE;
obj->handler(obj->id_arg, event);
}
}
}
static void gpio_irq_dispatcher_port0(void)
{
gpio_irq_dispatcher(0);
}
static void gpio_irq_dispatcher_port1(void)
{
gpio_irq_dispatcher(1);
}
static void gpio_irq_dispatcher_port2(void)
{
gpio_irq_dispatcher(2);
}
static void gpio_irq_dispatcher_port3(void)
{
gpio_irq_dispatcher(3);
}
static void gpio_irq_dispatcher_port4(void)
{
gpio_irq_dispatcher(4);
}
static void gpio_irq_dispatcher_port5(void)
{
gpio_irq_dispatcher(5);
}
static void gpio_irq_dispatcher_port6(void)
{
gpio_irq_dispatcher(6);
}
static void gpio_irq_dispatcher_port7(void)
{
gpio_irq_dispatcher(7);
}
static void gpio_irq_dispatcher_port8(void)
{
gpio_irq_dispatcher(8);
}
static void gpio_irq_dispatcher_port9(void)
{
gpio_irq_dispatcher(9);
}
static void gpio_irq_dispatcher_port10(void)
{
gpio_irq_dispatcher(10);
}
static void gpio_irq_dispatcher_port11(void)
{
gpio_irq_dispatcher(11);
}
static void gpio_irq_dispatcher_port12(void)
{
gpio_irq_dispatcher(12);
}
static void gpio_irq_dispatcher_port13(void)
{
gpio_irq_dispatcher(13);
}
static void gpio_irq_dispatcher_port14(void)
{
gpio_irq_dispatcher(14);
}
static void (*irq_dispatcher_table[])(void) = {
gpio_irq_dispatcher_port0,
gpio_irq_dispatcher_port1,
gpio_irq_dispatcher_port2,
gpio_irq_dispatcher_port3,
gpio_irq_dispatcher_port4,
gpio_irq_dispatcher_port5,
gpio_irq_dispatcher_port6,
gpio_irq_dispatcher_port7,
gpio_irq_dispatcher_port8,
gpio_irq_dispatcher_port9,
gpio_irq_dispatcher_port10,
gpio_irq_dispatcher_port11,
gpio_irq_dispatcher_port12,
gpio_irq_dispatcher_port13,
gpio_irq_dispatcher_port14
};
static IRQn_Type gpio_irq_allocate_channel(gpio_irq_t *obj)
{
#if defined (TARGET_MCU_PSOC6_M0)
obj->cm0p_irq_src = ioss_interrupts_gpio_0_IRQn + obj->port_id;
return cy_m0_nvic_allocate_channel(CY_GPIO_IRQN_ID + obj->port_id);
#else
return (IRQn_Type)(ioss_interrupts_gpio_0_IRQn + obj->port_id);
#endif // M0
}
static void gpio_irq_release_channel(IRQn_Type channel, uint32_t port_id)
{
#if defined (TARGET_MCU_PSOC6_M0)
cy_m0_nvic_release_channel(channel, CY_GPIO_IRQN_ID + port_id);
#endif //M0
}
static int gpio_irq_setup_channel(gpio_irq_t *obj)
{
cy_stc_sysint_t irq_config;
if (irq_port_usage[obj->port_id].pin_mask == 0) {
IRQn_Type irqn = gpio_irq_allocate_channel(obj);
if (irqn < 0) {
return (-1);
}
irq_port_usage[obj->port_id].irqn = irqn;
// Configure NVIC
irq_config.intrPriority = GPIO_DEFAULT_IRQ_PRIORITY;
irq_config.intrSrc = irqn;
#if defined (TARGET_MCU_PSOC6_M0)
irq_config.cm0pSrc = obj->cm0p_irq_src;
#endif
if (Cy_SysInt_Init(&irq_config, irq_dispatcher_table[obj->port_id]) != CY_SYSINT_SUCCESS) {
return(-1);
}
irq_port_usage[obj->port_id].pin_mask |= (1 << obj->pin);
NVIC_EnableIRQ(irqn);
}
return 0;
}
int gpio_irq_init(gpio_irq_t *obj, PinName pin, gpio_irq_handler handler, uint32_t id)
{
if (pin != NC) {
obj->port_id = CY_PORT(pin);
obj->port = Cy_GPIO_PortToAddr(obj->port_id);
obj->pin = CY_PIN(pin);
if (obj->pin > NUM_GPIO_PORT_PINS) {
MBED_ASSERT("Invalid pin ID!");
return (-1);
}
obj->handler = handler;
obj->id_arg = id;
return gpio_irq_setup_channel(obj);
} else {
return (-1);
}
}
void gpio_irq_free(gpio_irq_t *obj)
{
gpio_irq_disable(obj);
// TODO: Need atomicity for the following operations.
NVIC_DisableIRQ(irq_port_usage[obj->port_id].irqn);
irq_port_usage[obj->port_id].pin_mask &= ~(1 << obj->pin);
if (irq_port_usage[obj->port_id].pin_mask == 0) {
gpio_irq_release_channel(irq_port_usage[obj->port_id].irqn, obj->port_id);
return;
}
NVIC_EnableIRQ(irq_port_usage[obj->port_id].irqn);
}
void gpio_irq_set(gpio_irq_t *obj, gpio_irq_event event, uint32_t enable)
{
if (enable) {
if (event == IRQ_RISE) {
if (obj->mode == IRQ_FALL) {
obj->mode += IRQ_RISE;
Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_BOTH);
} else {
obj->mode = IRQ_RISE;
Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_RISING);
}
} else if (event == IRQ_FALL) {
if (obj->mode == IRQ_RISE) {
obj->mode += IRQ_FALL;
Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_BOTH);
} else {
obj->mode = IRQ_FALL;
Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_FALLING);
}
} else {
obj->mode = IRQ_NONE;
Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_DISABLE);
}
} else if (obj->mode != IRQ_NONE) {
if (event == IRQ_RISE) {
if (obj->mode == IRQ_RISE) {
obj->mode = IRQ_NONE;
Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_DISABLE);
} else {
obj->mode = IRQ_FALL;
Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_FALLING);
}
} else if (event == IRQ_FALL) {
if (obj->mode == IRQ_FALL) {
obj->mode = IRQ_NONE;
Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_DISABLE);
} else {
obj->mode = IRQ_RISE;
Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_RISING);
}
} else {
obj->mode = IRQ_NONE;
Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_DISABLE);
}
}
}
void gpio_irq_enable(gpio_irq_t *obj)
{
Cy_GPIO_SetInterruptMask(obj->port, obj->pin, 1);
}
void gpio_irq_disable(gpio_irq_t *obj)
{
Cy_GPIO_SetInterruptMask(obj->port, obj->pin, 0);
}

View File

@ -0,0 +1,85 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_GPIO_OBJECTS_H
#define MBED_GPIO_OBJECTS_H
#include "mbed_assert.h"
#include "PinNamesTypes.h"
#include "PinNames.h"
#include "drivers/peripheral/gpio/cy_gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
struct gpio_s {
GPIO_PRT_Type *port;
PinName pin;
PinDirection dir;
PinMode mode;
};
typedef struct gpio_s gpio_t;
/** Set the output value
*
* @param obj The GPIO object
* @param value The value to be set
*/
static inline void gpio_write(gpio_t *obj, int value)
{
MBED_ASSERT(obj->pin != NC);
Cy_GPIO_Write(obj->port, CY_PIN(obj->pin), value);
}
/** Read the input value
*
* @param obj The GPIO object
* @return An integer value 1 or 0
*/
static inline int gpio_read(gpio_t *obj)
{
MBED_ASSERT(obj->pin != NC);
return Cy_GPIO_Read(obj->port, CY_PIN(obj->pin));
}
static inline int gpio_is_connected(const gpio_t *obj)
{
return obj->pin != NC;
}
/** Get the pin name from the port's pin number
*
* @param port The port name
* @param pin_n The pin number within the specified port
* @return The pin name for the port's pin number
*/
inline PinName port_pin(PortName port, int pin_n)
{
return (PinName)((port << 8) + pin_n);
}
#ifdef __cplusplus
}
#endif
#endif // MBED_GPIO_OBJECTS_H

View File

@ -0,0 +1,548 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cmsis.h"
#include "mbed_assert.h"
#include "mbed_error.h"
#include "PeripheralPins.h"
#include "pinmap.h"
#include "i2c_api.h"
#include "psoc6_utils.h"
#include "drivers/peripheral/sysclk/cy_sysclk.h"
#include "drivers/peripheral/gpio/cy_gpio.h"
#include "drivers/peripheral/scb/cy_scb_i2c.h"
#include "drivers/peripheral/sysint/cy_sysint.h"
#define I2C_DEFAULT_SPEED 100000
#define NUM_I2C_PORTS 8
#define I2C_DEFAULT_IRQ_PRIORITY 3
#define I2C_NUM_DIVIDERS 3
#define MIN_I2C_CLOCK_FREQUENCY CY_SCB_I2C_SLAVE_STD_CLK_MIN
// Default timeout in milliseconds.
#define I2C_DEFAULT_TIMEOUT 1000
#define PENDING_NONE 0
#define PENDING_RX 1
#define PENDING_TX 2
#define PENDING_TX_RX 3
typedef enum {
I2C_DIVIDER_LOW = 0,
I2C_DIVIDER_MID,
I2C_DIVIDER_HIGH,
I2C_INVALID_DIVIDER = 0xff
} I2cDividerType;
typedef struct {
uint32_t div_num;
cy_en_divider_types_t div_type;
uint32_t clk_frequency;
} I2cDividerInfo;
static const cy_stc_scb_i2c_config_t default_i2c_config = {
.i2cMode = CY_SCB_I2C_MASTER,
.useRxFifo = true,
.useTxFifo = true,
.slaveAddress = 0,
.slaveAddressMask = 0,
.acceptAddrInFifo = false,
.ackGeneralAddr = false,
.enableWakeFromSleep = false
};
static I2cDividerInfo i2c_dividers[I2C_NUM_DIVIDERS] = {
{ I2C_INVALID_DIVIDER, 0, CY_SCB_I2C_SLAVE_STD_CLK_MIN }, // Low divider uses lowest possible frequency.
{ I2C_INVALID_DIVIDER, 0, CY_SCB_I2C_SLAVE_FST_CLK_MIN },
{ I2C_INVALID_DIVIDER, 0, CY_SCB_I2C_SLAVE_FSTP_CLK_MIN }
};
typedef struct i2c_s i2c_obj_t;
#if DEVICE_I2C_ASYNCH
#define OBJ_P(in) (&(in->i2c))
#else
#define OBJ_P(in) (in)
#endif
#if DEVICE_I2C_ASYNCH
static IRQn_Type i2c_irq_allocate_channel(i2c_obj_t *obj)
{
#if defined (TARGET_MCU_PSOC6_M0)
obj->cm0p_irq_src = scb_0_interrupt_IRQn + obj->i2c_id;
return cy_m0_nvic_allocate_channel(CY_SERIAL_IRQN_ID + obj->i2c_id);
#else
return (IRQn_Type)(ioss_interrupts_gpio_0_IRQn + obj->i2c_id);
#endif // M0
}
static void i2c_irq_release_channel(IRQn_Type channel, uint32_t i2c_id)
{
#if defined (TARGET_MCU_PSOC6_M0)
cy_m0_nvic_release_channel(channel, CY_SERIAL_IRQN_ID + i2c_id);
#endif //M0
}
static int i2c_irq_setup_channel(i2c_obj_t *obj)
{
cy_stc_sysint_t irq_config;
if (obj->irqn == unconnected_IRQn) {
IRQn_Type irqn = i2c_irq_allocate_channel(obj);
if (irqn < 0) {
return (-1);
}
// Configure NVIC
irq_config.intrPriority = I2C_DEFAULT_IRQ_PRIORITY;
irq_config.intrSrc = irqn;
#if defined (TARGET_MCU_PSOC6_M0)
irq_config.cm0pSrc = obj->cm0p_irq_src;
#endif
if (Cy_SysInt_Init(&irq_config, (cy_israddress)(obj->handler)) != CY_SYSINT_SUCCESS) {
return(-1);
}
obj->irqn = irqn;
NVIC_EnableIRQ(irqn);
}
return 0;
}
#endif // DEVICE_I2C_ASYNCH
static int allocate_divider(I2cDividerType divider)
{
I2cDividerInfo *p_div = &i2c_dividers[divider];
if (p_div->div_num == CY_INVALID_DIVIDER) {
p_div->div_num = cy_clk_allocate_divider(CY_SYSCLK_DIV_8_BIT);
if (p_div->div_num != CY_INVALID_DIVIDER) {
p_div->div_type = CY_SYSCLK_DIV_8_BIT;
} else {
p_div->div_num = cy_clk_allocate_divider(CY_SYSCLK_DIV_16_BIT);
if (p_div->div_num != CY_INVALID_DIVIDER) {
p_div->div_type = CY_SYSCLK_DIV_16_BIT;
}
}
}
if (p_div->div_num != CY_INVALID_DIVIDER) {
// Set up proper frequency;
uint32_t div_value = CY_CLK_PERICLK_FREQ_HZ / p_div->clk_frequency;
p_div->clk_frequency = CY_CLK_PERICLK_FREQ_HZ / div_value;
if (Cy_SysClk_PeriphSetDivider(p_div->div_type, p_div->div_num, div_value) == CY_SYSCLK_SUCCESS) {
Cy_SysClk_PeriphEnableDivider(p_div->div_type, p_div->div_num);
} else {
p_div->div_num = CY_INVALID_DIVIDER;
}
}
return (p_div->div_num == CY_INVALID_DIVIDER)? -1 : 0;
}
/*
* Select one of the 3 dividers used depending on the required frequency.
*/
static I2cDividerType select_divider(uint32_t frequency)
{
if (frequency <= (MIN_I2C_CLOCK_FREQUENCY / CY_SCB_I2C_DUTY_CYCLE_MAX)) {
// Required speed lower than min supported.
return I2C_INVALID_DIVIDER;
} else if (frequency <= CY_SCB_I2C_STD_DATA_RATE) {
return I2C_DIVIDER_LOW;
} else if (frequency <= CY_SCB_I2C_FST_DATA_RATE) {
return I2C_DIVIDER_MID;
} else if (frequency <= CY_SCB_I2C_FSTP_DATA_RATE) {
return I2C_DIVIDER_HIGH;
} else {
// Required speed too high;
return I2C_INVALID_DIVIDER;
}
}
/*
* Initializes i2c clock for the required speed
*/
static cy_en_sysclk_status_t i2c_init_clock(i2c_obj_t *obj, uint32_t speed)
{
I2cDividerInfo *p_div = NULL;
cy_en_sysclk_status_t status = CY_SYSCLK_INVALID_STATE;
I2cDividerType divider = select_divider(speed);
if (divider == I2C_INVALID_DIVIDER) {
error("i2c: required speed/frequency is out of valid range.");
return CY_SYSCLK_BAD_PARAM;
}
if (allocate_divider(divider) < 0) {
error("i2c: cannot allocate clock divider.");
return CY_SYSCLK_INVALID_STATE;
}
obj->divider = divider;
p_div = &i2c_dividers[divider];
status = Cy_SysClk_PeriphAssignDivider(obj->clock, p_div->div_type, p_div->div_num);
if (status != CY_SYSCLK_SUCCESS) {
error("i2c: cannot assign clock divider.");
return status;
}
/* Set desired speed/frequency */
obj->actual_speed = Cy_SCB_I2C_SetDataRate(obj->base, speed, p_div->clk_frequency);
return (obj->actual_speed != 0)? CY_SYSCLK_SUCCESS : CY_SYSCLK_BAD_PARAM;
}
/*
* Initializes i/o pins for i2c sda/scl.
*/
static void i2c_init_pins(i2c_obj_t *obj)
{
int sda_function = pinmap_function(obj->pin_sda, PinMap_I2C_SDA);
int scl_function = pinmap_function(obj->pin_scl, PinMap_I2C_SCL);
pin_function(obj->pin_sda, sda_function);
pin_function(obj->pin_scl, scl_function);
}
/*
* Initializes and enables I2C/SCB.
*/
static void i2c_init_peripheral(i2c_obj_t *obj)
{
cy_stc_scb_i2c_config_t i2c_config = default_i2c_config;
I2cDividerInfo *p_div = &i2c_dividers[obj->divider];
Cy_SCB_I2C_Init(obj->base, &i2c_config, &obj->context);
Cy_SCB_I2C_SetDataRate(obj->base,obj->actual_speed, p_div->clk_frequency);
Cy_SCB_I2C_Enable(obj->base);
}
/*
* Coverts PDL status into Mbed status.
*/
static int i2c_convert_status(cy_en_scb_i2c_status_t status)
{
switch (status) {
case CY_SCB_I2C_MASTER_NOT_READY:
case CY_SCB_I2C_MASTER_MANUAL_ARB_LOST:
case CY_SCB_I2C_MASTER_MANUAL_BUS_ERR:
case CY_SCB_I2C_MASTER_MANUAL_ABORT_START:
return I2C_ERROR_BUS_BUSY;
case CY_SCB_I2C_MASTER_MANUAL_TIMEOUT:
case CY_SCB_I2C_MASTER_MANUAL_ADDR_NAK:
case CY_SCB_I2C_MASTER_MANUAL_NAK:
return I2C_ERROR_NO_SLAVE;
case CY_SCB_I2C_SUCCESS:
case CY_SCB_I2C_BAD_PARAM:
default:
return 0;
}
}
/*
* Callback function to handle into and out of deep sleep state transitions.
*/
#if DEVICE_SLEEP && DEVICE_LPTICKER
static cy_en_syspm_status_t i2c_pm_callback(cy_stc_syspm_callback_params_t *callback_params)
{
cy_stc_syspm_callback_params_t params = *callback_params;
i2c_obj_t *obj = (i2c_obj_t *)params.context;
params.context = &obj->context;
return Cy_SCB_I2C_DeepSleepCallback(&params);
}
#endif // DEVICE_SLEEP && DEVICE_LPTICKER
void i2c_init(i2c_t *obj_in, PinName sda, PinName scl)
{
i2c_obj_t *obj = OBJ_P(obj_in);
uint32_t i2c = pinmap_peripheral(sda, PinMap_I2C_SDA);
i2c = pinmap_merge(i2c, pinmap_peripheral(scl, PinMap_I2C_SCL));
if (i2c != (uint32_t)NC) {
if (cy_reserve_io_pin(sda) || cy_reserve_io_pin(scl)) {
error("I2C pin reservation conflict.");
}
obj->base = (CySCB_Type*)i2c;
obj->i2c_id = ((I2CName)i2c - I2C_0) / (I2C_1 - I2C_0);
obj->pin_sda = sda;
obj->pin_scl = scl;
obj->clock = CY_PIN_CLOCK(pinmap_function(scl, PinMap_I2C_SCL));
obj->divider = I2C_INVALID_DIVIDER;
obj->mode = CY_SCB_I2C_MASTER;
obj->timeout = I2C_DEFAULT_TIMEOUT;
#if DEVICE_I2C_ASYNCH
obj->pending = PENDING_NONE;
obj->events = 0;
#endif // DEVICE_I2C_ASYNCH
i2c_init_clock(obj, I2C_DEFAULT_SPEED);
i2c_init_pins(obj);
i2c_init_peripheral(obj);
#if DEVICE_SLEEP && DEVICE_LPTICKER
obj->pm_callback_handler.callback = i2c_pm_callback;
obj->pm_callback_handler.type = CY_SYSPM_DEEPSLEEP;
obj->pm_callback_handler.skipMode = 0;
obj->pm_callback_handler.callbackParams = &obj->pm_callback_params;
obj->pm_callback_params.base = obj->base;
obj->pm_callback_params.context = obj;
if (!Cy_SysPm_RegisterCallback(&obj->pm_callback_handler)) {
error("PM callback registration failed!");
}
#endif // DEVICE_SLEEP && DEVICE_LPTICKER
} else {
error("I2C pinout mismatch. Requested pins Rx and Tx can't be used for the same I2C communication.");
}
}
void i2c_frequency(i2c_t *obj_in, int hz)
{
i2c_obj_t *obj = OBJ_P(obj_in);
Cy_SCB_I2C_Disable(obj->base, &obj->context);
i2c_init_clock(obj, hz);
Cy_SCB_I2C_Enable(obj->base);
}
int i2c_start(i2c_t *obj_in)
{
// Unsupported, start condition is sent automatically.
return 0;
}
int i2c_stop(i2c_t *obj_in)
{
// Unsupported, stop condition is sent automatically.
return 0;
}
int i2c_read(i2c_t *obj_in, int address, char *data, int length, int stop)
{
cy_en_scb_i2c_status_t status = CY_SCB_I2C_SUCCESS;
i2c_obj_t *obj = OBJ_P(obj_in);
cy_en_scb_i2c_command_t ack = CY_SCB_I2C_ACK;
int byte_count = 0;
address >>= 1;
// Start transaction, send address.
if (obj->context.state == CY_SCB_I2C_IDLE) {
status = Cy_SCB_I2C_MasterSendStart(obj->base, address, CY_SCB_I2C_READ_XFER, obj->timeout, &obj->context);
}
if (status == CY_SCB_I2C_SUCCESS) {
while (length > 0) {
if (length == 1) {
ack = CY_SCB_I2C_NAK;
}
status = Cy_SCB_I2C_MasterReadByte(obj->base, ack, (uint8_t *)data, obj->timeout, &obj->context);
if (status != CY_SCB_I2C_SUCCESS) {
break;
}
++byte_count;
--length;
++data;
}
// SCB in I2C mode is very time sensitive. In practice we have to request STOP after
// each block, otherwise it may break the transmission.
Cy_SCB_I2C_MasterSendStop(obj->base, obj->timeout, &obj->context);
}
if (status != CY_SCB_I2C_SUCCESS) {
Cy_SCB_I2C_MasterSendStop(obj->base, obj->timeout, &obj->context);
byte_count = i2c_convert_status(status);
}
return byte_count;
}
int i2c_write(i2c_t *obj_in, int address, const char *data, int length, int stop)
{
cy_en_scb_i2c_status_t status = CY_SCB_I2C_SUCCESS;
i2c_obj_t *obj = OBJ_P(obj_in);
int byte_count = 0;
address >>= 1;
// Start transaction, send address.
if (obj->context.state == CY_SCB_I2C_IDLE) {
status = Cy_SCB_I2C_MasterSendStart(obj->base, address, CY_SCB_I2C_WRITE_XFER, obj->timeout, &obj->context);
}
if (status == CY_SCB_I2C_SUCCESS) {
while (length > 0) {
status = Cy_SCB_I2C_MasterWriteByte(obj->base, *data, obj->timeout, &obj->context);
if (status != CY_SCB_I2C_SUCCESS) {
break;;
}
++byte_count;
--length;
++data;
}
// SCB in I2C mode is very time sensitive. In practice we have to request STOP after
// each block, otherwise it may break the transmission.
Cy_SCB_I2C_MasterSendStop(obj->base, obj->timeout, &obj->context);
}
if (status != CY_SCB_I2C_SUCCESS) {
Cy_SCB_I2C_MasterSendStop(obj->base, obj->timeout, &obj->context);
byte_count = i2c_convert_status(status);
}
return byte_count;
}
void i2c_reset(i2c_t *obj_in)
{
i2c_stop(obj_in);
}
int i2c_byte_read(i2c_t *obj_in, int last)
{
i2c_obj_t *obj = OBJ_P(obj_in);
uint8_t tmp_byte = 0;
cy_en_scb_i2c_command_t ack = last? CY_SCB_I2C_NAK : CY_SCB_I2C_ACK;
cy_en_scb_i2c_status_t status = Cy_SCB_I2C_MasterReadByte(obj->base, ack, &tmp_byte, obj->timeout, &obj->context);
if (status == CY_SCB_I2C_SUCCESS) {
return tmp_byte;
} else {
return 0;
}
}
int i2c_byte_write(i2c_t *obj_in, int data)
{
i2c_obj_t *obj = OBJ_P(obj_in);
cy_en_scb_i2c_status_t status = Cy_SCB_I2C_MasterWriteByte(obj->base, (uint8_t)data, obj->timeout, &obj->context);
switch (status) {
case CY_SCB_I2C_MASTER_MANUAL_TIMEOUT:
return 2;
case CY_SCB_I2C_MASTER_MANUAL_ADDR_NAK:
case CY_SCB_I2C_MASTER_MANUAL_NAK:
return 0;
case CY_SCB_I2C_SUCCESS:
return 1;
default:
// Error has occurred.
return (-1);
}
}
#if DEVICE_I2C_ASYNCH
void i2c_transfer_asynch(i2c_t *obj_in,
const void *tx,
size_t tx_length,
void *rx, size_t rx_length,
uint32_t address,
uint32_t stop,
uint32_t handler,
uint32_t event,
DMAUsage hint)
{
i2c_obj_t *obj = OBJ_P(obj_in);
(void)hint; // At the moment we do not support DMA transfers, so this parameter gets ignored.
if (obj->pending != PENDING_NONE) {
return;
}
obj->rx_config.slaveAddress = address >> 1;
obj->tx_config.slaveAddress = address >> 1;
obj->events = event;
obj->handler = handler;
if (i2c_irq_setup_channel(obj) < 0) {
return;
}
obj->rx_config.buffer = rx;
obj->rx_config.bufferSize = rx_length;
obj->rx_config.xferPending = !stop;
obj->tx_config.buffer = (void*)tx;
obj->tx_config.bufferSize = tx_length;
obj->tx_config.xferPending = rx_length || !stop;
if (tx_length) {
// Write first, then read, or write only.
if (rx_length > 0) {
obj->pending = PENDING_TX_RX;
} else {
obj->pending = PENDING_TX;
}
Cy_SCB_I2C_MasterWrite(obj->base, &obj->tx_config, &obj->context);
} else if (rx_length) {
// Read transaction;
obj->pending = PENDING_RX;
Cy_SCB_I2C_MasterRead(obj->base, &obj->rx_config, &obj->context);
}
}
uint32_t i2c_irq_handler_asynch(i2c_t *obj_in)
{
i2c_obj_t *obj = OBJ_P(obj_in);
uint32_t event = 0;
// Process actual interrupt.
Cy_SCB_I2C_Interrupt(obj->base, &obj->context);
if (obj->context.state == CY_SCB_I2C_MASTER_CMPLT) {
if (obj->context.masterStatus & CY_SCB_I2C_MASTER_ERR) {
if (obj->context.masterStatus & CY_SCB_I2C_MASTER_ADDR_NAK) {
event = I2C_EVENT_ERROR_NO_SLAVE;
} else if (obj->context.masterStatus & CY_SCB_I2C_MASTER_DATA_NAK) {
event = I2C_EVENT_TRANSFER_EARLY_NACK;
} else {
event = I2C_EVENT_ERROR;
}
} else {
// Check if a read phase is pending after write.
if (obj->pending == PENDING_TX_RX) {
obj->pending = PENDING_RX;
Cy_SCB_I2C_MasterRead(obj->base, &obj->rx_config, &obj->context);
} else {
event = I2C_EVENT_TRANSFER_COMPLETE;
}
}
}
if (event) {
obj->pending = PENDING_NONE;
}
return event & obj->events;
}
uint8_t i2c_active(i2c_t *obj_in)
{
i2c_obj_t *obj = OBJ_P(obj_in);
return (obj->pending != PENDING_NONE);
}
void i2c_abort_asynch(i2c_t *obj_in)
{
i2c_obj_t *obj = OBJ_P(obj_in);
if (obj->pending != PENDING_NONE) {
if (obj->pending == PENDING_RX) {
Cy_SCB_I2C_MasterAbortRead(obj->base, &obj->context);
} else {
Cy_SCB_I2C_MasterAbortWrite(obj->base, &obj->context);
}
}
}
#endif // DEVICE_ASYNCH

View File

@ -0,0 +1,162 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stddef.h>
#include "device.h"
#include "mbed_error.h"
#include "lp_ticker_api.h"
#include "device/drivers/peripheral/mcwdt/cy_mcwdt.h"
#include "device/drivers/peripheral/sysint/cy_sysint.h"
#include "psoc6_utils.h"
#if DEVICE_LPTICKER
/*
* Low Power Timer API on PSoC6 uses MCWD0 timer0 to implement functionality.
*/
#if defined(TARGET_MCU_PSOC6_M0)
#define LPT_MCWDT_UNIT MCWDT_STRUCT0
#define LPT_INTERRUPT_PRIORITY 3
#define LPT_INTERRUPT_SOURCE srss_interrupt_mcwdt_0_IRQn
#else
#define LPT_MCWDT_UNIT MCWDT_STRUCT1
#define LPT_INTERRUPT_PRIORITY 6
#define LPT_INTERRUPT_SOURCE srss_interrupt_mcwdt_1_IRQn
#endif
#define LPT_MCWDT_DELAY_WAIT 0 // Recommended value is 93, but then we fail function execution time test.
static const ticker_info_t lp_ticker_info = {
.frequency = CY_CLK_WCO_FREQ_HZ,
.bits = 16,
};
static bool lpt_init_done = false;
// Timer h/w configuration.
static cy_stc_mcwdt_config_t config = {
.c0Match = 0,
.c1Match = 0,
.c0Mode = CY_MCWDT_MODE_INT,
.c1Mode = CY_MCWDT_MODE_NONE,
.c2ToggleBit = 0,
.c2Mode = CY_MCWDT_MODE_NONE,
.c0ClearOnMatch = false,
.c1ClearOnMatch = false,
.c0c1Cascade = false,
.c1c2Cascade = false
};
// Interrupt configuration.
static cy_stc_sysint_t lpt_sysint_config = {
#if defined(TARGET_MCU_PSOC6_M0)
.intrSrc = (IRQn_Type)(-1),
.cm0pSrc = LPT_INTERRUPT_SOURCE,
#else
.intrSrc = LPT_INTERRUPT_SOURCE,
#endif
.intrPriority = LPT_INTERRUPT_PRIORITY
};
void lp_ticker_init(void)
{
lp_ticker_disable_interrupt();
lp_ticker_clear_interrupt();
if (lpt_init_done) {
return;
}
#ifdef TARGET_MCU_PSOC6_M0
// Allocate NVIC channel.
lpt_sysint_config.intrSrc = cy_m0_nvic_allocate_channel(CY_LP_TICKER_IRQN_ID);
if (lpt_sysint_config.intrSrc == (IRQn_Type)(-1)) {
// No free NVIC channel.
error("LP_TICKER NVIC channel allocation failed.");
return;
}
#endif
Cy_MCWDT_Init(LPT_MCWDT_UNIT, &config);
Cy_SysInt_Init(&lpt_sysint_config, lp_ticker_irq_handler);
NVIC_EnableIRQ(lpt_sysint_config.intrSrc);
Cy_MCWDT_Enable(LPT_MCWDT_UNIT, CY_MCWDT_CTR0, LPT_MCWDT_DELAY_WAIT);
lpt_init_done = true;
}
void lp_ticker_free(void)
{
NVIC_DisableIRQ(lpt_sysint_config.intrSrc);
Cy_MCWDT_Disable(LPT_MCWDT_UNIT, CY_MCWDT_CTR0, LPT_MCWDT_DELAY_WAIT);
#ifdef TARGET_MCU_PSOC6_M0
cy_m0_nvic_release_channel(CY_LP_TICKER_IRQN_ID, lpt_sysint_config.intrSrc);
lpt_sysint_config.intrSrc = (IRQn_Type)(-1);
#endif
lpt_init_done = 0;
}
uint32_t lp_ticker_read(void)
{
return Cy_MCWDT_GetCount(LPT_MCWDT_UNIT, CY_MCWDT_COUNTER0);
}
void lp_ticker_set_interrupt(timestamp_t timestamp)
{
uint16_t delay;
uint16_t current = Cy_MCWDT_GetCount(LPT_MCWDT_UNIT, CY_MCWDT_COUNTER0);
uint16_t new_ts = (uint16_t)timestamp;
delay = new_ts - current;
// Make sure the event is set for the future. Mbed internally will not schedule
// delays longer than 0x7000, so too large delay means it should occur already.
// MCWDT has internal delay of about 1.5 LF clock ticks, so this is the minimum
// that we can schedule.
if ((delay < 3) || (delay > (uint16_t)(-3))) {
// Cheating a bit here.
new_ts = current + 3;
}
// Cypress PDL manual says that valid match range is 1..65535.
if (new_ts == 0) {
new_ts = 1;
}
// Set up and enable match interrupt.
Cy_MCWDT_SetMatch(LPT_MCWDT_UNIT, CY_MCWDT_COUNTER0, new_ts, LPT_MCWDT_DELAY_WAIT);
Cy_MCWDT_SetInterruptMask(LPT_MCWDT_UNIT, CY_MCWDT_CTR0);
}
void lp_ticker_disable_interrupt(void)
{
Cy_MCWDT_SetInterruptMask(LPT_MCWDT_UNIT, 0);
}
void lp_ticker_clear_interrupt(void)
{
Cy_MCWDT_ClearInterrupt(LPT_MCWDT_UNIT, CY_MCWDT_CTR0);
}
void lp_ticker_fire_interrupt(void)
{
NVIC_SetPendingIRQ(lpt_sysint_config.intrSrc);
}
const ticker_info_t* lp_ticker_get_info(void)
{
return &lp_ticker_info;
}
#endif // DEVICE_LPTICKER

View File

@ -0,0 +1,52 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
* Copyright (c) 2016 u-blox
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_MBED_RTX_H
#define MBED_MBED_RTX_H
#if defined(TARGET_MCU_PSOC6_M0)
#ifdef TARGET_PSA
#ifndef INITIAL_SP
#define INITIAL_SP (PSA_SECURE_RAM_START + PSA_SECURE_RAM_SIZE)
#endif // INITIAL_SP
#else
#ifndef INITIAL_SP
#define INITIAL_SP (0x08000000 + 0x00010000) // Ram origin + length
#endif // INITIAL_SP
#endif // TARGET_PSA
#elif defined(TARGET_MCU_PSOC6_M4)
#ifdef TARGET_PSA
#ifndef INITIAL_SP
#define INITIAL_SP (PSA_NON_SECURE_RAM_START + PSA_NON_SECURE_RAM_SIZE)
#endif // INITIAL_SP
#else
#ifndef INITIAL_SP
#define INITIAL_SP (0x08010000 + 0x00037800) // Ram origin + length
#endif // INITIAL_SP
#endif // TARGET_PSA
#else
#error "Unknown board"
#endif
#endif // MBED_MBED_RTX_H

View File

@ -0,0 +1,227 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_OBJECTS_H
#define MBED_OBJECTS_H
#include "cmsis.h"
#include "PeripheralNames.h"
#include "PinNames.h"
#include "PortNames.h"
#include "gpio_irq_api.h"
#include "gpio_object.h"
#include "drivers/peripheral/sysclk/cy_sysclk.h"
#include "drivers/peripheral/syspm/cy_syspm.h"
#ifdef __cplusplus
extern "C" {
#endif
#if DEVICE_INTERRUPTIN
struct gpio_irq_s {
GPIO_PRT_Type* port;
uint32_t port_id;
uint32_t pin;
gpio_irq_event mode;
gpio_irq_handler handler;
uint32_t id_arg;
#if defined (TARGET_MCU_PSOC6_M0)
cy_en_intr_t cm0p_irq_src;
#endif
};
#endif // DEVICE_INTERRUPTIN
struct port_s {
GPIO_PRT_Type *port;
uint32_t port_id;
uint32_t mask;
PinDirection direction;
PinMode mode;
// __IO uint32_t *reg_in;
// __IO uint32_t *reg_out;
};
// struct analogin_s {
// ADCName adc;
// PinName pin;
// uint32_t channel;
// };
#if DEVICE_SERIAL
#include "cy_scb_uart.h"
struct serial_s {
CySCB_Type *base;
uint32_t serial_id;
PinName pin_rx;
PinName pin_tx;
PinName pin_cts;
PinName pin_rts;
en_clk_dst_t clock;
cy_en_divider_types_t div_type;
uint8_t div_num;
uint8_t data_width;
cy_en_scb_uart_stop_bits_t stop_bits;
cy_en_scb_uart_parity_t parity;
#if DEVICE_SERIAL_ASYNCH
uint32_t rx_events;
bool rx_pending;
uint32_t tx_events;
bool tx_pending;
cy_israddress async_handler;
#endif // DEVICE_SERIAL_ASYNCH
#if DEVICE_SLEEP && DEVICE_LPTICKER
cy_stc_syspm_callback_params_t pm_callback_params;
cy_stc_syspm_callback_t pm_callback_handler;
#endif
};
#endif // DEVICE_SERIAL
#if DEVICE_SPI
#include "cy_scb_spi.h"
struct spi_s {
CySCB_Type *base;
uint32_t spi_id;
PinName pin_miso;
PinName pin_mosi;
PinName pin_sclk;
PinName pin_ssel;
en_clk_dst_t clock;
uint32_t div_num;
cy_en_divider_types_t div_type;
uint32_t clk_frequency;
cy_en_scb_spi_mode_t ms_mode;
cy_en_scb_spi_sclk_mode_t clk_mode;
uint8_t data_bits;
cy_stc_scb_spi_context_t context;
#if DEVICE_SPI_ASYNCH
IRQn_Type irqn;
#if defined (TARGET_MCU_PSOC6_M0)
cy_en_intr_t cm0p_irq_src;
#endif
uint16_t pending;
uint16_t events;
uint32_t handler;
void *rx_buffer;
uint32_t rx_buffer_size;
void *tx_buffer;
uint32_t tx_buffer_size;
#endif // DEVICE_SPI_ASYNCH
#if DEVICE_SLEEP && DEVICE_LPTICKER
cy_stc_syspm_callback_params_t pm_callback_params;
cy_stc_syspm_callback_t pm_callback_handler;
#endif
};
#endif // DEVICE_SPI
#if DEVICE_I2C
#include "cy_scb_i2c.h"
struct i2c_s {
CySCB_Type *base;
uint32_t i2c_id;
PinName pin_sda;
PinName pin_scl;
en_clk_dst_t clock;
uint32_t divider;
uint32_t actual_speed;
cy_en_scb_i2c_mode_t mode;
uint32_t timeout;
#if DEVICE_I2C_SLAVE
uint16_t ADDRESS;
uint16_t is_setAddress;
#endif
cy_stc_scb_i2c_context_t context;
#if DEVICE_I2C_ASYNCH
cy_stc_scb_i2c_master_xfer_config_t rx_config;
cy_stc_scb_i2c_master_xfer_config_t tx_config;
IRQn_Type irqn;
#if defined (TARGET_MCU_PSOC6_M0)
cy_en_intr_t cm0p_irq_src;
#endif
uint16_t pending;
uint16_t events;
uint32_t handler;
#endif // DEVICE_I2C_ASYNCH
#if DEVICE_SLEEP && DEVICE_LPTICKER
cy_stc_syspm_callback_params_t pm_callback_params;
cy_stc_syspm_callback_t pm_callback_handler;
#endif
};
#endif // DEVICE_I2C
#if DEVICE_PWMOUT
#include "cy_tcpwm.h"
struct pwmout_s {
TCPWM_Type *base;
PinName pin;
uint32_t counter_id;
uint32_t clock;
uint32_t period;
uint32_t pulse_width;
uint32_t prescaler;
#if DEVICE_SLEEP && DEVICE_LPTICKER
cy_stc_syspm_callback_params_t pm_callback_params;
cy_stc_syspm_callback_t pm_callback_handler;
#endif
};
#endif // DEVICE_PWMOUT
#if DEVICE_ANALOGIN
#include "cy_sar.h"
struct analogin_s {
SAR_Type *base;
PinName pin;
uint32_t channel_mask;
uint32_t clock;
};
#endif // DEVICE_ANALOGIN
#if DEVICE_ANALOGOUT
#include "cy_ctdac.h"
struct dac_s {
CTDAC_Type *base;
PinName pin;
uint32_t clock;
};
#endif // DEVICE_ANALOGOUT
#if DEVICE_FLASH
struct flash_s {
/* nothing to be stored for now */
void *dummy;
};
#endif // DEVICE_FLASH
#if DEVICE_TRNG
struct trng_s {
/* nothing to be stored for now */
void *dummy;
};
#endif // DEVICE_TRNG
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,67 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed_assert.h"
#include "pinmap.h"
#include "mbed_error.h"
#include "cy_gpio.h"
#include "psoc6_utils.h"
void pin_function(PinName pin, int function)
{
if (pin != NC) {
GPIO_PRT_Type *port = Cy_GPIO_PortToAddr(CY_PORT(pin));
uint32_t mode = gpio_get_cy_drive_mode(CY_PIN_DIRECTION(function), CY_PIN_MODE(function));
Cy_GPIO_Pin_FastInit(port, CY_PIN(pin), mode, 1, CY_PIN_HSIOM(function));
// Force output to enable pulls.
switch (mode) {
case CY_GPIO_DM_PULLUP:
Cy_GPIO_Write(port, CY_PIN(pin), 1);
break;
case CY_GPIO_DM_PULLDOWN:
Cy_GPIO_Write(port, CY_PIN(pin), 0);
break;
default:
/* do nothing */
break;
}
}
}
void pin_mode(PinName pin, PinMode mode)
{
if (pin != NC) {
uint32_t cymode = gpio_get_cy_drive_mode(PIN_INPUT, mode);
GPIO_PRT_Type *port = Cy_GPIO_PortToAddr(CY_PORT(pin));
Cy_GPIO_SetDrivemode(port, CY_PIN(pin), cymode);
// Force output to enable pulls.
switch (cymode) {
case CY_GPIO_DM_PULLUP:
Cy_GPIO_Write(port, CY_PIN(pin), 1);
break;
case CY_GPIO_DM_PULLDOWN:
Cy_GPIO_Write(port, CY_PIN(pin), 0);
break;
default:
/* do nothing */
break;
}
}
}

View File

@ -0,0 +1,117 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cmsis.h"
#include "device.h"
#include "mbed_assert.h"
#include "gpio_object.h"
#include "port_api.h"
#include "psoc6_utils.h"
#include "mbed_error.h"
static void port_init_pins(port_t *obj)
{
uint32_t pin;
uint32_t cy_mode;
MBED_ASSERT(obj);
MBED_ASSERT(obj->port);
cy_mode = gpio_get_cy_drive_mode(obj->direction, obj->mode);
for (pin = 0; pin < 8; ++pin) {
if (obj->mask & (1 << pin)) {
Cy_GPIO_Pin_FastInit(obj->port, pin, cy_mode, 0, HSIOM_SEL_GPIO);
}
}
// Force output to enable pulls.
if (obj->direction == PIN_INPUT) {
switch (cy_mode) {
case CY_GPIO_DM_PULLUP:
port_write(obj, 0xff);
break; // Force output to enable pulls.
case CY_GPIO_DM_PULLDOWN:
port_write(obj, 0);
break;
}
}
}
void port_init(port_t *obj, PortName port, int mask, PinDirection dir)
{
uint32_t pin;
MBED_ASSERT(obj);
for (pin = 0; pin < 8; ++pin) {
if (mask & (1 << pin)) {
if (cy_reserve_io_pin((PinName)((port << 8)+pin))) {
error("Port pin reservation conflict.");
}
}
}
obj->port_id = port;
obj->port = Cy_GPIO_PortToAddr(port);
obj->mask = mask & 0xff; // Only 8 bits exist on a port in PSoC.
obj->direction = dir;
obj->mode = PullDefault;
port_init_pins(obj);
}
void port_mode(port_t *obj, PinMode mode)
{
MBED_ASSERT(obj);
MBED_ASSERT(obj->port);
obj->mode = mode;
port_init_pins(obj);
}
void port_dir(port_t *obj, PinDirection dir)
{
MBED_ASSERT(obj);
MBED_ASSERT(obj->port);
obj->direction = dir;
port_init_pins(obj);
}
void port_write(port_t *obj, int value)
{
MBED_ASSERT(obj);
MBED_ASSERT(obj->port);
if (obj->mask == 0xff) {
obj->port->OUT = value;
} else {
// In case some bits are used for different functionality we need to write
// each bit separately to not break things out, eg. pull up state on other bits.
uint32_t pin;
for (pin = 0; pin < 8; ++pin) {
if (obj->mask & (1 << pin)) {
Cy_GPIO_Write(obj->port, pin, value & 0x1);
value >>= 1;
}
}
}
}
int port_read(port_t *obj)
{
return obj->port->IN & obj->mask;
}

View File

@ -0,0 +1,473 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "psoc6_utils.h"
#if defined(__MBED__)
#include "mbed_critical.h"
#include "mbed_error.h"
#else
/** Adaptation layer to native Cypress environment */
/* Notice, that since we use critical section here only for operations
* that do not involve function calls, we can get away with using
* a global static variable for interrupt status saving.
*/
#include "syslib/cy_syslib.h"
#define error(arg) CY_ASSERT(0)
#define MBED_ASSERT CY_ASSERT
#define core_util_critical_section_enter() \
uint32_t _last_irq_status_ = Cy_SysLib_EnterCriticalSection()
#define core_util_critical_section_exit() \
Cy_SysLib_ExitCriticalSection(_last_irq_status_)
#endif /* defined(__MBED__) */
#define CY_NUM_PSOC6_PORTS 14
#define CY_NUM_DIVIDER_TYPES 4
#define NUM_SCB 8
#define NUM_TCPWM 32
#if defined(TARGET_MCU_PSOC6_M0) || PSOC6_DYNSRM_DISABLE || !defined(__MBED__)
/****************************************************************************/
/* Dynamic Shared Resource Manager */
/****************************************************************************/
/*
* This part of the code is responsible for management of the hardware
* resource shared between both CPUs of the PSoC 6.
* It supports allocation, freeing and conflict detection, so that never
* both CPUs try to use a single resource.
* It also detects conflicts arising from allocation of hardware devices
* for different modes of operation and when user tries to assign multiple
* functions to the same chip pin.
* It supports two modes of operation:
* 1. DYNAMIC (default mode)
* Resource manager is run on M0 core and M4 core asks it to allocate
* or free resources using RPC over IPC mechanism.
* M0 core communicates with manager via local function calls.
* 2. STATIC (enabled with PSOC6_DYNSRM_DISABLE compilation flag)
* In this mode resources are split statically between both cores.
* Targets using this mode should add psoc6_static_srm.h file to
* each core folder with declarations of resources assigned to it.
* See example file for details.
*/
#if PSOC6_DYNSRM_DISABLE
#define SRM_INIT_RESOURCE(_type_, _res_, _field_, ...) \
do { \
struct _init_s_ { \
uint8_t idx; \
_type_ val; \
} init[] = {{0, 0}, __VA_ARGS__}; \
uint32_t i; \
for (i = 1; i < sizeof(init)/sizeof(struct _init_s_); ++i) \
_res_[init[i].idx]_field_ = init[i].val; \
} while(0)
#if defined(TARGET_MCU_PSOC6_M0)
/*
* On M0 we start with all resources assigned to M4 and then clear reservations
* for those assigned to it (M0).
*/
#define SRM_PORT(port, pins) {(port), (uint8_t)~(pins)}
#define SRM_DIVIDER(type, dividers) {(type), (uint16_t)~(dividers)}
#define SRM_SCB(num) {(num), (0)}
#define SRM_TCPWM(num) {(num), (0)}
#define DEFAULT_PORT_RES 0xff
#define DEFAULT_DIVIDER_RES 0xffff
#define DEFAULT_DIVIDER8_RES 0xffff
#define DEFAULT_SCM_RES 1
#define DEFAULT_TCPWM_RES 1
#else // defined(TARGET_MCU_PSOC6_M0)
#define SRM_PORT(port, pins) {(port), (pins)}
#define SRM_DIVIDER(type, dividers) {(type), (dividers)}
#define SRM_SCB(num) {(num), (1)}
#define SRM_TCPWM(num) {(num), (1)}
#define DEFAULT_PORT_RES 0
#define DEFAULT_DIVIDER_RES 0
#define DEFAULT_DIVIDER8_RES 0
#define DEFAULT_SCM_RES 0
#define DEFAULT_TCPWM_RES 0
#endif // defined(TARGET_MCU_PSOC6_M0)
#include "psoc6_static_srm.h"
#else // PSOC6_DYNSRM_DISABLE
#define DEFAULT_PORT_RES 0
#define DEFAULT_DIVIDER_RES 0
#define DEFAULT_DIVIDER8_RES 0x3 // dividers 0 & 1 are reserved for us_ticker
#define DEFAULT_SCM_RES 0
#define DEFAULT_TCPWM_RES 0x3 // 32b counters 0 & 1 are reserved for us_ticker
#endif // PSOC6_DYNSRM_DISABLE
static uint8_t port_reservations[CY_NUM_PSOC6_PORTS] = {DEFAULT_PORT_RES};
typedef struct {
const uint32_t max_index;
uint32_t current_index;
uint32_t reservations;
} divider_alloc_t;
static divider_alloc_t divider_allocations[CY_NUM_DIVIDER_TYPES] = {
{ PERI_DIV_8_NR - 1, 2, DEFAULT_DIVIDER8_RES }, // CY_SYSCLK_DIV_8_BIT
{ PERI_DIV_16_NR - 1, 0, DEFAULT_DIVIDER_RES }, // CY_SYSCLK_DIV_16_BIT
{ PERI_DIV_16_5_NR - 1, 0, DEFAULT_DIVIDER_RES }, // CY_SYSCLK_DIV_16_5_BIT
{ PERI_DIV_24_5_NR - 1, 0, DEFAULT_DIVIDER_RES } // CY_SYSCLK_DIV_24_5_BIT
};
static uint8_t scb_reservations[NUM_SCB] = {DEFAULT_SCM_RES};
static uint8_t tcpwm_reservations[NUM_TCPWM] = {DEFAULT_TCPWM_RES};
int cy_reserve_io_pin(PinName pin_name)
{
uint32_t port = CY_PORT(pin_name);
uint32_t pin = CY_PIN(pin_name);
int result = (-1);
if ((port < CY_NUM_PSOC6_PORTS) && (pin <= 7)) {
core_util_critical_section_enter();
if (!(port_reservations[port] & (1 << pin))) {
port_reservations[port] |= (1 << pin);
result = 0;
}
core_util_critical_section_exit();
} else {
error("Trying to reserve non existing port/pin!");
}
return result;
}
void cy_free_io_pin(PinName pin_name)
{
uint32_t port = CY_PORT(pin_name);
uint32_t pin = CY_PIN(pin_name);
int result = (-1);
if ((port < CY_NUM_PSOC6_PORTS) && (pin <= 7)) {
core_util_critical_section_enter();
if (port_reservations[port] & (1 << pin)) {
port_reservations[port] &= ~(1 << pin);
result = 0;
}
core_util_critical_section_exit();
}
if (result) {
error("Trying to free wrong port/pin.");
}
}
uint32_t cy_clk_reserve_divider(cy_en_divider_types_t div_type, uint32_t div_num)
{
uint32_t divider = CY_INVALID_DIVIDER;
divider_alloc_t *p_alloc = &divider_allocations[div_type];
MBED_ASSERT(div_type < CY_NUM_DIVIDER_TYPES);
MBED_ASSERT(div_num <= p_alloc->max_index);
core_util_critical_section_enter();
if ((p_alloc->reservations & (1 << div_num)) == 0) {
p_alloc->reservations |= (1 << div_num);
divider = div_num;
p_alloc->current_index = ++div_num;
if (p_alloc->current_index > p_alloc->max_index) {
p_alloc->current_index = 0;
}
}
core_util_critical_section_exit();
return divider;
}
void cy_clk_free_divider(cy_en_divider_types_t div_type, uint32_t div_num)
{
int result = (-1);
divider_alloc_t *p_alloc = &divider_allocations[div_type];
MBED_ASSERT(div_type < CY_NUM_DIVIDER_TYPES);
MBED_ASSERT(div_num <= p_alloc->max_index);
core_util_critical_section_enter();
if ((p_alloc->reservations & (1 << div_num)) != 0) {
p_alloc->reservations &= ~(1 << div_num);
result = 0;
}
core_util_critical_section_exit();
if (result) {
error("Trying to release wrong clock divider.");
}
}
uint32_t cy_clk_allocate_divider(cy_en_divider_types_t div_type)
{
uint32_t divider = CY_INVALID_DIVIDER;
divider_alloc_t *p_alloc = &divider_allocations[div_type];
MBED_ASSERT(div_type < CY_NUM_DIVIDER_TYPES);
core_util_critical_section_enter();
MBED_ASSERT(p_alloc->current_index < p_alloc->max_index);
for ( uint32_t first_index = p_alloc->current_index;
CY_INVALID_DIVIDER == (divider = cy_clk_reserve_divider(div_type, p_alloc->current_index));
++p_alloc->current_index) {
if (p_alloc->current_index > p_alloc->max_index) {
p_alloc->current_index = 0;
}
if (p_alloc->current_index == first_index) {
break;
}
}
core_util_critical_section_exit();
return divider;
}
int cy_reserve_scb(uint32_t scb_num)
{
int result = (-1);
if (scb_num < NUM_SCB) {
core_util_critical_section_enter();
if (scb_reservations[scb_num] == 0) {
scb_reservations[scb_num] = 1;
}
core_util_critical_section_exit();
}
return result;
}
void cy_free_scb(uint32_t scb_num)
{
int result = (-1);
if (scb_num < NUM_SCB) {
core_util_critical_section_enter();
if (scb_reservations[scb_num] == 1) {
scb_reservations[scb_num] = 0;
}
core_util_critical_section_exit();
}
if (result) {
error("Trying to release wrong SCB.");
}
}
int cy_reserve_tcpwm(uint32_t tcpwm_num)
{
int result = (-1);
if (tcpwm_num < NUM_TCPWM) {
core_util_critical_section_enter();
if (tcpwm_reservations[tcpwm_num] == 0) {
tcpwm_reservations[tcpwm_num] = 1;
result = 0;
}
core_util_critical_section_exit();
}
return result;
}
void cy_free_tcpwm(uint32_t tcpwm_num)
{
int result = (-1);
if (tcpwm_num < NUM_TCPWM) {
core_util_critical_section_enter();
if (tcpwm_reservations[tcpwm_num] == 1) {
tcpwm_reservations[tcpwm_num] = 0;
result = 0;
}
core_util_critical_section_exit();
}
if (result) {
error("Trying to release wrong TCPWM.");
}
}
/*
* NVIC channel dynamic allocation (multiplexing) is used only on M0.
* On M4 IRQs are statically pre-assigned to NVIC channels.
*/
#if defined(TARGET_MCU_PSOC6_M0)
#define NUM_NVIC_CHANNELS ((uint32_t)(NvicMux31_IRQn - NvicMux0_IRQn) + 1)
static uint32_t irq_channels[NUM_NVIC_CHANNELS] = {0};
IRQn_Type cy_m0_nvic_allocate_channel(uint32_t channel_id)
{
IRQn_Type alloc = (IRQn_Type)(-1);
uint32_t chn;
MBED_ASSERT(channel_id);
core_util_critical_section_enter();
for (chn = 0; chn < NUM_NVIC_CHANNELS; ++chn) {
if (irq_channels[chn] == 0) {
irq_channels[chn] = channel_id;
alloc = NvicMux0_IRQn + chn;
break;
irq_channels[chn] = channel_id;
}
}
core_util_critical_section_exit();
return alloc;
}
IRQn_Type cy_m0_nvic_reserve_channel(IRQn_Type channel, uint32_t channel_id)
{
uint32_t chn = channel - NvicMux0_IRQn;
MBED_ASSERT(chn < NUM_NVIC_CHANNELS);
MBED_ASSERT(channel_id);
core_util_critical_section_enter();
if (irq_channels[chn]) {
channel = (IRQn_Type)(-1);
} else {
irq_channels[chn] = channel_id;
}
core_util_critical_section_exit();
return channel;
}
void cy_m0_nvic_release_channel(IRQn_Type channel, uint32_t channel_id)
{
uint32_t chn = channel - NvicMux0_IRQn;
MBED_ASSERT(chn < NUM_NVIC_CHANNELS);
MBED_ASSERT(channel_id);
core_util_critical_section_enter();
if (irq_channels[chn] == channel_id) {
irq_channels[chn] = 0;
} else {
error("NVIC channel cross-check failed on release.");
}
core_util_critical_section_exit();
}
#define CY_BLE_SFLASH_DIE_X_MASK (0x3Fu)
#define CY_BLE_SFLASH_DIE_X_BITS (6u)
#define CY_BLE_SFLASH_DIE_Y_MASK (0x3Fu)
#define CY_BLE_SFLASH_DIE_Y_BITS (6u)
#define CY_BLE_SFLASH_DIE_XY_BITS (CY_BLE_SFLASH_DIE_X_BITS + CY_BLE_SFLASH_DIE_Y_BITS)
#define CY_BLE_SFLASH_DIE_WAFER_MASK (0x1Fu)
#define CY_BLE_SFLASH_DIE_WAFER_BITS (5u)
#define CY_BLE_SFLASH_DIE_XYWAFER_BITS (CY_BLE_SFLASH_DIE_XY_BITS + CY_BLE_SFLASH_DIE_WAFER_BITS)
#define CY_BLE_SFLASH_DIE_LOT_MASK (0x7Fu)
#define CY_BLE_SFLASH_DIE_LOT_BITS (7u)
static uint8_t cy_ble_deviceAddress[6] = {0x19u, 0x00u, 0x00u, 0x50u, 0xA0u, 0x00u};
void cy_get_bd_mac_address(uint8_t *buffer)
{
uint32_t bdAddrLoc;
bdAddrLoc = ((uint32_t)SFLASH->DIE_X & (uint32_t)CY_BLE_SFLASH_DIE_X_MASK) |
((uint32_t)(((uint32_t)SFLASH->DIE_Y) & ((uint32_t)CY_BLE_SFLASH_DIE_Y_MASK)) <<
CY_BLE_SFLASH_DIE_X_BITS) |
((uint32_t)(((uint32_t)SFLASH->DIE_WAFER) & ((uint32_t)CY_BLE_SFLASH_DIE_WAFER_MASK)) <<
CY_BLE_SFLASH_DIE_XY_BITS) |
((uint32_t)(((uint32_t)SFLASH->DIE_LOT[0]) & ((uint32_t)CY_BLE_SFLASH_DIE_LOT_MASK)) <<
CY_BLE_SFLASH_DIE_XYWAFER_BITS);
cy_ble_deviceAddress[0] = (uint8_t)bdAddrLoc;
cy_ble_deviceAddress[1] = (uint8_t)(bdAddrLoc >> 8u);
cy_ble_deviceAddress[2] = (uint8_t)(bdAddrLoc >> 16u);
for (int i = 0; i < 6; ++i) {
buffer[i] = cy_ble_deviceAddress[i];
}
}
#endif // defined(TARGET_MCU_PSOC6_M0)
#endif // defined(TARGET_MCU_PSOC6_M0) || PSOC6_DSRM_DISABLE || !defined(__MBED__)
void cy_srm_initialize(void)
{
#if defined(TARGET_MCU_PSOC6_M0) || PSOC6_DYNSRM_DISABLE || !defined(__MBED__)
uint32_t i;
for (i = 0; i < CY_NUM_PSOC6_PORTS; ++i) {
port_reservations[i] = DEFAULT_PORT_RES;
}
for (i = 0; i < NUM_SCB; ++i) {
scb_reservations[i] = DEFAULT_SCM_RES;
}
for (i = 0; i < NUM_TCPWM; ++i) {
tcpwm_reservations[i] = DEFAULT_TCPWM_RES;
}
#if PSOC6_DYNSRM_DISABLE
#ifdef M0_ASSIGNED_PORTS
SRM_INIT_RESOURCE(uint8_t, port_reservations,, M0_ASSIGNED_PORTS);
#endif
#ifdef M0_ASSIGNED_DIVIDERS
SRM_INIT_RESOURCE(uint32_t, divider_allocations, .reservations, M0_ASSIGNED_DIVIDERS);
#endif
#ifdef M0_ASSIGNED_SCBS
SRM_INIT_RESOURCE(uint8_t, scb_reservations,, M0_ASSIGNED_SCBS);
#endif
#ifdef M0_ASSIGNED_TCPWMS
SRM_INIT_RESOURCE(uint8_t, tcpwm_reservations,, M0_ASSIGNED_TCPWMS);
#endif
#endif // PSOC6_DYNSRM_DISABLE
#endif // defined(TARGET_MCU_PSOC6_M0) || PSOC6_DSRM_DISABLE || !defined(__MBED__)
}

View File

@ -0,0 +1,223 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _PSOC6_UTILS_H_
#define _PSOC6_UTILS_H_
#if defined(__MBED__)
#include <stdint.h>
#include <device.h>
#include "drivers/peripheral/gpio/cy_gpio.h"
#include "drivers/peripheral/sysclk/cy_sysclk.h"
#else
#include "project.h"
#endif
#include "PinNamesTypes.h"
#include "PinNames.h"
#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */
#define CY_INVALID_DIVIDER 0xFF
/** \brief Allocates clock divider to be used for a new clock signal.
*
* \param div_type cy_en_divider_types_t Divider type.
* \return uint32_t Divider number (id) or CY_CLK_INVALID_DIVIDER if unavailable.
*
*/
uint32_t cy_clk_allocate_divider(cy_en_divider_types_t div_type);
/** \brief Reserves clock divider to be used for a new clock signal.
*
* \param div_type cy_en_divider_types_t Divider type.
* \param div_num Divider number to be reserved.
* \return uint32_t Divider number (id) or CY_CLK_INVALID_DIVIDER if unavailable.
*
*/
uint32_t cy_clk_reserve_divider(cy_en_divider_types_t div_type, uint32_t div_num);
/** \brief Releases already reserved clock divider.
*
* \param div_type cy_en_divider_types_t Divider type.
* \param div_num Divider number to be released.
*
*/
void cy_clk_free_divider(cy_en_divider_types_t div_type, uint32_t div_num);
#ifdef TARGET_MCU_PSOC6_M0
#include "gpio_irq_api.h"
/** \brief On PSoC6 M0 core interrupts are routed into NVIC via additional multiplexer.
* This function allocates free NVIC channel to be used by particular interrupt.
*
* \param allocation_id Unique identifier (for debug purposes).
* \return IRQ channel allocated or (-1) if no free channel is available.
*
*/
IRQn_Type cy_m0_nvic_allocate_channel(uint32_t allocation_id);
/** \brief Reserves particular NVIC channel if it is available.
*
* \param channel IRQn_Type Channel to be reserved.
* \param allocation_id uint32_t Identifier.
* \return IRQ channel allocated or (-1) if no free channel is available.
*
*/
IRQn_Type cy_m0_nvic_reserve_channel(IRQn_Type channel, uint32_t allocation_id);
/** \brief Releases NVIC channel.
*
* \param channel IRQn_Type Channel to be released.
* \param allocation_id uint32_t Id used during allocation (for cross check).
* \return void
*
*/
void cy_m0_nvic_release_channel(IRQn_Type channel, uint32_t allocation_id);
#endif /* M0+ core */
/** \brief Request allocation of SCB block.
*
* \param scb_num uint32_t Id of the SCB block.
* \return (0) when OK, (-1) when reservation conflict occurs.
*
*/
int cy_reserve_scb(uint32_t scb_num);
/** \brief Releases SCB block.
*
* \param scb_num uint32_t Id of the SCB block.
* \return void
*
*/
void cy_free_scb(uint32_t scb_num);
/** \brief Request allocation of TCPWM block.
*
* \param tcpwm_num uint32_t Id of the TCPWM block.
* \return (0) when OK, (-1) when reservation conflict occurs.
*
*/
int cy_reserve_tcpwm(uint32_t tcpwm_num);
/** \brief Releases TCPWM block.
*
* \param tcpwm_num uint32_t Id of the TCPWM block.
* \return void
*
*/
void cy_free_tcpwm(uint32_t tcpwm_num);
/** \brief Request allocation of i/o pin.
*
* \param pin PinName Id of the pin to allocate.
* \return (0) when OK, (-1) when reservation conflict occurs.
*
*/
int cy_reserve_io_pin(PinName pin);
/** \brief Releases i/o pin.
*
* \param pin PinName Id of the pin.
* \return void
*
*/
void cy_free_io_pin(PinName pin);
/** \brief Initializes shared resource manager.
*
* \param none.
* \return void
*
*/
void cy_srm_initialize(void);
/** \brief Returns board-specific hardware MAC address.
*
* \param uint8_t *buffer Buffer where address will be returned.
* \return void.
*
*/
void cy_get_bd_mac_address(uint8_t* buffer);
/** \brief Determines proper PSoC6 pin drive mode settings.
*
* \param dir PinDirection Pin direction, in or out.
* \param mode PinMode Mbed pin mode.
* \return PSoC6 pin drive mode.
*
*/
static inline uint32_t gpio_get_cy_drive_mode(PinDirection dir, PinMode mode)
{
uint32_t cymode = 0;
switch (mode) {
case PullNone:
switch (dir) {
case PIN_INPUT:
cymode = CY_GPIO_DM_HIGHZ;
break;
case PIN_OUTPUT:
cymode = CY_GPIO_DM_STRONG;
break;
}
break;
case PushPull:
cymode = CY_GPIO_DM_STRONG;
break;
case PullUp:
cymode = CY_GPIO_DM_PULLUP;
break;
case PullDown:
cymode = CY_GPIO_DM_PULLDOWN;
break;
case OpenDrainDriveLow:
cymode = CY_GPIO_DM_OD_DRIVESLOW;
break;
case OpenDrainDriveHigh:
cymode = CY_GPIO_DM_OD_DRIVESHIGH;
break;
case AnalogMode:
cymode = CY_GPIO_DM_ANALOG;
break;
}
return cymode;
}
#if defined(__cplusplus)
}
#endif /* __cplusplus */
#endif // _PSOC6_UTILS_H_

View File

@ -0,0 +1,321 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "device.h"
#include "pwmout_api.h"
#include "cy_tcpwm.h"
#include "cy_tcpwm_pwm.h"
#include "psoc6_utils.h"
#include "mbed_assert.h"
#include "mbed_error.h"
#include "pinmap.h"
#include "PeripheralPins.h"
#include "platform/mbed_error.h"
#include "cy_syspm.h"
#define PWMOUT_BASE_CLOCK_HZ 1000000UL
#define MAX_16_BIT_PERIOD 65536
static uint32_t pwm_clock_divider = CY_INVALID_DIVIDER;
static const cy_stc_tcpwm_pwm_config_t pwm_config = {
.pwmMode = CY_TCPWM_PWM_MODE_PWM,
.clockPrescaler = 0, // will be configured separately
.pwmAlignment = CY_TCPWM_PWM_LEFT_ALIGN,
.runMode = CY_TCPWM_PWM_CONTINUOUS,
.period0 = 0, // will be configured separately
.enablePeriodSwap = 0,
.compare0 = 0, // will be configured separately
.compare1 = 0, // will be configured separately
.enableCompareSwap = 0,
.interruptSources = 0, //CY_TCPWM_INT_ON_CC,
.invertPWMOut = CY_TCPWM_PWM_INVERT_DISABLE,
.invertPWMOutN = CY_TCPWM_PWM_INVERT_ENABLE,
.killMode = CY_TCPWM_PWM_ASYNC_KILL,
.countInputMode = CY_TCPWM_INPUT_LEVEL,
.countInput = CY_TCPWM_INPUT_1,
.swapInputMode = CY_TCPWM_INPUT_LEVEL,
.swapInput = CY_TCPWM_INPUT_1,
.reloadInputMode = CY_TCPWM_INPUT_LEVEL,
.reloadInput = CY_TCPWM_INPUT_0,
.startInputMode = CY_TCPWM_INPUT_LEVEL,
.startInput = CY_TCPWM_INPUT_0,
.killInputMode = CY_TCPWM_INPUT_LEVEL,
.killInput = CY_TCPWM_INPUT_0,
};
static void Cy_TCPWM_PWM_SetPrescaler(TCPWM_Type *base, uint32_t cntNum, uint32_t prescaler)
{
base->CNT[cntNum].CTRL = _CLR_SET_FLD32U(base->CNT[cntNum].CTRL, TCPWM_CNT_CTRL_GENERIC, prescaler);
}
static void pwm_start_32b(pwmout_t *obj, uint32_t new_period, uint32_t new_width)
{
obj->period = new_period;
obj->pulse_width = new_width;
Cy_TCPWM_PWM_SetPeriod0(obj->base, obj->counter_id, obj->period - 1);
Cy_TCPWM_PWM_SetCompare0(obj->base, obj->counter_id, obj->pulse_width);
Cy_TCPWM_PWM_Enable(obj->base, obj->counter_id);
Cy_TCPWM_TriggerStart(obj->base, 1UL << obj->counter_id);
}
static void pwm_start_16b(pwmout_t *obj, uint32_t period, uint32_t width)
{
uint32_t prescaler = 0;
obj->period = period;
obj->pulse_width = width;
// For 16-bit counters we need to configure prescaler appropriately.
while ((period > MAX_16_BIT_PERIOD) && (prescaler < CY_TCPWM_PWM_PRESCALER_DIVBY_128)) {
period /= 2;
prescaler += 1;
}
if (period > MAX_16_BIT_PERIOD) {
// We have reached the prescaler limit, set period to max value.
error("Can't configure required PWM period.");
period = MAX_16_BIT_PERIOD;
}
obj->prescaler = prescaler;
width >>= prescaler;
Cy_TCPWM_PWM_SetPeriod0(obj->base, obj->counter_id, period - 1);
Cy_TCPWM_PWM_SetPrescaler(obj->base, obj->counter_id, prescaler);
Cy_TCPWM_PWM_SetCompare0(obj->base, obj->counter_id, width);
Cy_TCPWM_PWM_Enable(obj->base, obj->counter_id);
Cy_TCPWM_TriggerStart(obj->base, 1UL << obj->counter_id);
}
static void pwm_start(pwmout_t *obj, uint32_t new_period, uint32_t new_pulse_width)
{
obj->period = new_period;
obj->pulse_width = new_pulse_width;
Cy_TCPWM_PWM_Disable(obj->base, obj->counter_id);
if (new_period > 0) {
if (obj->base == TCPWM0) {
pwm_start_32b(obj, new_period, new_pulse_width);
} else {
pwm_start_16b(obj, new_period, new_pulse_width);
}
}
}
/*
* Callback handler to restart the timer after deep sleep.
*/
#if DEVICE_SLEEP && DEVICE_LPTICKER
static cy_en_syspm_status_t pwm_pm_callback(cy_stc_syspm_callback_params_t *callback_params)
{
pwmout_t *obj = (pwmout_t *)callback_params->context;
switch (callback_params->mode) {
case CY_SYSPM_BEFORE_TRANSITION:
/* Disable timer before transition */
Cy_TCPWM_PWM_Disable(obj->base, obj->counter_id);
break;
case CY_SYSPM_AFTER_TRANSITION:
/* Enable the timer to operate */
if (obj->period > 0) {
Cy_TCPWM_PWM_Enable(obj->base, obj->counter_id);
Cy_TCPWM_TriggerStart(obj->base, 1UL << obj->counter_id);
}
break;
default:
break;
}
return CY_SYSPM_SUCCESS;
}
#endif // DEVICE_SLEEP && DEVICE_LPTICKER
void pwmout_init(pwmout_t *obj, PinName pin)
{
uint32_t pwm_cnt = 0;
uint32_t pwm_function = 0;
uint32_t abs_cnt_num = 0;
MBED_ASSERT(obj);
MBED_ASSERT(pin != (PinName)NC);
// Allocate and setup clock.
if (pwm_clock_divider == CY_INVALID_DIVIDER) {
pwm_clock_divider = cy_clk_allocate_divider(CY_SYSCLK_DIV_8_BIT);
if (pwm_clock_divider == CY_INVALID_DIVIDER) {
error("PWM clock divider allocation failed.");
return;
}
Cy_SysClk_PeriphSetDivider(CY_SYSCLK_DIV_8_BIT,
pwm_clock_divider,
(CY_CLK_PERICLK_FREQ_HZ / PWMOUT_BASE_CLOCK_HZ) - 1);
Cy_SysClk_PeriphEnableDivider(CY_SYSCLK_DIV_8_BIT, pwm_clock_divider);
}
pwm_cnt = pinmap_peripheral(pin, PinMap_PWM_OUT);
if (pwm_cnt != (uint32_t)NC) {
if (cy_reserve_io_pin(pin)) {
error("PWMOUT pin reservation conflict.");
}
obj->base = (TCPWM_Type*)CY_PERIPHERAL_BASE(pwm_cnt);
obj->pin = pin;
if (obj->base == TCPWM0) {
obj->counter_id = ((PWMName)pwm_cnt - PWM_32b_0) / (PWM_32b_1 - PWM_32b_0);
abs_cnt_num = obj->counter_id;
} else {
// TCPWM1 is used.
obj->counter_id = ((PWMName)pwm_cnt - PWM_16b_0) / (PWM_16b_1 - PWM_16b_0);
abs_cnt_num = obj->counter_id + 8;
}
if (cy_reserve_tcpwm(abs_cnt_num)) {
error("PWMOUT Timer/Counter reservation conflict.");
}
// Configure clock.
pwm_function = pinmap_function(pin, PinMap_PWM_OUT);
obj->clock = CY_PIN_CLOCK(pwm_function);
Cy_SysClk_PeriphAssignDivider(obj->clock, CY_SYSCLK_DIV_8_BIT, pwm_clock_divider);
Cy_TCPWM_PWM_Init(obj->base, obj->counter_id, &pwm_config);
pin_function(pin, pwm_function);
// These will be properly configured later on.
obj->period = 0;
obj->pulse_width = 0;
obj->prescaler = 0;
#if DEVICE_SLEEP && DEVICE_LPTICKER
obj->pm_callback_handler.callback = pwm_pm_callback;
obj->pm_callback_handler.type = CY_SYSPM_DEEPSLEEP;
obj->pm_callback_handler.skipMode = 0;
obj->pm_callback_handler.callbackParams = &obj->pm_callback_params;
obj->pm_callback_params.base = obj->base;
obj->pm_callback_params.context = obj;
if (!Cy_SysPm_RegisterCallback(&obj->pm_callback_handler)) {
error("PM callback registration failed!");
}
#endif // DEVICE_SLEEP && DEVICE_LPTICKER
} else {
error("PWM OUT pinout mismatch.");
}
}
void pwmout_free(pwmout_t *obj)
{
// TODO: Not implemented yet.
}
void pwmout_write(pwmout_t *obj, float percent)
{
uint32_t pulse_width;
MBED_ASSERT(obj);
if (percent < 0.0) {
percent = 0.0;
} else if (percent > 1.0) {
percent = 1.0;
}
pulse_width = (uint32_t)(percent * obj->period + 0.5);
pwm_start(obj, obj->period, pulse_width);
}
float pwmout_read(pwmout_t *obj)
{
MBED_ASSERT(obj);
return (float)(obj->pulse_width) / obj->period;
}
void pwmout_period(pwmout_t *obj, float seconds)
{
uint32_t period;
uint32_t pulse_width;
MBED_ASSERT(obj);
if (seconds < 0.0) {
seconds = 0.0;
}
period = (uint32_t)(seconds * 1000000 + 0.5);
pulse_width = (uint32_t)((uint64_t)period * obj->pulse_width / obj->period);
pwm_start(obj, period, pulse_width);
}
void pwmout_period_ms(pwmout_t *obj, int ms)
{
uint32_t period;
uint32_t pulse_width;
MBED_ASSERT(obj);
if (ms < 0.0) {
ms = 0.0;
}
period = (uint32_t)(ms * 1000 + 0.5);
pulse_width = (uint32_t)((uint64_t)period * obj->pulse_width / obj->period);
pwm_start(obj, period, pulse_width);
}
void pwmout_period_us(pwmout_t *obj, int us)
{
uint32_t pulse_width;
MBED_ASSERT(obj);
if (us < 0) {
us = 0;
}
pulse_width = (uint32_t)((uint64_t)us * obj->pulse_width / obj->period);
pwm_start(obj, us, pulse_width);
}
void pwmout_pulsewidth(pwmout_t *obj, float seconds)
{
uint32_t pulse_width;
MBED_ASSERT(obj);
if (seconds < 0.0) {
seconds = 0.0;
}
pulse_width = (uint32_t)(seconds * 1000000 + 0.5);
pwm_start(obj, obj->period, pulse_width);
}
void pwmout_pulsewidth_ms(pwmout_t *obj, int ms)
{
uint32_t pulse_width;
MBED_ASSERT(obj);
if (ms < 0.0) {
ms = 0.0;
}
pulse_width = (uint32_t)(ms * 1000 + 0.5);
pwm_start(obj, obj->period, pulse_width);
}
void pwmout_pulsewidth_us(pwmout_t *obj, int us)
{
MBED_ASSERT(obj);
if (us < 0) {
us = 0;
}
pwm_start(obj, obj->period, us);
}

View File

@ -0,0 +1,159 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "device.h"
#include "rtc_api.h"
#include "mbed_error.h"
#include "mbed_mktime.h"
#include "cy_rtc.h"
#if DEVICE_RTC
/*
* Since Mbed tests insist on supporting 1970 - 2106 years range
* and Cypress h/w supports only 2000 - 2099 years range,
* two backup registers are used to flag century correction.
*/
#define BR_LAST_YEAR_READ 14
#define BR_CENTURY_CORRECTION 15
static int enabled = 0;
static uint32_t rtc_read_convert_year(uint32_t short_year)
{
uint32_t century = BACKUP->BREG[BR_CENTURY_CORRECTION];
if (BACKUP->BREG[BR_LAST_YEAR_READ] > short_year) {
BACKUP->BREG[BR_CENTURY_CORRECTION] = ++century;
}
BACKUP->BREG[BR_LAST_YEAR_READ] = short_year;
return century * 100 + short_year;
}
static uint32_t rtc_write_convert_year(uint32_t long_year)
{
uint32_t short_year = long_year;
uint32_t century = short_year / 100;
short_year -= century * 100;
BACKUP->BREG[BR_CENTURY_CORRECTION] = century;
BACKUP->BREG[BR_LAST_YEAR_READ] = short_year;
return short_year;
}
void rtc_init(void)
{
static cy_stc_rtc_config_t init_val = {
/* Time information */
.hrFormat = CY_RTC_24_HOURS,
.sec = 0,
.min = 0,
.hour = 0,
.dayOfWeek = CY_RTC_SATURDAY,
.date = 1,
.month = 1,
.year = 0 // 2000 - 30 == 1970
};
cy_stc_rtc_config_t cy_time;
if (!enabled) {
// Setup power management callback.
// Setup century interrupt.
// Verify RTC time consistency.
Cy_RTC_GetDateAndTime(&cy_time);
if ( CY_RTC_IS_SEC_VALID(cy_time.sec) &&
CY_RTC_IS_MIN_VALID(cy_time.min) &&
CY_RTC_IS_HOUR_VALID(cy_time.hour) &&
CY_RTC_IS_DOW_VALID(cy_time.dayOfWeek) &&
CY_RTC_IS_MONTH_VALID(cy_time.month) &&
CY_RTC_IS_YEAR_SHORT_VALID(cy_time.year) &&
(cy_time.hrFormat == CY_RTC_24_HOURS)) {
enabled = 1;
} else {
// reinitialize
init_val.year = rtc_write_convert_year(1970);
if (Cy_RTC_Init(&init_val) == CY_RTC_SUCCESS) {
enabled = 1;
}
}
}
}
void rtc_free(void)
{
// Nothing to do
}
int rtc_isenabled(void)
{
return enabled;
}
time_t rtc_read(void)
{
cy_stc_rtc_config_t cy_time;
struct tm gmt;
time_t timestamp = 0;
uint32_t interrupt_state;
// Since RTC reading function is unreliable when the RTC is busy with previous update
// we have to make sure it's not before calling it.
while (CY_RTC_BUSY == Cy_RTC_GetSyncStatus()) {}
interrupt_state = Cy_SysLib_EnterCriticalSection();
Cy_RTC_GetDateAndTime(&cy_time);
gmt.tm_sec = cy_time.sec;
gmt.tm_min = cy_time.min;
gmt.tm_hour = cy_time.hour;
gmt.tm_mday = cy_time.date;
gmt.tm_mon = cy_time.month - 1;
gmt.tm_year = rtc_read_convert_year(cy_time.year);
gmt.tm_isdst = 0;
Cy_SysLib_ExitCriticalSection(interrupt_state);
_rtc_maketime(&gmt, &timestamp, RTC_4_YEAR_LEAP_YEAR_SUPPORT);
return timestamp;
}
void rtc_write(time_t t)
{
cy_en_rtc_status_t status;
struct tm gmt;
if ( _rtc_localtime(t, &gmt, RTC_4_YEAR_LEAP_YEAR_SUPPORT)) {
uint32_t year;
uint32_t interrupt_state;
// Make sure RTC is not busy and can be updated.
while (CY_RTC_BUSY == Cy_RTC_GetSyncStatus()) {}
interrupt_state = Cy_SysLib_EnterCriticalSection();
year = rtc_write_convert_year(gmt.tm_year);
status = Cy_RTC_SetDateAndTimeDirect(gmt.tm_sec,
gmt.tm_min,
gmt.tm_hour,
gmt.tm_mday,
gmt.tm_mon + 1,
year);
Cy_SysLib_ExitCriticalSection(interrupt_state);
if (status != CY_RTC_SUCCESS) {
error("Error 0x%x while setting RTC time.", status);
}
}
}
#endif // DEVICE_RTC

View File

@ -0,0 +1,823 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if DEVICE_SERIAL
#include <string.h>
#include "cmsis.h"
#include "mbed_assert.h"
#include "mbed_error.h"
#include "PeripheralPins.h"
#include "pinmap.h"
#include "serial_api.h"
#include "psoc6_utils.h"
#include "drivers/peripheral/sysclk/cy_sysclk.h"
#include "drivers/peripheral/gpio/cy_gpio.h"
#include "drivers/peripheral/scb/cy_scb_uart.h"
#include "drivers/peripheral/sysint/cy_sysint.h"
#define UART_OVERSAMPLE 12
#define UART_DEFAULT_BAUDRATE 115200
#define NUM_SERIAL_PORTS 8
#define SERIAL_DEFAULT_IRQ_PRIORITY 3
typedef struct serial_s serial_obj_t;
#if DEVICE_SERIAL_ASYNCH
#define OBJ_P(in) (&(in->serial))
#else
#define OBJ_P(in) (in)
#endif
/*
* NOTE: Cypress PDL high level API implementation of USART doe not
* align well with Mbed interface for interrupt-driven serial I/O.
* For this reason only low level PDL API is used here.
*/
static const cy_stc_scb_uart_config_t default_uart_config = {
.uartMode = CY_SCB_UART_STANDARD,
.enableMutliProcessorMode = false,
.smartCardRetryOnNack = false,
.irdaInvertRx = false,
.irdaEnableLowPowerReceiver = false,
.oversample = UART_OVERSAMPLE,
.enableMsbFirst = false,
.dataWidth = 8UL,
.parity = CY_SCB_UART_PARITY_NONE,
.stopBits = CY_SCB_UART_STOP_BITS_1,
.enableInputFilter = false,
.breakWidth = 11UL,
.dropOnFrameError = false,
.dropOnParityError = false,
.receiverAddress = 0x0UL,
.receiverAddressMask = 0x0UL,
.acceptAddrInFifo = false,
.enableCts = false,
.ctsPolarity = CY_SCB_UART_ACTIVE_LOW,
.rtsRxFifoLevel = 20UL,
.rtsPolarity = CY_SCB_UART_ACTIVE_LOW,
.rxFifoTriggerLevel = 0UL,
.rxFifoIntEnableMask = 0x0UL,
.txFifoTriggerLevel = 0UL,
.txFifoIntEnableMask = 0x0UL
};
int stdio_uart_inited = false;
serial_t stdio_uart;
typedef struct irq_info_s {
serial_obj_t *serial_obj;
uart_irq_handler handler;
uint32_t id_arg;
IRQn_Type irqn;
#if defined (TARGET_MCU_PSOC6_M0)
cy_en_intr_t cm0p_irq_src;
#endif
} irq_info_t;
static irq_info_t irq_info[NUM_SERIAL_PORTS] = {
{NULL, NULL, 0, unconnected_IRQn},
{NULL, NULL, 0, unconnected_IRQn},
{NULL, NULL, 0, unconnected_IRQn},
{NULL, NULL, 0, unconnected_IRQn},
{NULL, NULL, 0, unconnected_IRQn},
{NULL, NULL, 0, unconnected_IRQn},
{NULL, NULL, 0, unconnected_IRQn},
{NULL, NULL, 0, unconnected_IRQn}
};
static void serial_irq_dispatcher(uint32_t serial_id)
{
MBED_ASSERT(serial_id < NUM_SERIAL_PORTS);
irq_info_t *info = &irq_info[serial_id];
serial_obj_t *obj = info->serial_obj;
MBED_ASSERT(obj);
#if DEVICE_SERIAL_ASYNCH
if (obj->async_handler) {
obj->async_handler();
return;
}
#endif
if (Cy_SCB_GetRxInterruptStatusMasked(obj->base) & CY_SCB_RX_INTR_NOT_EMPTY) {
info->handler(info->id_arg, RxIrq);
Cy_SCB_ClearRxInterrupt(obj->base, CY_SCB_RX_INTR_NOT_EMPTY);
}
if (Cy_SCB_GetTxInterruptStatusMasked(obj->base) & (CY_SCB_TX_INTR_LEVEL | CY_SCB_UART_TX_DONE)) {
info->handler(info->id_arg, TxIrq);
}
}
static void serial_irq_dispatcher_uart0(void)
{
serial_irq_dispatcher(0);
}
static void serial_irq_dispatcher_uart1(void)
{
serial_irq_dispatcher(1);
}
static void serial_irq_dispatcher_uart2(void)
{
serial_irq_dispatcher(2);
}
static void serial_irq_dispatcher_uart3(void)
{
serial_irq_dispatcher(3);
}
static void serial_irq_dispatcher_uart4(void)
{
serial_irq_dispatcher(4);
}
static void serial_irq_dispatcher_uart5(void)
{
serial_irq_dispatcher(5);
}
void serial_irq_dispatcher_uart6(void)
{
serial_irq_dispatcher(6);
}
static void serial_irq_dispatcher_uart7(void)
{
serial_irq_dispatcher(7);
}
static void (*irq_dispatcher_table[])(void) = {
serial_irq_dispatcher_uart0,
serial_irq_dispatcher_uart1,
serial_irq_dispatcher_uart2,
serial_irq_dispatcher_uart3,
serial_irq_dispatcher_uart4,
serial_irq_dispatcher_uart5,
serial_irq_dispatcher_uart6,
serial_irq_dispatcher_uart7
};
static IRQn_Type serial_irq_allocate_channel(serial_obj_t *obj)
{
#if defined (TARGET_MCU_PSOC6_M0)
irq_info[obj->serial_id].cm0p_irq_src = scb_0_interrupt_IRQn + obj->serial_id;
return cy_m0_nvic_allocate_channel(CY_SERIAL_IRQN_ID + obj->serial_id);
#else
return (IRQn_Type)(scb_0_interrupt_IRQn + obj->serial_id);
#endif // M0
}
static void serial_irq_release_channel(IRQn_Type channel, uint32_t serial_id)
{
#if defined (TARGET_MCU_PSOC6_M0)
cy_m0_nvic_release_channel(channel, CY_SERIAL_IRQN_ID + serial_id);
#endif //M0
}
static int serial_irq_setup_channel(serial_obj_t *obj)
{
cy_stc_sysint_t irq_config;
irq_info_t *info = &irq_info[obj->serial_id];
if (info->irqn == unconnected_IRQn) {
IRQn_Type irqn = serial_irq_allocate_channel(obj);
if (irqn < 0) {
return (-1);
}
// Configure NVIC
irq_config.intrPriority = SERIAL_DEFAULT_IRQ_PRIORITY;
irq_config.intrSrc = irqn;
#if defined (TARGET_MCU_PSOC6_M0)
irq_config.cm0pSrc = info->cm0p_irq_src;
#endif
if (Cy_SysInt_Init(&irq_config, irq_dispatcher_table[obj->serial_id]) != CY_SYSINT_SUCCESS) {
return(-1);
}
info->irqn = irqn;
info->serial_obj = obj;
NVIC_EnableIRQ(irqn);
}
return 0;
}
/*
* Calculates fractional divider value.
*/
static uint32_t divider_value(uint32_t frequency, uint32_t frac_bits)
{
/* UARTs use peripheral clock */
return ((CY_CLK_PERICLK_FREQ_HZ * (1 << frac_bits)) + (frequency / 2)) / frequency;
}
static cy_en_sysclk_status_t serial_init_clock(serial_obj_t *obj, uint32_t baudrate)
{
cy_en_sysclk_status_t status = CY_SYSCLK_BAD_PARAM;
if (obj->div_num == CY_INVALID_DIVIDER) {
uint32_t divider_num = cy_clk_allocate_divider(CY_SYSCLK_DIV_16_5_BIT);
if (divider_num < PERI_DIV_16_5_NR) {
/* Assign fractional divider. */
status = Cy_SysClk_PeriphAssignDivider(obj->clock, CY_SYSCLK_DIV_16_5_BIT, divider_num);
if (status == CY_SYSCLK_SUCCESS) {
obj->div_type = CY_SYSCLK_DIV_16_5_BIT;
obj->div_num = divider_num;
}
} else {
// Try 16-bit divider.
divider_num = cy_clk_allocate_divider(CY_SYSCLK_DIV_16_BIT);
if (divider_num < PERI_DIV_16_NR) {
/* Assign 16-bit divider. */
status = Cy_SysClk_PeriphAssignDivider(obj->clock, CY_SYSCLK_DIV_16_BIT, divider_num);
if (status == CY_SYSCLK_SUCCESS) {
obj->div_type = CY_SYSCLK_DIV_16_BIT;
obj->div_num = divider_num;
}
} else {
error("Serial: cannot assign clock divider.");
}
}
} else {
status = CY_SYSCLK_SUCCESS;
}
if (status == CY_SYSCLK_SUCCESS) {
/* Set baud rate */
if (obj->div_type == CY_SYSCLK_DIV_16_5_BIT) {
Cy_SysClk_PeriphDisableDivider(CY_SYSCLK_DIV_16_5_BIT, obj->div_num);
uint32_t divider = divider_value(baudrate * UART_OVERSAMPLE, 5);
status = Cy_SysClk_PeriphSetFracDivider(CY_SYSCLK_DIV_16_5_BIT,
obj->div_num,
(divider >> 5) - 1, // integral part
divider & 0x1F); // fractional part
Cy_SysClk_PeriphEnableDivider(CY_SYSCLK_DIV_16_5_BIT, obj->div_num);
} else if (obj->div_type == CY_SYSCLK_DIV_16_BIT) {
Cy_SysClk_PeriphDisableDivider(CY_SYSCLK_DIV_16_BIT, obj->div_num);
status = Cy_SysClk_PeriphSetDivider(CY_SYSCLK_DIV_16_BIT,
obj->div_num,
divider_value(baudrate * UART_OVERSAMPLE, 0));
Cy_SysClk_PeriphEnableDivider(CY_SYSCLK_DIV_16_BIT, obj->div_num);
}
}
return status;
}
/*
* Initializes i/o pins for UART tx/rx.
*/
static void serial_init_pins(serial_obj_t *obj)
{
int tx_function = pinmap_function(obj->pin_tx, PinMap_UART_TX);
int rx_function = pinmap_function(obj->pin_rx, PinMap_UART_RX);
if (cy_reserve_io_pin(obj->pin_tx) || cy_reserve_io_pin(obj->pin_rx)) {
error("Serial TX/RX pin reservation conflict.");
}
pin_function(obj->pin_tx, tx_function);
pin_function(obj->pin_rx, rx_function);
}
/*
* Initializes i/o pins for UART flow control.
*/
static void serial_init_flow_pins(serial_obj_t *obj)
{
if (obj->pin_rts != NC) {
int rts_function = pinmap_function(obj->pin_rts, PinMap_UART_RTS);
if (cy_reserve_io_pin(obj->pin_rts)) {
error("Serial RTS pin reservation conflict.");
}
pin_function(obj->pin_rts, rts_function);
}
if (obj->pin_cts != NC) {
int cts_function = pinmap_function(obj->pin_cts, PinMap_UART_CTS);
if (cy_reserve_io_pin(obj->pin_cts)) {
error("Serial CTS pin reservation conflict.");
}
pin_function(obj->pin_cts, cts_function);
}
}
/*
* Initializes and enables UART/SCB.
*/
static void serial_init_peripheral(serial_obj_t *obj)
{
cy_stc_scb_uart_config_t uart_config = default_uart_config;
uart_config.dataWidth = obj->data_width;
uart_config.parity = obj->parity;
uart_config.stopBits = obj->stop_bits;
uart_config.enableCts = (obj->pin_cts != NC);
Cy_SCB_UART_Init(obj->base, &uart_config, NULL);
Cy_SCB_UART_Enable(obj->base);
}
#if DEVICE_SLEEP && DEVICE_LPTICKER && SERIAL_PM_CALLBACK_ENABLED
static cy_en_syspm_status_t serial_pm_callback(cy_stc_syspm_callback_params_t *params)
{
serial_obj_t *obj = (serial_obj_t *)params->context;
cy_en_syspm_status_t status = CY_SYSPM_FAIL;
switch (params->mode) {
case CY_SYSPM_CHECK_READY:
/* If all data elements are transmitted from the TX FIFO and
* shifter and the RX FIFO is empty: the UART is ready to enter
* Deep Sleep mode.
*/
if (Cy_SCB_UART_IsTxComplete(obj->base)) {
if (0UL == Cy_SCB_UART_GetNumInRxFifo(obj->base)) {
/* Disable the UART. The transmitter stops driving the
* lines and the receiver stops receiving data until
* the UART is enabled.
* This happens when the device failed to enter Deep
* Sleep or it is awaken from Deep Sleep mode.
*/
Cy_SCB_UART_Disable(obj->base, NULL);
status = CY_SYSPM_SUCCESS;
}
}
break;
case CY_SYSPM_CHECK_FAIL:
/* Enable the UART to operate */
Cy_SCB_UART_Enable(obj->base);
status = CY_SYSPM_SUCCESS;
break;
case CY_SYSPM_BEFORE_TRANSITION:
status = CY_SYSPM_SUCCESS;
break;
case CY_SYSPM_AFTER_TRANSITION:
/* Enable the UART to operate */
Cy_SCB_UART_Enable(obj->base);
status = CY_SYSPM_SUCCESS;
break;
default:
break;
}
return status;
}
#endif // DEVICE_SLEEP && DEVICE_LPTICKER
void serial_init(serial_t *obj_in, PinName tx, PinName rx)
{
serial_obj_t *obj = OBJ_P(obj_in);
bool is_stdio = (tx == CY_STDIO_UART_TX) || (rx == CY_STDIO_UART_RX);
if (is_stdio && stdio_uart_inited) {
memcpy(obj_in, &stdio_uart, sizeof(serial_t));
return;
}
{
uint32_t uart = pinmap_peripheral(tx, PinMap_UART_TX);
uart = pinmap_merge(uart, pinmap_peripheral(rx, PinMap_UART_RX));
if (uart != (uint32_t)NC) {
obj->base = (CySCB_Type*)uart;
obj->serial_id = ((UARTName)uart - UART_0) / (UART_1 - UART_0);
obj->pin_tx = tx;
obj->pin_rx = rx;
obj->clock = CY_PIN_CLOCK(pinmap_function(tx, PinMap_UART_TX));
obj->div_num = CY_INVALID_DIVIDER;
obj->data_width = 8;
obj->stop_bits = CY_SCB_UART_STOP_BITS_1;
obj->parity = CY_SCB_UART_PARITY_NONE;
obj->pin_rts = NC;
obj->pin_cts = NC;
serial_init_clock(obj, UART_DEFAULT_BAUDRATE);
serial_init_peripheral(obj);
//Cy_GPIO_Write(Cy_GPIO_PortToAddr(CY_PORT(P13_6)), CY_PIN(P13_6), 1);
serial_init_pins(obj);
//Cy_GPIO_Write(Cy_GPIO_PortToAddr(CY_PORT(P13_6)), CY_PIN(P13_6), 0);
#if DEVICE_SLEEP && DEVICE_LPTICKER && SERIAL_PM_CALLBACK_ENABLED
obj->pm_callback_handler.callback = serial_pm_callback;
obj->pm_callback_handler.type = CY_SYSPM_DEEPSLEEP;
obj->pm_callback_handler.skipMode = 0;
obj->pm_callback_handler.callbackParams = &obj->pm_callback_params;
obj->pm_callback_params.base = obj->base;
obj->pm_callback_params.context = obj;
if (!Cy_SysPm_RegisterCallback(&obj->pm_callback_handler)) {
error("PM callback registration failed!");
}
#endif // DEVICE_SLEEP && DEVICE_LPTICKER
if (is_stdio) {
memcpy(&stdio_uart, obj_in, sizeof(serial_t));
stdio_uart_inited = true;
}
} else {
error("Serial pinout mismatch. Requested pins Rx and Tx can't be used for the same Serial communication.");
}
}
}
void serial_baud(serial_t *obj_in, int baudrate)
{
serial_obj_t *obj = OBJ_P(obj_in);
Cy_SCB_UART_Disable(obj->base, NULL);
serial_init_clock(obj, baudrate);
Cy_SCB_UART_Enable(obj->base);
}
void serial_format(serial_t *obj_in, int data_bits, SerialParity parity, int stop_bits)
{
serial_obj_t *obj = OBJ_P(obj_in);
if ((data_bits >= 5) && (data_bits <= 9)) {
obj->data_width = data_bits;
}
switch (parity) {
case ParityNone:
obj->parity = CY_SCB_UART_PARITY_NONE;
break;
case ParityOdd:
obj->parity = CY_SCB_UART_PARITY_ODD;
break;
case ParityEven:
obj->parity = CY_SCB_UART_PARITY_EVEN;
break;
case ParityForced1:
case ParityForced0:
MBED_ASSERT("Serial parity mode not supported!");
break;
}
switch (stop_bits) {
case 1:
obj->stop_bits = CY_SCB_UART_STOP_BITS_1;
break;
case 2:
obj->stop_bits = CY_SCB_UART_STOP_BITS_2;
break;
case 3:
obj->stop_bits = CY_SCB_UART_STOP_BITS_3;
break;
case 4:
obj->stop_bits = CY_SCB_UART_STOP_BITS_4;
break;
}
Cy_SCB_UART_Disable(obj->base, NULL);
serial_init_peripheral(obj);
}
void serial_putc(serial_t *obj_in, int c)
{
serial_obj_t *obj = OBJ_P(obj_in);
while (!serial_writable(obj_in)) {
// empty
}
Cy_SCB_UART_Put(obj->base, c);
}
int serial_getc(serial_t *obj_in)
{
serial_obj_t *obj = OBJ_P(obj_in);
while (!serial_readable(obj_in)) {
// empty
}
return Cy_SCB_UART_Get(obj->base);
}
int serial_readable(serial_t *obj_in)
{
serial_obj_t *obj = OBJ_P(obj_in);
return Cy_SCB_GetNumInRxFifo(obj->base) != 0;
}
int serial_writable(serial_t *obj_in)
{
serial_obj_t *obj = OBJ_P(obj_in);
return Cy_SCB_GetNumInTxFifo(obj->base) != Cy_SCB_GetFifoSize(obj->base);
}
void serial_clear(serial_t *obj_in)
{
serial_obj_t *obj = OBJ_P(obj_in);
Cy_SCB_UART_Disable(obj->base, NULL);
Cy_SCB_ClearTxFifo(obj->base);
Cy_SCB_ClearRxFifo(obj->base);
serial_init_peripheral(obj);
}
void serial_break_set(serial_t *obj_in)
{
serial_obj_t *obj = OBJ_P(obj_in);
/* Cypress SCB does not support transmitting break directly.
* We emulate functionality by switching TX pin to GPIO mode.
*/
GPIO_PRT_Type *port_tx = Cy_GPIO_PortToAddr(CY_PORT(obj->pin_tx));
Cy_GPIO_Pin_FastInit(port_tx, CY_PIN(obj->pin_tx), CY_GPIO_DM_STRONG_IN_OFF, 0, HSIOM_SEL_GPIO);
Cy_GPIO_Write(port_tx, CY_PIN(obj->pin_tx), 0);
}
void serial_break_clear(serial_t *obj_in)
{
serial_obj_t *obj = OBJ_P(obj_in);
/* Connect TX pin back to SCB, see a comment in serial_break_set() above */
GPIO_PRT_Type *port_tx = Cy_GPIO_PortToAddr(CY_PORT(obj->pin_tx));
int tx_function = pinmap_function(obj->pin_tx, PinMap_UART_TX);
Cy_GPIO_Pin_FastInit(port_tx, CY_PIN(obj->pin_tx), CY_GPIO_DM_STRONG_IN_OFF, 0, CY_PIN_HSIOM(tx_function));
}
void serial_set_flow_control(serial_t *obj_in, FlowControl type, PinName rxflow, PinName txflow)
{
serial_obj_t *obj = OBJ_P(obj_in);
Cy_SCB_UART_Disable(obj->base, NULL);
switch (type) {
case FlowControlNone:
obj->pin_rts = NC;
obj->pin_cts = NC;
break;
case FlowControlRTS:
obj->pin_rts = rxflow;
obj->pin_cts = NC;
break;
case FlowControlCTS:
obj->pin_rts = NC;
obj->pin_cts = txflow;
break;
case FlowControlRTSCTS:
obj->pin_rts = rxflow;
obj->pin_cts = txflow;
break;
}
serial_init_peripheral(obj);
serial_init_flow_pins(obj);
}
#if DEVICE_SERIAL_ASYNCH
void serial_irq_handler(serial_t *obj_in, uart_irq_handler handler, uint32_t id)
{
serial_obj_t *obj = OBJ_P(obj_in);
irq_info_t *info = &irq_info[obj->serial_id];
if (info->irqn != unconnected_IRQn) {
NVIC_DisableIRQ(info->irqn);
}
info->handler = handler;
info->id_arg = id;
serial_irq_setup_channel(obj);
}
void serial_irq_set(serial_t *obj_in, SerialIrq irq, uint32_t enable)
{
serial_obj_t *obj = OBJ_P(obj_in);
switch (irq) {
case RxIrq:
if (enable) {
Cy_SCB_SetRxInterruptMask(obj->base, CY_SCB_RX_INTR_NOT_EMPTY);
} else {
Cy_SCB_SetRxInterruptMask(obj->base, 0);
}
break;
case TxIrq:
if (enable) {
Cy_SCB_SetTxInterruptMask(obj->base, CY_SCB_TX_INTR_LEVEL | CY_SCB_UART_TX_DONE);
} else {
Cy_SCB_SetTxInterruptMask(obj->base, 0);
}
break;
}
}
static void serial_finish_tx_asynch(serial_obj_t *obj)
{
Cy_SCB_SetTxInterruptMask(obj->base, 0);
obj->tx_pending = false;
}
static void serial_finish_rx_asynch(serial_obj_t *obj)
{
Cy_SCB_SetRxInterruptMask(obj->base, 0);
obj->rx_pending = false;
}
int serial_tx_asynch(serial_t *obj_in, const void *tx, size_t tx_length, uint8_t tx_width, uint32_t handler, uint32_t event, DMAUsage hint)
{
serial_obj_t *obj = OBJ_P(obj_in);
const uint8_t *p_buf = tx;
(void)tx_width; // Obsolete argument
(void)hint; // At the moment we do not support DAM transfers, so this parameter gets ignored.
if (obj->tx_pending) {
return 0;
}
obj->tx_events = event;
obj->async_handler = (cy_israddress)handler;
if (serial_irq_setup_channel(obj) < 0) {
return 0;
}
// Write as much as possible into the FIFO first.
while ((tx_length > 0) && Cy_SCB_UART_Put(obj->base, *p_buf)) {
++p_buf;
--tx_length;
}
if (tx_length > 0) {
obj_in->tx_buff.buffer = (void *)p_buf;
obj_in->tx_buff.length = tx_length;
obj_in->tx_buff.pos = 0;
obj->tx_pending = true;
// Enable interrupts to complete transmission.
Cy_SCB_SetTxInterruptMask(obj->base, CY_SCB_TX_INTR_LEVEL | CY_SCB_UART_TX_DONE);
} else {
// Enable interrupt to signal completing of the transmission.
Cy_SCB_SetTxInterruptMask(obj->base, CY_SCB_UART_TX_DONE);
}
return tx_length;
}
void serial_rx_asynch(serial_t *obj_in, void *rx, size_t rx_length, uint8_t rx_width, uint32_t handler, uint32_t event, uint8_t char_match, DMAUsage hint)
{
serial_obj_t *obj = OBJ_P(obj_in);
(void)rx_width; // Obsolete argument
(void)hint; // At the moment we do not support DAM transfers, so this parameter gets ignored.
if (obj->rx_pending || (rx_length == 0)) {
return;
}
obj_in->char_match = char_match;
obj_in->char_found = false;
obj->rx_events = event;
obj_in->rx_buff.buffer = rx;
obj_in->rx_buff.length = rx_length;
obj_in->rx_buff.pos = 0;
obj->async_handler = (cy_israddress)handler;
if (serial_irq_setup_channel(obj) < 0) {
return;
}
obj->rx_pending = true;
// Enable interrupts to start receiving.
Cy_SCB_SetRxInterruptMask(obj->base, CY_SCB_UART_RX_INTR_MASK & ~CY_SCB_RX_INTR_UART_BREAK_DETECT);
}
uint8_t serial_tx_active(serial_t *obj)
{
return obj->serial.tx_pending;
}
uint8_t serial_rx_active(serial_t *obj)
{
return obj->serial.rx_pending;
}
int serial_irq_handler_asynch(serial_t *obj_in)
{
uint32_t cur_events = 0;
uint32_t tx_status;
uint32_t rx_status;
serial_obj_t *obj = OBJ_P(obj_in);
rx_status = Cy_SCB_GetRxInterruptStatusMasked(obj->base);
tx_status = Cy_SCB_GetTxInterruptStatusMasked(obj->base);
if (tx_status & CY_SCB_TX_INTR_LEVEL) {
// FIFO has space available for more TX
uint8_t *ptr = obj_in->tx_buff.buffer;
ptr += obj_in->tx_buff.pos;
while ((obj_in->tx_buff.pos < obj_in->tx_buff.length) &&
Cy_SCB_UART_Put(obj->base, *ptr)) {
++ptr;
++(obj_in->tx_buff.pos);
}
if (obj_in->tx_buff.pos == obj_in->tx_buff.length) {
// No more bytes to follow; check to see if we need to signal completion.
if (obj->tx_events & SERIAL_EVENT_TX_COMPLETE) {
// Disable FIFO interrupt as there are no more bytes to follow.
Cy_SCB_SetTxInterruptMask(obj->base, CY_SCB_UART_TX_DONE);
} else {
// Nothing more to do, mark end of transmission.
serial_finish_tx_asynch(obj);
}
}
}
if (tx_status & CY_SCB_TX_INTR_UART_DONE) {
// Mark end of the transmission.
serial_finish_tx_asynch(obj);
cur_events |= SERIAL_EVENT_TX_COMPLETE & obj->tx_events;
}
Cy_SCB_ClearTxInterrupt(obj->base, tx_status);
if (rx_status & CY_SCB_RX_INTR_OVERFLOW) {
cur_events |= SERIAL_EVENT_RX_OVERRUN_ERROR & obj->rx_events;
}
if (rx_status & CY_SCB_RX_INTR_UART_FRAME_ERROR) {
cur_events |= SERIAL_EVENT_RX_FRAMING_ERROR & obj->rx_events;
}
if (rx_status & CY_SCB_RX_INTR_UART_PARITY_ERROR) {
cur_events |= SERIAL_EVENT_RX_PARITY_ERROR & obj->rx_events;
}
if (rx_status & CY_SCB_RX_INTR_LEVEL) {
uint8_t *ptr = obj_in->rx_buff.buffer;
ptr += obj_in->rx_buff.pos;
uint32_t fifo_cnt = Cy_SCB_UART_GetNumInRxFifo(obj->base);
while ((obj_in->rx_buff.pos < obj_in->rx_buff.length) && fifo_cnt) {
uint32_t c = Cy_SCB_UART_Get(obj->base);
*ptr++ = (uint8_t)c;
++(obj_in->rx_buff.pos);
--fifo_cnt;
// Check for character match condition.
if (obj_in->char_match != SERIAL_RESERVED_CHAR_MATCH) {
if (c == obj_in->char_match) {
obj_in->char_found = true;
cur_events |= SERIAL_EVENT_RX_CHARACTER_MATCH & obj->rx_events;
// Clamp RX.
obj_in->rx_buff.length = obj_in->rx_buff.pos;
break;
}
}
}
if (obj_in->rx_buff.pos == obj_in->rx_buff.length) {
cur_events |= SERIAL_EVENT_RX_COMPLETE & obj->rx_events;
}
}
// Any event should end operation.
if (cur_events & SERIAL_EVENT_RX_ALL) {
serial_finish_rx_asynch(obj);
}
Cy_SCB_ClearRxInterrupt(obj->base, rx_status);
return cur_events;
}
void serial_tx_abort_asynch(serial_t *obj_in)
{
serial_obj_t *obj = OBJ_P(obj_in);
serial_finish_tx_asynch(obj);
Cy_SCB_UART_ClearTxFifo(obj->base);
}
void serial_rx_abort_asynch(serial_t *obj_in)
{
serial_obj_t *obj = OBJ_P(obj_in);
serial_finish_rx_asynch(obj);
Cy_SCB_UART_ClearRxFifo(obj->base);
}
#endif // DEVICE_SERIAL_ASYNCH
#endif // DEVICE_SERIAL

View File

@ -0,0 +1,42 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cmsis.h"
#include "device.h"
#include "cy_syspm.h"
#if DEVICE_SLEEP
void hal_sleep(void)
{
Cy_SysPm_Sleep(CY_SYSPM_WAIT_FOR_INTERRUPT);
}
void hal_deepsleep(void)
{
#if DEVICE_LPTICKER
if(CY_SYSPM_SUCCESS == Cy_SysPm_DeepSleep(CY_SYSPM_WAIT_FOR_INTERRUPT)) {
// Have to make sure PLL clock is restored before continuing.
// FLL clock is not used in basic configuration.
while(!Cy_SysClk_PllLocked(1)) {
// Just wait here.
}
}
#endif
}
#endif // DEVICE_SLEEP

View File

@ -0,0 +1,566 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cmsis.h"
#include "mbed_assert.h"
#include "mbed_error.h"
#include "mbed_debug.h"
#include "PeripheralPins.h"
#include "pinmap.h"
#include "spi_api.h"
#include "psoc6_utils.h"
#include "drivers/peripheral/sysclk/cy_sysclk.h"
#include "drivers/peripheral/gpio/cy_gpio.h"
#include "drivers/peripheral/scb/cy_scb_spi.h"
#include "drivers/peripheral/sysint/cy_sysint.h"
#define SPI_DEFAULT_SPEED 100000
#define NUM_SPI_PORTS 8
#define SPI_DEFAULT_IRQ_PRIORITY 3
#define SPI_OVERSAMPLE 4 /* 4..16 */
// Default timeout in milliseconds.
#define SPI_DEFAULT_TIMEOUT 1000
#define PENDING_NONE 0
#define PENDING_RX 1
#define PENDING_TX 2
#define PENDING_TX_RX 3
static const cy_stc_scb_spi_config_t default_spi_config = {
.spiMode = CY_SCB_SPI_MASTER,
.subMode = CY_SCB_SPI_MOTOROLA,
.sclkMode = CY_SCB_SPI_CPHA0_CPOL0,
.oversample = SPI_OVERSAMPLE,
.rxDataWidth = 8,
.txDataWidth = 8,
.enableMsbFirst = true,
.enableFreeRunSclk = false,
.enableInputFilter = false,
.enableMisoLateSample = false,
.enableTransferSeperation = false,
.enableWakeFromSleep = false,
.ssPolarity = CY_SCB_SPI_ACTIVE_LOW,
.rxFifoTriggerLevel = 0,
.rxFifoIntEnableMask = 0,
.txFifoTriggerLevel = 0,
.txFifoIntEnableMask = 0,
.masterSlaveIntEnableMask = 0
};
typedef struct spi_s spi_obj_t;
#if DEVICE_SPI_ASYNCH
#define OBJ_P(in) (&(in->spi))
#else
#define OBJ_P(in) (in)
#endif
#if DEVICE_SPI_ASYNCH
static IRQn_Type spi_irq_allocate_channel(spi_obj_t *obj)
{
#if defined (TARGET_MCU_PSOC6_M0)
obj->cm0p_irq_src = scb_0_interrupt_IRQn + obj->spi_id;
return cy_m0_nvic_allocate_channel(CY_SERIAL_IRQN_ID + obj->spi_id);
#else
return (IRQn_Type)(ioss_interrupts_gpio_0_IRQn + obj->spi_id);
#endif // M0
}
static void spi_irq_release_channel(IRQn_Type channel, uint32_t spi_id)
{
#if defined (TARGET_MCU_PSOC6_M0)
cy_m0_nvic_release_channel(channel, CY_SERIAL_IRQN_ID + spi_id);
#endif //M0
}
static int spi_irq_setup_channel(spi_obj_t *obj)
{
cy_stc_sysint_t irq_config;
if (obj->irqn == unconnected_IRQn) {
IRQn_Type irqn = spi_irq_allocate_channel(obj);
if (irqn < 0) {
return (-1);
}
// Configure NVIC
irq_config.intrPriority = SPI_DEFAULT_IRQ_PRIORITY;
irq_config.intrSrc = irqn;
#if defined (TARGET_MCU_PSOC6_M0)
irq_config.cm0pSrc = obj->cm0p_irq_src;
#endif
if (Cy_SysInt_Init(&irq_config, (cy_israddress)(obj->handler)) != CY_SYSINT_SUCCESS) {
return(-1);
}
obj->irqn = irqn;
NVIC_EnableIRQ(irqn);
}
return 0;
}
#endif // DEVICE_SPI_ASYNCH
static int allocate_divider(spi_obj_t *obj)
{
if (obj->div_num == CY_INVALID_DIVIDER) {
obj->div_type = CY_SYSCLK_DIV_16_BIT;
obj->div_num = cy_clk_allocate_divider(CY_SYSCLK_DIV_16_BIT);
}
return (obj->div_num == CY_INVALID_DIVIDER)? -1 : 0;
}
/*
* Initializes spi clock for the required speed
*/
static cy_en_sysclk_status_t spi_init_clock(spi_obj_t *obj, uint32_t frequency)
{
cy_en_sysclk_status_t status = CY_SYSCLK_INVALID_STATE;
uint32_t div_value;
if (obj->div_num == CY_INVALID_DIVIDER) {
if (allocate_divider(obj) < 0) {
error("spi: cannot allocate clock divider.");
return CY_SYSCLK_INVALID_STATE;
}
}
// Set up proper frequency; round up the divider so the frequency is not higher than specified.
div_value = (CY_CLK_PERICLK_FREQ_HZ + frequency *(SPI_OVERSAMPLE - 1)) / frequency / SPI_OVERSAMPLE;
obj->clk_frequency = CY_CLK_PERICLK_FREQ_HZ / div_value / SPI_OVERSAMPLE;
Cy_SysClk_PeriphDisableDivider(obj->div_type, obj->div_num);
if (Cy_SysClk_PeriphSetDivider(obj->div_type, obj->div_num, div_value) != CY_SYSCLK_SUCCESS) {
obj->div_num = CY_INVALID_DIVIDER;
}
Cy_SysClk_PeriphEnableDivider(obj->div_type, obj->div_num);
if (obj->div_num != CY_INVALID_DIVIDER) {
status = Cy_SysClk_PeriphAssignDivider(obj->clock, obj->div_type, obj->div_num);
if (status != CY_SYSCLK_SUCCESS) {
error("spi: cannot assign clock divider.");
return status;
}
}
return CY_SYSCLK_SUCCESS;
}
/*
* Initializes i/o pins for spi.
*/
static void spi_init_pins(spi_obj_t *obj)
{
bool conflict = false;
conflict = cy_reserve_io_pin(obj->pin_sclk);
if (!conflict) {
pin_function(obj->pin_sclk, pinmap_function(obj->pin_sclk, PinMap_SPI_SCLK));
}
if (obj->pin_mosi != NC) {
if (!cy_reserve_io_pin(obj->pin_mosi)) {
pin_function(obj->pin_mosi, pinmap_function(obj->pin_mosi, PinMap_SPI_MOSI));
} else {
conflict = true;
}
}
if (obj->pin_miso != NC) {
if (!cy_reserve_io_pin(obj->pin_miso)) {
pin_function(obj->pin_miso, pinmap_function(obj->pin_miso, PinMap_SPI_MISO));
} else {
conflict = true;
}
}
if (obj->pin_ssel != NC) {
if (!cy_reserve_io_pin(obj->pin_ssel)) {
pin_function(obj->pin_ssel, pinmap_function(obj->pin_ssel, PinMap_SPI_SSEL));
} else {
conflict = true;
}
}
if (conflict) {
error("SPI pin reservation conflict.");
}
// Pin configuration in PinMap defaults to Master mode; revert for Slave.
if (obj->ms_mode == CY_SCB_SPI_SLAVE) {
pin_mode(obj->pin_sclk, PullNone);
pin_mode(obj->pin_mosi, PullNone);
pin_mode(obj->pin_miso, PushPull);
pin_mode(obj->pin_ssel, PullNone);
}
}
/*
* Initializes and enables SPI/SCB.
*/
static void spi_init_peripheral(spi_obj_t *obj)
{
cy_stc_scb_spi_config_t spi_config = default_spi_config;
spi_config.spiMode = obj->ms_mode;
spi_config.sclkMode = obj->clk_mode;
spi_config.rxDataWidth = obj->data_bits;
spi_config.txDataWidth = obj->data_bits;
Cy_SCB_SPI_Init(obj->base, &spi_config, &obj->context);
Cy_SCB_SPI_Enable(obj->base);
}
/* Callback function to handle into and out of deep sleep state transitions.
*
*/
#if DEVICE_SLEEP && DEVICE_LOWPOWERTIMER
static cy_en_syspm_status_t spi_pm_callback(cy_stc_syspm_callback_params_t *callback_params)
{
cy_stc_syspm_callback_params_t params = *callback_params;
spi_obj_t *obj = (spi_obj_t *)params.context;
params.context = &obj->context;
return Cy_SCB_SPI_DeepSleepCallback(&params);
}
#endif // DEVICE_SLEEP && DEVICE_LOWPOWERTIMER
void spi_init(spi_t *obj_in, PinName mosi, PinName miso, PinName sclk, PinName ssel)
{
spi_obj_t *obj = OBJ_P(obj_in);
uint32_t spi = (uint32_t)NC;
en_clk_dst_t clock;
if (mosi != NC) {
spi = pinmap_merge(spi, pinmap_peripheral(mosi, PinMap_SPI_MOSI));
clock = CY_PIN_CLOCK(pinmap_function(mosi, PinMap_SPI_MOSI));
}
if (miso != NC) {
spi = pinmap_merge(spi, pinmap_peripheral(miso, PinMap_SPI_MISO));
clock = CY_PIN_CLOCK(pinmap_function(miso, PinMap_SPI_MISO));
}
if (sclk != NC) {
spi = pinmap_merge(spi, pinmap_peripheral(sclk, PinMap_SPI_SCLK));
clock = CY_PIN_CLOCK(pinmap_function(sclk, PinMap_SPI_SCLK));
}
if (ssel != NC) {
spi = pinmap_merge(spi, pinmap_peripheral(ssel, PinMap_SPI_SSEL));
clock = CY_PIN_CLOCK(pinmap_function(ssel, PinMap_SPI_SSEL));
}
if (spi != (uint32_t)NC) {
obj->base = (CySCB_Type*)spi;
obj->spi_id = ((SPIName)spi - SPI_0) / (SPI_1 - SPI_0);
obj->pin_mosi = mosi;
obj->pin_miso = miso;
obj->pin_sclk = sclk;
obj->pin_ssel = ssel;
obj->data_bits = 8;
obj->clock = clock;
obj->div_num = CY_INVALID_DIVIDER;
obj->ms_mode = CY_SCB_SPI_MASTER;
#if DEVICE_SPI_ASYNCH
obj->pending = PENDING_NONE;
obj->events = 0;
obj->tx_buffer = NULL;
obj->tx_buffer_size = 0;
obj->rx_buffer = NULL;
obj->rx_buffer_size = 0;
#endif // DEVICE_SPI_ASYNCH
spi_init_clock(obj, SPI_DEFAULT_SPEED);
spi_init_pins(obj);
spi_init_peripheral(obj);
#if DEVICE_SLEEP && DEVICE_LOWPOWERTIMER
obj->pm_callback_handler.callback = spi_pm_callback;
obj->pm_callback_handler.type = CY_SYSPM_DEEPSLEEP;
obj->pm_callback_handler.skipMode = 0;
obj->pm_callback_handler.callbackParams = &obj->pm_callback_params;
obj->pm_callback_params.base = obj->base;
obj->pm_callback_params.context = obj;
if (!Cy_SysPm_RegisterCallback(&obj->pm_callback_handler)) {
error("PM callback registration failed!");
}
#endif // DEVICE_SLEEP && DEVICE_LOWPOWERTIMER
} else {
error("SPI pinout mismatch. Requested Rx and Tx pins can't be used for the same SPI communication.");
}
}
void spi_format(spi_t *obj_in, int bits, int mode, int slave)
{
spi_obj_t *obj = OBJ_P(obj_in);
cy_en_scb_spi_mode_t new_mode = slave? CY_SCB_SPI_SLAVE : CY_SCB_SPI_MASTER;
if ((bits < 4) || (bits > 16)) return;
Cy_SCB_SPI_Disable(obj->base, &obj->context);
obj->data_bits = bits;
obj->clk_mode = (cy_en_scb_spi_sclk_mode_t)(mode & 0x3);
if (obj->ms_mode != new_mode) {
obj->ms_mode = new_mode;
spi_init_pins(obj);
}
spi_init_peripheral(obj);
}
void spi_frequency(spi_t *obj_in, int hz)
{
spi_obj_t *obj = OBJ_P(obj_in);
Cy_SCB_SPI_Disable(obj->base, &obj->context);
spi_init_clock(obj, hz);
Cy_SCB_SPI_Enable(obj->base);
}
int spi_master_write(spi_t *obj_in, int value)
{
spi_obj_t *obj = OBJ_P(obj_in);
if (obj->ms_mode == CY_SCB_SPI_MASTER) {
while (spi_busy(obj_in)) {
// wait for the device to become ready
}
Cy_SCB_SPI_Write(obj->base, value);
while (!Cy_SCB_SPI_IsTxComplete(obj->base)) {
// wait for the transmission to complete
}
return Cy_SCB_SPI_Read(obj->base);
} else {
return (int)CY_SCB_SPI_RX_NO_DATA;
}
}
int spi_master_block_write(spi_t *obj_in, const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length, char write_fill)
{
spi_obj_t *obj = OBJ_P(obj_in);
int trans_length = 0;
int rx_count = 0;
int tx_count = 0;
uint8_t tx_byte = (uint8_t)write_fill;
if (obj->ms_mode != CY_SCB_SPI_MASTER) {
return 0;
}
// Make sure no leftovers from previous transactions.
Cy_SCB_SPI_ClearRxFifo(obj->base);
// Calculate transaction length,
trans_length = (tx_length > rx_length)? tx_length : rx_length;
// get first byte to transmit.
if (tx_count < tx_length) {
tx_byte = *tx_buffer++;
}
// Send required number of bytes.
while (tx_count < trans_length) {
if (Cy_SCB_SPI_Write(obj->base, tx_byte)) {
++tx_count;
// Get next byte to transfer.
if (tx_count < tx_length) {
tx_byte = *tx_buffer++;
} else {
tx_byte = (uint8_t)write_fill;
}
}
// If we have bytes to receive check the rx fifo.
if (rx_count < rx_length) {
if (Cy_SCB_SPI_GetNumInRxFifo(obj->base) > 0) {
*rx_buffer++ = (char)Cy_SCB_SPI_Read(obj->base);
++rx_count;
}
}
}
// Wait for tx fifo to empty while reading received bytes.
while (!Cy_SCB_SPI_IsTxComplete(obj->base)) {
if ((rx_count < rx_length) && (Cy_SCB_SPI_GetNumInRxFifo(obj->base) > 0)) {
*rx_buffer++ = (char)Cy_SCB_SPI_Read(obj->base);
++rx_count;
}
}
// Read any remaining bytes from the fifo.
while (rx_count < rx_length) {
*rx_buffer++ = (char)Cy_SCB_SPI_Read(obj->base);
++rx_count;
}
// Clean up if we have read less bytes than available.
Cy_SCB_SPI_ClearRxFifo(obj->base);
return trans_length;
}
int spi_slave_receive(spi_t *obj_in)
{
spi_obj_t *obj = OBJ_P(obj_in);
if (obj->ms_mode == CY_SCB_SPI_SLAVE) {
return Cy_SCB_SPI_GetNumInRxFifo(obj->base);
} else {
return 0;
}
}
int spi_slave_read(spi_t *obj_in)
{
spi_obj_t *obj = OBJ_P(obj_in);
if (obj->ms_mode == CY_SCB_SPI_SLAVE) {
while (Cy_SCB_SPI_GetNumInRxFifo(obj->base) == 0) {
// Wait for data.
}
return Cy_SCB_SPI_GetNumInRxFifo(obj->base);
} else {
return (int)CY_SCB_SPI_RX_NO_DATA;
}
}
void spi_slave_write(spi_t *obj_in, int value)
{
spi_obj_t *obj = OBJ_P(obj_in);
if (obj->ms_mode == CY_SCB_SPI_SLAVE) {
while ((Cy_SCB_SPI_GetTxFifoStatus(obj->base) & CY_SCB_SPI_TX_NOT_FULL) == 0) {
// Wait for a place available in a fifo.
}
Cy_SCB_SPI_Write(obj->base, value);
}
}
int spi_busy(spi_t *obj)
{
return !Cy_SCB_SPI_IsTxComplete(OBJ_P(obj)->base);
}
uint8_t spi_get_module(spi_t *obj_in)
{
return (uint8_t) OBJ_P(obj_in)->spi_id;
}
#if DEVICE_SPI_ASYNCH
void spi_master_transfer(spi_t *obj_in,
const void *tx,
size_t tx_length,
void *rx,
size_t rx_length,
uint8_t bit_width,
uint32_t handler,
uint32_t event,
DMAUsage hint)
{
spi_obj_t *obj = OBJ_P(obj_in);
(void)hint; // At the moment we do not support DAM transfers, so this parameter gets ignored.
if (obj->pending != PENDING_NONE) {
return;
}
// Validate buffer parameters.
if (((obj->data_bits <= 8) && (bit_width != 8)) || ((obj->data_bits > 8) && (bit_width != 16))) {
error("spi: buffer configurations does not match device configuration");
return;
}
obj->events = event;
obj->handler = handler;
if (spi_irq_setup_channel(obj) < 0) {
return;
}
if (tx_length > rx_length) {
if (rx_length > 0) {
// I) write + read, II) write only
obj->pending = PENDING_TX_RX;
obj->rx_buffer = NULL;
obj->tx_buffer = (bit_width == 8)?
(void*)(((uint8_t*)tx) + rx_length) :
(void*)(((uint16_t*)tx) + rx_length);
obj->tx_buffer_size = tx_length - rx_length;
Cy_SCB_SPI_Transfer(obj->base, (void*)tx, rx, rx_length, &obj->context);
} else {
// I) write only.
obj->pending = PENDING_TX;
obj->rx_buffer = NULL;
obj->tx_buffer = NULL;
Cy_SCB_SPI_Transfer(obj->base, (void*)tx, NULL, tx_length, &obj->context);
}
} else if (rx_length > tx_length) {
if (tx_length > 0) {
// I) write + read, II) read only
obj->pending = PENDING_TX_RX;
obj->rx_buffer = (bit_width == 8)?
(void*)(((uint8_t*)rx) + tx_length) :
(void*)(((uint16_t*)rx) + tx_length);
obj->rx_buffer_size = rx_length - tx_length;
obj->tx_buffer = NULL;
Cy_SCB_SPI_Transfer(obj->base, (void*)tx, rx, tx_length, &obj->context);
} else {
// I) read only.
obj->pending = PENDING_RX;
obj->rx_buffer = NULL;
obj->tx_buffer = NULL;
Cy_SCB_SPI_Transfer(obj->base, NULL, rx, rx_length, &obj->context);
}
} else {
// Rx and Tx of the same size
// I) write + read.
obj->pending = PENDING_TX_RX;
obj->rx_buffer = NULL;
obj->tx_buffer = NULL;
Cy_SCB_SPI_Transfer(obj->base, (void*)tx, rx, tx_length, &obj->context);
}
}
uint32_t spi_irq_handler_asynch(spi_t *obj_in)
{
spi_obj_t *obj = OBJ_P(obj_in);
uint32_t event = 0;
void *buf;
// Process actual interrupt.
Cy_SCB_SPI_Interrupt(obj->base, &obj->context);
if (obj->context.status & CY_SCB_SPI_TRANSFER_OVERFLOW) {
event = SPI_EVENT_RX_OVERFLOW;
} else if (obj->context.status & (CY_SCB_SPI_SLAVE_TRANSFER_ERR | CY_SCB_SPI_TRANSFER_OVERFLOW)) {
event = SPI_EVENT_ERROR;
} else if (0 == (obj->context.status & CY_SCB_SPI_TRANSFER_ACTIVE)) {
// Check to see if the second transfer phase needs to be started.
MBED_ASSERT(!(obj->tx_buffer && obj->rx_buffer));
if (obj->tx_buffer) {
obj->pending = PENDING_TX;
buf = obj->tx_buffer;
obj->tx_buffer = NULL;
Cy_SCB_SPI_Transfer(obj->base, buf, NULL, obj->tx_buffer_size, &obj->context);
} else if (obj->rx_buffer) {
obj->pending = PENDING_RX;
buf = obj->rx_buffer;
obj->rx_buffer = NULL;
Cy_SCB_SPI_Transfer(obj->base, NULL, buf, obj->rx_buffer_size, &obj->context);
} else {
event = SPI_EVENT_COMPLETE;
obj->pending = PENDING_NONE;
}
}
return event & obj->events;
}
uint8_t spi_active(spi_t *obj_in)
{
spi_obj_t *obj = OBJ_P(obj_in);
return (obj->pending != PENDING_NONE);
}
void spi_abort_asynch(spi_t *obj_in)
{
spi_obj_t *obj = OBJ_P(obj_in);
Cy_SCB_SPI_AbortTransfer(obj->base, &obj->context);
}
#endif // DEVICE_ASYNCH

View File

@ -0,0 +1,215 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stddef.h>
#include <limits.h>
#include "device.h"
#include "PeripheralNames.h"
#include "us_ticker_api.h"
#include "mbed_error.h"
#include "psoc6_utils.h"
#include "drivers/peripheral/sysint/cy_sysint.h"
#include "drivers/peripheral/sysclk/cy_sysclk.h"
#include "drivers/peripheral/tcpwm/cy_tcpwm_counter.h"
#include "drivers/peripheral/syspm/cy_syspm.h"
/** Each CPU core in PSoC6 needs its own usec timer.
** Although each of TCPWM timers have two compare registers,
** it has only one interrupt line, so we need to allocate
** two TCPWM counters for the purpose of us_ticker
**/
#if defined(TARGET_MCU_PSOC6_M0)
#define TICKER_COUNTER_UNIT TCPWM0
#define TICKER_COUNTER_NUM 0
#define TICKER_COUNTER_INTERRUPT_SOURCE tcpwm_0_interrupts_0_IRQn
#define TICKER_COUNTER_NVIC_IRQN CY_M0_CORE_IRQ_CHANNEL_US_TICKER
#define TICKER_COUNTER_INTERRUPT_PRIORITY 3
#define TICKER_CLOCK_DIVIDER_NUM 0
#elif defined(TARGET_MCU_PSOC6_M4)
#define TICKER_COUNTER_UNIT TCPWM0
#define TICKER_COUNTER_NUM 1
#define TICKER_COUNTER_INTERRUPT_SOURCE tcpwm_0_interrupts_1_IRQn
#define TICKER_COUNTER_NVIC_IRQN TICKER_COUNTER_INTERRUPT_SOURCE
#define TICKER_COUNTER_INTERRUPT_PRIORITY 6
#define TICKER_CLOCK_DIVIDER_NUM 1
#else
#error "Unknown MCU type."
#endif
static const ticker_info_t us_ticker_info = {
.frequency = 1000000UL,
.bits = 32,
};
static const cy_stc_sysint_t us_ticker_sysint_cfg = {
.intrSrc = TICKER_COUNTER_NVIC_IRQN,
#if defined(TARGET_MCU_PSOC6_M0)
.cm0pSrc = TICKER_COUNTER_INTERRUPT_SOURCE,
#endif
.intrPriority = TICKER_COUNTER_INTERRUPT_PRIORITY
};
static int us_ticker_inited = 0;
static const cy_stc_tcpwm_counter_config_t cy_counter_config = {
.period = 0xFFFFFFFFUL,
.clockPrescaler = CY_TCPWM_COUNTER_PRESCALER_DIVBY_1,
.runMode = CY_TCPWM_COUNTER_CONTINUOUS,
.countDirection = CY_TCPWM_COUNTER_COUNT_UP,
.compareOrCapture = CY_TCPWM_COUNTER_MODE_COMPARE,
.enableCompareSwap = false,
.interruptSources = CY_TCPWM_INT_ON_CC,
.countInputMode = CY_TCPWM_INPUT_LEVEL,
.countInput = CY_TCPWM_INPUT_1
};
// PM callback to be executed when exiting deep sleep.
static cy_en_syspm_status_t ticker_pm_callback(cy_stc_syspm_callback_params_t *callbackParams);
static cy_stc_syspm_callback_params_t ticker_pm_callback_params = {
.base = TICKER_COUNTER_UNIT
};
static cy_stc_syspm_callback_t ticker_pm_callback_handler = {
.callback = ticker_pm_callback,
.type = CY_SYSPM_DEEPSLEEP,
.skipMode = CY_SYSPM_SKIP_CHECK_READY | CY_SYSPM_SKIP_CHECK_FAIL | CY_SYSPM_SKIP_BEFORE_TRANSITION,
.callbackParams = &ticker_pm_callback_params
};
/*
* Callback handler to restart the timer after deep sleep.
*/
static cy_en_syspm_status_t ticker_pm_callback(cy_stc_syspm_callback_params_t *params)
{
if (params->mode == CY_SYSPM_AFTER_TRANSITION) {
Cy_TCPWM_Counter_Enable(TICKER_COUNTER_UNIT, TICKER_COUNTER_NUM);
Cy_TCPWM_TriggerStart(TICKER_COUNTER_UNIT, 1UL << TICKER_COUNTER_NUM);
}
return CY_SYSPM_SUCCESS;
}
/*
* Interrupt handler.
*/
static void local_irq_handler(void)
{
us_ticker_clear_interrupt();
us_ticker_disable_interrupt();
us_ticker_irq_handler();
}
void us_ticker_init(void)
{
us_ticker_disable_interrupt();
us_ticker_clear_interrupt();
if (us_ticker_inited)
return;
us_ticker_inited = 1;
// Configure the clock, us_ticker 1 MHz from PCLK 50 MHz
Cy_SysClk_PeriphAssignDivider(PCLK_TCPWM0_CLOCKS0 + TICKER_COUNTER_NUM, CY_SYSCLK_DIV_8_BIT, TICKER_CLOCK_DIVIDER_NUM);
Cy_SysClk_PeriphSetDivider(CY_SYSCLK_DIV_8_BIT, TICKER_CLOCK_DIVIDER_NUM, (CY_CLK_PERICLK_FREQ_HZ / 1000000UL) - 1);
Cy_SysClk_PeriphEnableDivider(CY_SYSCLK_DIV_8_BIT, TICKER_CLOCK_DIVIDER_NUM);
/*
Configure the counter
*/
Cy_TCPWM_Counter_Init(TICKER_COUNTER_UNIT, TICKER_COUNTER_NUM, &cy_counter_config);
Cy_TCPWM_Counter_Enable(TICKER_COUNTER_UNIT, TICKER_COUNTER_NUM);
if (!Cy_SysPm_RegisterCallback(&ticker_pm_callback_handler)) {
error("PM callback registration failed!");
}
Cy_TCPWM_TriggerStart(TICKER_COUNTER_UNIT, 1UL << TICKER_COUNTER_NUM);
#if defined (TARGET_MCU_PSOC6_M0)
if (cy_m0_nvic_reserve_channel(TICKER_COUNTER_NVIC_IRQN, CY_US_TICKER_IRQN_ID) == (IRQn_Type)(-1)) {
error("Microsecond ticker NVIC channel reservation conflict.");
}
#endif //
Cy_SysInt_Init(&us_ticker_sysint_cfg, local_irq_handler);
}
void us_ticker_free(void)
{
us_ticker_disable_interrupt();
Cy_TCPWM_Counter_Disable(TICKER_COUNTER_UNIT, TICKER_COUNTER_NUM);
Cy_SysPm_UnregisterCallback(&ticker_pm_callback_handler);
#if defined (TARGET_MCU_PSOC6_M0)
cy_m0_nvic_release_channel(TICKER_COUNTER_NVIC_IRQN, CY_US_TICKER_IRQN_ID);
#endif //
us_ticker_inited = 0;
}
uint32_t us_ticker_read(void)
{
if (!us_ticker_inited)
us_ticker_init();
return Cy_TCPWM_Counter_GetCounter(TICKER_COUNTER_UNIT, TICKER_COUNTER_NUM);
}
void us_ticker_set_interrupt(timestamp_t timestamp)
{
uint32_t current_ts = Cy_TCPWM_Counter_GetCounter(TICKER_COUNTER_UNIT, TICKER_COUNTER_NUM);
uint32_t delta = timestamp - current_ts;
if (!us_ticker_inited)
us_ticker_init();
// Set new output compare value
if ((delta < 2) || (delta > (uint32_t)(-3))) {
timestamp = current_ts + 2;
}
Cy_TCPWM_Counter_SetCompare0(TICKER_COUNTER_UNIT, TICKER_COUNTER_NUM, timestamp);
// Enable int
NVIC_EnableIRQ(TICKER_COUNTER_NVIC_IRQN);
}
void us_ticker_disable_interrupt(void)
{
NVIC_DisableIRQ(TICKER_COUNTER_NVIC_IRQN);
}
void us_ticker_clear_interrupt(void)
{
Cy_TCPWM_ClearInterrupt(TICKER_COUNTER_UNIT, TICKER_COUNTER_NUM, CY_TCPWM_INT_ON_CC);
}
void us_ticker_fire_interrupt(void)
{
NVIC_EnableIRQ(TICKER_COUNTER_NVIC_IRQN);
NVIC_SetPendingIRQ(TICKER_COUNTER_NVIC_IRQN);
}
const ticker_info_t* us_ticker_get_info(void)
{
return &us_ticker_info;
}