From cd98eb619a0c9de40fe5516c43d4842d9396c46a Mon Sep 17 00:00:00 2001 From: Volodymyr Medvid Date: Fri, 25 Jan 2019 18:10:53 -0800 Subject: [PATCH] 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). --- .../TARGET_PSOC6/PeripheralPins.h | 62 ++ .../TARGET_PSOC6/PinNamesTypes.h | 84 ++ .../TARGET_Cypress/TARGET_PSOC6/PortNames.h | 47 + .../TARGET_PSOC6/analogin_api.c | 171 ++++ .../TARGET_PSOC6/analogout_api.c | 150 ++++ .../TARGET_Cypress/TARGET_PSOC6/flash_api.c | 92 ++ .../TARGET_Cypress/TARGET_PSOC6/gpio_api.c | 99 +++ .../TARGET_PSOC6/gpio_irq_api.c | 282 ++++++ .../TARGET_Cypress/TARGET_PSOC6/gpio_object.h | 85 ++ targets/TARGET_Cypress/TARGET_PSOC6/i2c_api.c | 548 ++++++++++++ .../TARGET_Cypress/TARGET_PSOC6/lp_ticker.c | 162 ++++ .../TARGET_Cypress/TARGET_PSOC6/mbed_rtx.h | 52 ++ targets/TARGET_Cypress/TARGET_PSOC6/objects.h | 227 +++++ targets/TARGET_Cypress/TARGET_PSOC6/pinmap.c | 67 ++ .../TARGET_Cypress/TARGET_PSOC6/port_api.c | 117 +++ .../TARGET_Cypress/TARGET_PSOC6/psoc6_utils.c | 473 ++++++++++ .../TARGET_Cypress/TARGET_PSOC6/psoc6_utils.h | 223 +++++ .../TARGET_Cypress/TARGET_PSOC6/pwmout_api.c | 321 +++++++ targets/TARGET_Cypress/TARGET_PSOC6/rtc_api.c | 159 ++++ .../TARGET_Cypress/TARGET_PSOC6/serial_api.c | 823 ++++++++++++++++++ .../TARGET_Cypress/TARGET_PSOC6/sleep_api.c | 42 + targets/TARGET_Cypress/TARGET_PSOC6/spi_api.c | 566 ++++++++++++ .../TARGET_Cypress/TARGET_PSOC6/us_ticker.c | 215 +++++ 23 files changed, 5067 insertions(+) create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/PeripheralPins.h create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/PinNamesTypes.h create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/PortNames.h create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/analogin_api.c create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/analogout_api.c create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/flash_api.c create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/gpio_api.c create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/gpio_irq_api.c create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/gpio_object.h create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/i2c_api.c create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/lp_ticker.c create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/mbed_rtx.h create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/objects.h create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/pinmap.c create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/port_api.c create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/psoc6_utils.c create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/psoc6_utils.h create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/pwmout_api.c create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/rtc_api.c create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/serial_api.c create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/sleep_api.c create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/spi_api.c create mode 100644 targets/TARGET_Cypress/TARGET_PSOC6/us_ticker.c diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/PeripheralPins.h b/targets/TARGET_Cypress/TARGET_PSOC6/PeripheralPins.h new file mode 100644 index 0000000000..da01a569ea --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/PeripheralPins.h @@ -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 diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/PinNamesTypes.h b/targets/TARGET_Cypress/TARGET_PSOC6/PinNamesTypes.h new file mode 100644 index 0000000000..559e3cc2c3 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/PinNamesTypes.h @@ -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 diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/PortNames.h b/targets/TARGET_Cypress/TARGET_PSOC6/PortNames.h new file mode 100644 index 0000000000..e30039df79 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/PortNames.h @@ -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 diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/analogin_api.c b/targets/TARGET_Cypress/TARGET_PSOC6/analogin_api.c new file mode 100644 index 0000000000..9e57aa551b --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/analogin_api.c @@ -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 + diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/analogout_api.c b/targets/TARGET_Cypress/TARGET_PSOC6/analogout_api.c new file mode 100644 index 0000000000..98be654530 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/analogout_api.c @@ -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 + diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/flash_api.c b/targets/TARGET_Cypress/TARGET_PSOC6/flash_api.c new file mode 100644 index 0000000000..7141757bf0 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/flash_api.c @@ -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 diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/gpio_api.c b/targets/TARGET_Cypress/TARGET_PSOC6/gpio_api.c new file mode 100644 index 0000000000..8f3bc0b074 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/gpio_api.c @@ -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); +} diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/gpio_irq_api.c b/targets/TARGET_Cypress/TARGET_PSOC6/gpio_irq_api.c new file mode 100644 index 0000000000..54baf3e18c --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/gpio_irq_api.c @@ -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); +} + diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/gpio_object.h b/targets/TARGET_Cypress/TARGET_PSOC6/gpio_object.h new file mode 100644 index 0000000000..2fd677f247 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/gpio_object.h @@ -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 diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/i2c_api.c b/targets/TARGET_Cypress/TARGET_PSOC6/i2c_api.c new file mode 100644 index 0000000000..76cc0afda8 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/i2c_api.c @@ -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(¶ms); +} +#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 diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/lp_ticker.c b/targets/TARGET_Cypress/TARGET_PSOC6/lp_ticker.c new file mode 100644 index 0000000000..fc298529d3 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/lp_ticker.c @@ -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 +#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 diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/mbed_rtx.h b/targets/TARGET_Cypress/TARGET_PSOC6/mbed_rtx.h new file mode 100644 index 0000000000..0513b0b92e --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/mbed_rtx.h @@ -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 diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/objects.h b/targets/TARGET_Cypress/TARGET_PSOC6/objects.h new file mode 100644 index 0000000000..0ba07af1f7 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/objects.h @@ -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 diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/pinmap.c b/targets/TARGET_Cypress/TARGET_PSOC6/pinmap.c new file mode 100644 index 0000000000..23fe537aa7 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/pinmap.c @@ -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; + } + } +} diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/port_api.c b/targets/TARGET_Cypress/TARGET_PSOC6/port_api.c new file mode 100644 index 0000000000..5e124dd5b4 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/port_api.c @@ -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; +} diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/psoc6_utils.c b/targets/TARGET_Cypress/TARGET_PSOC6/psoc6_utils.c new file mode 100644 index 0000000000..1ea8352f53 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/psoc6_utils.c @@ -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 = ÷r_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 = ÷r_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 = ÷r_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__) +} + diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/psoc6_utils.h b/targets/TARGET_Cypress/TARGET_PSOC6/psoc6_utils.h new file mode 100644 index 0000000000..78d5a977e7 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/psoc6_utils.h @@ -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 +#include +#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_ diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/pwmout_api.c b/targets/TARGET_Cypress/TARGET_PSOC6/pwmout_api.c new file mode 100644 index 0000000000..d0c25e061e --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/pwmout_api.c @@ -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); +} diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/rtc_api.c b/targets/TARGET_Cypress/TARGET_PSOC6/rtc_api.c new file mode 100644 index 0000000000..4d94c3d514 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/rtc_api.c @@ -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, ×tamp, 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 diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/serial_api.c b/targets/TARGET_Cypress/TARGET_PSOC6/serial_api.c new file mode 100644 index 0000000000..a2b8149684 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/serial_api.c @@ -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 + +#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 diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/sleep_api.c b/targets/TARGET_Cypress/TARGET_PSOC6/sleep_api.c new file mode 100644 index 0000000000..f9f3f5e30c --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/sleep_api.c @@ -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 diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/spi_api.c b/targets/TARGET_Cypress/TARGET_PSOC6/spi_api.c new file mode 100644 index 0000000000..45f71cc148 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/spi_api.c @@ -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(¶ms); +} +#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 diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/us_ticker.c b/targets/TARGET_Cypress/TARGET_PSOC6/us_ticker.c new file mode 100644 index 0000000000..a4f79b4e48 --- /dev/null +++ b/targets/TARGET_Cypress/TARGET_PSOC6/us_ticker.c @@ -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 +#include +#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; +}