mirror of https://github.com/ARMmbed/mbed-os.git
PSOC6: reuse FUTURE_SEQUANA porting layer
Copy the porting layer from TARGET_PSOC6_FUTURE to TARGET_PSOC6. This commit is intended to make the history and changes applied easier to follow. ipcpipe_transport.c, ipcpipe_transport.h, rpc_api.h, rpc_defs.h are excluded (not used by Cypress port). PeripheralNames.h is moved to BSP layer introduced in subsequent commits (the peripheral names and count are board-specific).pull/9481/head
parent
6b0658dd30
commit
cd98eb619a
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* mbed Microcontroller Library
|
||||
* Copyright (c) 2017-2018 Future Electronics
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include "device.h"
|
||||
#include "mbed_error.h"
|
||||
#include "lp_ticker_api.h"
|
||||
#include "device/drivers/peripheral/mcwdt/cy_mcwdt.h"
|
||||
#include "device/drivers/peripheral/sysint/cy_sysint.h"
|
||||
#include "psoc6_utils.h"
|
||||
|
||||
#if DEVICE_LPTICKER
|
||||
|
||||
/*
|
||||
* Low Power Timer API on PSoC6 uses MCWD0 timer0 to implement functionality.
|
||||
*/
|
||||
|
||||
#if defined(TARGET_MCU_PSOC6_M0)
|
||||
#define LPT_MCWDT_UNIT MCWDT_STRUCT0
|
||||
#define LPT_INTERRUPT_PRIORITY 3
|
||||
#define LPT_INTERRUPT_SOURCE srss_interrupt_mcwdt_0_IRQn
|
||||
#else
|
||||
#define LPT_MCWDT_UNIT MCWDT_STRUCT1
|
||||
#define LPT_INTERRUPT_PRIORITY 6
|
||||
#define LPT_INTERRUPT_SOURCE srss_interrupt_mcwdt_1_IRQn
|
||||
#endif
|
||||
#define LPT_MCWDT_DELAY_WAIT 0 // Recommended value is 93, but then we fail function execution time test.
|
||||
|
||||
static const ticker_info_t lp_ticker_info = {
|
||||
.frequency = CY_CLK_WCO_FREQ_HZ,
|
||||
.bits = 16,
|
||||
};
|
||||
|
||||
static bool lpt_init_done = false;
|
||||
// Timer h/w configuration.
|
||||
static cy_stc_mcwdt_config_t config = {
|
||||
.c0Match = 0,
|
||||
.c1Match = 0,
|
||||
.c0Mode = CY_MCWDT_MODE_INT,
|
||||
.c1Mode = CY_MCWDT_MODE_NONE,
|
||||
.c2ToggleBit = 0,
|
||||
.c2Mode = CY_MCWDT_MODE_NONE,
|
||||
.c0ClearOnMatch = false,
|
||||
.c1ClearOnMatch = false,
|
||||
.c0c1Cascade = false,
|
||||
.c1c2Cascade = false
|
||||
};
|
||||
|
||||
// Interrupt configuration.
|
||||
static cy_stc_sysint_t lpt_sysint_config = {
|
||||
#if defined(TARGET_MCU_PSOC6_M0)
|
||||
.intrSrc = (IRQn_Type)(-1),
|
||||
.cm0pSrc = LPT_INTERRUPT_SOURCE,
|
||||
#else
|
||||
.intrSrc = LPT_INTERRUPT_SOURCE,
|
||||
#endif
|
||||
.intrPriority = LPT_INTERRUPT_PRIORITY
|
||||
};
|
||||
|
||||
|
||||
void lp_ticker_init(void)
|
||||
{
|
||||
lp_ticker_disable_interrupt();
|
||||
lp_ticker_clear_interrupt();
|
||||
|
||||
if (lpt_init_done) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TARGET_MCU_PSOC6_M0
|
||||
// Allocate NVIC channel.
|
||||
lpt_sysint_config.intrSrc = cy_m0_nvic_allocate_channel(CY_LP_TICKER_IRQN_ID);
|
||||
if (lpt_sysint_config.intrSrc == (IRQn_Type)(-1)) {
|
||||
// No free NVIC channel.
|
||||
error("LP_TICKER NVIC channel allocation failed.");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
Cy_MCWDT_Init(LPT_MCWDT_UNIT, &config);
|
||||
Cy_SysInt_Init(&lpt_sysint_config, lp_ticker_irq_handler);
|
||||
NVIC_EnableIRQ(lpt_sysint_config.intrSrc);
|
||||
Cy_MCWDT_Enable(LPT_MCWDT_UNIT, CY_MCWDT_CTR0, LPT_MCWDT_DELAY_WAIT);
|
||||
lpt_init_done = true;
|
||||
}
|
||||
|
||||
void lp_ticker_free(void)
|
||||
{
|
||||
NVIC_DisableIRQ(lpt_sysint_config.intrSrc);
|
||||
Cy_MCWDT_Disable(LPT_MCWDT_UNIT, CY_MCWDT_CTR0, LPT_MCWDT_DELAY_WAIT);
|
||||
#ifdef TARGET_MCU_PSOC6_M0
|
||||
cy_m0_nvic_release_channel(CY_LP_TICKER_IRQN_ID, lpt_sysint_config.intrSrc);
|
||||
lpt_sysint_config.intrSrc = (IRQn_Type)(-1);
|
||||
#endif
|
||||
lpt_init_done = 0;
|
||||
}
|
||||
|
||||
uint32_t lp_ticker_read(void)
|
||||
{
|
||||
return Cy_MCWDT_GetCount(LPT_MCWDT_UNIT, CY_MCWDT_COUNTER0);
|
||||
}
|
||||
|
||||
void lp_ticker_set_interrupt(timestamp_t timestamp)
|
||||
{
|
||||
uint16_t delay;
|
||||
uint16_t current = Cy_MCWDT_GetCount(LPT_MCWDT_UNIT, CY_MCWDT_COUNTER0);
|
||||
uint16_t new_ts = (uint16_t)timestamp;
|
||||
delay = new_ts - current;
|
||||
// Make sure the event is set for the future. Mbed internally will not schedule
|
||||
// delays longer than 0x7000, so too large delay means it should occur already.
|
||||
// MCWDT has internal delay of about 1.5 LF clock ticks, so this is the minimum
|
||||
// that we can schedule.
|
||||
if ((delay < 3) || (delay > (uint16_t)(-3))) {
|
||||
// Cheating a bit here.
|
||||
new_ts = current + 3;
|
||||
}
|
||||
|
||||
// Cypress PDL manual says that valid match range is 1..65535.
|
||||
if (new_ts == 0) {
|
||||
new_ts = 1;
|
||||
}
|
||||
|
||||
// Set up and enable match interrupt.
|
||||
Cy_MCWDT_SetMatch(LPT_MCWDT_UNIT, CY_MCWDT_COUNTER0, new_ts, LPT_MCWDT_DELAY_WAIT);
|
||||
Cy_MCWDT_SetInterruptMask(LPT_MCWDT_UNIT, CY_MCWDT_CTR0);
|
||||
}
|
||||
|
||||
void lp_ticker_disable_interrupt(void)
|
||||
{
|
||||
Cy_MCWDT_SetInterruptMask(LPT_MCWDT_UNIT, 0);
|
||||
}
|
||||
|
||||
void lp_ticker_clear_interrupt(void)
|
||||
{
|
||||
Cy_MCWDT_ClearInterrupt(LPT_MCWDT_UNIT, CY_MCWDT_CTR0);
|
||||
}
|
||||
|
||||
void lp_ticker_fire_interrupt(void)
|
||||
{
|
||||
NVIC_SetPendingIRQ(lpt_sysint_config.intrSrc);
|
||||
}
|
||||
|
||||
const ticker_info_t* lp_ticker_get_info(void)
|
||||
{
|
||||
return &lp_ticker_info;
|
||||
}
|
||||
|
||||
#endif // DEVICE_LPTICKER
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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__)
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* mbed Microcontroller Library
|
||||
* Copyright (c) 2017-2018 Future Electronics
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _PSOC6_UTILS_H_
|
||||
#define _PSOC6_UTILS_H_
|
||||
|
||||
#if defined(__MBED__)
|
||||
|
||||
#include <stdint.h>
|
||||
#include <device.h>
|
||||
#include "drivers/peripheral/gpio/cy_gpio.h"
|
||||
#include "drivers/peripheral/sysclk/cy_sysclk.h"
|
||||
|
||||
#else
|
||||
|
||||
#include "project.h"
|
||||
|
||||
#endif
|
||||
|
||||
#include "PinNamesTypes.h"
|
||||
#include "PinNames.h"
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#define CY_INVALID_DIVIDER 0xFF
|
||||
|
||||
/** \brief Allocates clock divider to be used for a new clock signal.
|
||||
*
|
||||
* \param div_type cy_en_divider_types_t Divider type.
|
||||
* \return uint32_t Divider number (id) or CY_CLK_INVALID_DIVIDER if unavailable.
|
||||
*
|
||||
*/
|
||||
uint32_t cy_clk_allocate_divider(cy_en_divider_types_t div_type);
|
||||
|
||||
/** \brief Reserves clock divider to be used for a new clock signal.
|
||||
*
|
||||
* \param div_type cy_en_divider_types_t Divider type.
|
||||
* \param div_num Divider number to be reserved.
|
||||
* \return uint32_t Divider number (id) or CY_CLK_INVALID_DIVIDER if unavailable.
|
||||
*
|
||||
*/
|
||||
uint32_t cy_clk_reserve_divider(cy_en_divider_types_t div_type, uint32_t div_num);
|
||||
|
||||
/** \brief Releases already reserved clock divider.
|
||||
*
|
||||
* \param div_type cy_en_divider_types_t Divider type.
|
||||
* \param div_num Divider number to be released.
|
||||
*
|
||||
*/
|
||||
void cy_clk_free_divider(cy_en_divider_types_t div_type, uint32_t div_num);
|
||||
|
||||
#ifdef TARGET_MCU_PSOC6_M0
|
||||
|
||||
#include "gpio_irq_api.h"
|
||||
|
||||
/** \brief On PSoC6 M0 core interrupts are routed into NVIC via additional multiplexer.
|
||||
* This function allocates free NVIC channel to be used by particular interrupt.
|
||||
*
|
||||
* \param allocation_id Unique identifier (for debug purposes).
|
||||
* \return IRQ channel allocated or (-1) if no free channel is available.
|
||||
*
|
||||
*/
|
||||
IRQn_Type cy_m0_nvic_allocate_channel(uint32_t allocation_id);
|
||||
|
||||
/** \brief Reserves particular NVIC channel if it is available.
|
||||
*
|
||||
* \param channel IRQn_Type Channel to be reserved.
|
||||
* \param allocation_id uint32_t Identifier.
|
||||
* \return IRQ channel allocated or (-1) if no free channel is available.
|
||||
*
|
||||
*/
|
||||
IRQn_Type cy_m0_nvic_reserve_channel(IRQn_Type channel, uint32_t allocation_id);
|
||||
|
||||
/** \brief Releases NVIC channel.
|
||||
*
|
||||
* \param channel IRQn_Type Channel to be released.
|
||||
* \param allocation_id uint32_t Id used during allocation (for cross check).
|
||||
* \return void
|
||||
*
|
||||
*/
|
||||
void cy_m0_nvic_release_channel(IRQn_Type channel, uint32_t allocation_id);
|
||||
|
||||
#endif /* M0+ core */
|
||||
|
||||
|
||||
/** \brief Request allocation of SCB block.
|
||||
*
|
||||
* \param scb_num uint32_t Id of the SCB block.
|
||||
* \return (0) when OK, (-1) when reservation conflict occurs.
|
||||
*
|
||||
*/
|
||||
int cy_reserve_scb(uint32_t scb_num);
|
||||
|
||||
/** \brief Releases SCB block.
|
||||
*
|
||||
* \param scb_num uint32_t Id of the SCB block.
|
||||
* \return void
|
||||
*
|
||||
*/
|
||||
void cy_free_scb(uint32_t scb_num);
|
||||
|
||||
/** \brief Request allocation of TCPWM block.
|
||||
*
|
||||
* \param tcpwm_num uint32_t Id of the TCPWM block.
|
||||
* \return (0) when OK, (-1) when reservation conflict occurs.
|
||||
*
|
||||
*/
|
||||
int cy_reserve_tcpwm(uint32_t tcpwm_num);
|
||||
|
||||
/** \brief Releases TCPWM block.
|
||||
*
|
||||
* \param tcpwm_num uint32_t Id of the TCPWM block.
|
||||
* \return void
|
||||
*
|
||||
*/
|
||||
void cy_free_tcpwm(uint32_t tcpwm_num);
|
||||
|
||||
/** \brief Request allocation of i/o pin.
|
||||
*
|
||||
* \param pin PinName Id of the pin to allocate.
|
||||
* \return (0) when OK, (-1) when reservation conflict occurs.
|
||||
*
|
||||
*/
|
||||
int cy_reserve_io_pin(PinName pin);
|
||||
|
||||
|
||||
/** \brief Releases i/o pin.
|
||||
*
|
||||
* \param pin PinName Id of the pin.
|
||||
* \return void
|
||||
*
|
||||
*/
|
||||
void cy_free_io_pin(PinName pin);
|
||||
|
||||
/** \brief Initializes shared resource manager.
|
||||
*
|
||||
* \param none.
|
||||
* \return void
|
||||
*
|
||||
*/
|
||||
void cy_srm_initialize(void);
|
||||
|
||||
|
||||
/** \brief Returns board-specific hardware MAC address.
|
||||
*
|
||||
* \param uint8_t *buffer Buffer where address will be returned.
|
||||
* \return void.
|
||||
*
|
||||
*/
|
||||
void cy_get_bd_mac_address(uint8_t* buffer);
|
||||
|
||||
|
||||
/** \brief Determines proper PSoC6 pin drive mode settings.
|
||||
*
|
||||
* \param dir PinDirection Pin direction, in or out.
|
||||
* \param mode PinMode Mbed pin mode.
|
||||
* \return PSoC6 pin drive mode.
|
||||
*
|
||||
*/
|
||||
static inline uint32_t gpio_get_cy_drive_mode(PinDirection dir, PinMode mode)
|
||||
{
|
||||
uint32_t cymode = 0;
|
||||
|
||||
switch (mode) {
|
||||
case PullNone:
|
||||
switch (dir) {
|
||||
case PIN_INPUT:
|
||||
cymode = CY_GPIO_DM_HIGHZ;
|
||||
break;
|
||||
case PIN_OUTPUT:
|
||||
cymode = CY_GPIO_DM_STRONG;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case PushPull:
|
||||
cymode = CY_GPIO_DM_STRONG;
|
||||
break;
|
||||
|
||||
case PullUp:
|
||||
cymode = CY_GPIO_DM_PULLUP;
|
||||
break;
|
||||
case PullDown:
|
||||
cymode = CY_GPIO_DM_PULLDOWN;
|
||||
break;
|
||||
case OpenDrainDriveLow:
|
||||
cymode = CY_GPIO_DM_OD_DRIVESLOW;
|
||||
break;
|
||||
case OpenDrainDriveHigh:
|
||||
cymode = CY_GPIO_DM_OD_DRIVESHIGH;
|
||||
break;
|
||||
case AnalogMode:
|
||||
cymode = CY_GPIO_DM_ANALOG;
|
||||
break;
|
||||
}
|
||||
|
||||
return cymode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#endif // _PSOC6_UTILS_H_
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,823 @@
|
|||
/*
|
||||
* mbed Microcontroller Library
|
||||
* Copyright (c) 2017-2018 Future Electronics
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if DEVICE_SERIAL
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "cmsis.h"
|
||||
#include "mbed_assert.h"
|
||||
#include "mbed_error.h"
|
||||
#include "PeripheralPins.h"
|
||||
#include "pinmap.h"
|
||||
#include "serial_api.h"
|
||||
#include "psoc6_utils.h"
|
||||
|
||||
#include "drivers/peripheral/sysclk/cy_sysclk.h"
|
||||
#include "drivers/peripheral/gpio/cy_gpio.h"
|
||||
#include "drivers/peripheral/scb/cy_scb_uart.h"
|
||||
#include "drivers/peripheral/sysint/cy_sysint.h"
|
||||
|
||||
#define UART_OVERSAMPLE 12
|
||||
#define UART_DEFAULT_BAUDRATE 115200
|
||||
#define NUM_SERIAL_PORTS 8
|
||||
#define SERIAL_DEFAULT_IRQ_PRIORITY 3
|
||||
|
||||
typedef struct serial_s serial_obj_t;
|
||||
#if DEVICE_SERIAL_ASYNCH
|
||||
#define OBJ_P(in) (&(in->serial))
|
||||
#else
|
||||
#define OBJ_P(in) (in)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* NOTE: Cypress PDL high level API implementation of USART doe not
|
||||
* align well with Mbed interface for interrupt-driven serial I/O.
|
||||
* For this reason only low level PDL API is used here.
|
||||
*/
|
||||
|
||||
|
||||
static const cy_stc_scb_uart_config_t default_uart_config = {
|
||||
.uartMode = CY_SCB_UART_STANDARD,
|
||||
.enableMutliProcessorMode = false,
|
||||
.smartCardRetryOnNack = false,
|
||||
.irdaInvertRx = false,
|
||||
.irdaEnableLowPowerReceiver = false,
|
||||
|
||||
.oversample = UART_OVERSAMPLE,
|
||||
|
||||
.enableMsbFirst = false,
|
||||
.dataWidth = 8UL,
|
||||
.parity = CY_SCB_UART_PARITY_NONE,
|
||||
.stopBits = CY_SCB_UART_STOP_BITS_1,
|
||||
.enableInputFilter = false,
|
||||
.breakWidth = 11UL,
|
||||
.dropOnFrameError = false,
|
||||
.dropOnParityError = false,
|
||||
|
||||
.receiverAddress = 0x0UL,
|
||||
.receiverAddressMask = 0x0UL,
|
||||
.acceptAddrInFifo = false,
|
||||
|
||||
.enableCts = false,
|
||||
.ctsPolarity = CY_SCB_UART_ACTIVE_LOW,
|
||||
.rtsRxFifoLevel = 20UL,
|
||||
.rtsPolarity = CY_SCB_UART_ACTIVE_LOW,
|
||||
|
||||
.rxFifoTriggerLevel = 0UL,
|
||||
.rxFifoIntEnableMask = 0x0UL,
|
||||
|
||||
.txFifoTriggerLevel = 0UL,
|
||||
.txFifoIntEnableMask = 0x0UL
|
||||
};
|
||||
|
||||
int stdio_uart_inited = false;
|
||||
serial_t stdio_uart;
|
||||
|
||||
typedef struct irq_info_s {
|
||||
serial_obj_t *serial_obj;
|
||||
uart_irq_handler handler;
|
||||
uint32_t id_arg;
|
||||
IRQn_Type irqn;
|
||||
#if defined (TARGET_MCU_PSOC6_M0)
|
||||
cy_en_intr_t cm0p_irq_src;
|
||||
#endif
|
||||
} irq_info_t;
|
||||
|
||||
static irq_info_t irq_info[NUM_SERIAL_PORTS] = {
|
||||
{NULL, NULL, 0, unconnected_IRQn},
|
||||
{NULL, NULL, 0, unconnected_IRQn},
|
||||
{NULL, NULL, 0, unconnected_IRQn},
|
||||
{NULL, NULL, 0, unconnected_IRQn},
|
||||
{NULL, NULL, 0, unconnected_IRQn},
|
||||
{NULL, NULL, 0, unconnected_IRQn},
|
||||
{NULL, NULL, 0, unconnected_IRQn},
|
||||
{NULL, NULL, 0, unconnected_IRQn}
|
||||
};
|
||||
|
||||
|
||||
static void serial_irq_dispatcher(uint32_t serial_id)
|
||||
{
|
||||
MBED_ASSERT(serial_id < NUM_SERIAL_PORTS);
|
||||
irq_info_t *info = &irq_info[serial_id];
|
||||
serial_obj_t *obj = info->serial_obj;
|
||||
MBED_ASSERT(obj);
|
||||
|
||||
#if DEVICE_SERIAL_ASYNCH
|
||||
if (obj->async_handler) {
|
||||
obj->async_handler();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (Cy_SCB_GetRxInterruptStatusMasked(obj->base) & CY_SCB_RX_INTR_NOT_EMPTY) {
|
||||
info->handler(info->id_arg, RxIrq);
|
||||
Cy_SCB_ClearRxInterrupt(obj->base, CY_SCB_RX_INTR_NOT_EMPTY);
|
||||
}
|
||||
|
||||
if (Cy_SCB_GetTxInterruptStatusMasked(obj->base) & (CY_SCB_TX_INTR_LEVEL | CY_SCB_UART_TX_DONE)) {
|
||||
info->handler(info->id_arg, TxIrq);
|
||||
}
|
||||
}
|
||||
|
||||
static void serial_irq_dispatcher_uart0(void)
|
||||
{
|
||||
serial_irq_dispatcher(0);
|
||||
}
|
||||
|
||||
static void serial_irq_dispatcher_uart1(void)
|
||||
{
|
||||
serial_irq_dispatcher(1);
|
||||
}
|
||||
|
||||
static void serial_irq_dispatcher_uart2(void)
|
||||
{
|
||||
serial_irq_dispatcher(2);
|
||||
}
|
||||
|
||||
static void serial_irq_dispatcher_uart3(void)
|
||||
{
|
||||
serial_irq_dispatcher(3);
|
||||
}
|
||||
|
||||
static void serial_irq_dispatcher_uart4(void)
|
||||
{
|
||||
serial_irq_dispatcher(4);
|
||||
}
|
||||
|
||||
static void serial_irq_dispatcher_uart5(void)
|
||||
{
|
||||
serial_irq_dispatcher(5);
|
||||
}
|
||||
|
||||
void serial_irq_dispatcher_uart6(void)
|
||||
{
|
||||
serial_irq_dispatcher(6);
|
||||
}
|
||||
|
||||
static void serial_irq_dispatcher_uart7(void)
|
||||
{
|
||||
serial_irq_dispatcher(7);
|
||||
}
|
||||
|
||||
|
||||
static void (*irq_dispatcher_table[])(void) = {
|
||||
serial_irq_dispatcher_uart0,
|
||||
serial_irq_dispatcher_uart1,
|
||||
serial_irq_dispatcher_uart2,
|
||||
serial_irq_dispatcher_uart3,
|
||||
serial_irq_dispatcher_uart4,
|
||||
serial_irq_dispatcher_uart5,
|
||||
serial_irq_dispatcher_uart6,
|
||||
serial_irq_dispatcher_uart7
|
||||
};
|
||||
|
||||
|
||||
static IRQn_Type serial_irq_allocate_channel(serial_obj_t *obj)
|
||||
{
|
||||
#if defined (TARGET_MCU_PSOC6_M0)
|
||||
irq_info[obj->serial_id].cm0p_irq_src = scb_0_interrupt_IRQn + obj->serial_id;
|
||||
return cy_m0_nvic_allocate_channel(CY_SERIAL_IRQN_ID + obj->serial_id);
|
||||
#else
|
||||
return (IRQn_Type)(scb_0_interrupt_IRQn + obj->serial_id);
|
||||
#endif // M0
|
||||
}
|
||||
|
||||
static void serial_irq_release_channel(IRQn_Type channel, uint32_t serial_id)
|
||||
{
|
||||
#if defined (TARGET_MCU_PSOC6_M0)
|
||||
cy_m0_nvic_release_channel(channel, CY_SERIAL_IRQN_ID + serial_id);
|
||||
#endif //M0
|
||||
}
|
||||
|
||||
static int serial_irq_setup_channel(serial_obj_t *obj)
|
||||
{
|
||||
cy_stc_sysint_t irq_config;
|
||||
irq_info_t *info = &irq_info[obj->serial_id];
|
||||
|
||||
if (info->irqn == unconnected_IRQn) {
|
||||
IRQn_Type irqn = serial_irq_allocate_channel(obj);
|
||||
if (irqn < 0) {
|
||||
return (-1);
|
||||
}
|
||||
// Configure NVIC
|
||||
irq_config.intrPriority = SERIAL_DEFAULT_IRQ_PRIORITY;
|
||||
irq_config.intrSrc = irqn;
|
||||
#if defined (TARGET_MCU_PSOC6_M0)
|
||||
irq_config.cm0pSrc = info->cm0p_irq_src;
|
||||
#endif
|
||||
if (Cy_SysInt_Init(&irq_config, irq_dispatcher_table[obj->serial_id]) != CY_SYSINT_SUCCESS) {
|
||||
return(-1);
|
||||
}
|
||||
|
||||
info->irqn = irqn;
|
||||
info->serial_obj = obj;
|
||||
NVIC_EnableIRQ(irqn);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates fractional divider value.
|
||||
*/
|
||||
static uint32_t divider_value(uint32_t frequency, uint32_t frac_bits)
|
||||
{
|
||||
/* UARTs use peripheral clock */
|
||||
return ((CY_CLK_PERICLK_FREQ_HZ * (1 << frac_bits)) + (frequency / 2)) / frequency;
|
||||
}
|
||||
|
||||
static cy_en_sysclk_status_t serial_init_clock(serial_obj_t *obj, uint32_t baudrate)
|
||||
{
|
||||
cy_en_sysclk_status_t status = CY_SYSCLK_BAD_PARAM;
|
||||
|
||||
if (obj->div_num == CY_INVALID_DIVIDER) {
|
||||
uint32_t divider_num = cy_clk_allocate_divider(CY_SYSCLK_DIV_16_5_BIT);
|
||||
|
||||
if (divider_num < PERI_DIV_16_5_NR) {
|
||||
/* Assign fractional divider. */
|
||||
status = Cy_SysClk_PeriphAssignDivider(obj->clock, CY_SYSCLK_DIV_16_5_BIT, divider_num);
|
||||
if (status == CY_SYSCLK_SUCCESS) {
|
||||
obj->div_type = CY_SYSCLK_DIV_16_5_BIT;
|
||||
obj->div_num = divider_num;
|
||||
}
|
||||
} else {
|
||||
// Try 16-bit divider.
|
||||
divider_num = cy_clk_allocate_divider(CY_SYSCLK_DIV_16_BIT);
|
||||
if (divider_num < PERI_DIV_16_NR) {
|
||||
/* Assign 16-bit divider. */
|
||||
status = Cy_SysClk_PeriphAssignDivider(obj->clock, CY_SYSCLK_DIV_16_BIT, divider_num);
|
||||
if (status == CY_SYSCLK_SUCCESS) {
|
||||
obj->div_type = CY_SYSCLK_DIV_16_BIT;
|
||||
obj->div_num = divider_num;
|
||||
}
|
||||
} else {
|
||||
error("Serial: cannot assign clock divider.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
status = CY_SYSCLK_SUCCESS;
|
||||
}
|
||||
|
||||
if (status == CY_SYSCLK_SUCCESS) {
|
||||
/* Set baud rate */
|
||||
if (obj->div_type == CY_SYSCLK_DIV_16_5_BIT) {
|
||||
Cy_SysClk_PeriphDisableDivider(CY_SYSCLK_DIV_16_5_BIT, obj->div_num);
|
||||
uint32_t divider = divider_value(baudrate * UART_OVERSAMPLE, 5);
|
||||
status = Cy_SysClk_PeriphSetFracDivider(CY_SYSCLK_DIV_16_5_BIT,
|
||||
obj->div_num,
|
||||
(divider >> 5) - 1, // integral part
|
||||
divider & 0x1F); // fractional part
|
||||
Cy_SysClk_PeriphEnableDivider(CY_SYSCLK_DIV_16_5_BIT, obj->div_num);
|
||||
} else if (obj->div_type == CY_SYSCLK_DIV_16_BIT) {
|
||||
Cy_SysClk_PeriphDisableDivider(CY_SYSCLK_DIV_16_BIT, obj->div_num);
|
||||
status = Cy_SysClk_PeriphSetDivider(CY_SYSCLK_DIV_16_BIT,
|
||||
obj->div_num,
|
||||
divider_value(baudrate * UART_OVERSAMPLE, 0));
|
||||
Cy_SysClk_PeriphEnableDivider(CY_SYSCLK_DIV_16_BIT, obj->div_num);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes i/o pins for UART tx/rx.
|
||||
*/
|
||||
static void serial_init_pins(serial_obj_t *obj)
|
||||
{
|
||||
int tx_function = pinmap_function(obj->pin_tx, PinMap_UART_TX);
|
||||
int rx_function = pinmap_function(obj->pin_rx, PinMap_UART_RX);
|
||||
if (cy_reserve_io_pin(obj->pin_tx) || cy_reserve_io_pin(obj->pin_rx)) {
|
||||
error("Serial TX/RX pin reservation conflict.");
|
||||
}
|
||||
pin_function(obj->pin_tx, tx_function);
|
||||
pin_function(obj->pin_rx, rx_function);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes i/o pins for UART flow control.
|
||||
*/
|
||||
static void serial_init_flow_pins(serial_obj_t *obj)
|
||||
{
|
||||
if (obj->pin_rts != NC) {
|
||||
int rts_function = pinmap_function(obj->pin_rts, PinMap_UART_RTS);
|
||||
if (cy_reserve_io_pin(obj->pin_rts)) {
|
||||
error("Serial RTS pin reservation conflict.");
|
||||
}
|
||||
pin_function(obj->pin_rts, rts_function);
|
||||
}
|
||||
|
||||
if (obj->pin_cts != NC) {
|
||||
int cts_function = pinmap_function(obj->pin_cts, PinMap_UART_CTS);
|
||||
if (cy_reserve_io_pin(obj->pin_cts)) {
|
||||
error("Serial CTS pin reservation conflict.");
|
||||
}
|
||||
pin_function(obj->pin_cts, cts_function);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initializes and enables UART/SCB.
|
||||
*/
|
||||
static void serial_init_peripheral(serial_obj_t *obj)
|
||||
{
|
||||
cy_stc_scb_uart_config_t uart_config = default_uart_config;
|
||||
|
||||
uart_config.dataWidth = obj->data_width;
|
||||
uart_config.parity = obj->parity;
|
||||
uart_config.stopBits = obj->stop_bits;
|
||||
uart_config.enableCts = (obj->pin_cts != NC);
|
||||
|
||||
Cy_SCB_UART_Init(obj->base, &uart_config, NULL);
|
||||
Cy_SCB_UART_Enable(obj->base);
|
||||
}
|
||||
|
||||
#if DEVICE_SLEEP && DEVICE_LPTICKER && SERIAL_PM_CALLBACK_ENABLED
|
||||
static cy_en_syspm_status_t serial_pm_callback(cy_stc_syspm_callback_params_t *params)
|
||||
{
|
||||
serial_obj_t *obj = (serial_obj_t *)params->context;
|
||||
cy_en_syspm_status_t status = CY_SYSPM_FAIL;
|
||||
|
||||
switch (params->mode) {
|
||||
case CY_SYSPM_CHECK_READY:
|
||||
/* If all data elements are transmitted from the TX FIFO and
|
||||
* shifter and the RX FIFO is empty: the UART is ready to enter
|
||||
* Deep Sleep mode.
|
||||
*/
|
||||
if (Cy_SCB_UART_IsTxComplete(obj->base)) {
|
||||
if (0UL == Cy_SCB_UART_GetNumInRxFifo(obj->base)) {
|
||||
/* Disable the UART. The transmitter stops driving the
|
||||
* lines and the receiver stops receiving data until
|
||||
* the UART is enabled.
|
||||
* This happens when the device failed to enter Deep
|
||||
* Sleep or it is awaken from Deep Sleep mode.
|
||||
*/
|
||||
Cy_SCB_UART_Disable(obj->base, NULL);
|
||||
status = CY_SYSPM_SUCCESS;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case CY_SYSPM_CHECK_FAIL:
|
||||
/* Enable the UART to operate */
|
||||
Cy_SCB_UART_Enable(obj->base);
|
||||
status = CY_SYSPM_SUCCESS;
|
||||
break;
|
||||
|
||||
case CY_SYSPM_BEFORE_TRANSITION:
|
||||
status = CY_SYSPM_SUCCESS;
|
||||
break;
|
||||
|
||||
case CY_SYSPM_AFTER_TRANSITION:
|
||||
/* Enable the UART to operate */
|
||||
Cy_SCB_UART_Enable(obj->base);
|
||||
status = CY_SYSPM_SUCCESS;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
#endif // DEVICE_SLEEP && DEVICE_LPTICKER
|
||||
|
||||
void serial_init(serial_t *obj_in, PinName tx, PinName rx)
|
||||
{
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
bool is_stdio = (tx == CY_STDIO_UART_TX) || (rx == CY_STDIO_UART_RX);
|
||||
|
||||
if (is_stdio && stdio_uart_inited) {
|
||||
memcpy(obj_in, &stdio_uart, sizeof(serial_t));
|
||||
return;
|
||||
}
|
||||
{
|
||||
uint32_t uart = pinmap_peripheral(tx, PinMap_UART_TX);
|
||||
uart = pinmap_merge(uart, pinmap_peripheral(rx, PinMap_UART_RX));
|
||||
if (uart != (uint32_t)NC) {
|
||||
obj->base = (CySCB_Type*)uart;
|
||||
obj->serial_id = ((UARTName)uart - UART_0) / (UART_1 - UART_0);
|
||||
obj->pin_tx = tx;
|
||||
obj->pin_rx = rx;
|
||||
obj->clock = CY_PIN_CLOCK(pinmap_function(tx, PinMap_UART_TX));
|
||||
obj->div_num = CY_INVALID_DIVIDER;
|
||||
obj->data_width = 8;
|
||||
obj->stop_bits = CY_SCB_UART_STOP_BITS_1;
|
||||
obj->parity = CY_SCB_UART_PARITY_NONE;
|
||||
obj->pin_rts = NC;
|
||||
obj->pin_cts = NC;
|
||||
|
||||
serial_init_clock(obj, UART_DEFAULT_BAUDRATE);
|
||||
serial_init_peripheral(obj);
|
||||
//Cy_GPIO_Write(Cy_GPIO_PortToAddr(CY_PORT(P13_6)), CY_PIN(P13_6), 1);
|
||||
serial_init_pins(obj);
|
||||
//Cy_GPIO_Write(Cy_GPIO_PortToAddr(CY_PORT(P13_6)), CY_PIN(P13_6), 0);
|
||||
#if DEVICE_SLEEP && DEVICE_LPTICKER && SERIAL_PM_CALLBACK_ENABLED
|
||||
obj->pm_callback_handler.callback = serial_pm_callback;
|
||||
obj->pm_callback_handler.type = CY_SYSPM_DEEPSLEEP;
|
||||
obj->pm_callback_handler.skipMode = 0;
|
||||
obj->pm_callback_handler.callbackParams = &obj->pm_callback_params;
|
||||
obj->pm_callback_params.base = obj->base;
|
||||
obj->pm_callback_params.context = obj;
|
||||
if (!Cy_SysPm_RegisterCallback(&obj->pm_callback_handler)) {
|
||||
error("PM callback registration failed!");
|
||||
}
|
||||
#endif // DEVICE_SLEEP && DEVICE_LPTICKER
|
||||
if (is_stdio) {
|
||||
memcpy(&stdio_uart, obj_in, sizeof(serial_t));
|
||||
stdio_uart_inited = true;
|
||||
}
|
||||
} else {
|
||||
error("Serial pinout mismatch. Requested pins Rx and Tx can't be used for the same Serial communication.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void serial_baud(serial_t *obj_in, int baudrate)
|
||||
{
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
|
||||
Cy_SCB_UART_Disable(obj->base, NULL);
|
||||
serial_init_clock(obj, baudrate);
|
||||
Cy_SCB_UART_Enable(obj->base);
|
||||
}
|
||||
|
||||
void serial_format(serial_t *obj_in, int data_bits, SerialParity parity, int stop_bits)
|
||||
{
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
|
||||
if ((data_bits >= 5) && (data_bits <= 9)) {
|
||||
obj->data_width = data_bits;
|
||||
}
|
||||
|
||||
switch (parity) {
|
||||
case ParityNone:
|
||||
obj->parity = CY_SCB_UART_PARITY_NONE;
|
||||
break;
|
||||
case ParityOdd:
|
||||
obj->parity = CY_SCB_UART_PARITY_ODD;
|
||||
break;
|
||||
case ParityEven:
|
||||
obj->parity = CY_SCB_UART_PARITY_EVEN;
|
||||
break;
|
||||
case ParityForced1:
|
||||
case ParityForced0:
|
||||
MBED_ASSERT("Serial parity mode not supported!");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (stop_bits) {
|
||||
case 1:
|
||||
obj->stop_bits = CY_SCB_UART_STOP_BITS_1;
|
||||
break;
|
||||
case 2:
|
||||
obj->stop_bits = CY_SCB_UART_STOP_BITS_2;
|
||||
break;
|
||||
case 3:
|
||||
obj->stop_bits = CY_SCB_UART_STOP_BITS_3;
|
||||
break;
|
||||
case 4:
|
||||
obj->stop_bits = CY_SCB_UART_STOP_BITS_4;
|
||||
break;
|
||||
}
|
||||
|
||||
Cy_SCB_UART_Disable(obj->base, NULL);
|
||||
serial_init_peripheral(obj);
|
||||
}
|
||||
|
||||
void serial_putc(serial_t *obj_in, int c)
|
||||
{
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
while (!serial_writable(obj_in)) {
|
||||
// empty
|
||||
}
|
||||
Cy_SCB_UART_Put(obj->base, c);
|
||||
}
|
||||
|
||||
int serial_getc(serial_t *obj_in)
|
||||
{
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
while (!serial_readable(obj_in)) {
|
||||
// empty
|
||||
}
|
||||
return Cy_SCB_UART_Get(obj->base);
|
||||
}
|
||||
|
||||
int serial_readable(serial_t *obj_in)
|
||||
{
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
return Cy_SCB_GetNumInRxFifo(obj->base) != 0;
|
||||
}
|
||||
|
||||
int serial_writable(serial_t *obj_in)
|
||||
{
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
return Cy_SCB_GetNumInTxFifo(obj->base) != Cy_SCB_GetFifoSize(obj->base);
|
||||
}
|
||||
|
||||
void serial_clear(serial_t *obj_in)
|
||||
{
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
|
||||
Cy_SCB_UART_Disable(obj->base, NULL);
|
||||
Cy_SCB_ClearTxFifo(obj->base);
|
||||
Cy_SCB_ClearRxFifo(obj->base);
|
||||
serial_init_peripheral(obj);
|
||||
}
|
||||
|
||||
void serial_break_set(serial_t *obj_in)
|
||||
{
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
|
||||
/* Cypress SCB does not support transmitting break directly.
|
||||
* We emulate functionality by switching TX pin to GPIO mode.
|
||||
*/
|
||||
GPIO_PRT_Type *port_tx = Cy_GPIO_PortToAddr(CY_PORT(obj->pin_tx));
|
||||
Cy_GPIO_Pin_FastInit(port_tx, CY_PIN(obj->pin_tx), CY_GPIO_DM_STRONG_IN_OFF, 0, HSIOM_SEL_GPIO);
|
||||
Cy_GPIO_Write(port_tx, CY_PIN(obj->pin_tx), 0);
|
||||
}
|
||||
|
||||
void serial_break_clear(serial_t *obj_in)
|
||||
{
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
|
||||
/* Connect TX pin back to SCB, see a comment in serial_break_set() above */
|
||||
GPIO_PRT_Type *port_tx = Cy_GPIO_PortToAddr(CY_PORT(obj->pin_tx));
|
||||
int tx_function = pinmap_function(obj->pin_tx, PinMap_UART_TX);
|
||||
Cy_GPIO_Pin_FastInit(port_tx, CY_PIN(obj->pin_tx), CY_GPIO_DM_STRONG_IN_OFF, 0, CY_PIN_HSIOM(tx_function));
|
||||
}
|
||||
|
||||
void serial_set_flow_control(serial_t *obj_in, FlowControl type, PinName rxflow, PinName txflow)
|
||||
{
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
|
||||
Cy_SCB_UART_Disable(obj->base, NULL);
|
||||
|
||||
switch (type) {
|
||||
case FlowControlNone:
|
||||
obj->pin_rts = NC;
|
||||
obj->pin_cts = NC;
|
||||
break;
|
||||
case FlowControlRTS:
|
||||
obj->pin_rts = rxflow;
|
||||
obj->pin_cts = NC;
|
||||
break;
|
||||
case FlowControlCTS:
|
||||
obj->pin_rts = NC;
|
||||
obj->pin_cts = txflow;
|
||||
break;
|
||||
case FlowControlRTSCTS:
|
||||
obj->pin_rts = rxflow;
|
||||
obj->pin_cts = txflow;
|
||||
break;
|
||||
}
|
||||
|
||||
serial_init_peripheral(obj);
|
||||
serial_init_flow_pins(obj);
|
||||
}
|
||||
|
||||
#if DEVICE_SERIAL_ASYNCH
|
||||
|
||||
void serial_irq_handler(serial_t *obj_in, uart_irq_handler handler, uint32_t id)
|
||||
{
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
irq_info_t *info = &irq_info[obj->serial_id];
|
||||
|
||||
if (info->irqn != unconnected_IRQn) {
|
||||
NVIC_DisableIRQ(info->irqn);
|
||||
}
|
||||
info->handler = handler;
|
||||
info->id_arg = id;
|
||||
serial_irq_setup_channel(obj);
|
||||
}
|
||||
|
||||
void serial_irq_set(serial_t *obj_in, SerialIrq irq, uint32_t enable)
|
||||
{
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
|
||||
switch (irq) {
|
||||
case RxIrq:
|
||||
if (enable) {
|
||||
Cy_SCB_SetRxInterruptMask(obj->base, CY_SCB_RX_INTR_NOT_EMPTY);
|
||||
} else {
|
||||
Cy_SCB_SetRxInterruptMask(obj->base, 0);
|
||||
}
|
||||
break;
|
||||
case TxIrq:
|
||||
if (enable) {
|
||||
Cy_SCB_SetTxInterruptMask(obj->base, CY_SCB_TX_INTR_LEVEL | CY_SCB_UART_TX_DONE);
|
||||
} else {
|
||||
Cy_SCB_SetTxInterruptMask(obj->base, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void serial_finish_tx_asynch(serial_obj_t *obj)
|
||||
{
|
||||
Cy_SCB_SetTxInterruptMask(obj->base, 0);
|
||||
obj->tx_pending = false;
|
||||
}
|
||||
|
||||
static void serial_finish_rx_asynch(serial_obj_t *obj)
|
||||
{
|
||||
Cy_SCB_SetRxInterruptMask(obj->base, 0);
|
||||
obj->rx_pending = false;
|
||||
}
|
||||
|
||||
int serial_tx_asynch(serial_t *obj_in, const void *tx, size_t tx_length, uint8_t tx_width, uint32_t handler, uint32_t event, DMAUsage hint)
|
||||
{
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
const uint8_t *p_buf = tx;
|
||||
|
||||
(void)tx_width; // Obsolete argument
|
||||
(void)hint; // At the moment we do not support DAM transfers, so this parameter gets ignored.
|
||||
|
||||
if (obj->tx_pending) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
obj->tx_events = event;
|
||||
obj->async_handler = (cy_israddress)handler;
|
||||
if (serial_irq_setup_channel(obj) < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Write as much as possible into the FIFO first.
|
||||
while ((tx_length > 0) && Cy_SCB_UART_Put(obj->base, *p_buf)) {
|
||||
++p_buf;
|
||||
--tx_length;
|
||||
}
|
||||
|
||||
if (tx_length > 0) {
|
||||
obj_in->tx_buff.buffer = (void *)p_buf;
|
||||
obj_in->tx_buff.length = tx_length;
|
||||
obj_in->tx_buff.pos = 0;
|
||||
obj->tx_pending = true;
|
||||
// Enable interrupts to complete transmission.
|
||||
Cy_SCB_SetTxInterruptMask(obj->base, CY_SCB_TX_INTR_LEVEL | CY_SCB_UART_TX_DONE);
|
||||
|
||||
} else {
|
||||
// Enable interrupt to signal completing of the transmission.
|
||||
Cy_SCB_SetTxInterruptMask(obj->base, CY_SCB_UART_TX_DONE);
|
||||
}
|
||||
return tx_length;
|
||||
}
|
||||
|
||||
void serial_rx_asynch(serial_t *obj_in, void *rx, size_t rx_length, uint8_t rx_width, uint32_t handler, uint32_t event, uint8_t char_match, DMAUsage hint)
|
||||
{
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
|
||||
(void)rx_width; // Obsolete argument
|
||||
(void)hint; // At the moment we do not support DAM transfers, so this parameter gets ignored.
|
||||
|
||||
if (obj->rx_pending || (rx_length == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
obj_in->char_match = char_match;
|
||||
obj_in->char_found = false;
|
||||
obj->rx_events = event;
|
||||
obj_in->rx_buff.buffer = rx;
|
||||
obj_in->rx_buff.length = rx_length;
|
||||
obj_in->rx_buff.pos = 0;
|
||||
obj->async_handler = (cy_israddress)handler;
|
||||
if (serial_irq_setup_channel(obj) < 0) {
|
||||
return;
|
||||
}
|
||||
obj->rx_pending = true;
|
||||
// Enable interrupts to start receiving.
|
||||
Cy_SCB_SetRxInterruptMask(obj->base, CY_SCB_UART_RX_INTR_MASK & ~CY_SCB_RX_INTR_UART_BREAK_DETECT);
|
||||
}
|
||||
|
||||
uint8_t serial_tx_active(serial_t *obj)
|
||||
{
|
||||
return obj->serial.tx_pending;
|
||||
}
|
||||
|
||||
uint8_t serial_rx_active(serial_t *obj)
|
||||
{
|
||||
return obj->serial.rx_pending;
|
||||
}
|
||||
|
||||
int serial_irq_handler_asynch(serial_t *obj_in)
|
||||
{
|
||||
uint32_t cur_events = 0;
|
||||
uint32_t tx_status;
|
||||
uint32_t rx_status;
|
||||
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
|
||||
rx_status = Cy_SCB_GetRxInterruptStatusMasked(obj->base);
|
||||
|
||||
tx_status = Cy_SCB_GetTxInterruptStatusMasked(obj->base);
|
||||
|
||||
|
||||
if (tx_status & CY_SCB_TX_INTR_LEVEL) {
|
||||
// FIFO has space available for more TX
|
||||
uint8_t *ptr = obj_in->tx_buff.buffer;
|
||||
ptr += obj_in->tx_buff.pos;
|
||||
while ((obj_in->tx_buff.pos < obj_in->tx_buff.length) &&
|
||||
Cy_SCB_UART_Put(obj->base, *ptr)) {
|
||||
++ptr;
|
||||
++(obj_in->tx_buff.pos);
|
||||
}
|
||||
if (obj_in->tx_buff.pos == obj_in->tx_buff.length) {
|
||||
// No more bytes to follow; check to see if we need to signal completion.
|
||||
if (obj->tx_events & SERIAL_EVENT_TX_COMPLETE) {
|
||||
// Disable FIFO interrupt as there are no more bytes to follow.
|
||||
Cy_SCB_SetTxInterruptMask(obj->base, CY_SCB_UART_TX_DONE);
|
||||
} else {
|
||||
// Nothing more to do, mark end of transmission.
|
||||
serial_finish_tx_asynch(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tx_status & CY_SCB_TX_INTR_UART_DONE) {
|
||||
// Mark end of the transmission.
|
||||
serial_finish_tx_asynch(obj);
|
||||
cur_events |= SERIAL_EVENT_TX_COMPLETE & obj->tx_events;
|
||||
}
|
||||
|
||||
Cy_SCB_ClearTxInterrupt(obj->base, tx_status);
|
||||
|
||||
if (rx_status & CY_SCB_RX_INTR_OVERFLOW) {
|
||||
cur_events |= SERIAL_EVENT_RX_OVERRUN_ERROR & obj->rx_events;
|
||||
}
|
||||
|
||||
if (rx_status & CY_SCB_RX_INTR_UART_FRAME_ERROR) {
|
||||
cur_events |= SERIAL_EVENT_RX_FRAMING_ERROR & obj->rx_events;
|
||||
}
|
||||
|
||||
if (rx_status & CY_SCB_RX_INTR_UART_PARITY_ERROR) {
|
||||
cur_events |= SERIAL_EVENT_RX_PARITY_ERROR & obj->rx_events;
|
||||
}
|
||||
|
||||
if (rx_status & CY_SCB_RX_INTR_LEVEL) {
|
||||
uint8_t *ptr = obj_in->rx_buff.buffer;
|
||||
ptr += obj_in->rx_buff.pos;
|
||||
uint32_t fifo_cnt = Cy_SCB_UART_GetNumInRxFifo(obj->base);
|
||||
while ((obj_in->rx_buff.pos < obj_in->rx_buff.length) && fifo_cnt) {
|
||||
uint32_t c = Cy_SCB_UART_Get(obj->base);
|
||||
*ptr++ = (uint8_t)c;
|
||||
++(obj_in->rx_buff.pos);
|
||||
--fifo_cnt;
|
||||
// Check for character match condition.
|
||||
if (obj_in->char_match != SERIAL_RESERVED_CHAR_MATCH) {
|
||||
if (c == obj_in->char_match) {
|
||||
obj_in->char_found = true;
|
||||
cur_events |= SERIAL_EVENT_RX_CHARACTER_MATCH & obj->rx_events;
|
||||
// Clamp RX.
|
||||
obj_in->rx_buff.length = obj_in->rx_buff.pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (obj_in->rx_buff.pos == obj_in->rx_buff.length) {
|
||||
cur_events |= SERIAL_EVENT_RX_COMPLETE & obj->rx_events;
|
||||
}
|
||||
}
|
||||
|
||||
// Any event should end operation.
|
||||
if (cur_events & SERIAL_EVENT_RX_ALL) {
|
||||
serial_finish_rx_asynch(obj);
|
||||
}
|
||||
|
||||
Cy_SCB_ClearRxInterrupt(obj->base, rx_status);
|
||||
|
||||
return cur_events;
|
||||
}
|
||||
|
||||
void serial_tx_abort_asynch(serial_t *obj_in)
|
||||
{
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
|
||||
serial_finish_tx_asynch(obj);
|
||||
Cy_SCB_UART_ClearTxFifo(obj->base);
|
||||
}
|
||||
|
||||
void serial_rx_abort_asynch(serial_t *obj_in)
|
||||
{
|
||||
serial_obj_t *obj = OBJ_P(obj_in);
|
||||
|
||||
serial_finish_rx_asynch(obj);
|
||||
Cy_SCB_UART_ClearRxFifo(obj->base);
|
||||
}
|
||||
|
||||
#endif // DEVICE_SERIAL_ASYNCH
|
||||
|
||||
#endif // DEVICE_SERIAL
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* mbed Microcontroller Library
|
||||
* Copyright (c) 2017-2018 Future Electronics
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <limits.h>
|
||||
#include "device.h"
|
||||
#include "PeripheralNames.h"
|
||||
#include "us_ticker_api.h"
|
||||
#include "mbed_error.h"
|
||||
#include "psoc6_utils.h"
|
||||
|
||||
#include "drivers/peripheral/sysint/cy_sysint.h"
|
||||
#include "drivers/peripheral/sysclk/cy_sysclk.h"
|
||||
#include "drivers/peripheral/tcpwm/cy_tcpwm_counter.h"
|
||||
#include "drivers/peripheral/syspm/cy_syspm.h"
|
||||
|
||||
/** Each CPU core in PSoC6 needs its own usec timer.
|
||||
** Although each of TCPWM timers have two compare registers,
|
||||
** it has only one interrupt line, so we need to allocate
|
||||
** two TCPWM counters for the purpose of us_ticker
|
||||
**/
|
||||
|
||||
|
||||
#if defined(TARGET_MCU_PSOC6_M0)
|
||||
|
||||
#define TICKER_COUNTER_UNIT TCPWM0
|
||||
#define TICKER_COUNTER_NUM 0
|
||||
#define TICKER_COUNTER_INTERRUPT_SOURCE tcpwm_0_interrupts_0_IRQn
|
||||
#define TICKER_COUNTER_NVIC_IRQN CY_M0_CORE_IRQ_CHANNEL_US_TICKER
|
||||
#define TICKER_COUNTER_INTERRUPT_PRIORITY 3
|
||||
#define TICKER_CLOCK_DIVIDER_NUM 0
|
||||
|
||||
#elif defined(TARGET_MCU_PSOC6_M4)
|
||||
|
||||
#define TICKER_COUNTER_UNIT TCPWM0
|
||||
#define TICKER_COUNTER_NUM 1
|
||||
#define TICKER_COUNTER_INTERRUPT_SOURCE tcpwm_0_interrupts_1_IRQn
|
||||
#define TICKER_COUNTER_NVIC_IRQN TICKER_COUNTER_INTERRUPT_SOURCE
|
||||
#define TICKER_COUNTER_INTERRUPT_PRIORITY 6
|
||||
#define TICKER_CLOCK_DIVIDER_NUM 1
|
||||
|
||||
#else
|
||||
#error "Unknown MCU type."
|
||||
#endif
|
||||
|
||||
|
||||
static const ticker_info_t us_ticker_info = {
|
||||
.frequency = 1000000UL,
|
||||
.bits = 32,
|
||||
};
|
||||
|
||||
static const cy_stc_sysint_t us_ticker_sysint_cfg = {
|
||||
.intrSrc = TICKER_COUNTER_NVIC_IRQN,
|
||||
#if defined(TARGET_MCU_PSOC6_M0)
|
||||
.cm0pSrc = TICKER_COUNTER_INTERRUPT_SOURCE,
|
||||
#endif
|
||||
.intrPriority = TICKER_COUNTER_INTERRUPT_PRIORITY
|
||||
};
|
||||
|
||||
static int us_ticker_inited = 0;
|
||||
|
||||
static const cy_stc_tcpwm_counter_config_t cy_counter_config = {
|
||||
.period = 0xFFFFFFFFUL,
|
||||
.clockPrescaler = CY_TCPWM_COUNTER_PRESCALER_DIVBY_1,
|
||||
.runMode = CY_TCPWM_COUNTER_CONTINUOUS,
|
||||
.countDirection = CY_TCPWM_COUNTER_COUNT_UP,
|
||||
.compareOrCapture = CY_TCPWM_COUNTER_MODE_COMPARE,
|
||||
.enableCompareSwap = false,
|
||||
.interruptSources = CY_TCPWM_INT_ON_CC,
|
||||
.countInputMode = CY_TCPWM_INPUT_LEVEL,
|
||||
.countInput = CY_TCPWM_INPUT_1
|
||||
};
|
||||
|
||||
// PM callback to be executed when exiting deep sleep.
|
||||
static cy_en_syspm_status_t ticker_pm_callback(cy_stc_syspm_callback_params_t *callbackParams);
|
||||
|
||||
static cy_stc_syspm_callback_params_t ticker_pm_callback_params = {
|
||||
.base = TICKER_COUNTER_UNIT
|
||||
};
|
||||
|
||||
static cy_stc_syspm_callback_t ticker_pm_callback_handler = {
|
||||
.callback = ticker_pm_callback,
|
||||
.type = CY_SYSPM_DEEPSLEEP,
|
||||
.skipMode = CY_SYSPM_SKIP_CHECK_READY | CY_SYSPM_SKIP_CHECK_FAIL | CY_SYSPM_SKIP_BEFORE_TRANSITION,
|
||||
.callbackParams = &ticker_pm_callback_params
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Callback handler to restart the timer after deep sleep.
|
||||
*/
|
||||
static cy_en_syspm_status_t ticker_pm_callback(cy_stc_syspm_callback_params_t *params)
|
||||
{
|
||||
if (params->mode == CY_SYSPM_AFTER_TRANSITION) {
|
||||
Cy_TCPWM_Counter_Enable(TICKER_COUNTER_UNIT, TICKER_COUNTER_NUM);
|
||||
Cy_TCPWM_TriggerStart(TICKER_COUNTER_UNIT, 1UL << TICKER_COUNTER_NUM);
|
||||
}
|
||||
return CY_SYSPM_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt handler.
|
||||
*/
|
||||
static void local_irq_handler(void)
|
||||
{
|
||||
us_ticker_clear_interrupt();
|
||||
us_ticker_disable_interrupt();
|
||||
us_ticker_irq_handler();
|
||||
}
|
||||
|
||||
|
||||
void us_ticker_init(void)
|
||||
{
|
||||
us_ticker_disable_interrupt();
|
||||
us_ticker_clear_interrupt();
|
||||
|
||||
if (us_ticker_inited)
|
||||
return;
|
||||
|
||||
us_ticker_inited = 1;
|
||||
|
||||
// Configure the clock, us_ticker 1 MHz from PCLK 50 MHz
|
||||
Cy_SysClk_PeriphAssignDivider(PCLK_TCPWM0_CLOCKS0 + TICKER_COUNTER_NUM, CY_SYSCLK_DIV_8_BIT, TICKER_CLOCK_DIVIDER_NUM);
|
||||
Cy_SysClk_PeriphSetDivider(CY_SYSCLK_DIV_8_BIT, TICKER_CLOCK_DIVIDER_NUM, (CY_CLK_PERICLK_FREQ_HZ / 1000000UL) - 1);
|
||||
Cy_SysClk_PeriphEnableDivider(CY_SYSCLK_DIV_8_BIT, TICKER_CLOCK_DIVIDER_NUM);
|
||||
|
||||
/*
|
||||
Configure the counter
|
||||
*/
|
||||
|
||||
Cy_TCPWM_Counter_Init(TICKER_COUNTER_UNIT, TICKER_COUNTER_NUM, &cy_counter_config);
|
||||
Cy_TCPWM_Counter_Enable(TICKER_COUNTER_UNIT, TICKER_COUNTER_NUM);
|
||||
if (!Cy_SysPm_RegisterCallback(&ticker_pm_callback_handler)) {
|
||||
error("PM callback registration failed!");
|
||||
}
|
||||
Cy_TCPWM_TriggerStart(TICKER_COUNTER_UNIT, 1UL << TICKER_COUNTER_NUM);
|
||||
|
||||
#if defined (TARGET_MCU_PSOC6_M0)
|
||||
if (cy_m0_nvic_reserve_channel(TICKER_COUNTER_NVIC_IRQN, CY_US_TICKER_IRQN_ID) == (IRQn_Type)(-1)) {
|
||||
error("Microsecond ticker NVIC channel reservation conflict.");
|
||||
}
|
||||
#endif //
|
||||
|
||||
Cy_SysInt_Init(&us_ticker_sysint_cfg, local_irq_handler);
|
||||
}
|
||||
|
||||
void us_ticker_free(void)
|
||||
{
|
||||
us_ticker_disable_interrupt();
|
||||
Cy_TCPWM_Counter_Disable(TICKER_COUNTER_UNIT, TICKER_COUNTER_NUM);
|
||||
Cy_SysPm_UnregisterCallback(&ticker_pm_callback_handler);
|
||||
#if defined (TARGET_MCU_PSOC6_M0)
|
||||
cy_m0_nvic_release_channel(TICKER_COUNTER_NVIC_IRQN, CY_US_TICKER_IRQN_ID);
|
||||
#endif //
|
||||
us_ticker_inited = 0;
|
||||
}
|
||||
|
||||
uint32_t us_ticker_read(void)
|
||||
{
|
||||
if (!us_ticker_inited)
|
||||
us_ticker_init();
|
||||
return Cy_TCPWM_Counter_GetCounter(TICKER_COUNTER_UNIT, TICKER_COUNTER_NUM);
|
||||
}
|
||||
|
||||
void us_ticker_set_interrupt(timestamp_t timestamp)
|
||||
{
|
||||
uint32_t current_ts = Cy_TCPWM_Counter_GetCounter(TICKER_COUNTER_UNIT, TICKER_COUNTER_NUM);
|
||||
uint32_t delta = timestamp - current_ts;
|
||||
|
||||
if (!us_ticker_inited)
|
||||
us_ticker_init();
|
||||
|
||||
// Set new output compare value
|
||||
if ((delta < 2) || (delta > (uint32_t)(-3))) {
|
||||
timestamp = current_ts + 2;
|
||||
}
|
||||
Cy_TCPWM_Counter_SetCompare0(TICKER_COUNTER_UNIT, TICKER_COUNTER_NUM, timestamp);
|
||||
// Enable int
|
||||
NVIC_EnableIRQ(TICKER_COUNTER_NVIC_IRQN);
|
||||
}
|
||||
|
||||
void us_ticker_disable_interrupt(void)
|
||||
{
|
||||
NVIC_DisableIRQ(TICKER_COUNTER_NVIC_IRQN);
|
||||
}
|
||||
|
||||
void us_ticker_clear_interrupt(void)
|
||||
{
|
||||
Cy_TCPWM_ClearInterrupt(TICKER_COUNTER_UNIT, TICKER_COUNTER_NUM, CY_TCPWM_INT_ON_CC);
|
||||
}
|
||||
|
||||
void us_ticker_fire_interrupt(void)
|
||||
{
|
||||
NVIC_EnableIRQ(TICKER_COUNTER_NVIC_IRQN);
|
||||
NVIC_SetPendingIRQ(TICKER_COUNTER_NVIC_IRQN);
|
||||
}
|
||||
|
||||
const ticker_info_t* us_ticker_get_info(void)
|
||||
{
|
||||
return &us_ticker_info;
|
||||
}
|
||||
Loading…
Reference in New Issue